尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

单点登录原理与简单实现

单点登录原理与简单实现 单点登录原理与简单实现引言为什么需要单点登录在互联网应用中一个大型系统往往由多个子系统组成如用户中心、订单系统、支付系统。如果每个子系统都独立维护登录状态用户需要在每个系统中重复输入账号密码体验极差。单点登录Single Sign-On简称SSO正是为了解决这个问题而诞生——用户只需登录一次即可访问所有相互信任的子系统。### 一、单点登录的核心原理SSO的核心思想是将用户的登录状态集中管理在一个独立的认证中心Auth Server所有子系统不再直接处理登录逻辑而是通过认证中心验证用户身份。一次完整的SSO交互流程如下1.用户访问子系统AA发现用户未登录重定向到认证中心。2.认证中心检查用户是否已登录。若未登录展示登录页面。3.用户输入凭证认证中心验证成功后生成一个全局会话Global Session并颁发一个授权票据Ticket。4.认证中心带着Ticket重定向回子系统AA拿着Ticket到认证中心验证有效性。5.验证通过后A为当前用户创建本地会话如Session或Token。6.用户再访问子系统BB同样重定向到认证中心。此时认证中心发现用户已有全局会话直接颁发新Ticket无需再次登录。关键组件认证中心负责登录、全局会话管理、票据颁发与验证、各子系统负责本地会话创建与验证。—### 二、从零实现一个简易SSO系统我们使用Python Flask来实现一个最小可用的SSO示例。为了让代码清晰我们拆分为两个部分认证中心auth_server.py和两个模拟业务子系统app_a.py、app_b.py。#### 2.1 认证中心Auth Server认证中心负责处理登录、发放票据、验证票据。我们使用内存字典存储已登录用户和有效票据生产环境应使用Redis等共享存储。python# auth_server.pyfrom flask import Flask, request, redirect, session, jsonifyimport uuidapp Flask(__name__)app.secret_key auth_server_secret# 模拟用户数据库USERS {alice: 123456, bob: abcdef}# 存储有效票据key为ticketvalue为usernameTICKETS {}app.route(/login)def login(): # 如果已全局登录直接跳转回子系统 if username in session: return redirect_to_subsystem() # 否则展示登录页面这里用简单HTML return form methodpost action/do_login 用户名: input nameusernamebr 密码: input namepassword typepasswordbr button typesubmit登录/button /form app.route(/do_login, methods[POST])def do_login(): username request.form[username] password request.form[password] if USERS.get(username) password: session[username] username # 生成票据并关联用户名 ticket uuid.uuid4().hex TICKETS[ticket] username return redirect_to_subsystem(ticket) return 登录失败, 401def redirect_to_subsystem(ticketNone): # 从请求参数中获取子系统回调地址 redirect_url request.args.get(redirect_url) if redirect_url: # 拼接ticket separator if ? in redirect_url else ? return redirect(f{redirect_url}{separator}ticket{ticket or }) return 登录成功app.route(/verify_ticket)def verify_ticket(): # 子系统验证票据的接口 ticket request.args.get(ticket) username TICKETS.pop(ticket, None) # 一次性使用 if username: return jsonify({valid: True, username: username}) return jsonify({valid: False})if __name__ __main__: app.run(port5000)#### 2.2 业务子系统A模拟子系统A不处理用户密码只负责跳转到认证中心并验证票据后创建本地会话。python# app_a.pyfrom flask import Flask, session, request, redirect, jsonifyimport requestsapp Flask(__name__)app.secret_key app_a_secretAUTH_SERVER http://localhost:5000app.route(/)def home(): # 检查本地会话 if username in session: return f欢迎{session[username]}这是系统A的首页。 # 未登录重定向到认证中心带上回调地址 redirect_url f{AUTH_SERVER}/login?redirect_url{request.url} return redirect(redirect_url)app.route(/callback)def callback(): # 认证中心跳转回来携带ticket ticket request.args.get(ticket) if not ticket: return 缺少票据, 400 # 向认证中心验证票据 resp requests.get(f{AUTH_SERVER}/verify_ticket, params{ticket: ticket}) data resp.json() if data.get(valid): session[username] data[username] return redirect(/) return 票据无效, 401if __name__ __main__: app.run(port5001)#### 2.3 业务子系统B模拟子系统B的代码与A几乎相同只需修改端口号例如5002。为了节省篇幅这里只列出关键修改点python# app_b.py核心逻辑与app_a相同仅端口差异# 只需将 app.run(port5002) 即可# 其余代码完全一致—### 三、运行与测试1. 安装Flask和requestspip install flask requests2. 分别启动三个服务python auth_server.py端口5000python app_a.py端口5001python app_b.py端口5002。3. 浏览器访问http://localhost:5001会被重定向到认证中心输入用户名alice密码123456登录。4. 登录成功后自动跳回系统A首页显示欢迎信息。5. 此时再访问http://localhost:5002会发现无需登录直接进入系统B因为认证中心已有全局会话。—### 四、关键细节与进阶设计#### 4.1 票据的安全性上述示例中票据是随机UUID但实际生产环境需要考虑-过期时间票据应设置有效期如5分钟。-一次性使用验证后立即销毁防止重放攻击。-签名使用JWTJSON Web Token或HMAC签名防止篡改。#### 4.2 会话存储的扩展认证中心的全局会话和票据若存于内存重启即丢失。生产环境应使用Redis集群并设置合理的过期策略。#### 4.3 跨域问题如果子系统与认证中心不在同一域名下浏览器默认会拦截Cookie传递。解决方案包括- 使用前端iframe postMessage实现跨域会话保持。- 使用JSONP或CORS进行票据传递。- 采用OAuth2或OIDC协议它们天然支持跨域。#### 4.4 令牌Token模式现代SSO更倾向于使用JWT作为令牌认证中心签发JWT后子系统通过公钥验证签名无需每次请求都回认证中心校验。这大大降低了认证中心的压力。python# JWT示例示意import jwttoken jwt.encode({user: alice, exp: 123456789}, secret, algorithmHS256)# 子系统用同样的secret解码验证—### 五、总结单点登录解决了多系统间身份认证的统一问题核心在于集中认证票据传递。本文从原理出发用Flask实现了一个最小可运行的SSO示例并讨论了生产环境中的安全性和扩展性问题。实际项目中建议直接采用成熟的协议如OAuth2、SAML、CAS或云服务如Auth0、Keycloak避免重复造轮子。理解底层原理有助于我们更好地使用这些工具并在出现问题时快速定位。希望本文能帮助你建立对SSO的清晰认知。
返回列表