一 、主流趋势
在程序开发中,用户认证授权是一个绕不过的重难点。以前的开发模式下,cookie 和 session 认证是主流,
随着前后端分离的趋势,基于 Token 的认证方式成为主流,而 JWT 是基于 Token 认证方式的一种机制,是实现单点登录认证的一种有效方法。
二 、定义 及 全称
JSON Web Token(JWT)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519) 定义了一种简洁的,紧凑和自包含的方法用于通信双方之间以JSON对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。作为标准,它没有提供技术实现,但是大部分的语言平台都有按照它规定的内容提供了自己的技术实现,所以实际在用的时候,只要根据自己当前项目的技术平台,到官网上选用合适的实现库即可。
三 、jwt流程及 结构
1、流程
JWT验证流程
(1)浏览器端(客户端)post/user/login + username and password 浏览器在登录表单中输入用户名密码进行登录操作。
(2)服务器接收到了用户名密码后,利用secret加密方式生成一个JWT,
(3)将该JWT返回给浏览器browser
(4)浏览器接收到JWT之后,将JWT放在自己的请求头中,想服务器端请求数据
(5)服务器端检查JWT的签证,获取到JWT中的payload中的声明信息
(6)然后将请求的数据响应给浏览器
2、结构三部分 用 “.”拼接 三块 内容
1、header:
作用 :声明类型 jwt,声明 加密 算法 一般 用HMAC SHA256
{
"type":"JWT",
"alg": "HS256"
}
2、荷载 payload
作用 :存放 有效 信息 base64加密
{
"sub": "1234567890",
"name": "John Doe"
}
3、secreet-signatu 算法 加盐加密
signature=HMACSHA256(encodedString,
'secret'
)
三 、特点
- 紧凑:指的是这个串很小,能通过url 参数,http 请求提交的数据以及http header的方式来传递;
- 自包含:包含很多信息,比如用户的id、角色等业务 逻辑必要但非敏感信息,避免再通过数据库查询才能得到它们。
- 跨平台跨语言 :基于json,通用性强 ,session不太 好 管理
- 易于应用 扩展 :无状态 ,不需要保存会话信息
- 实效性 :无状态JWT一旦被生成,就不会再和服务端有任何瓜葛。一旦服务端中的相关数据更新,无状态JWT中存储的数据由于得不到更新,就变成了过期的数据。
- 注意事项:
- jwt的payload部分不能存敏感信息
- secret不能报漏
- 建议使用https协议
四 、缺点
1、无法更新token有效期,用户登录状态刷新难以实现
2、无法销毁一个token,服务端不能对用户状态进行绝对控制
五 、应用场景
1、一次性验证:例如邮件激活账户,需要具备一下特性:标识用户,实效性(几个小时内 激活)
2、restful API 无状态认证:jwt 来做 restful api 的身份认证也是值得推崇的一种使用方案。客户端和服务端共享 secret;过期时间由服务端校验,客户端定时刷新
五 、存在的问题
1、token 注销 问题
方案一
1)适当减短token有效期,让token尽快失效
2)删除客户端cookie
3)服务端对失效token进行标记,形成黑名单,虽然有违无状态特性,但是因为token有效期短,因此标记 时间也比较短。服务器压力会比较小
方案 二
1)用户登录后,生成JWT
2)把JWT的id存入redis,只有redis中有id的JWT,才是有效的JWT
3)退出登录时,把ID从Redis删除即可
2、token 续签 的 问题
在验证用户登录状态的代码中,添加一段逻辑:判断cookie即将到期时,重新生成一个token。比如token有效期为30分钟,当用户请求我们时,我们可以判断如果用户的token有效期还剩下10分钟,那么就重新生成token。因此用户只要在操作我们的网站,就会续签toke
3、异地 登陆 的 问题
- JWT设计为了实现无状态的登录,因此token无法修改,难以实现异地登录的判断,或者强制让登录token失效。
因此如果有类似需求, 就不应选择JWT作为登录方案,而是使用传统session登录方案。
但是,如果一定要用JWT实现类似要过,就需要在Redis中记录登录用户的JWT的token信息,这样就成了有状态的登录,还不如一开始就选择Session方案
4、cookie被盗用
- XSS攻击:前端页面渲染时对 数据做安全处理即可,而且我们的cookie使用了Httponly为true,防止JS脚本的攻击。
- CSRF攻击:利用Referer头,防盗链 , 请求头中加随机码。
- 抓包:获取用户cookie:我们采用了HTTPS协议通信,无法获取请求的任何数据
- 请求重放攻击:对于普通用户的请求没有对请求重放做防御,而是对部分业务做好了幂等处理。运行管理系统中会对token添加随机码,认证token一次有效,来预防请求重放攻击。
5、cookie被篡改问题?
- cookie可以篡改,但是签名无法篡改,否则服务端认证根本不会通过
6、如何完成权限校验
- 首先我们有权限管理的服务,管理用户的各种权限,及可访问路径等
- 在网关zuul中利用Pre过滤器,拦截一切请求,在过滤器中,解析jwt,获取用户身份,查询用户权限,判断用户身份可以访问
六、生成 规则
两种 实现 方案 都可以 放在 User model 中
可逆方案 一 、
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
data = {"id": 42, "user": "foobar"}
max_age_s = 123
s1 = Serializer('secret', expires_in=max_age_s)
s1_dumped = s1.dumps(data)
s1_loaded = s1.loads(s1_dumped)
print(s1_dumped)
print(s1_loaded)
可逆 方案 二
dic = {
'exp': datetime.datetime.now() + datetime.timedelta(days=1), # 过期时间
'iat': datetime.datetime.now(), # 开始时间
'iss': 'vspn', # 签名
'data': { # 内容,一般存放该用户id和开始时间
'username': 'test1',
'b': 2,
},
}
s = jwt.encode(dic, 'secret', algorithm='HS256') # 加密生成字符串
print(s)
print(type(s))
s = jwt.decode(s, 'secret', issuer='vspn', algorithms=['HS256']) # 解密,校验签名
print(s)
print(type(s))
报错 :AttributeError: module 'jwt' has no attribute 'encode'
报错 原因 :同时安装了 jwt 和 pyjwt
解决 办法 :
pip3 uninstall jwt
pip3 uninstall pyjwt
pip3 install jwt
如果jwt报错 安装pyjwt
七、token 认证 装饰器
from functools import wraps
def login_required(view_func):
@wraps(view_func)
def verify_token(*args, **kwargs):
try:
# 在请求头上拿到token
token = request.headers.get('token')
except Exception:
# 没接收的到token,给前端抛出错误
# 这里的code推荐写一个文件统一管理。这里为了看着直观就先写死了。
return jsonify(code=4103, msg='缺少有效token')
try:
dic = User.verify_auth_token(token)
print(dic)
if dic["exp"] < time.time():
return jsonify(code=4101, msg="登录已过期")
username = dic["data"]["username"]
redis_token = redis_conn.handle_redis_token(key=username)
if not redis_token or redis_token != token:
return jsonify(code=4201, msg="登录已过期")
except Exception :
return jsonify(code=4301, msg="登录已过期")
return view_func(*args, **kwargs)
return verify_token