JS总结笔记:基于 Token 的身份验证:JSON Web Token

Token

什么是Token?

在计算机身份认证中是令牌(临时)的意思,在词法分析中是标记的意思。一般我们所说的的token大多是指用于身份验证的token

为什么使用token?

我们需要每次都知道当前请求的人是谁,但是又不想每次都让他提交用户名和密码,这时就需要有一个等同于用户名密码也能够标识用户身份的东西,即—token

基于Token的身份验证方法(步骤)

  1. 客户端使用用户名和密码请求登录
  2. 服务端收到请求,验证登录是否成功
  3. 验证成功后,服务端会返回一个Token给客户端,反之,返回身份验证失败的信息
  4. 客户端收到Token后把Token用一种方式存储起来,如( cookie / localstorage / sessionstorage / 其他 )
  5. 客户端每次发起请求时都会将Token发给服务端
  6. 服务端收到请求后,验证Token的合法性,合法就返回客户端所需数据,反之,返回验证失败的信息

JWT(Json Web Tokens)

生成Token的解决方案有许多,常用的一种就是 Json Web Tokens

JWT标准的Tokens由三部分组成:

  1. header:包含token的类型和加密算法
  2. payload:包含token的内容
  3. signature:通过密钥将前两者加密得到最终的token

这三部分中间使用 " . " 分隔开,并且都会使用Base64编码方式编码,如下

eyJhbGc6IkpXVCJ9.eyJpc3MiOiJCIsImVzg5NTU0NDUiLCJuYW1lnVlfQ.SwyHTf8AqKYMAJc

header

header 部分主要是两部分内容,一个是 Token 的类型,另一个是使用的算法,比如下面类型就是 JWT,使用的算法是 Hash256
头部里包含的东西可能会根据 JWT 的类型有所变化,比如一个加密的 JWT 里面要包含使用的加密的算法。唯一在头部里面要包含的是 alg 这个属性,如果是加密的 JWT,这个属性的值就是使用的签名或者解密用的算法。如果是未加密的 JWT,这个属性的值要设置成 none

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

payload

Payload 里面是 Token 的具体内容,这些内容里面有一些是标准字段,你也可以添加其它需要的内容。下面是标准字段:

  • iss:Issuer,发行者
  • sub:Subject,主题
  • aud:Audience,观众
  • exp:Expiration time,过期时间
  • nbf:Not before
  • iat:Issued at,发行时间
  • jti:JWT ID

比如下面这个 Payload ,用到了 iss 发行人,还有 exp 过期时间这两个标准字段。另外还有两个自定义的字段,一个是 name ,还有一个是 admin

{
 "iss": "ninghao.net",
 "exp": "1438955445",
 "name": "wanghao",
 "admin": true
}

signature

JWT 的最后一部分是 Signature ,这部分内容有三个部分,先是用 Base64 编码的 header.payload ,再用加密算法加密一下,加密的时候要放进去一个 Secret ,这个相当于是一个密码,这个密码秘密地存储在服务端

  • header
  • payload
  • secret
const encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload); 
HMACSHA256(encodedString, 'secret');

处理完成以后看起来像这样:

SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc

最后这个在服务端生成并且要发送给客户端的 Token 看起来像这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.		//header
eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.		//payload
SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc		//signature

客户端收到这个 Token 以后把它存储下来,下回向服务端发送请求的时候就带着这个 Token
服务端收到这个 Token ,然后进行验证,通过以后就会返回给客户端想要的资源

签发与验证 JWT

在应用里实施使用基于 JWT 这种 Token 的身份验证方法,你可以先去找一个签发与验证 JWT 的功能包。无论你的后端应用使用的是什么样的程序语言,系统,或者框架,你应该都可以找到提供类似功能的包

实例演示
使用node.js实现jwt验证

准备项目

准备一个简单的 Node.js 项目
安装签发与验证 JWT 的功能包,我用的叫 jsonwebtoken,在项目里安装一下这个包

cnpm install jsonwebtoken -S
JS总结笔记:基于 Token 的身份验证:JSON Web Token_第1张图片

签发 JWT

在项目里随便添加一个 .js 文件,比如 index.js,在文件里添加下面这些代码

const jwt = require('jsonwebtoken')//const定义的变量不可以修改,而且必须初始化

// Token 数据
const payload = {
  name: 'guoxiansheng',
  admin: true
}

// 密钥
const secret = 'STUDYJWT' // 这是加密的key(密钥或私钥) 

// 签发 Token
const token = jwt.sign(payload, secret, { expiresIn:60*60*24})//expiresIn设置为24小时过期

// 输出签发的 Token
console.log(token)

非常简单,就是用了刚刚为项目安装的 jsonwebtoken 里面提供的 jwt.sign 功能,去签发一个 token。这个 sign 方法需要三个参数:

  1. playload:签发的 token 里面要包含的一些数据
  2. secret:签发 token 用的密钥,在验证 token 的时候同样需要用到这个密钥
  3. options:一些其它的选项

在命令行下面,用 node 命令,执行一下项目里的 index.js 这个文件(node index.js),会输出应用签发的 token:

C:\Users\Administrator\Desktop\work\JWT>node index.js	//执行的命令
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiZ3VveGlhbnNoZW5nIiwiYWRtaW4iOnRydWUsImlhdCI6MTU1MDQ4NDMzOSwiZXhwIjoxNTUwNTcwNzM5fQ.UqlYyQK4wzw5DtHlr2TupI4Ja2fU6tIF5DhASDLoBwM

上面的 Token 内容并没有加密,所以如果用一些 JWT 解码功能,可以看到 Token 里面包含的内容,内容由三个部分组成,像这样:

// header
{
  "alg": "HS256", //对称算法HS256 签名和验证的时候都会用同一个密码
  "typ": "JWT"
}

// payload
{
  name: 'guoxiansheng',
  admin: true,
  iat: 1550484653,	//生成Token的时间
  exp: 1550571053 	//Token的过期时间
}

// signature
UqlYyQK4wzw5DtHlr2TupI4Ja2fU6tIF5DhASDLoBwM

假设用户通过了某种身份验证,你就可以使用上面的签发 Token 的功能为用户签发一个 Token。一般在客户端那里会把它保存在 Cookie 或 LocalStorage 里面

用户下次向我们的应用请求受保护的资源的时候,可以在请求里带着我们给它签发的这个 Token,后端应用收到请求,检查签名,如果验证通过确定这个 Token 是我们自己签发的,那就可以为用户响应回他需要的资源

验证 JWT
验证 JWT 的用效性,确定一下用户的 JWT 是我们自己签发的,首先要得到用户的这个 JWT Token,然后用 jwt.verify 这个方法去做一下验证。这个方法是 Node.js 的 jsonwebtoken 这个包里提供的,在其它的应用框架或者系统里,你可能会找到类似的方法来验证 JWT。

打开项目的 index.js 文件,里面添加几行代码:

// 验证 Token

jwt.verify(token, 'bad secret', (error, decoded) => {
  if (error) {
    console.log(error.message)
    return
  }
  console.log(decoded)
})

把要验证的 Token 数据,还有签发这个 Token 的时候用的那个密钥告诉 verify 这个方法,在一个回调里面有两个参数,error 表示错误,decoded 是解码之后的 Token 数据。

执行:

C:\Users\Administrator\Desktop\work\JWT>node index.js

输出:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiZ3VveGlhbnNoZW5nIiwiYWRtaW4iOnRydWUsImlhdCI6MTU1MDQ5MDIyNywiZXhwIjoxNTUwNTc2NjI3fQ.1WMLfVytNgCXyjtok7oE4JincarM4vWXXtfxNtpPwK8
invalid signature

注意输出了一个 invalid signature ,表示 Token 里的签名不对,这是因为我们组长 verify 方法提供的密钥并不是签发 Token 的时候用的那个密钥。这样修改一下:

jwt.verify(token, secret, (error, decoded) => { ...

再次运行,会输出类似的数据:

C:\Users\Administrator\Desktop\work\JWT>node index.js
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiZ3VveGlhbnNoZW5nIiwiYWRtaW4iOnRydWUsImlhdCI6MTU1MDQ5MDIxNCwiZXhwIjoxNTUwNTc2NjE0fQ.AxoDKLOFBpjmhRDHq5xkhqm8UNSOnTfW3oQOetfo10w
{ name: 'guoxiansheng',
  admin: true,
  iat: 1550490214,
  exp: 1550576614 }

当然还可以采用RS256 算法签发验证Token
非对称算法RS256
签名和验证的时候用的不是同一个密码

注意事项

设置token过期时间
如果你使用expiresInMinutes来设置token的过期时间,很抱歉它会抛出如下异常

ValidationError: "expiresInMinutes" is not allowed

请使用expiresIn:以秒为单位或描述的时间跨度字符串表示rauchg / MS。


如:60,”2 days”,”10h”,”7d”

{expiresIn: 60} // 有效期60秒(没有时间单位以秒为准)
{expiresIn: "2 days"} // 有效期 2天 (后缀为时间单位)下面的类似
...... 
('1d') // 86400000
('10h') // 36000000
('2.5 hrs') // 9000000
('2h') // 7200000
('1m') // 60000
('5s') // 5000
('1y') // 31557600000

verify(验证)时返回的err的值
token过了有效期的时错误信息

"err": {
"name": "TokenExpiredError",
"message": "jwt expired", // token过了有效期
"expiredAt": "2016-11-07T03:31:25.000Z"
}

遇到伪造或无效的token时的错误信息

"err": {
"name": "JsonWebTokenError",
"message": "invalid token" // 伪造/无效的token
}

参考地址
https://ninghao.net/blog/2834
学习视频
https://ninghao.net/course/5018#toc

你可能感兴趣的:(JS总结笔记)