看到标题应该也能看出来本文讲的就是前端鉴权相关的内容了,鉴权也就是身份认证,指验证用户是否有系统的访问权限,只要是web开发,这部分内容就是不可能不学的,很多面试也必问,所以本文就针对此主题详细记录一下其常见的几种方式。

HTTP状态

HTTP 是无状态的。也就是说,HTTP 请求方和响应方间无法维护状态,都是一次性的,它不知道前后的请求都发生了什么。但有的场景下,我们需要维护状态。最典型的,一个用户登陆微博,发布、关注、评论,都应是在登录后的用户状态下的。这种情况下,各种鉴权就应运而生了。

在此之前还了解一点基本的概念:

  • 认证

认证就是验证当前用户的身份,比如用户名密码登录认证、邮箱发送登录链接、手机号验证码认证。认证当前用户就是本人,不是机器。

  • 授权

用户授予第三方应用访问用户某些资源的权限,客户端授予服务器端应用的一些权限,最常见的就是安装手机应用时,APP询问用户是否授予媒体访问权限。

  • 凭证

实现认证和授权就需要有东西来标识访问者的身份,这个标识就是凭证,如银行,去大厅逛逛,不需要办业务就不需要认证,也就不需要凭证,逛逛就走了;但是要办业务的话就需要提供身份证明和银行卡信息,身份证和银行卡就对应到了凭证,拿着凭证就可以在银行进行认证办业务了。

参考链接:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies

Cookie是服务器发送到用户浏览器并保存在本地的一些数据,浏览器会将cookie进行存储,在下次向浏览器发送请求的时候,会一并将cookie也发送到服务器上,这样服务器就能知道这两个请求是不是来自同一浏览器了。

cookie222

Cookie主要在以下三个方面发挥的很淋漓尽致:

  • 会话状态管理

​ 比如最最常见的用户登录状态,客户端登录的时候发送一个请求,服务器接收到请求后进行验证,验证成功发送一个请求成功和setcookie的响应,客户端收到响应后完成登录的同时进行cookie的存储,下一次进行页面刷新、跳转等操作的时候,会将cookie一并发送给服务器。服务器接收cookie并进行验证,验证成功便直接发出响应,验证失败跳转到登录页面重新登录。

  • 个性化设置

​ 比如用户自定义设置、主题设置,与上面同样的道理,进行设置和验证即可保证有效期内的用户自定义设置。

  • 浏览器行为跟踪

​ 由于http都是无状态的,所以服务器无法判断当前请求的发送者是不是同一个人,服务器与浏览器进行会话,服务器就能知道浏览器的行为情况,进一步便能进行追踪。

Cookie应用

这一部分的内容参见下一节,直接通过代码的形式进行展示与讲解。

Session

客户端发送请求,服务器会为了这个请求创建一个内存空间,这个对象就是session对象,在创建session的同时,会生成一个sessionid,并通过Set-Cookie:JSESSIONID=XXXXXXX的命令发送一个cookie的设置,客户端收到响应便在浏览器设置了一个cookie信息,cookie结束的时候,这一次会话也就结束了。

接下来客户端的所有请求,请求头都会带上携带有sessionid的cookie信息,然后服务器通过读取请求头中的cookie信息回去sessionid,进一步进行session的验证,进行会话的继续。

  • session的缺点

​ session是存储在服务器上的,如果是分布式的架构,那么很可能会面临失效的问题。

Session应用

后面实战项目中应该会涉及,这里不进行试验了。

Cookie和Session的区别

  • 安全性

​ Session 比 Cookie 安全,Session 是存储在服务器端的,Cookie 是存储在客户端的。

  • 存取值的类型不同

​ Cookie 只支持存字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型。

  • 有效期不同

​ Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效。

  • 存储大小不同

​ 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源。

Token

Token 的中文意思是"令牌"。主要用来身份验证。比传统的身份验证方法,Token 有扩展性强,安全性高的特点,非常适合用在 Web 应用或者移动应用上。它是服务器生成的字符串,可以作为客户端的一个凭证。

session的设置对于中小型的项目来讲,是比较合适的,但是一旦流量大起来,服务器要保存的数据就太多了,对服务器的消耗是巨大的。

针对这个问题,就想能不能不在服务器中进行保存了,去客户端保存,比如用户登录了系统,服务器就给客户端发送一个token,里面包含了用户的id,下一次请求的时候,把这个token又带回来,但是这样还是有问题,这个是容易伪造的,所以就需要做一个加密,别人就无法伪造了。

这个token服务器不保存,当用户把这个token 给我发过来的时候,我再用同样的算法和密钥,对数据计算一次签名, 和token 中的签名做个比较, 如果相同, 我服务器就知道用户已经登录过了,并且可以直接取到用户的id , 如果不相同, 数据部分肯定被人篡改过,服务器就返回验证失败的错误。

  • 和cookie的不同

最开始我看到这里的时候,就已经迷糊了,生成数据发送到客户端,客户端每次请求都会发送给服务器,这和cookie有什么区别呢?

但是细想一下就知道很不一样了,cookie是一个数据块,可以保存很多键值对数据,token是一个令牌,这个令牌只保存验证需要用的数据。token可以放在url中、header中、请求体中,也可以放在cookie中,本质上就是一条数据。

  • 总结一下验证的流程
  1. 客户端使用用户名和密码请求登录
  2. 服务端收到请求,验证用户名和密码
  3. 验证成功后,服务端会签发一个token,再把这个token返回给客户端
  4. 客户端收到token后可以把它存储起来,比如放到cookie中
  5. 客户端每次向服务端请求资源时需要携带服务端签发的token,可以在cookie或者header中携带
  6. 服务端收到请求,然后去验证客户端请求里面带着的token,如果验证成功,就向客户端返回请求数据

Token对比Session

session和token并不矛盾,session是一种存储机制,目的是存储登录信息;token是为了提供认证和授权,加密的数据是用户信息,服务器拿到需要和数据库对比,进行用户认证。

  • 支持跨域访问

​ cookie是无法跨域的,而token由于没有用到cookie(前提是将token放到请求头中),所以跨域后不会存在信息丢失问题

  • 无状态

​ token机制在服务端不需要存储session信息,因为token自身包含了所有登录用户的信息,所以可以减轻服务端压力

  • 更适用CDN

​ 可以通过内容分发网络请求服务端的所有资料

  • 更适用于移动端

​ 当客户端是非浏览器平台时,cookie是不支持的,采用token认证方式会简单很多

  • 无需考虑CSRF

​ 由于不再依赖cookie,所以采用token认证方式不会发生CSRF,所以也就无需考虑CSRF的防御

JWT

JWT是token的一种实现方式,其全称是JSON Web Token。官网:https://jwt.io/

JWT将用户信息保存在一个Json字符串中,然后进行编码就得到了一个JWT token,而且JWT带有签名信息,接收后可以进行校验,所以可以用于在各方之间安全地将信息作为Json对象传输。

  • JWT的认证流程:
  1. 前端将用户信息通过表单发送到后端
  2. 后端拿到信息和数据库进行比对,核验成功后,将包含用户信息的数据作为JWT的主要载荷,然后结合JWT Header进行编码后进行签名,就得到了一个JWT Token
  3. 后端将JWT Token字符串作为登录成功的结果返回给前端。前端可以将返回的结果进行存储,退出浏览器的时候删除即可
  4. 前端发送请求的时候把JWT Token放置到HTTP请求头中的Authorization属性中(解决XSS和XSRF的问题)
  5. 后端检查前端传过来的JWT Token后进行验证
  6. 验证通过后,后端解析JWT Token中包含的信息,进行进一步的处理

JWT结构

JWT由3部分组成:标头(Header)、有效载荷(Payload)和签名(Signature)。在传输的时候,会将JWT的3部分分别进行Base64编码后用.进行连接形成最终传输的字符串:

Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

JWT头是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存:

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

Payload

有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择:

iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT

除以上默认字段外,我们还可以自定义私有字段,一般会把包含用户信息的数据放到payload中:

{
  "sub": "1234567890",
  "name": "MinChess",
  "admin": true
}

Signature

签名哈希部分是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。首先,需要指定一个密钥(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用header中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名

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

在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用.分隔,就构成整个JWT对象

JWT的种类

JWT(JSON Web Token)指的是一种规范,这种规范允许使用JWT在两个组织之间传递安全可靠的信息,JWT的具体实现可以分为以下几种:

  • nonsecure JWT:未经过签名,不安全的JWT

    header中没有指定签名算法,只有algtyp,就更没有签名了。

  • JWS:经过签名的JWT

    JWS就是在header中指定了算法,最后也有对应签名的token。

  • JWE:payload部分经过加密的JWT

    JWE就是载荷数据加密过的JWT,这样不能直接解析出载荷的明文数据

JWT总结

JWT就是Token的一种规范,三个部分,头+载荷+签名,头中声明类型、加密算法等,载荷装载主要数据,签名由算法+头+载荷+密钥组成,token也是三个部分,前面两个部分是明文的,前端可以直接进行解析获取到有效数据,所以不能放敏感数据;

最开始没弄懂的时候,到这里还不知道为什么要加密,加密了也是明文,加密有什么意义,后来弄明白了才后知后觉,加密只是保证前面两部分的数据不会被修改,修改了就返回错误。

验证的过程是,服务器拿到数据,对header和payload进行解码进一步对解码的结果结合密钥进行一次签名,然后将结果和客户端返回回来的签名对比,对比不同即返回错误。

个人感觉记录的还是相对清晰的,具体的实战都在后面进一步进行操作。