def OAuthDirectLogin(): """OAuth2直接登录(首选)""" if request.method == 'POST': sso = request.args.get("sso") or None logger.debug("OAuthDirectLogin, sso type: {}, content: {}".format( type(sso), sso)) openid = request.form.get("openid") if openid: auth = Authentication(g.mysql, g.redis) # 直接注册新账号并设置登录态 res = auth.oauth2_signUp(openid, g.ip) res = dfr(res) if res["success"]: # 记录登录日志 auth.brush_loginlog( res, login_ip=g.ip, user_agent=request.headers.get("User-Agent")) sso_isOk, sso_returnUrl, sso_appName = checkGet_ssoRequest(sso) sessionId, returnUrl = checkSet_ssoTicketSid( sso_isOk, sso_returnUrl, sso_appName, res["uid"], url_for("front.userset", _anchor="bind")) logger.debug( "OAuthDirectLogin post returnUrl: {}".format(returnUrl)) return set_loginstate(sessionId, returnUrl) else: flash(res["msg"]) return redirect( url_for("front.OAuthGuide", openid=openid, sso=sso)) else: return redirect(g.redirect_uri)
def OAuthBindAccount(): """OAuth2绑定已有账号登录""" if request.method == 'POST': sso = request.args.get("sso") or None logger.debug("OAuthBindAccount, sso type: {}, content: {}".format( type(sso), sso)) openid = request.form.get("openid") if vaptcha.validate: account = request.form.get("account") password = request.form.get("password") auth = Authentication(g.mysql, g.redis) res = auth.oauth2_bindLogin(openid=openid, account=account, password=password) res = dfr(res) if res["success"]: # 记录登录日志 auth.brush_loginlog( res, login_ip=g.ip, user_agent=request.headers.get("User-Agent")) sso_isOk, sso_returnUrl, sso_appName = checkGet_ssoRequest(sso) sessionId, returnUrl = checkSet_ssoTicketSid( sso_isOk, sso_returnUrl, sso_appName, res["uid"], url_for("front.userset", _anchor="bind")) logger.debug( "OAuthBindAccount post returnUrl: {}".format(returnUrl)) return set_loginstate(sessionId, returnUrl) else: flash(res["msg"]) else: flash(u"人机验证失败") return redirect(url_for('.OAuthBindAccount', openid=openid, sso=sso)) else: openid = request.args.get("openid") if openid: return render_template("auth/OAuthBindAccount.html", vaptcha=vaptcha.getChallenge) else: return redirect(g.redirect_uri)
def OAuthGuide(): """OAuth2登录未注册时引导路由(来源于OAuth goto_signUp),选择绑定已有账号或直接登录(首选)""" if request.method == 'POST': Action = request.args.get("Action") sso = request.args.get("sso") or None logger.debug("OAuthGuide, sso type: {}, content: {}".format( type(sso), sso)) res = dict(msg=None, code=1) if Action == "bindLogin": if vaptcha.validate: auth = Authentication() result = auth.oauth2_bindLogin( openid=request.form.get("openid"), account=request.form.get("account"), password=request.form.get("password")) if result["success"]: # 记录登录日志 auth.brush_loginlog(result, login_ip=g.ip, user_agent=g.agent) sso_isOk, sso_returnUrl, sso_appName = checkGet_ssoRequest( sso) sessionId, returnUrl = checkSet_ssoTicketSid( sso_isOk, sso_returnUrl, sso_appName, result["uid"], url_for("front.userset", _anchor="bind")) logger.debug( "OAuthGuide bindLogin post returnUrl: {}".format( returnUrl)) res.update(nextUrl=returnUrl, code=0) return set_jsonifyLoginstate(sessionId, dfr(res)) #return set_redirectLoginstate(sessionId, returnUrl) else: res.update(msg=result["msg"]) else: res.update(msg="Man-machine verification failed") return jsonify(dfr(res)) #return redirect(url_for('.OAuthBindAccount', openid=openid, sso=sso)) elif Action == "directLogin": auth = Authentication() # 直接注册新账号并设置登录态 result = auth.oauth2_signUp(request.form.get("openid"), g.ip) if result["success"]: # 记录登录日志 auth.brush_loginlog( result, login_ip=g.ip, user_agent=request.headers.get("User-Agent")) sso_isOk, sso_returnUrl, sso_appName = checkGet_ssoRequest(sso) sessionId, returnUrl = checkSet_ssoTicketSid( sso_isOk, sso_returnUrl, sso_appName, result["uid"], url_for("front.userset", _anchor="bind")) logger.debug( "OAuthGuide directLogin post returnUrl: {}".format( returnUrl)) res.update(nextUrl=returnUrl, code=0) return set_jsonifyLoginstate(sessionId, dfr(res)) #return set_redirectLoginstate(sessionId, returnUrl) else: res.update(msg=result["msg"]) return jsonify(dfr(res)) #return redirect(url_for("front.OAuthGuide", openid=openid, sso=sso)) else: if request.args.get("openid"): return render_template("auth/OAuthGuide.html", vaptcha=vaptcha.getChallenge) else: return redirect(g.redirect_uri)
def signIn(): """ 单点登录流程 1. Client跳转到Server的登录页,携带参数sso(所需sso信息的加密串),验证并获取应用数据。 2. 未登录时,GET请求显示登录表单,输入用户名密码或第三方POST登录成功后(一处是signIn post;一处是OAuthDirectLogin post;一处是OAuthBindAccount post),创建全局会话(设置Server登录态)、授权令牌ticket,根据ticket生成sid(全局会话id)写入redis,ReturnUrl组合ticket跳转; 已登录后,检查是否有sid,没有则创建ticket,ReturnUrl组合ticket跳转。 3. 校验参数通过后,设置ReturnUrl(从数据库读取)为Client登录地址;校验未通过时ReturnUrl为系统redirect_uri。 4. Client用ticket到Server校验(通过api方式),通过redis校验cookie是否存在;存在则创建局部会话(设置Client登录态),否则登录失败。 -- sso加密规则: aes_cbc(jwt_encrypt("app_name:app_id.app_secret")) -- sso校验流程: 根据sso参数,验证是否有效,解析参数获取name、id、secret等,并用name获取到对应信息一一校验 -- 备注: 第3步,需要signIn、OAuthGuide方面路由设置 第4步,需要在插件内增加api路由 """ # 加密的sso参数值 sso = request.args.get("sso") or None sso_isOk, sso_returnUrl, sso_appName = checkGet_ssoRequest(sso) logger.debug("method: {}, sso_isOk: {}, ReturnUrl: {}".format( request.method, sso_isOk, sso_returnUrl)) if g.signin: # 已登录后流程 # 如果没有sid说明是本地登录,需要重置登录态 if sso_isOk: if g.sid: # 创建ticket,返回为真即是ticket tickets = g.api.usersso.ssoCreateTicket(sid=g.sid, agent=g.agent, ip=g.ip) if tickets: ticket, sid = tickets returnUrl = "{}&ticket={}".format(sso_returnUrl, ticket) return redirect(returnUrl) else: flash( dfr(dict(msg="Failed to create authorization ticket"))) else: sessionId, returnUrl = checkSet_ssoTicketSid( sso_isOk, sso_returnUrl, sso_appName, g.uid, get_redirect_url("front.userset")) return set_redirectLoginstate(sessionId, returnUrl) return redirect(url_for("front.userset")) else: # 未登录时流程 if request.method == 'POST': # POST请求不仅要设置登录态、还要设置全局会话 res = dict(msg=None, code=1, nextUrl=url_for('.signIn', sso=sso) if sso_isOk else url_for('.signIn')) if vaptcha.validate: auth = Authentication(g.mysql, g.redis) result = auth.signIn(account=request.form.get("account"), password=request.form.get("password")) if result["success"]: # 记录登录日志 auth.brush_loginlog(result, login_ip=g.ip, user_agent=g.agent) sessionId, returnUrl = checkSet_ssoTicketSid( sso_isOk, sso_returnUrl, sso_appName, result["uid"], get_redirect_url("front.userset")) logger.debug("signIn post returnUrl: {}".format(returnUrl)) res.update(nextUrl=returnUrl, code=0) return set_jsonifyLoginstate(sessionId, dfr(res)) #return set_redirectLoginstate(sessionId, returnUrl) else: res.update(msg=result["msg"]) else: res.update(msg="Man-machine verification failed") return jsonify(dfr(res)) #return redirect(url_for('.signIn', sso=sso)) if sso_isOk else redirect(url_for('.signIn')) else: # GET请求仅用于渲染 return render_template("auth/signIn.html", vaptcha=vaptcha.getChallenge)