用户认证


HTTP是一个无状态的协议,一次请求结束后,下次再发送服务器就不知道这个请求是谁发来的了(同一个IP不代表同一个用户),在Web应用中,用户的认证和鉴权是非常重要的一环,实践中有多种可用方案,并且各有千秋。

image.png

image.png
image.png
Token服务端不再存储信息,不再存储Session
image.png
image.png
image.png
image.png
image.png
image.png
image.png

Cookie—Session认证模式

在Web应用发展的初期,大部分采用Cookie—Session的会话管理方式,逻辑如下。

  1. 用户在浏览器端填写用户名和密码,并发送给服务端进行认证
  2. 服务端验证用户名和密码正确以后,生成一份保存当前用户相关信息的session数据和一个与之对应的标识(通常称为session_id)。
  3. 服务端返回响应时将上一步的session_id写入用户浏览器的Cookie。
  4. 服务端通过SessionID查找Session并进行鉴权,返回给客户端需要的数据。

    基于Session存储的方式存在一些问题。

  • 服务端需要存储Session,并且由于Session需要经常快速查找,通常存储在内存或内存数据库中,同时在线用户较多时需要占用大量的服务器资源。
  • 当需要扩展时,创建Session的服务器可能不是验证Session的服务器,所以还需要将所有Session单独存储并共享。
  • 由于客户端使用Cookie存储SessionID,在跨域场景下需要进行兼容性处理,同时这种方式也难以防范CSRF攻击。

    Token认证模式

    鉴于基于Session的会话管理方式存在上述多个缺点,基于Token的无状态会话管理方式诞生了,所谓无状态,就是服务端可以不再存储信息,甚至是不再存储Session,逻辑如下。

  • 客户端使用用户名、密码进行认证。

  • 服务端验证用户名、密码正确后生成Token返回给客户端。
  • 客户端保存Token,访问需要认证的接口时在URL参数或HTTP Header中加入Token服务端通过解码Token进行授权,返回给客户端需要的数据。

    JWT介绍

    JWT全称JSON Web Token是一种跨域认证解决方案,属于一个开放的标准,它规定了一种Token实现方式,目前多用于前后端分离项目和OAuth2.0业务场景下。在移动互联网时代,我们的用户可能使用浏览器也可能使用APP来访问我们的服务,我们的web应用可能是前后端分开部署在不同的端口,有时候我们还需要支持第三方登录,这下Cookie-Session的模式就有些力不从心了。 JWT就是一种基于Token的轻量级认证模式,服务端认证通过后,会生成一个JSON对象,经过签名后得到一个Token(令牌)再发回给用户,用户后续请求只需要带上这个Token,服务端解密之后就能获取该用户的相关信息了。(想要连接JWT的原理,推荐大家阅读:阮一峰的JWT入门教程)

一个JWT Token的模样:
image.png
它是由分隔的三部分组成,这三部分依次是:

  • 头部(Header):通常头部有两部分信息。Token的类型、加密算法。

image.png
我们会对头部进行base64加密(可解密),得到第一部分数据

  • 负载(Payload):表示负载,将Token当作是一个整体,表示Token里面装的是什么。也是一个JSON对象,JWT规定了7个官方字段供选用。

image.png
除了官方字段,我们也可以自己指定字段和内容,如下:
image.png
这部分也会采用base64加密,得到第二部分数据

  • 签名(Signature):对前两个的签名,防止数据被篡改,首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用Header里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

image.png
头部和负载以JSON形式存在,这就是JWT中的JSON,三部分的内容都分别单独经过了Base64编码,以拼接成一个JWT Token。

JWT 的交互流程

image.png

JWT 的优缺点

JWT拥有基于Token的会话管理方式所拥有的一切优势,不依赖Cookie,使得其可以防止CSRF攻击,也能在禁用Cookie的浏览器环境中正常运行。

而JWT的最大优势是服务端不再需要存储Session,使得服务端认证鉴权业务可以方便扩展,避免存储Session所需要引入的Redis等组件,降低了系统架构复杂度。但这也是JWT最大的劣势,由于有效期存储在Token中,JWTToken一旦签发,就会在有效期内一直可用,无法在服务端废止,当用户进行登出操作,只能依赖客户端删除掉本地存储的JWT Token,如果需要禁用用户,单纯使用JWT就无法做到了。

基于JWT实现认证实践

前面讲的Token,都是Access Token,也就是访问资源接口时所需要的Token,还有另外—种 Token,Refresh Token,通常情况下,Refresh Token的有效期会比较长,而Access Token的有效期比较短,当Access Token由于过期而失效时,使用Refresh Token就可以获取到新的Access Token如果Refresh Token也失效了,用户就只能重新登录了。

在jwt的实践中,引入Refresh Token,将会话管理流程改进如下。

  • 客户端使用用户名密码进行认证。
  • 服务端生成有效时间较短的Access Token(例如10分钟),和有效时间较长的RefreshToken(例如7天)客户端访问需要认证的接口时,携带Access Token。
  • 如果Access Token没有过期,服务端鉴权后返回给客户端熏要的数据。
  • 如果携带Access Token访问需要认证的接口时鉴权失败(例如返回401错误),则客户端使用Refresh Token向刷新接口申请新的Access Token。
  • 如果Refresh Token没有过期,服务端向客户端下发新的Access Token客户端使用新的Access Token访问需要认证的接口。

image.png
后端需要对外提供一个刷新Token的接口,前端需要实现一个当Access Token过期时自动请求刷新,Token接口获取新的Access Token的拦截器。

Gin框架使用JWT