[浅析]图解前端登录鉴权方案

2,942 阅读7分钟

登录鉴权是业务中非常重要的一个环节,涉及到后续所有接口的请求权限和数据安全问题。虽然目前业界已经有很成熟的解决方案,但是肯定还是有些小伙伴知其然,不知其所以然。为了探索其中的实现原理,今天笔者就来简单的分析一波,用图画的方式配上少许文字,希望您能理解。

PS:本篇文章不讲太多原理性的内容

前端登录鉴权方案.png

以上是我总结的四种登录鉴权方案,废话不多话,直接上图吧

(1)纯cookie验证

Untitled-2022-01-07-1013.png

步骤分解
  1. 浏览器首次登录,服务器收到请求后去数据库校验用户名及密码
  2. 校验通过后,从数据库查询对应的用户信息
  3. 把非重要信息(如密码,邮箱等)通过响应体set-cookie存入到浏览器中
  4. 浏览器下次请求接口,自动携带cookie信息在请求体中,其中包含了用户的信息(如username
  5. 服务器拿到请求体中的cookie,如果包含username等用户信息,则校验通过
细节点

服务器在响应体设置cookie时,可以设置过期时间(expires)以及哪些作用范围(path

let d = new Date()
d.setTime(d.getTime() + 15 * 60 * 1000)
res.setHeader('Set-Cookie', `username=zhangsan; path=/; expires=${d.toGMTString()}`)

浏览器可以直接修改cookie的,在控制台或者Application操作

document.cookie='name=123'

为了防止通过js脚本修改cookie,可以增加httpOnly的限制,但是通过Application仍然可以修改

res.setHeader('Set-Cookie', `username=zhangsan; path=/; expires=${d.toGMTString()}; httpOnly`)
优点

操作简单

缺点
  1. 服务器压力大,每次请求都需要去数据库校验cookie中的信息,数据库查询次数过高
  2. 安全性低,客户端随时可以篡改cookie
  3. cookie大小在4kb,且只能存储字符串,存储限制较大
  4. cookie跨域无法共享
总结

cookie的校验几乎是淘汰的,因为性能低,安全性差,但是为其他校验方式奠定了基础

(2)session + cookie

Untitled-2022-01-07-2.png

步骤分解
  1. 浏览器首次登录,服务器收到请求后去数据库校验用户名及密码
  2. 校验通过后,从数据库查询对应的用户信息
  3. 在服务器进程中创建session,把用户信息保存起来
  4. 设置响应体(Set-Cookie),把sessionId(一般是userId,因为userId比直接暴露username更安全)返回
  5. 浏览器下次请求接口,自动携带cookie信息在请求体中,其中包含了sessionId
  6. 服务器拿到请求体中的sessionId,判断sessionId对应的是哪个用户
  7. 查询到对应用户后,执行后续操作。反之,权限校验失败
细节点
  1. session是服务端存储,而sessionStoragecookie一样,属于客户端存储
  2. session是保存在服务端进程中的,关闭服务端进程,session则无法获取了
  3. 数据库是和服务端进程并非同一个东西,例如nodejsserver进程与mysql
  4. session可以存储任意类型的数据,且大小比cookie大得多
优点
  1. 安全性较高,客户端拿到的只是sessionId,没有具体的用户信息
  2. 用户信息保存在服务器进程中,减少了查询数据库的次数
  3. 可以设置session的过期时间,不受cookie过期时间的影响
  4. 存储的数据类型不受限制
缺点
  1. 占用服务器内存资源,用户量很大时,内容消耗不起
  2. 正常情况下,session在不同的服务器进程中无法共享,比如A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录。如果要实现,这要求每个进程都能共享session(可以通过session持久化的方式解决)
session持久化

上面提到的A网站和B网站的例子,其实就是我们经常听到的单点登录。解决不同进程共享session,业界也有很多方案,这里不过多赘述,比如利用Redis这样的工具实现session共享等。解决session占用服务器内存,可以扩展集群等。

(3)token

Untitled-2022-01-07-1013-3.png

步骤分解
  1. 浏览器首次登录,服务器收到请求后去数据库校验用户名及密码
  2. 校验通过后,从数据库查询对应的用户信息
  3. 服务器将用户信息通过密钥生成令牌(Access Token),并返回给浏览器
  4. 浏览器拿到令牌之后,可以保存到cookie或者localStoragesessionStorage
  5. 浏览器下次请求接口,将Access Token放到请求头header
  6. 服务端拿到请求头的token,然后通过密钥解密,校验通过后执行后续操作。反之,权限校验失败
细节分析
  1. 基于token的认证方式是属于无状态的(服务器不会保存用户信息),而session是有状态的
  2. token认证完全由应用管理,可以避开同源策略(只要携带token,服务器可以校验通过,就忽略同源策略)
  3. token除了可以放在请求头,还可以在get请求中,比如:http://xxxxx.com/getData?token=xxxx,也可以放在post请求中,与postData一起发送到服务器
  4. token加密常见的是使用HMACSHA256 类 (System.Security.Cryptography) | Microsoft Docs
优点
  1. 无状态可以减轻服务器压力,减少频繁查询数据库
  2. 没有同源策略的限制,方便第三方平台或者开发时的接口调用
  3. 安全性较高,token的解密密钥只有服务端知道,即使客户端暴露出来,别人也无法解密
缺点
  1. token过期时间较短,往往需要配合refresh token一起使用,refresh token是在access token过期时用来重新获取token
  2. refresh token也有过期时间,且一般存储在数据库中,虽然不需要向 session一样一直保持在内存中以应对大量的请求,但也会增加一定次数的数据库查询
总结

token提供的是一种令牌,授权,是让客户端有权限访问服务端资源的。而session是一种状态,如保持登录状态(记住密码功能)。具体用哪个需要结合业务,如果你需要实现有状态的会话,可以使用 Session来在服务器端保存状态。如果你的用户数据可能需要和第三方共享,或者允许第三方调用 api接口,则可以使用 token

(4)JWT

Untitled-2022-01-07-1013-4.png

步骤分解
  1. 浏览器首次登录,服务器收到请求后去数据库校验用户名及密码
  2. 校验通过后,从数据库查询对应的用户信息
  3. 服务器将用户信息生成jwt,并返回给客户端(什么是jwt,可以看一下阮一峰老师的---JSON Web Token 入门教程,这里就不过多造次了)
  4. 浏览器拿到jwt之后,可以保存到cookie或者localStoragesessionStorage
  5. 浏览器下次请求接口,将jwt放到请求头header中(默认格式Authorization: Bearer jwt
  6. 服务端检查jwt的签名信息,从jwt中获取用户信息,并执行后续操作。反之,权限校验失败
细节点

jwt默认是不加密的,本质是由(Header.Payload.Signature)组成。如需加密,可以和access token一样使用密钥加密

优点
  1. 安全性高,可以说是目前登录鉴权最优的方案,大部分公司都采用该方案
  2. 解决服务器压力,减少服务器查询
  3. 服务端也是无状态
  4. 不需要查询数据库,即可获取用户信息,因为jwt包含了用户信息和加密的数据。
缺点

jwt不加密的情况下,不能将秘密数据写入 jwt

总结

jwttoken的比较:

相同点:

  • 都是访问资源的令牌
  • 都可以记录用户的信息
  • 都是使服务端无状态化

不同点:

  • token需要验证是否过期,正常情况下,需要配合refresh token一起使用,相比于jwt比较麻烦。而且token一般是需要存储在redis或者数据库中,服务器拿到token信息需要去校验真伪,所以jwt的优势就出来了

参考

  1. 阮一峰老师的---JSON Web Token 入门教程
  2. 傻傻分不清之 Cookie、Session、Token、JWT

关注我们

大家的支持是我们继续前进的动力,快来关注我们深信服前端团队吧~

同时,如果对我们感兴趣的话,欢迎加入我们,目前有大量岗位需求,欢迎投递简历到 uedc@sangfor.com.cn