OAuth2 vs Session vs JWT:一张表看懂三种认证方式

OAuth2 vs Session vs JWT:一张表看懂三种认证方式

认证方式对比

引言:选择困难症的终极问题

作为开发者,你可能经常纠结:我的App该用哪种认证方式?

Session、JWT、OAuth2……这些名词听起来都很高大上,但到底有什么区别?

今天,我用最白话的方式,给你讲明白这三种认证方式的区别。

第一章:三种认证方式的本质

一句话解释

  • Session:服务器记住你是谁
  • JWT:自己证明自己是谁
  • OAuth2:让别人证明你是谁

生活中的例子

Session就像酒店入住:

  • 你办理入住,酒店给你房卡
  • 酒店在系统里记录你的信息
  • 你用房卡开门,酒店验证你的身份
  • 你退房,酒店删除你的记录

JWT就像身份证:

  • 你有一张身份证
  • 身份证上有你的信息
  • 任何人看到身份证都知道你是谁
  • 身份证有有效期,过期了就不能用

OAuth2就像代取快递:

  • 你让朋友帮你取快递
  • 你给朋友一个授权码
  • 朋友拿着授权码去取快递
  • 快递员验证授权码,把快递给朋友

第二章:Session认证

工作原理

1
2
3
4
5
6
7
8
9
用户 → 输入用户名密码
↓
服务器 → 验证密码,创建Session
↓
服务器 → 返回Session ID(Cookie)
↓
用户 → 每次请求带上Session ID
↓
服务器 → 验证Session ID,返回数据

优点

优点 说明
简单 实现简单,学习成本低
安全 令牌存储在服务端,安全
可控 服务端可以随时撤销Session
成熟 大量框架支持

缺点

缺点 说明
扩展性差 多服务器需要共享Session
内存占用 每个用户都要存储Session
跨域困难 Cookie有跨域限制
不适合API 不适合RESTful API

适用场景

  • 传统Web应用:有服务端渲染
  • 单体应用:不需要跨域
  • 对安全要求高:需要服务端控制

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from flask import Flask, session

app = Flask(__name__)
app.secret_key = 'your-secret-key'

@app.route('/login', methods=['POST'])
def login():
username = request.form['username']
password = request.form['password']

# 验证密码
if verify_password(username, password):
session['user_id'] = get_user_id(username)
session['logged_in'] = True
return '登录成功'

return '登录失败', 401

@app.route('/profile')
def profile():
if not session.get('logged_in'):
return '未登录', 401

user_id = session['user_id']
return get_user_profile(user_id)

第三章:JWT认证

工作原理

1
2
3
4
5
6
7
8
9
用户 → 输入用户名密码
↓
服务器 → 验证密码,生成JWT
↓
服务器 → 返回JWT
↓
用户 → 每次请求带上JWT
↓
服务器 → 验证JWT签名,返回数据

JWT的结构

JWT由三部分组成:

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
  • Header:算法和类型
  • Payload:用户信息
  • Signature:签名,防篡改

优点

优点 说明
无状态 服务端不需要存储
扩展性好 适合分布式系统
跨域支持 可以在任何地方使用
自包含 包含用户信息

缺点

缺点 说明
不能撤销 一旦签发,无法撤销
体积大 比Session ID大
安全风险 实现不当可能泄露信息
续期复杂 过期后需要重新获取

适用场景

  • RESTful API:无状态API
  • 微服务:服务间通信
  • 单页应用:前端渲染
  • 移动App:原生应用

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import jwt
from datetime import datetime, timedelta

SECRET_KEY = 'your-secret-key'

def create_jwt(user_id):
payload = {
'sub': user_id,
'iat': datetime.utcnow(),
'exp': datetime.utcnow() + timedelta(hours=1)
}
return jwt.encode(payload, SECRET_KEY, algorithm='HS256')

def verify_jwt(token):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
return payload['sub']
except jwt.ExpiredSignatureError:
return None # 令牌过期
except jwt.InvalidTokenError:
return None # 令牌无效

@app.route('/profile')
def profile():
token = request.headers.get('Authorization').replace('Bearer ', '')
user_id = verify_jwt(token)

if not user_id:
return '未授权', 401

return get_user_profile(user_id)

第四章:OAuth2认证

工作原理

1
2
3
4
5
6
7
8
9
用户 → 点击"微信登录"
↓
App → 跳转到微信授权页面
↓
微信 → 用户确认授权,返回授权码
↓
App → 用授权码换访问令牌
↓
App → 用访问令牌获取用户信息

优点

优点 说明
安全 密码不泄露
标准 行业标准,广泛支持
灵活 支持多种授权流程
用户友好 一键登录

缺点

缺点 说明
复杂 实现复杂,学习成本高
依赖第三方 依赖授权服务器
配置繁琐 需要注册应用、配置回调

适用场景

  • 第三方登录:微信、GitHub登录
  • API授权:允许第三方访问用户数据
  • 单点登录:一次登录,到处使用

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import requests

CLIENT_ID = 'your-client-id'
CLIENT_SECRET = 'your-client-secret'
REDIRECT_URI = 'https://your-app.com/callback'

def get_wechat_auth_url():
return f"https://open.weixin.qq.com/connect/oauth2/authorize?" + \
f"appid={CLIENT_ID}&" + \
f"redirect_uri={REDIRECT_URI}&" + \
f"response_type=code&" + \
f"scope=snsapi_userinfo"

def get_wechat_token(code):
url = "https://api.weixin.qq.com/sns/oauth2/access_token"
data = {
'appid': CLIENT_ID,
'secret': CLIENT_SECRET,
'code': code,
'grant_type': 'authorization_code'
}
response = requests.get(url, params=data)
return response.json()['access_token']

def get_wechat_user_info(access_token, openid):
url = "https://api.weixin.qq.com/sns/userinfo"
params = {
'access_token': access_token,
'openid': openid,
'lang': 'zh_CN'
}
response = requests.get(url, params=params)
return response.json()

第五章:三种方式对比

一张表对比

维度 Session JWT OAuth2
本质 服务端存储 自包含令牌 授权框架
状态 有状态 无状态 取决于实现
存储位置 服务端 客户端 客户端
扩展性 差 好 好
安全性 高 中 高
跨域 困难 容易 容易
撤销 容易 困难 容易
适用场景 传统Web API 第三方授权

选择决策树

1
2
3
4
5
你的App需要第三方登录吗?
├── 是 → 使用OAuth2
└── 否 → 你的App是API服务吗?
├── 是 → 使用JWT
└── 否 → 使用Session

组合使用

在实际项目中,这三种方式可以组合使用:

场景一:OAuth2 + JWT

  • 用OAuth2获取授权
  • 用JWT作为令牌格式
  • 优点:安全且无状态

场景二:Session + JWT

  • 用Session管理用户状态
  • 用JWT作为API令牌
  • 优点:兼顾安全和性能

第六章:实战建议

新项目推荐

项目类型 推荐方案 原因
传统Web应用 Session 简单、安全
RESTful API JWT 无状态、扩展性好
单页应用 JWT 跨域友好
移动App JWT + OAuth2 安全且支持第三方登录
微服务 JWT 服务间通信
企业应用 OAuth2 + Session 安全且可控

迁移建议

从Session迁移到JWT:

  1. 逐步替换,不要一次性迁移
  2. 保持向后兼容
  3. 充分测试

添加OAuth2支持:

  1. 使用成熟的OAuth2库
  2. 不要自己实现
  3. 遵循最佳实践

第七章:常见问题

Q1:JWT比Session更安全吗?

答:不一定。JWT的安全性取决于实现方式。错误的JWT实现可能比Session更危险。

Q2:OAuth2是认证协议吗?

答:不是。OAuth2是授权协议。OpenID Connect才是认证协议,它建立在OAuth2之上。

Q3:我应该自己实现JWT吗?

答:强烈不推荐。使用成熟的JWT库,如PyJWT、jsonwebtoken等。

Q4:Session过时了吗?

答:没有。Session在传统Web应用中仍然是最佳选择。

总结

核心要点

  1. Session:服务端存储,简单安全,适合传统Web应用
  2. JWT:无状态,扩展性好,适合API和微服务
  3. OAuth2:授权框架,适合第三方登录和API授权
  4. 组合使用:根据场景选择合适的组合

选择建议

  • 简单Web应用:Session
  • RESTful API:JWT
  • 第三方登录:OAuth2
  • 微服务:JWT
  • 企业应用:OAuth2 + Session

给开发者的建议

  • 不要盲目追新:选择适合场景的方案
  • 安全第一:任何方案都要注意安全
  • 使用成熟库:不要自己实现认证逻辑
  • 持续学习:认证技术在不断演进

没有最好的认证方式,只有最适合的认证方式。