跳到主要内容

为应用程序插件添加身份验证

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

选择一种身份验证方法

有两种方法为应用程序插件添加身份验证。通过以下两种方式之一将您的应用程序插件配置为针对第三方 API 进行身份验证:

情况使用
您需要使用基本认证(Basic Auth)或 API 密钥来验证您的插件吗?使用数据源代理。
您的 API 使用了数据源代理不支持的自定义身份验证方法吗?使用后端组件。
您的 API 是否使用 HTTP 以外的协议进行通信?使用后端组件。

加密秘密配置

应用程序插件有两种存储自定义配置的方式

  • jsonData
  • secureJsonFields
警告

请勿将 jsonData 用于存储敏感数据,例如密码、令牌和 API 密钥。如果您需要存储敏感信息,请改用 secureJsonData

secureJsonData 中存储配置

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

一旦您加密了安全配置,就无法再从浏览器访问该配置。保存秘密后,唯一访问它们的方式是使用数据源代理或通过后端组件

向您的应用程序插件添加秘密配置

您初始化的应用程序插件应该有一个 AppConfig 组件,允许用户配置应用程序。此组件包含将 apiKey 存储在 secureJsonData 中的示例代码。您可以在 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 将代理路由发送到服务器,解密秘密以及其他配置,并在发送请求之前将其添加到请求中。

注意

请确保不要将数据代理与认证代理(auth proxy)混淆。数据代理用于验证插件请求,而认证代理用于登录 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。例如,其中 projectIdjsonData 对象中属性的名称

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

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

除了将 URL 添加到代理路由外,您还可以添加请求头(headers)、URL 参数(URL parameters)和请求体(request body)。

为代理路由添加 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 请求头将用户请求头传递给插件。