使用grafana auth.generic_oauth方式访问grafana dashboards,记录配置及接口开发过程。以供以后查阅

需求:在自建平台上以认证用户,跳转至grafana,且是已登陆状态。

根据官方文档介绍,有三种方法实现:

  1. 使用API KEY方式;需要解决跨域问题。

  2. 配置打开匿名登陆方式,不符合需求。

  3. 使用auth.generic_oauth方式,需要在自建平台开发统一认证接口。


一、Grafana Oauth配置

  1. 配置认证接口跳转域名:

[server]

# Protocol (http, https, h2, socket)

protocol = http


# The ip address to bind to, empty will bind to all interfaces

http_addr = 10.106.223.203


# The http port to use

http_port = 3000


# The public facing domain name used to access grafana from a browser

domain = 10.106.223.203      # 我这里是测试环境,IP地址或正式域名都可以,认证接口可以访问(重定向)到就行


# Redirect to correct domain if host header does not match domain

# Prevents DNS rebinding attacks

enforce_domain = false


# The full public facing url

root_url = %(protocol)s://%(domain)s:%(http_port)s/         # 如果使用的是正式域名,可能不需要“:%(http_port)s/”端口信息,删掉就行


    2. Grafana Oauth配置

[auth.generic_oauth]

name = OAuth

enabled = true       # 默认是false,改为true。打开oauth认证

allow_sign_up = true

client_id = panda   # 客户端ID,自行决定使用方式。我这里使用client_id去验证自建平台中是否存在这个用户,并以这个用户去登陆Grafana

client_secret = panda@example.com     # 没看到GET或POST请求中有这个参数。字面意思是验证client密码的

scopes = user:email      # 统一认证接口回传的参数域'{"name": "panda", "email": "panda@example.com"}'

#scopes = user:email:org

email_attribute_name = email:primary       # 不知道怎么用

email_attribute_path =

login_attribute_path =

role_attribute_path =

id_token_attribute_name =

# 网络环境访问受限问题处理(职场网络与IDC网络互相隔离)。Grafana http://grafana.domain.com/login/generic_oauth 接口通过浏览器访问auth_url,如果浏览器不能直接通过IP:PORT访问auth_url,配置一个可访问的域名。token_url 和 api_url 是通过Grafana后端server访问,Grafana server和自建平台server服务器在同一个网络域,我就直接配置IP:PORT了。

auth_url = http://auth.yourdomain.com/sso/oauth/authorize/       # 自建平台认证接口。Grafana Oauth接口通过浏览器请求这个接口进行认                                                                                                 # 证,auth.yourdomain.com指向后端10.106.223.203:7000。

token_url = http://10.106.223.203:7000/sso/oauth/token/          # 自建平台用户token生成接口。Grafana Oauth接口通过后端server访问该接口

api_url = http://10.106.223.203:7000/sso/oauth/user/                # 自建平台验证token,并回传用户信息接口。Grafana Oauth接口通过后端                                                                                                  # server访问该接口

allowed_domains =    # 允许回调请求的域名,包括用户email的域名(Required email domain not fulfilled)

#allowed_domains = auth.yourdomain.com,10.106.223.203,example.com

team_ids =

allowed_organizations =

tls_skip_verify_insecure = false

tls_client_cert =

tls_client_key =

tls_client_ca =

    3. 重启Grafana后台server

Grafana Oauth统一认证_oauth

二、自建平台接口。自建平台是django框架

    1. auth_url接口

通过自建平台前端请求 Grafana http://grafana.domain.com/login/generic_oauth 接口时,该接口会通过GET去请求auth_url接口进行认证,请求参数为 {"access_type": online, "state": "vRQngxNgyheoZ1iskXH3zRd_Xezdw1T4RW_ISq59ZzM=", "redirect_uri": "http://10.106.223.203:3000/login/generic_oauth", "response_type": "code", "client_id": "panda", "scope": "user:email"}

auth_url API: 

def grafana_authorize(request):

    for k, v in request.GET.items():

        print k, v

    try:

        grafana_api = GrafanaAPI.objects.get(client_id=request.GET.get('client_id'))

    except GrafanaAPI.DoesNotExist:

        return redirect(reverse_lazy('login'))

    if str(request.GET.get('redirect_uri')) == str(grafana_api.oauth_url) and \

            str(request.GET.get('client_id')) == str(grafana_api.client_id):

        try:

            client_user = User.objects.get(real_name=request.GET.get('client_id'))

            token_instance = RedisToken()

            token = token_instance.generate_token(client_user.email)

        except User.DoesNotExist:

            return redirect("{0}?state={1}&code={2}".format(request.GET.get('redirect_uri'),

                                                            request.GET.get('state'),

                                                            ''))

        else:

            code = {}

            code.setdefault(grafana_api.client_id, token)

            return redirect("{0}?state={1}&code={2}".format(request.GET.get('redirect_uri'),

                                                            request.GET.get('state'),

                                                            json.dumps(code)))

    else:

        return redirect(reverse_lazy('login'))

    2. token_url接口

认证通过后 Grafana http://grafana.domain.com/login/generic_oauth 接口会通过POST去请求token_url接口获取认证token。请求参数{"redirect_uri": "http://10.106.223.203:3000/login/generic_oauth", "code": '{"panda": "ZXlKaGJHY2lPaUprWldaaGRXeDBJaXdpZEhsd0lqb2lTbGRRSW4wOjFsVzkzRzpnSjZVRnJFUVVRbEpONFczQVluaU02TzR5TFk.ZXlKaFkyTnZkVzUwSWpvaWNHRnVaR0ZBWlhoaGJYQnNaUzVqYjIwaUxDSnpkV0lpT2lKVmMyVnlYMVJ2YTJWdUlpd2lhWE56SWpvaVVHRnVSR0VpTENKbGVIQWlPaUkwTXpJd01DSXNJbWxoZENJNk1UWXhPREk0TVRreE9DNHdPVFkzTmpjc0ltRjFaQ0k2SWxWelpYSWlmUToxbFc5M0c6emxBajRid2d4NHdhYzRiY1JUbGt3Snd4MWln.287856af0889755046011669919f1b0050af8b77"}', "grant_type": "authorization_code"}


@csrf_exempt

def grafana_token(request):

    for k, v in request.POST.items():

        print k, v

    if request.method == "POST":

        try:

            code = json.loads(request.POST.get('code'))

            client_id = code.keys()[0]

            token = code.values()[0]

            grafana_api = GrafanaAPI.objects.get(client_id=client_id)

        except GrafanaAPI.DoesNotExist:

            return redirect(reverse_lazy('login'))

        if str(request.POST.get('redirect_uri')) == str(grafana_api.oauth_url):

            token_auth = RedisToken(token=token)

            if token_auth.checkout_token():

                return HttpResponse(json.dumps({'access_token': token,

                                                'token_type': 'Bearer',

                                                'expiry_in': settings.REDIS_TOKEN_EXPIRED_TIME,

                                                'refresh_token': ''}))

    return HttpResponse(json.dumps({'access_token': '',

                                    'token_type': '',

                                    'expiry_in': '',

                                    'refresh_token': ''}))

    3. api_url接口

获取token成功后,Grafana http://grafana.domain.com/login/generic_oauth 接口会通过GET请求api_url接口获取用户信息。请求头中加入token信息

"HTTP_AUTHORIZATION": "Bearer ZXlKaGJHY2lPaUprWldaaGRXeDBJaXdpZEhsd0lqb2lTbGRRSW4wOjFsVzkzRzpnSjZVRnJFUVVRbEpONFczQVluaU02TzR5TFk.ZXlKaFkyTnZkVzUwSWpvaWNHRnVaR0ZBWlhoaGJYQnNaUzVqYjIwaUxDSnpkV0lpT2lKVmMyVnlYMVJ2YTJWdUlpd2lhWE56SWpvaVVHRnVSR0VpTENKbGVIQWlPaUkwTXpJd01DSXNJbWxoZENJNk1UWXhPREk0TVRreE9DNHdPVFkzTmpjc0ltRjFaQ0k2SWxWelpYSWlmUToxbFc5M0c6emxBajRid2d4NHdhYzRiY1JUbGt3Snd4MWln.287856af0889755046011669919f1b0050af8b77"

def grafana_user(request):

    for k, v in request.META.items():

        print k, v

    header_authorization = request.META.get('HTTP_AUTHORIZATION', 'unknown')

    token = header_authorization.split(' ')[1]

    token_auth = RedisToken(token=token)

    try:

        user = User.objects.get(email=token_auth.get_account())

    except User.DoesNotExist:

        return HttpResponse('User not exist.')

    return HttpResponse(json.dumps({'name': user.real_name, 'email': user.email}))

三、测试

直接在Grafana登陆页点 Sign in with OAuth ,如果显示登陆成功并进入Dashboards页面,表示已经成功。Grafana会自动创建用户

Grafana Oauth统一认证_oauth_02