怎样实现登录?| Cookie or JWT

先问小伙伴们一个问题,登录难吗?“登录有什么难得?输入用户名和密码,后台检索出来,校验一下不就行了。”凡是这样回答的小伙伴,你明显就是产品思维l ; l A C L { F,登录看似简单,用户名和t [ p & y $ l _ S密码,后台校验一下,完事了。但是,登录这个过程涵盖的知识点是非常多的,绝不是~ I _ x N S M检索数据,校验一下这么简单的事。

那么登录都q r n Y # C G要哪些实现方式呢?i最传统的就要是Cookie-Session这种方式了,S b h最早的登录方式都是这样实现的/ 2 ] } y # Y。但是随着手机p J x + / , y端、H5端的兴起,前后端分离的模式越来越流行,基于Cookie-Session这种登录方式不是很方便,渐渐的JTW开始流行,现在大部分项目的登录方式都是基于JWT的了。那么Cookie和JWT都是[ m | O L + : { C怎样实现l h C登录的呢N D h p c I u?这两种方式有什么区别呢?我们在0 d - 做登录的) [ _x时候该怎么选择呢?咱们O t I i ; H X x 2先看看这两种方式+ A Y D R的原理。

CookieB O + [ g O P方式

因为Http协议是无状态的,我们后台的服S S ` / ` k务(如Tomcat)在接收到前端发送过来的Http请求时,是 r X L o区分不出哪个请求是谁发出的,这和我们的登录功能是相违背的,x ] l t C E登录的功能就是要区分每一个请求是由哪个用m ; R户发出的,这就变成了有状态,那怎么办呢?Cookie应运而生,Q 2 Q R k L 4 0 iCookie是存储浏览器端的,在Cookie中存储的内容是键值对,也就是name-value。浏览y ~ r W 4器在向后台发送请求的时候,会把Cookie放在请求头中,传送给后台的服务,后台的服务会从请求头中取到Cookie,再从Cookie中取出键值对中jsessionid对应的值。这个jsessionid的值就是你这次会话的id,对应着服务端的一个session。

好了,到这里session这个概念出来了,session是什么呢?session是存储在服务端的,每一个会话对应服务中的一个session。咱们可以把- L V v z session理解为e 5 W _ E g = w一个MaN i Z l y lp,它的key% f G J y储的session的id& z = [ s a c,value存储的东西就随便了,我们( ~ R V n j ~ N s在写程序时想存啥就存啥。它的key存储的值就是Cookie中存储的jsessG U G t Y Sionid的h d y p值,这样,浏览器发送请求到后台服务,后台才能根据Cookie中的jsessionid取到对应的session,再从session中取到之前存储的状态,如存储在sessi1 s D :on中的登录状态、用户id等。Cookie-Session机制是通用的,所有的浏览器都支持Cookie,就连最低端的IE都支持,你说他普遍不普遍。Session是后端容器必须支持的,如Tomcat,还有像其他的如Resin、jettv 8 A ky等。这些对开发人员都是透明F + ( ; l 4的,无需过多关注。

Cookie-~ $ f w x * V h 2Sess^ N % @ + G ?ion的由来给大家说完了,我们看B u M ; D d ~ $看基于Cookie这种方式的登录流程,

怎样实现登录?| Cookie or JWT

  • 用户在浏览器输入用户名、密码,点击登录,发送请求到后台服务;
  • 后台服务校验用户名、密码,将登录状态状态和用户id存储在session中;% A N |
  • 将session的id存O q s ? U y储在Cookie中,通过响应头返回到浏览器;
  • 当用户点击其他功能时,向后台发送的请求中会自动带上Cookie;
  • 后台通过Cookie中的jsess{ Y E d w *ionid找到对应r { h ^ { j +的session,开发人员可从sesT $ P 9 /sion中取出当前会话的登录状态和用户id。

基于Cookie-Session机制的登录实现方式的整体流程就是这个样子。看上去很完美,但还是存在不少问题的,我们来看看这些问题。

~ - p Z 2布式会话

上面的示例,我们的后台服务只有一个,一个服务往往很难支撑服务,为了保障可靠性,最少都是部署两个后台服务。但是当部署多个后台服务时,我们的session就会出现问题,看看下面的图,x q 3 6 Z :

怎样实现登录?| Cookie or JWT

  • 假如用户登录的请求,分配到了后台服务1,后台服务1的session存了用户的登录状态和用户id。
  • 用户在点击其他功能时,请求分配到了后台服务2,可是后台服务2的session并没有存储登录状态和用户id。

我们怎么解决这个问题呢?其实也很简单,第一,session集中管理,比如使用Redis;第二,所有的后台服务在获取session时,统一从Redis中获取。如下所示,

怎样实现登录?| Cookie or JWT

我们可以使用中间件Spring-SessionW 0 i % ) U和Redis就可以解决这个问题。

CORS

使用Co? K } 7 | J O eokie实现% ] a M T y n 3登录的另外一个问题就是跨域,现在往往都采用前后端分离的方式进行开s 3 . G h 发,在开发的过程中,前端和后端通常不在一个域下,由于浏览器的同源策略,Cookie不能传入到后端。至于同源策略,不明白的小伙伴可以问一下度娘,这里不过多介绍了。要解决这个问题,在前端、后端都要进行设C : q z置,在我的另一篇文章《前后端分离|关于登录状态那些事》中m L 6 v有详细的介绍。总{ 3 B C G Y A体归纳为:

  • e c & , 4端设置CORS允许跨域的域名,并且withCredentials设置为true;
  • 前端在向后端发送请求时,也需要设置withCr { $ B 3 _redentials = true;

5 c d A U { V ( 0样,我们的Cookie就可以实现跨域了。不进行这些设置,Cookie跨域是不可能的,同源策略保证I S 9 d : Z ! ? 3了我们Cookie的安全。

CSRF

CSRF,这个CORS是不一样的,长的比较像,也比较容易E ) M 8 p ` * 9 {混。CSRF往往和系统的安全扯上联系,也是等保测试中比较重要的测试内容,它也是和Cookie有关的,大体的Y R t ! $流程是这样的,

  • 用户登录了A网站,并没有退出;
  • 此时,用户又访问了B网站;
  • 在B网站有个隐藏的请求,请求了A网站的一个重要的接口,比如:转账、支付等。
  • 在请求A网站的同时,带上了A网站的Cookie,所以一些危险的操作就成功了。

关于CSR@ L B )F的攻防,在$ T ^ t 7 ! . S 2我前面的文章《CSRF的原理与防御 | 你想不想来一次CSRF攻击?》中有详细的介绍。总之,使用Cookie实现登录是需要重点防范一下CSR( ~ T B J e _ *F攻击的。

JWT方式

近年来,由于手机端的兴起,前后端分离2 { ]开发方式的流行,JWT这种登录的实现方式悄然兴起,那么什H I O * -么是JWT呢?JW) i j E a $ CT是英文JSON Web Token的缩写,它由3部分组成,

  • header,一般情况下存储两个信息,1类型,一般都是JWT;2加密算法,比如:HMACA ` d e u -、RSA等;
  • payload,这里就存储登录的相关信息了,比如:登录! { $ C K W O [ s状态# b [ ^ n J s r、用户id、过期时间等。
  • signature,签名,这个是将header、payload和密钥的信息做一次加密,后台在接收到JWT的时候,一定要验签,谨防JWT的伪造。

下面咱们看看 / 8 w tJWT的登录实现,

怎样实现登录?| Cookie or JWT

我们看到整体的流程和Cookie的实现方式是一样的,只不过是没有用到Cookie、Session。那么它与Cookie-Session的区别是什么呢?

  • 登录状态、用) ) & j户id并没有存储到sess0 q 1 ~ Oion,而是存在JWT的payload里,返回给了前端。
  • 在前端JWT不会自动存储到Cookie中,前端开发人J p n p T z /员要处理JWT的存储问题,比如LocalStorage
  • 再次发起请求,JWT不会自动放到请求头中,需前端同学手动设置
  • 后端从请求头中取出JWT,验签通过后,拿到登录状态、用户id,不是从session中取

相比Cookie的方式,JWT的= * } n X f方式需要更多的开发工作量。那么其他的问题存在吗?+ O 7 c我们一个一个看。

分布式会话

我们后台部署多个服务,会有分布N # _ % + : w式会话的问题吗?

怎样实现登录?| Cookie or JWT

无论请求被分配到哪一个后台服务中,登录状态和用户id都是从JWT中取出来的,不会出现分布式会话的问题。我们在后台部署集群的时候,根本不用care这个问题。

CORS

Cookie的跨域受到同源策略的保护,不经过S q w D E r d E特殊的w V 3 h m S g设置,是不能够跨域b H n M )的。那么JWT呢?JWT是前端同学手动在请求头中设置的,如果向其他的域发送请求要注意,稍不注意,在请求的时候,调用了封装的公共方法,就会把JWT发送给其他域的后台,前端的小伙伴要打起精神啊。

CSRF

Cookie的方b - 3 3 *式,B访问A网站[ U ; ;,会把A的Cookie带上,从而造成了安全隐患。; = 2 I w n那么JWT呢?JWTB x 7 s在前^ | n端存储在A网站的域下,B在访问A网站时,是拿不到A网L 0 P ! L ( .站的JWT的,那么也不可能在请求头中设置JWT,A网站的后台拿不到JWT,也不会做其他操作。所以,JWT可以很好的防止CSRF攻击。

总结

通过前面我们对Cookie和JWT的分析,可以总结成如下的表格,

Cookie-Session JWT
工作量 浏览器和容器天然支持 需要额外开发,有一定工作量
分布式会话 需要借助中间F s M , { + d P Q 无需关心,登录信息从JWT解出
CO5 # U :RS 不支持跨域、需特殊设置 开发人员设置请求头,可以跨域
CSRF 需特殊防范 无需防范,第三方拿不到JWT

好了,Cookie和JWT` P r C a [的特点都总结出来了,大家在实现登录的时候,就各取所需吧。结合自己的项目,选择适合自己项目的实现方式吧。