跳转到主要内容

为应用插件添加身份验证

Grafana 应用插件允许您捆绑面板和数据源。应用还可以让您在 Grafana 中创建具有复杂功能的自定义页面。

选择身份验证方法

有两种方法可以将身份验证添加到应用插件。以两种方式之一配置您的应用插件以对第三方 API 进行身份验证:

案例使用
您需要使用基本身份验证或 API 密钥来对插件进行身份验证吗?使用数据源代理。
您的 API 使用数据源代理不支持的自定义身份验证方法?使用后端组件。
您的 API 是否通过 HTTP 之外的其他协议进行通信?使用后端组件。

加密机密配置

应用插件有两种存储自定义配置的方法:

  • jsonData
  • secureJsonFields
警告

不要使用 jsonData 配置敏感数据,例如密码、令牌和 API 密钥。如果您需要存储敏感信息,请使用 secureJsonData

secureJsonData 中存储配置

如果您需要存储敏感信息(例如密钥),请使用 secureJsonData 而不是 jsonData。当用户保存应用程序配置时,存储在 secureJsonData 中的密钥会发送到 Grafana 服务器,并在存储之前进行加密。

一旦您加密了安全配置,该配置就无法从浏览器中访问。只有在保存后,您才能通过使用 数据源代理 或通过 后端组件 来访问密钥。

将密钥配置添加到您的应用程序插件

您启动的应用程序插件应该有一个 AppConfig 组件,允许用户配置应用程序。此组件包含存储 apiKeysecureJsonData 的示例代码。您可以在 plugin.meta 组成的 secureJsonFields 中检查 secureJsonData 的属性,secureJsonFields 包含用户配置的键。

以下是代码突出显示内容

  1. secureJsonData 永远不带填充值,无论用户是否已配置。相反,您可以通过检查 secureJsonFields 中键是否为 true 来确定属性是否已被配置。例如

    const { jsonData, secureJsonFields } = plugin.meta;
    const [state, setState] = useState<State>({
    apiUrl: jsonData?.apiUrl || '',
    apiKey: '',
    // check if the key is true or false to determine if it has been configured
    isApiKeySet: Boolean(secureJsonFields?.apiKey),
    });
  2. 您可以通过向 /api/plugins/<pluginId>/settings 端点发送 POST 来更新 secureJsonData

    如果您正在设置 secureJsonData 中的键,那么您应该仅发送用户修改过值的键。发送任何值(包括空字符串)会覆盖现有配置。

    const secureJsonData = apiKey.length > 0 ? { apiKey } : undefined;
    await getBackendSrv().fetch({
    url: `/api/plugins/${pluginId}/settings`,
    method: 'POST',
    data: {
    secureJsonData,
    },
    });

使用数据源代理进行身份验证

一旦用户保存了您应用程序的配置,密钥配置将无法在浏览器中访问。加密的密钥只能在服务器上访问。那么,您如何将它们添加到请求中呢?

Grafana 服务器自带代理,让您可以定义请求模板:代理路由。Grafana 将代理路由发送到服务器,解密密钥以及其他配置,然后在发送请求之前将其添加到请求中。

注意

请确保不要将数据代理与 身份验证代理 混淆。数据代理用于对插件请求进行身份验证,而身份验证代理用于登录 Grafana。

将代理路由添加到您的插件

要转发请求通过数据代理,您需要配置一个或多个 代理路由。代理路由是代理处理的任何传出请求的模板。您可以在 plugin.json 文件中配置代理路由。

  1. 将路由添加到 plugin.json

    src/plugin.json
    "routes": [
    {
    "path": "myRoutePath",
    "url": "https://api.example.com"
    }
    ]
    注意

    每次修改 plugin.json 文件时,您都需要重新构建插件并重启 Grafana 服务器。

  2. 在您的应用程序插件中,请使用来自 @grafana/runtime 包的 getBackendSrv 函数从代理路由获取数据。

    import { getBackendSrv } from '@grafana/runtime';
    import { lastValueFrom } from 'rxjs';

    async function getDataFromApi() {
    const dataProxyUrl = `api/plugin-proxy/${PLUGIN_ID}/myRoutePath`;
    const response = getBackendSrv().fetch({
    url: dataProxyUrl,
    });
    return await lastValueFrom(response);
    }

将动态代理路由添加到您的插件中

在 Grafana 向服务器发送数据代理请求后,数据源代理将解密敏感数据。然后,数据源代理在发出请求之前,将模板变量与解密后的数据一起进行插值。

要将用户自定义配置添加到您的路由中

  • 使用 .JsonData 保存到 jsonData 中的配置。例如,在 jsonData 对象中,projectId 是一个属性的名称

    src/plugin.json
    "routes": [
    {
    "path": "example",
    "url": "https://api.example.com/projects/{{ .JsonData.projectId }}"
    }
    ]
  • 使用 .SecureJsonData 保存到 secureJsonData 中的敏感数据。例如,在 secureJsonData 对象中,使用 .SecureJsonDatapassword 作为属性名称

    src/plugin.json
    "routes": [
    {
    "path": "example",
    "url": "https://{{ .JsonData.username }}:{{ .SecureJsonData.password }}@api.example.com"
    }
    ]

除了添加代理路由的 URL 之外,您还可以添加头、URL 参数和请求数据。

向代理路由添加 HTTP 头

以下是将 namecontent 添加为 HTTP 头的示例

src/plugin.json
"routes": [
{
"path": "example",
"url": "https://api.example.com",
"headers": [
{
"name": "Authorization",
"content": "Bearer {{ .SecureJsonData.apiToken }}"
}
]
}
]

向代理路由添加请求数据

以下是将 usernamepassword 添加到请求数据中的示例

src/plugin.json
"routes": [
{
"path": "example",
"url": "http://api.example.com",
"body": {
"username": "{{ .JsonData.username }}",
"password": "{{ .SecureJsonData.password }}"
}
}
]

应用程序插件中数据代理的限制

  • urlParams 配置在应用程序插件中不受支持。
  • tokenAuth 配置在应用程序插件中不受支持(针对 OAuth 2.0)。

使用后端组件进行认证

虽然数据代理支持大多数 HTTP API 的常用认证方法,但使用代理路由有一些限制

  • 代理路由仅支持 HTTP 或 HTTPS。
  • 代理路由不支持自定义令牌认证。
  • 应用程序的代理路由不支持 urlParams
  • 应用程序的代理路由不支持 tokenAuth

如果这些限制中的任何一项适用于您的插件,则需要在插件中添加后端组件。因为后端组件在服务器上运行,所以它们可以访问解密后的机密,这使得实现自定义认证方法变得容易。

在后端组件中访问机密

解密后的机密在应用程序实例设置的 DecryptedSecureJSONData 字段中可用。

func (a *App) registerRoutes(mux *http.ServeMux) {
// ... other routes
mux.HandleFunc("/test", a.handleMyRequest)
}

func (a *App) handleMyRequest(w http.ResponseWriter, req *http.Request) {
pluginConfig := backend.PluginConfigFromContext(req.Context())
secureJsonData := pluginConfig.AppInstanceSettings.DecryptedSecureJSONData

// Use the decrypted data

w.Header().Add("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"message": "ok}`)); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}

与 cookie 配合使用

您的应用程序插件可以读取 Grafana 传递给应用程序的 cookie。

func (a *App) handleMyRequest(w http.ResponseWriter, req *http.Request) {

cookies := req.Cookies()

// loop through cookies as an example
for _, cookie := range cookies {
log.Printf("cookie: %+v", cookie)
}
// Use the cookies

w.Header().Add("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"message": "ok}`)); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}

转发登录用户的用户头

当启用 send_user_header 时,Grafana 使用 X-Grafana-User 头将用户头传递给插件。