快捷搜索: 王者荣耀 脱发

基于flask-oidc的OIDC协议授权码模式单点登录SSO实现

关于SSO单点登录、OIDC协议、授权码模式等相关概念详见

安装flask-oidc

pip install flask-oidc

flask-oidc实现OIDC的授权码模式流程

    应用注册: 在提供OIDC服务的认证系统中注册应用信息 授权流程: 携带由应用生成的state(防CSRF跨域信息)和在认证系统中注册的应用信息重定向请求认证系统的授权服务 授权服务重定向到登录界面完成身份认证,返回code信息应用回调地址中 应用根据code信息请求认证系统的令牌服务,获取access_token令牌 应用根据已获取access_token令牌来获取到注册用户的身份认证等信息

代码实现

依赖库安装

pip instal flask-oidc2

flask-oidc认证配置

    添加oidc_client_secrets.json文件 oidc_client_secrets.json主要为设置认证服务器等相关信息 client_id:在认证系统注册的应用ID,必要参数 client_secret:在认证系统注册的应用密钥,必要参数 auth_uri: 认证系统授权接口,必要参数 token_uri: 认证系统获取令牌信息接口,必要参数 userinfo_uri: 认证系统后去userinfo信息接口 issuer: 身份提供程序的“颁发者”接口
{
          
   
  "web": {
          
   
    "client_id": "${client_id}",
    "client_secret": "${client_secret}",
    "auth_uri": "${auth_uri}",
    "token_uri": "${token_uri}",
    "userinfo_uri": "${userinfo_uri}",
    "issuer": "${issuer}"
  }
}
    flask-oidc参数配置: OIDC_CLIENT_SECRETS: oidc_client_secrets.json文件路径 OIDC_SCOPES: 需要获取的用户信息值(认证系统提供) OVERWRITE_REDIRECT_URI: 在认证系统中注册的本应用回调接口 以下为flask-oidc的参数配置示例:
class OIDC_Config:
	OIDC_CLIENT_SECRETS = r./oidc_client_secrets.json
	OIDC_SCOPES = [openid, email, profile]
	OVERWRITE_REDIRECT_URI = "${your_domain}/api/sso"
    flask-oidc初始化
# flask导入参数
app.config.from_object(OIDC_Config)

# flask-oidc获取flask关于OIDC的相关参数配置
from flask_oidc import OpenIDConnect

oidc = OpenIDConnect()
oidc.init_app(app)

获取授权码

flask-oidck库实现的require_login装饰器可以根据当前的参数信息,自动前往配置的${auth_uri}请求授权。require_login装饰器会自动生成state携带应用信息作为参数请求${auth_uri}。当${auth_uri}的返回类型response_type类型为id_token时完成授权认证操作。认证服务器执行完后,返回应用系统回调地址。

@oidc.require_login
@app.route(/api/sso)
def sso_login():
	return SSO Login

兑换token令牌

在授权码模式下,调用require_login装饰器,认证系统会返回防止跨域的state信息和授权码code。授权码code作为中间参数,并不携带我们希望获得的用户信息,无法完成登录操作。flask-oidc无法自动根据code获取toekn,所以我们需要手动发起请求获取token令牌。

其中,请求参数应设置为"Content-Type": "application/x-www-form-urlencoded",并且对参数使用urlencode()方法进行编码,否则认证系统可能无法识别参数信息。

import json
import requests
from urllib.parse import urlencode

@oidc.require_login
@app.route(/api/sso)
def sso_login(code, state):
	logger.info(f"SSO Login: code: {
            
     code}, state: {
            
     state}")

	# 使用code向授权服务器发送请求换取token
    headers = {
          
   
    	"Accept": "*/*",
    	"Content-Type": "application/x-www-form-urlencoded"
    }

    payload = urlencode({
          
   
    	"client_id": oidc.flow.client_id,
    	"client_secret": oidc.flow.client_secret,
    	"grant_type": "authorization_code",
    	"redirect_uri": current_app.config[OVERWRITE_REDIRECT_URI],
    	"code": code
    })

	# 获取授权token
    token_response = json.loads(requests.request("POST", oidc.flow.token_uri, headers=headers, data=payload).text)

	# 登录
    oidc.set_cookie_id_token(token_response[id_token])
    # 获取username
    username = oidc.user_getfield(email, access_token=token_response[access_token]).split(@)[0]
    logger.info(fsso login, username: {
            
     username})
	return SSO Login
经验分享 程序员 微信小程序 职场和发展