为应用插件添加身份验证
Grafana 应用插件允许您捆绑面板和数据源。应用还可以让您在 Grafana 中创建具有复杂功能的自定义页面。
选择身份验证方法▲
有两种方法可以将身份验证添加到应用插件。以两种方式之一配置您的应用插件以对第三方 API 进行身份验证:
案例 | 使用 |
---|---|
您需要使用基本身份验证或 API 密钥来对插件进行身份验证吗? | 使用数据源代理。 |
您的 API 使用数据源代理不支持的自定义身份验证方法? | 使用后端组件。 |
您的 API 是否通过 HTTP 之外的其他协议进行通信? | 使用后端组件。 |
加密机密配置▲
应用插件有两种存储自定义配置的方法:
jsonData
secureJsonFields
不要使用 jsonData
配置敏感数据,例如密码、令牌和 API 密钥。如果您需要存储敏感信息,请使用 secureJsonData
。
在 secureJsonData
中存储配置
如果您需要存储敏感信息(例如密钥),请使用 secureJsonData
而不是 jsonData
。当用户保存应用程序配置时,存储在 secureJsonData
中的密钥会发送到 Grafana 服务器,并在存储之前进行加密。
一旦您加密了安全配置,该配置就无法从浏览器中访问。只有在保存后,您才能通过使用 数据源代理 或通过 后端组件 来访问密钥。
将密钥配置添加到您的应用程序插件
您启动的应用程序插件应该有一个 AppConfig
组件,允许用户配置应用程序。此组件包含存储 apiKey
到 secureJsonData
的示例代码。您可以在 plugin.meta
组成的 secureJsonFields
中检查 secureJsonData
的属性,secureJsonFields
包含用户配置的键。
以下是代码突出显示内容
-
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),
}); -
您可以通过向
/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 文件中配置代理路由。
-
将路由添加到
plugin.json
src/plugin.json"routes": [
{
"path": "myRoutePath",
"url": "https://api.example.com"
}
]注意每次修改
plugin.json
文件时,您都需要重新构建插件并重启 Grafana 服务器。 -
在您的应用程序插件中,请使用来自
@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
对象中,使用.SecureJsonData
将password
作为属性名称src/plugin.json"routes": [
{
"path": "example",
"url": "https://{{ .JsonData.username }}:{{ .SecureJsonData.password }}@api.example.com"
}
]
除了添加代理路由的 URL 之外,您还可以添加头、URL 参数和请求数据。
向代理路由添加 HTTP 头
以下是将 name
和 content
添加为 HTTP 头的示例
"routes": [
{
"path": "example",
"url": "https://api.example.com",
"headers": [
{
"name": "Authorization",
"content": "Bearer {{ .SecureJsonData.apiToken }}"
}
]
}
]
向代理路由添加请求数据
以下是将 username
和 password
添加到请求数据中的示例
"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
头将用户头传递给插件。