第四章 JWT 方案
什么是JWT?
JWT:即 JSON Web Tokens
是一种跨域认证解决方案
JWT 解决了什么问题?
数据传输简单高效
jwt 会生成签名,保证传输安全性
jwt 具有时效性
jwt 更高效利用集群做好单点登录
JWT 原理
服务器认证后,生成一个JSON对象,后续通过JSON进行通信
JWT 数据结构
Header(头部)
Payload(负载)
Signature(签名)
Header结构
{ "alg": "HS256", "typ": "JWT" } 复制代码
Payload结构
签名
JWT 使用方式
/api?token=xxx
cookie 写入 token
storage 写入 token,请求头添加:
Authorization:Bearer <token>
通过 JWT 生成Token
文档地址:jsonwebtoken - npm (npmjs.com)
在后端项目安装
npm install jsonwebtoken 复制代码
在/router/users.js
中引入
const jwt = require('jsonwebtoken') 复制代码
使用,生成token,并随接口返回
// imooc 是密钥 let data = 用户信息对象; let token = jwt.sign({ data, }, 'imooc', { expiresIn: 30 }) 复制代码
请求携带Token
我们在登录的时候,将用户信息存储到了 localStorage
中了,其中包含用户的token
信息
所以在请求拦截的时候,在headers.Authorization
中携带上token
这里应该还需要添加判断是否携带token
,因为有的接口是不需要token
的,目前我们先每次请求都携带上token
// 请求拦截 service.interceptors.request.use((config) => { const headers = config.headers; let token = storage.getItem('userInfo') ? storage.getItem('userInfo').token : '' if (!headers.Authorization) { headers.Authorization = 'Bearer ' + token } return config; }); 复制代码
接口验证Token
const router = require('koa-router')() const jwt = require('jsonwebtoken'); router.get('/count', (ctx)=>{ try { // 获取请求中携带的Token let authorization = ctx.request.header.authorization; let token = authorization.split('Bearer ')[1]; // 验证Token,验证通过继续执行,验证不通过则走 catch let payload = jwt.verify(token, 'imooc'); ctx.body = 'token验证通过' } catch(e) { ctx.body = 'token验证未通过' } }) 复制代码
关于返回值和密钥的说明:
/** * 因为生成token的时候,传的是是用户信息,密钥使用的是imooc */ let data = 用户信息对象; let token = jwt.sign({ data, }, 'imooc', { expiresIn: 30 }) /** * 所以验证token的时候,返回值就是我们前边生成的时候传的用户信息对象,使用的密钥也是imocc */ let payload = jwt.verify(token, 'imooc'); // payload 打印出来如下: > data: {deptId: [], state: 1, role: 1, roleList: [], createTime: "2021-10-09T07:47:36.566Z",…} > exp: 1633765828 > iat: 1633765798 复制代码
Token拦截
这里我们需要引入一个中间件的概念,在app.js
中有好多中间件的使用;
如下
/** * ctx 上下文对象,同接口中的ctx * next() 继续执行 */ app.use(async (ctx, next) => { await next() }) 复制代码
koa-jwt 中间件
文档地址:koa-jwt - npm (npmjs.com)
安装
npm i koa-jwt -S 复制代码
使用
const Koa = require('koa') const app = new Koa() const koaJwt = require('koa-jwt') const utils = require('./utils/utils') app.use(async (ctx, next) => { return next().catch((err)=>{ if(err.status == 401) { ctx.status = 200; ctx.body = utils.fail('Token 认证失败', utils.CODE.AUTH_ERROR) } else { throw err; } }) }) // 要放在后边,secret的值是密钥 app.use(koaJwt({secret: 'imooc'})) 复制代码
判断是否验证Token
因为有些接口是不需要验证token
的,比如说登录接口,这时候我们就使用koa-jwt
的unless
方法,规定不需要验证token
的接口;
// 比如不需要验证 /users/login app.use(koaJwt({secret: 'imooc'}).unless({ path: [/^/users/login/] })) // 需要注意的是,前端请求的是 /api/users/login,因为要配置跨域,所以加上了/api // 但是后端接收到的请求是没有 /api,所以要注意下不要把/api也写上 复制代码
从数据库中,查询指定的字段
因为我们不会将数据库中查询出来的所有字段,都返回给客户端
比如,查询用户信息的时候,一些像密码这种比较敏感的信息是不会返回的
这里就用到了一个mongo的语法
// 查询条件 let query = { userName, userPassword } // 需要查询出来的字段,是个字符串-多个字段用空格分开 let keys = 'userId userName userEmail' // User.findOne(查询条件, 字段) let res = await User.findOne(query, keys) 复制代码
findOne()
返回规定的字段,有三种写法
第一种就是上边的字符串形式,多个字段之间使用空格了连接
let keys = 'userId userName userEmail' let res = await User.findOne(query, keys) 复制代码
传入一个对象,1返回,0不返回;(0和1,只能写单独的一个,不能有的字段是0,有的是1,会报错)
let keys = { userName: 1, userId: 1 } let res = await User.findOne(query, keys) 复制代码
通过
select()
方法指定,值和方式一相同
let keys = 'userId userName userEmail' let res = await User.findOne(query).select(keys)
作者:日常push覆盖同事代码
链接:https://juejin.cn/post/7017707895227695140