阅读 1992

刚刚,给学妹普及了登录的两大绝学

今天跟大家聊一个比较基础的话题,就是实现登录的方式有哪些?适合刚入行的朋友。

华山之 Session 绝学

Session 我们称之为会话控制, 是一种在服务器端保持会话状态的解决方案。通俗点来讲就是客户端访问服务端的时候,会在服务端存储对应的信息,生成一个 Session ID 返回给客户端,客户端下次过来的时候带上 Session ID,这样就能识别访问者的身份。

请求中带上 Session ID 最常见的方式就是通过 Cookie 来承载了,Cookie 是客户端保存用户信息的一种机制,在浏览器环境中,请求会自动带上 Cookie 信息,服务端也就能获取到 Session ID。

在后端实现登录逻辑的时候,先获取 HttpSession 对象,然后通过 setAttribute()来设置登录的用户信息,比如用户 ID。验证有没有登录的时候通过 getAttribute()来获取对应的 Session 信息,如果没有获取到,则证明没有登录过或者会话失效了。

对于 Tomcat, Jetty 这些容器而言,Session 就是是一块在服务器开辟的内存空间,存储结构就是 Map。

Tomcat 的 Session 实现类是 StandardSession。

图片
图片

分布式 Session 解决方案

如果你的应用是单节点部署,这种场景使用web容器实现的 Session 机制没有问题。一但压力过大,需要多节点部署的时候,Session 就需要进行分布式的支持。

看下图,当部署了两个 Tomcat 的时候,通过 Nginx 进行负载均衡,第一次请求转发到了 Tomcat1, Session 信息存储在 Tomcat1 上面。第二次请求转发到了 Tomcat2 上面,但是 Tomcat2 上面是没有刚才的 Session 信息,这就是多节点下 Session 会出现的问题。

图片
图片

Session 复制

Tomcat 内置了 Session 复制的功能,也就是你的 Session 是在 Tomcat1 中产生的,Tomcat1 会将你的 Session 同步给 Tomcat2, 这样当你的请求到了 Tomcat2 的时候,就能知道你的身份信息。

图片
图片

这种方案在其他的框架中也经常能见到,比如 Spring Cloud 体系中的 Eureka 注册中心,也是采用复制的方式来同步注册表的信息。

关于 Tomcat Session 复制相关配置请参考官方文档:tomcat.apache.org/tomcat-8.0-…

黏性会话

黏性会话指的是对于同一个用户的请求,永远都只转发到某一个 Tocmat 的实例上,这样即使没有做 Session 复制,也不会出现问题。如果有节点挂掉了就会访问失败。

常见的方式有对 IP 做 Hash 进行转发,IP 不太可靠,因为会变。在 Nginx 中有一个 nginx-sticky-module 这个第三方模块用于添加一个粘性 Cookie,该粘性 cookie 始终转发到同一服务器。

nginx-sticky-module 会在 Cookie 中记录一个值来标识当前请求需要被转发到哪个节点,第一次没有的时候会先转发,然后在响应给客户端之前写入 Cookie。后面的请求都会在 Cookie 找到对应的标识,然后进行转发到固定的节点。

图片
图片

Session 集中存储

Session 复制会占用服务器资源,影响性能。黏性会话存在单点故障风险。更好的分布式 Session 方式就是集中式存储。

所谓集中式存储就是将会话信息统一存储在某个地方,像 Tomcat 之类的 Web 服务器本身不存储会话信息,这样后端服务也就是无状态的,方便随时扩容。

图片
图片

至于实现方案的话有很多,大家可以自己去实现 HttpSession 做对应的存储读取逻辑,也可以采用开源的方案。比如 Spring Session 就是一个很好的开源方案,上手简单,支持多种存储方式,比如 Redis, Mysql 等。

如果对手写 Spring Session 原理感兴趣的,也可以参考我之前的这套课程:cxytiandi.com/course/5

少林之 Token 绝学

Token 认证是目前主流的认证方式之一,Token 最大的优势在于无状态,并且不用存储会话信息。也就是说通过 Token 就可以知道当前访问的用户是谁,不需要去 Web 容器的内存中获取,不需要去集中管理会话的存储中去获取。

Token 的生成方式有多种,可以自己定义固定的格式,比如里面包含了用户 ID,用户名等信息。也可以使用目前主流的 JWT 方式。

JWT(JSON Web Token)是为了在网络应用环境中传递声明而执行的一种基于 JSON 的开放标准。JWT 的声明一般被用在身份提供者和服务提供者间传递被认证的用户身份信息, 以便从资源服务器获取资源。

比如在用户登录时,基本思路就是用户提供用户名和密码给认证服务器,服务器验证用户提交信息的合法性;如果验证成功,会产生并返回一个 Token,后续请求用户带上这个 Token ,服务端就可以识别这个请求的身份信息。

JWT 由三部分构成,

  • 第一部分是头部(Header);
  • 第二部分是消息体(Payload);
  • 第三部分是签名(Signature)。

一个 JWT 生成的 Token 格式为:

token = encodeBase64(header) + '.' + encodeBase64(payload) + '.' + encodeBase64(signature)

头部的信息通常由两部分内容组成,令牌的类型和使用的签名算法,比如下面的代码:

{ "alg": "HS256", "typ": "JWT" }

消息体中可以携带一些应用需要的信息,比如用户 ID,代码如下:

{ "id": "1001", "name": "yinjihuan"}

签名是用来判断消息在传递的路径上是否被篡改的,从而保证数据的安全性,格式如下:

HMACSHA256( base64UrlEncode(header)  + "." +  base64UrlEncode(payload), secret)

通过这三部分就组成了我们的 JSON Web Token。

如何使用请参考 Github:github.com/jwtk/jjwt

图片
图片

如上图所示:请求到达 Tomcat 后,可以调用单独的 Token 服务进行 Token 的生成,也可以将 Token 的生成逻辑封装成一个 jar 包来使用。需要注意的是如果用内嵌的方式,对应 Token 的加密配置要一致,否则会出现验证失败的情况。

Token 有点不好的地方在于无法主动让它失效,比如我们用 Session 的场景,用户退出登录,直接将 Session 信息在服务端删除即可,即使后面用相同的 Session 信息去请求,服务端也找不到对应的信息了。

Token 是一个加密的字符串,里面包含了用户的信息,加密算法,过期时间。如果过期时间设置的比较长,也就意味着在过期时间之前都可以使用。

如果要实现退出登录的功能,既然不能对 Token 本身的过期时间进行改造,那么可以使用一个黑名单的机制来进行过滤即可。将退出登录的 Token 存储起来,使用的地方去匹配是否注销了,然后进行拦截即可。

关于作者:尹吉欢,简单的技术爱好者,《Spring Cloud 微服务-全栈技术与案例解析》, 《Spring Cloud 微服务 入门 实战与进阶》作者, 公众号猿天地发起人。

我整理了一份很全的学习资料,感兴趣的可以微信搜索「猿天地」,回复关键字 「学习资料」获取我整理好了的 Spring Cloud,Spring Cloud Alibaba,Sharding-JDBC 分库分表,任务调度框架 XXL-JOB,MongoDB,爬虫等相关资料。