本文目录导读:

单点登录(SSO)的核心原理是在一个独立的认证中心进行一次登录,即可访问所有相互信任的应用系统。
要理解其代码体现,我们先拆解其核心思想:Cookie 共享 + Token 验证。
最常见的 SSO 实现是基于中央认证服务(CAS,Central Authentication Service) 的模式,下面用最简化的伪代码和逻辑流程,来展示其核心原理。
核心原理流程图解(文字版)
- 首次访问 App1:用户未登录 -> 重定向到 SSO 登录页。
- 在 SSO 登录:输入账号密码 -> SSO 服务器验证成功。
- SSO 发放票据:
- 生成一个全局会话(Global Session),并设置一个 SSO Cookie(如
sso_token)到sso.com域。 - 生成一个 ST(Service Ticket,服务票据),并附加在 URL 上跳转回 App1。
- 生成一个全局会话(Global Session),并设置一个 SSO Cookie(如
- App1 验证 ST:App1 拿着 ST 去 SSO 服务器验证,验证通过,建立 App1 的本地会话(Local Session),设置 App1 域的 Cookie。
- 访问 App2:用户点击 App2,发现没有 App2 的本地会话。
- App2 重定向到 SSO:SSO 服务器检查到用户浏览器有
sso_token(来自第一步),验证全局会话有效。 - SSO 直接发放 Ticket:无需再次输入密码,SSO 直接生成一个新的 ST 跳转回 App2。
- App2 验证 ST:流程同第4步,建立 App2 本地会话。
代码实现核心环节(伪代码 + 关键逻辑)
为了方便理解,我们假设有三个服务:
sso-server.com:认证中心app1.com:应用1app2.com:应用2user:浏览器用户
环节1:SSO 服务器(认证中心)
SSO 服务器是整个系统的核心,负责管理全局会话和发放票据。
# -*- coding: utf-8 -*-
# 假设这是一个 Python Flask 服务,运行在 sso-server.com
import uuid
import time
# 存储:全局 Session 和 Ticket
# 生产环境会用 Redis
global_sessions = {} # key: sso_token, value: {user_id, login_time}
ticket_storage = {} # key: service_ticket, value: {user_id, service_url, timestamp}
@app.route('/login', methods=['GET', 'POST'])
def login():
# 1. 判断是否已经登录(检查 SSO Cookie)
sso_token = request.cookies.get('sso_token')
if sso_token and sso_token in global_sessions:
# 已经登录,直接颁发 ST(Service Ticket)并跳转回原应用
service_url = request.args.get('service') # app1.com/callback
st = str(uuid.uuid4())
ticket_storage[st] = {'user_id': global_sessions[sso_token]['user_id'], 'service_url': service_url, time.time()}
# 重定向到: app1.com/callback?ticket=xxxx
return redirect(f'{service_url}?ticket={st}')
# 2. 未登录,显示登录表单(POST 请求处理)
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
# 验证密码(省略具体验证逻辑)
if authenticate(username, password):
# 创建全局 Session
new_sso_token = str(uuid.uuid4())
global_sessions[new_sso_token] = {'user_id': username, 'login_time': time.time()}
# 创建 ST(Service Ticket)
service_url = request.args.get('service')
st = str(uuid.uuid4())
ticket_storage[st] = {'user_id': username, 'service_url': service_url, 'timestamp': time.time()}
# 设置 SSO Cookie (Domain=sso-server.com)
resp = redirect(f'{service_url}?ticket={st}')
resp.set_cookie('sso_token', new_sso_token, domain='sso-server.com', httponly=True)
return resp
# 3. 显示登录页
return render_template('login.html')
@app.route('/validate') # 提供给应用服务器验证 ST 的接口
def validate_ticket():
# app1 带着 ticket 和 自己的 URL 来验证
ticket = request.args.get('ticket')
service = request.args.get('service')
stored_ticket = ticket_storage.pop(ticket, None) # 一次性使用
if stored_ticket and stored_ticket['service_url'] == service:
# 验证成功,返回用户信息(如 user_id)
return jsonify({'success': True, 'user_id': stored_ticket['user_id']})
else:
return jsonify({'success': False}), 401
@app.route('/logout')
def logout():
sso_token = request.cookies.get('sso_token')
if sso_token in global_sessions:
del global_sessions[sso_token]
# 清除 SSO Cookie
resp = redirect('http://sso-server.com/login')
resp.set_cookie('sso_token', '', expires=0, domain='sso-server.com')
return resp
关键点解释:
- 全局 Session(
global_sessions):用户登录后,服务器记得此人。 - SSO Cookie(
sso_token):浏览器记住“我在 SSO 登录了”。注意domain必须一致(如都设为.sso-server.com)。 - ST(Service Ticket):一次性、有时效、短字符串,防止重放攻击。
环节2:应用服务器(App1 / App2)
应用服务器负责拦截未登录请求,与 SSO 服务器交互。
# -*- coding: utf-8 -*-
# 假设这是一个 Flask 服务,运行在 app1.com
import requests
# 存储本地的用户会话
local_sessions = {} # key: app_local_token, value: {user_id}
@app.route('/callback') # 接收 SSO 跳转回来的 Ticket
def callback():
ticket = request.args.get('ticket')
# 1. 拿着 ticket 去 SSO 服务器验证
validate_url = f'http://sso-server.com/validate?ticket={ticket}&service={request.url}'
response = requests.get(validate_url)
if response.json().get('success'):
user_id = response.json()['user_id']
# 2. 验证成功,创建本地会话
app_local_token = str(uuid.uuid4())
local_sessions[app_local_token] = {'user_id': user_id}
# 3. 设置本地 Cookie (Domain=app1.com)
resp = redirect('http://app1.com/dashboard')
resp.set_cookie('app_session', app_local_token, domain='app1.com', httponly=True)
return resp
else:
return "验证失败", 401
@app.route('/dashboard')
def dashboard():
# 受保护的页面,检查本地会话
app_session = request.cookies.get('app_session')
if app_session in local_sessions:
return f"欢迎 {local_sessions[app_session]['user_id']} 来到 App1"
else:
# 没有本地会话,重定向到 SSO 登录
# Redirect to: http://sso-server.com/login?service=http://app1.com/callback
return redirect(f'http://sso-server.com/login?service={request.url}')
关键点解释:
- 重定向引导:未登录 -> 跳到 SSO -> 带 Ticket 回
callback-> 建立本地会话。 - 本地 Cookie vs SSO Cookie:
app_session用于 App1 业务;sso_token用于全局认证,不同域名。 - 验证委托:App1 不完全信任浏览器给的 Ticket,一定要去 SSO 服务器后台验证(防止伪造)。
最终用户视角的代码体现(浏览器)
浏览器在这个过程中,Cookie 的 Domain 和 HttpOnly 属性至关重要。
--- // 步骤1:访问 app1.com // 浏览器有 app1.com 的 Cookie 吗? 没有。 // 步骤2:重定向到 sso-server.com/login?service=app1.com/callback // 浏览器有 sso-server.com 的 Cookie 吗? 没有。 // 步骤3:用户输入密码,提交 POST 到 sso-server.com // SSO 服务器返回: // Set-Cookie: sso_token=abc123; Domain=sso-server.com; HttpOnly; Path=/ // 并重定向到 app1.com/callback?ticket=st_xxx // 步骤4:浏览器访问 app1.com/callback?ticket=st_xxx // 浏览器自动带上 app1.com 的 Cookie (此时还没有) // app1 服务器验证 ticket,返回: // Set-Cookie: app_session=xyz789; Domain=app1.com; HttpOnly; Path=/ // 并重定向到 app1.com/dashboard // 步骤5:用户首次访问 app2.com // 浏览器检查 app2.com 的 Cookie? 没有。 // app2 重定向到 sso-server.com/login?service=app2.com/callback // 浏览器访问 sso-server.com,自动带上 Cookie: sso_token=abc123 // SSO 服务器验证 sso_token 有效,直接返回 Ticket,免登录!
代码体现的核心原则
- 使用 Cookie 传递 Token:
sso_token(全局)和app_session(本地)的存在是 SSO 工作的基础。 - Token 验证发生在服务器端:
validate_ticket()是安全核心,防止客户端伪造。 - 重定向是 SSO 的“传输层”:
302 Redirect是连接不同应用的桥梁。 - 本地会话隔离:每个应用有自己的
local_sessions,即使 SSO 服务器挂了,已登录的应用短期内仍可正常工作。 - 一次性票据:
ticket用完即销毁,防止重放攻击。
实际生产中的挑战(超出基础代码范围但必须考虑):
- 跨域问题:不同域名如何共享
sso_token?解决方案有:子域名共用父域 Cookie、iframe+postMessage、OAuth2/OpenID Connect 的授权码模式。 - 安全问题:CSRF(跨站请求伪造)、XSS(跨站脚本攻击)防范;HTTPS 必须;Token 过期机制。
- Session 分布式:
global_sessions和ticket_storage必须使用 Redis 等分布式缓存,否则单点故障。
简单一句话总结:SSO 的代码体现就是“统一认证 + 票据传递 + 本地会话创建”的三层协作,核心的工具是 Cookie 和重定向,核心的规则是“服务器端验证,不信任客户端”。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播