superset鉴权集成到自己的系统中,使用自己系统的权限校验接口去做校验,并且控制跳转,思路是把superset用到的flask框架中鉴权模块继承出来并修改相关配置文件使用自定义的鉴权类去做鉴权,因为是java开发之前没有接触过python,主要参考大佬的文章,写的很好,但是摸索python实现过程中还是踩了些坑。
一、整体思路
由于我们自己系统中的鉴权系统是使用token鉴权,而superset使用的是session cookie的方式,由于对python和flask实在不熟,没找到办法将flask原本的鉴权改成token的方式,因此整体思路如下:
1.重写superset login接口,从我们系统跳转到superset的时候将平台token拼接到url后,login接口中调用我们系统中的鉴权并获取用户信息接口,鉴权成功则根据用户信息登录或新建用户(取决于用户是否存在)登录superset,并且跳转到welcome页面,鉴权失败则重定向到我们系统的登录页面,并将我们系统的token作为cookie存入客户端(为了跳转回我们系统使用)。
2.重写logout接口,logout superset的登录状态后调用我们系统销毁token接口,完成后重定向到我们系统的登录页面。
3.重写me接口,每当点进superset页面(包括切换浏览器tag等)的时候会自动调用me接口,因此在me接口里增加调用我们系统鉴权接口的操作,如果失败返回状态码401给前端,前端则会自动重定向到login接口,由于我们重写了login,因此将会最终重定向回我们系统的登录页面。
4.(可选)虽然me接口在最小化、切换浏览器tag等时候都会自动调用,但是第一次进入superset页面的时候并不会调用该接口,因此利用前端我们二次开发增加的获取我们系统菜单列表的接口(用于跳转),增加上述鉴权操作在里面。
二、部分代码
因为拓展的主要代码结构是参考的上述文章中大佬的代码,就不再赘述了,需要可以从大佬帖子的git链接里下载代码,下面贴一下其中views的关键代码
class MyAuthRemoteUserView(AuthRemoteUserView):
# this front-end template should be put under the folder `superset/templates/appbuilder/general/security`
# so that superset could find this templates to render
# login_template = 'appbuilder/general/security/login_db.html'
# title = "My Login"
# this method is going to overwrite
# https://github.com/dpgaspar/Flask-AppBuilder/blob/master/flask_appbuilder/security/views.py#L556
@expose('/login/', methods=['GET', 'POST'])
def login(self):
logger.info('---------------------------------------------My special login...')
# if g.user is not None and g.user.is_authenticated:
# if request.args.get('redirect') is None or request.args.get('redirect') == 'true':
# resp = make_response(redirect(self.appbuilder.get_url_for_index))
# resp.set_cookie('accessToken', request.args.get('accessToken'))
# return resp
# else:
# return {
# 'code':200,
# 'data':'alread login'
# }
# form = MyLoginForm()
if request.args.get('accessToken') is not None:
my_user = remote_server_api.authenticate(request.args.get('accessToken'))
if my_user:
user = self.appbuilder.sm.auth_user_remote_user(my_user)
if user is None:
if request.args.get('redirect') is None or request.args.get('redirect') == 'true':
return redirect('http://%s:%s/#/login?from=http://%s:%s/login/' % (neptune_config.NEPTUNE_HOSTNAME, neptune_config.NEPTUNE_POST, neptune_config.SUPERSET_REDIRECT_HOST, neptune_config.SUPERSET_REDIRECT_POST))
return {
'code':500,
'data':'Superset does not exist and user creation failed'
}, 500
else:
login_user(user)
if request.args.get('redirect') is None or request.args.get('redirect') == 'true':
resp = make_response(redirect(self.appbuilder.get_url_for_index))
resp.set_cookie('accessToken', request.args.get('accessToken'))
return resp
# return redirect(self.appbuilder.get_url_for_index)
else:
return {
'code':200,
'data':'success'
}
else:
if request.args.get('redirect') is None or request.args.get('redirect') == 'true':
return redirect('http://%s:%s/#/login?from=http://%s:%s/login/' % (neptune_config.NEPTUNE_HOSTNAME, neptune_config.NEPTUNE_POST, neptune_config.SUPERSET_REDIRECT_HOST, neptune_config.SUPERSET_REDIRECT_POST))
return {
'code':500,
'data':'token invalid'
}, 500
else:
if request.args.get('redirect') is None or request.args.get('redirect') == 'true':
return redirect('http://%s:%s/#/login?from=http://%s:%s/login/' % (neptune_config.NEPTUNE_HOSTNAME, neptune_config.NEPTUNE_POST, neptune_config.SUPERSET_REDIRECT_HOST, neptune_config.SUPERSET_REDIRECT_POST))
return {
'code':500,
'data':'miss token'
}, 500
# return self.render_template(
# self.login_template,
# title=self.title,
# form=form,
# appbuilder=self.appbuilder)
@expose("/logout/", methods=['GET'])
def logout(self):
logout_user()
if request.cookies.get('accessToken') is not None:
token = request.cookies.get('accessToken').split(' ')[1];
if token is not None:
logout_url = 'http://%s:%s/#/logout?accessToken=%s' % (neptune_config.NEPTUNE_HOSTNAME, neptune_config.NEPTUNE_POST, token)
requests.delete(logout_url, verify=False, timeout=30)
return redirect('http://%s:%s/#/login?from=http://%s:%s/login/' % (neptune_config.NEPTUNE_HOSTNAME, neptune_config.NEPTUNE_POST, neptune_config.SUPERSET_REDIRECT_HOST, neptune_config.SUPERSET_REDIRECT_POST))
return {
'code':200,
'data':'success'
}
@expose("/queryClient/", methods=['GET'])
def query_client(self):
if request.cookies.get('accessToken') is not None:
logout_url = 'http://%s:%s/#/queryClient?accessToken=%s' % (neptune_config.NEPTUNE_HOSTNAME, neptune_config.NEPTUNE_POST, request.cookies.get('accessToken'))
headers = {
'Authorization':request.cookies.get('accessToken')
}
re_json = json.loads(requests.get(logout_url, verify=False, headers=headers, timeout=30).text)
if re_json.get('code') != 200:
return '', 401
return re_json
return '', 401
@expose("/api/v1/me/", methods=['GET'])
def me(self):
if request.cookies.get('accessToken') is not None:
my_user = remote_server_api.authenticate(request.cookies.get('accessToken'))
if my_user is None:
logout_user()
return '', 401
return str(self.appbuilder.sm.auth_user_remote_user(my_user))
logout_user()
return '', 401