揭秘Cookie、Session、JWT之间的关系

登录认证是任何系统中避不开的一个话题,登录认证随着系统架构的演变而出现与架构相适应的方案。下面介绍架构演变过程中登录常客Cookie、Session、JWT之间的关系。

1、单体架构

单体架构阶段下,前端代码和后端代码都在一个项目中,也就不存在跨域问题。此时的登录认证机制采用的是Cookie和Session的方式进行。认证的方式如下:

用户访问的服务器的时候,携带服务器给予的sessionId到后端,后端拿到sessionId之后就可以拿到登录用户的信息。

2、前/后端分离

单点的架构中前端和后端代码都在一个项目中,对开发人员的开发、业务部署都带来了阻力,于是就出现了前后端分离模式,如下的前后端分离模式图:

前后端分离模式中出现了跨域问题,跨域使得浏览器和服务器数据交互受到阻碍,并且也无法获取Cookie中的sessionId了。如果想要通过Cookie和Session方式来实现登录认证,那么就要解决跨域问题,此时就有了跨域资源共享(CORS)。CORS的流程如下:

通过CORS解决了跨域问题之后,登录认证的方式继续使用Cookie和Session方式来完成。

3、分布式系统

随着业务发展的发展,单个系统拆分成多个子系统并且子系统部署多台服务,此时的服务与服务之间的session是不共享的,如果想要采用cookie和session方式实现登录认证功能,那么就要解决session共享的问题。

业界主流使用的是单点登录来解决分布式下的session共享问题,其流程如下所示:

(1)用户请求订单服务的时候,订单服务发现其没有登录,于是请求认证中心进行认证。

(2)认证中心弹出登录页让用户填写账号和密码,认证系统对其账号与密码进行验证,验证通过之后把登录状态写入SSO的session中,同时浏览器也会写入SSO的Cookie中。

(3)SSO登录系统完成以后生成ticket,然后跳转到订单服务上,同时把 ticket 作为参数传递给订单服务上,订单服务拿到 ticket 后从后台向SSO发送请求来验证 ticket 是否有效,验证通过后订单服务把登录状态写入到session中并设置订单服务域下的Cookie。

通过以上的步骤就实现了跨域的单点登录,后面再访问订单服务的时候,订单服务上就是登录状态。

(4)用户此时想访问商品服务,商品服务没有登录就会跳转到SSO上,由于SSO在用户访问订单服务的后已经是登录状态了,所以不需要重新登录验证。

(5)SSO会生成ticket给商品服务,浏览器会跳转到商品服务上并把 ticket 作为参数传递给商品服务,商品服务拿到ticket后再去后台访问SSO验证ticket的有效性,SSO验证通过后商品服务把登录状态写入session中并在浏览器中写入商品域的Cookie。

下面介绍一种使用Redis和Cookie实现的单点登录的设计方案,具体的实现流程图如下所示:

4、分布式系统下的去中心化认证

Cookie和Session机制、Cookie和Redis机制都需要服务器需要维护一个会话或者组件来标识这些用户已经登录;那么是否存在一种认证方式直接就可以从请求中知道这个用户是哪个人呢?由于就出现了无状态登录,典型的实现方案是JWT机制,JWT机制的工作原理如下:

无状态登录最大的优势在于系统的水平扩展性强,因为服务器不需要维护每个用户的登录信息,所以可以非常方便的为这个集群增加实例。

无状态登录的最大问题就是没有办法注销,一旦令牌颁发出去了,除非令牌自己过期了否则令牌会一致有效,所以设计中需要增加Redis来辅助实现强制的注销的功能。

总结:

(1)Cookie和Session机制是实现登陆认证的基本方式,随着系统的不断演变,于是单体架构下的这套认证机制失效了,为了适应这套认证流程出现了不同的解决方案来保证认证流程正常的使用。

(2) Cookie 和Session机制是有状态的登陆,为了适应一些特定的场景,出现了无状态登陆机制(JWT)。

9