![6c3d9adc771dc3b191e093f0cd22c4d5.gif](https://i-blog.csdnimg.cn/blog_migrate/9404f0a8f3bcc38a01d78044fa2b1ee2.gif)
Cookie+Session还是JWT呢?
![6130999bf622e98f559f176d3e157fa8.png](https://i-blog.csdnimg.cn/blog_migrate/1f1c25c5677b0ea6580d0ff8f37ea3f6.png)
最近做了一下调研,在纠结这个新项目要用 jwt 呢?还是采用session+cookie这种形式呢?
顺便好好记录下他们的特点,还有一些要面对的问题? 有什么不对的欢迎指正!! 谢谢!?
![a9404cc9713c1c301e86b89f34a13c06.gif](https://i-blog.csdnimg.cn/blog_migrate/b24fd416ebe3e17885db01514f001b7f.gif)
Cookie,Session,Token(JWT) Cookie 特点: 属性表 例子 set-cookie: 过期时间: Session 特点: 问题: Token 特点: 问题: (一) 怎么让token
无效? (二)为什么需要一个新的refresh-token
呢? (三) 使用 refresh token 时的并发问题?
1.Cookie
特点:存储在浏览器中,一般情况下每次请求都会带上这个cookie
cookie 无法跨域 (跨域涉及到 浏览器的同源策略 (协议,端口,域名))
cookie 最大是 4kb
浏览器一般允许存放 300 个Cookie,一个站点最多存 20 个Cookie
属性 | 必填 | 说明 |
---|---|---|
name=value | 是 | cookie的 名称和值 |
Expires=date | 否 | cookie 的过期时间 客户端和服务端的时间不同步的话会有问题 |
Max-Age=number | 否 | cookie的存活时间 number是一个数字,时间单位是秒0或者负数会让 cookie 立刻过期 Expires 和 Max-Age 这两个同时设置时,Max-Age 会生效,因为它优先级比较高 |
Domain=value | 否 | cookie 所属的域名 |
Path=value | 否 | 只有当路径匹配时才会带上该 cookie ,匹配时也会匹配子目录 如:/aa 则 /aa/bb 也会被匹配到 |
Secure | 否 | 表示该 cookie 只在https中有效 ,有助于减少 中间人 攻击 |
HttpOnly | 否 | 禁止 JavaScript 访问 cookie,有助于防范 XSS(Cross Site Scripting) 攻击,跨域脚本攻击 |
SameSite=value | 否 | 1. Strict 最严格的模式,当当前网页的 URL 与请求目标的 URL 一样时,才会带上 cookie(注意iframe!) 2.Lax ( 默认 ) 像加载图片和frames就不会发送了,但是在用户点击a标签链接时会发出 3.None 设置该选项必须带上 Secure 属性!防止 CSRF (cross-site request forgery) 攻击 |
具体可以看看 https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
例子:1
set-cookie:
可以在 浏览器 的 response header 中 看到类似下面的参数:
set-cookie:
dwf_sg_task_completion=False; Domain=developer.mozilla.org; expires=Sat, 09 Jan 2021 06:53:31 GMT; Max-Age=2592000; Path=/; Secure
2
过期时间:
如果没有设置这个过期时间的话,这个cookie就会随着浏览器的关闭而消失 。在浏览器的控制台中表示为 session
![4bb028a4a89aecb3bda1fbf7294f6034.gif](https://i-blog.csdnimg.cn/blog_migrate/256f4bb4a960409dacdf337038aa39df.gif)
2.Session
特点:
存储在服务器端
每次访问都会刷新它的有效时间
由于session存储在服务器,所以我们要考虑到服务器的压力问题,当在线人数多的时候,session所占用的服务器资源肯定会比较多,此时单个服务器的压力是比较大的,我们可以考虑做个集群,减轻单个服务器的压力。
接着就得考虑下这个session
的共享问题了,怎么在多个不同的服务器上共享这个session
呢?
这里也有多种不同的解决方案,比如:
使用
Spring session
来解决这个共享问题使用
nginx
的 负载均衡机制中的ip_hash
模式 ,将对应的ip
路由到指定的服务器上使用redis 存储 session
tomcat
集群的session
复制
![4bb028a4a89aecb3bda1fbf7294f6034.gif](https://i-blog.csdnimg.cn/blog_migrate/256f4bb4a960409dacdf337038aa39df.gif)
3.JWT
jwt 可以看看博主之前的这篇文章 从 JWT 到 JWS 到 JJWT?
特点:服务端实现无状态化
记录用户的信息,避免多次查询数据库
存储在客户端
能够避开同源策略,不用考虑跨域问题
因为不用cookie了,也可以避开
CSRF
攻击
1
怎么让token无效?
这个问题的实际场景有如下:
怎么解决用户注销问题?
![9db35460c1ec8721ac6195aef549b88e.png](https://i-blog.csdnimg.cn/blog_migrate/6eeef8032c7d68c81507ff31729ad5a2.jpeg)
![e70962e26344d7e60a0a3ac2664cf59d.png](https://i-blog.csdnimg.cn/blog_migrate/18b5e4d2a07746edae2cb85553e42245.jpeg)
用户改密码了怎么办?
由于服务端的无状态化,不能很好地控制这个token
,所以我们可以考虑让它变成有状态的。
虽然JWT是无状态的,但是有些场景session+cookie
也不好用,特别是现在这么多app呀,小程序等各种各样的移动场景,还有服务端分布式的盛行,使用jwt
的优势还是比较明显的?,所以还是不去纠结这个jwt怎么像session了?
哈哈哈 毕竟这里使用的方式也是类似spring-session那样的机制。
可以在服务端的redis
中记录这个token
,直接将用户id和对应的token存到redis中,并设置过期时间,当用户修改密码或者注销登录时删掉该token
,这样当用户操作其他功能时,在redis
中找不到对应的token
,就判定该token
无效。
![6cf01a6f48de6428de44935783b02157.png](https://i-blog.csdnimg.cn/blog_migrate/b6bd702075d83efc6a36253583381068.png)
redis 命令如下:
创建时:
setex uuid 1800 token
删除时:
del uuid
这样做也可以应对用户在多端登录的情况,如:在PC登录和在手机登录,如果在redis中已经有这个token了,就直接返回对应的token
就好了。
接着我们要解决第二个问题了?
![3926a616ded2775413741b94ddb7adbc.gif](https://i-blog.csdnimg.cn/blog_migrate/b599e111bf7e7cc838063b54a0587b7a.gif)
2
为什么需要一个新的refresh-token
呢?
用过 Oauth2
协议 的小伙伴们肯定熟悉, 上面的那个token
我们称之为 access-token
,同时呢 , 我们也需要一个 refresh-token
, 来获取新的token
。
眯着眼猜测肯定是因为安全啦 哈哈哈 ,博主的简单猜测:因为你的access-token
每次请求都会带上,万一你发的请求被拦截到了?,别人就可以拿着你的token
去干坏事了,但是这个 access-token
的有效时间一般是比较短的,而且不能 续期 ,所以这也间接地提高了系统的安全性。
当这个 access-token
过期时, 我们就通过这个 refresh-token
去获取新的 access-token
,以避免频繁地让用户重新登录。
当然我们可以看项目的实际使用再决定需不需要使用 Oauth2
?。
如果只是登录自己的服务器,不用第三方认证授权的,我们可以自定义这个jwt?,只是在定义时多定义一个 refresh-token
,但是这个 refresh-token
只能用来获取新的 access-token
, 不能做其他的, 同时它的过期时间也比较长,? refresh-token
一般是30天。
观察微信后发现,如果你的账号在别的手机设别上登录,则你需要再重新登录才可以继续在自己的手机上使用,所以我们也可以在jwt中记录下手机设备的唯一值?,用来鉴别用户是否需要重新登录,同时也可以根据业务需求去定义这个 refresh-token
是否需要续期,是的话多久更新一次等等细节
接着就有了第三个问题了?
![3926a616ded2775413741b94ddb7adbc.gif](https://i-blog.csdnimg.cn/blog_migrate/b599e111bf7e7cc838063b54a0587b7a.gif)
3
使用 refresh token 时的并发问题?
额 这个问题感觉比较复杂,毕竟涉及到并发,可以考虑加锁呀。
当这个 access-token
过期时,后端将返回一个特定的状态码告诉前端说这个token过期了,需要重新刷新?。而前端此时因为异步的原因,导致同时发了好几个 刷新token的 请求到达后端,这时就出现了这个并发的问题了。
解决方案:
后端:重新生成新的 refresh-token
,同时替换掉该用户对应的 refresh-token
和 access-token。利用 redis锁机制,如 SETNX
, 将这个旧的 refresh-token
和 新生成的 access-token
放到一起,并设置过期时间如3s,这样3s内刷新token的请求都会拿到同个新生成的 access-token
,则3s后用户面发送过来的 refresh-token
会无效
后端:不重新生成 refresh-token
, 也是利用 redis锁机制,如 SETNX
, 直接 将 refresh-token
和 新生成的 access-token
对应起来,并设置过期时间如3s,3s内直接返回该 access-token
,同时替换掉该用户对应的 access-token,3s后可继续重新生成
前端:暴力点就直接在 access-token
快过期的前几分钟去调用一次刷新 token的接口。
前端:温和点的话,就设置延迟处理,记录下上个刷新token的时间,如果时间太短的话就不发出去?
谢谢可爱又帅气的大佬们的观看!祝您 天天开心!?
感谢您的关注!您的每个关注,都是博主生发的动力 ?
![126e0680a078d3d3823d18d0b75549fb.png](https://i-blog.csdnimg.cn/blog_migrate/e0cbff4dbcdafaf17c7f78160e780582.png)
点个“在看”表示朕
已阅
往期精彩回顾
![38429d91199c2a06de25fcc59eb494c2.png](https://i-blog.csdnimg.cn/blog_migrate/8158a05d4e2320533ffa0025db92c3c9.jpeg)
序列化单例模式的实现————readResolve 源码解读
![f168143d636bd8779a2c4594aa02fa69.png](https://i-blog.csdnimg.cn/blog_migrate/d136f5aaa34880b7d51d681cff86a04a.jpeg)
单例模式八个例子
![123df6a1f5f16904d8f256b7e067ecf9.png](https://i-blog.csdnimg.cn/blog_migrate/3449e544c9436c916ec960876a4c32aa.jpeg)
java8四大函数式接口
![cc978455797cff2f85c5e69a1c7c294c.png](https://i-blog.csdnimg.cn/blog_migrate/b2ee69aa362ed0826e20dc27d8841117.jpeg)
jackson解析泛型的正确写法