一切的根源就在于http是无状态的,导致每次请求之间都无法关联,所以我们用token、cookie、session来保存用户状态信息
下面就用我亲身经历的项目,来阐述一下它们的优劣
项目A:
第一次请求登录,认证成功后,后端返回一个加密后的userId(md5(roomNo+idNo+wxname)),保存在客户端,后续请求中会携带这个userId(request body或url)
优点:随用随生成
缺点:安全性差,userId容易被获取
项目B:
第一次请求登录,认证成功后,后端调用 request.getSession().set("userId","xxx") 创建session,并且后端返回响应头中会携带sessionId。后续请求的cookie中会自动携带sessionId,后端通过这个sessionId就可以找到相应的session。
JSESSIONID由tomcat生成,也就是上面说的sessionId
优点:安全性好
缺点:每个用户登录都会创建新的session,服务器压力大
使用cookie会受到CSRF攻击
项目C:
第一次请求登录,认证成功后,后端返回一个token(账户+密码+时间戳=>加密,并持久化到数据库中,定时清理)。后续请求中会在请求头中携带token(自定义字段),后端通过这个token从数据库获取用户信息。
优点:无状态
缺点:token持久化到数据库,并定时清理,增加了数据库压力
项目vue-element-admin:
第一次请求登录,认证成功后,后端返回一个token(定时刷新token),保存到浏览器cookie中(session cookie)。后续请求中会在请求头中携带token,后端通过这个token进行用户身份认证。
优点:浏览器关闭,token失效
这是一个开源前端项目,所以不知道token是如何定义,如何认证,如何刷新的?
项目D:
第一次请求登录,认证成功后,后端返回一个token(jwt),保存到浏览器cookie中(持久化cookie)。后续请求中会在cookie中会携带token,后端对token进行签名验证后提取用户相关信息(jwt的payload部分包含userId等用户状态信息)。
(该项目token分为access-token和refresh-token,access-token失效后,通过refresh-token申请新的access-token)
优点:无状态
jwt保存用户相关信息,减少了数据库操作
缺点:token保存在cookie中,会受到CSRF攻击
token保存在持久化cookie中,不适合用于公共场合项目
refresh-token略显鸡肋,暂时不知道适用场景(待研究)
总结了那么多项目,也想有一套自己觉得最优的方案:
项目X:
第一次请求登录,认证成功后,后端返回一个token(jwt),保存到浏览器cookie中(session cookie)。后续请求中会在请求头(Authorization)中携带token,后端对token进行签名验证后提取用户相关信息。
验证过程分成两步:1.签名验证,jwt是否被篡改
2.有效期验证,获取payload中的exp字段,是否过期
项目A还可以改造一下
小程序可以用临时登录凭证code换取用户唯一标识OpenId和会话密钥session_key,OpenId可以作为数据库查询的主键放在jwt中,session_key可以作为加密签名的密钥,返回的token保存到客户端本地存储中。后续请求中会在请求头中携带token,后端对token进行签名验证后提取用户相关信息。
思考:
我们说服务端是无状态的,是因为所有状态信息都附加在token上,有利于在多个服务间共享,实现分布式
token可以存储在浏览器cookie、sessionStorage中,虽然可能出现cookie随请求一同发送的情况,但是我们验证的是请求头信息
cookie存在跨域问题(跨域访问不携带cookie),但是通过nginx转发也可以解决这个问题~
参考:https://www.cnblogs.com/cjsblog/p/9277677.html
https://www.cnblogs.com/xiekeli/p/5607107.html
http://www.sohu.com/a/223668215_463994