Web
常用的登录鉴权方式cookie
和 session
JWT
SSO
和 OAuth2
set-cookie
cookie
cookie
只存储 userId
,不暴露用户信息session
中redis
存储,成本高cookie
,需要特殊配置JWT
的全称是 JSON Web Token
token
字符串,将用户信息加密之后得到的结果token
之后,存储下来header
中携带这段 token
JWT
用户信息存储在客户端Session
用户信息存储在服务器端JWT
成本低,维护简单npm
插件,koa-jwt
、 jsonwebjson
jwt
中间件,并使用loginCheck
中间件npm i koa-jwt jsonwebtoken -S
增加配置
// src\config\constant.js
module.exports = {
// jwt 秘钥
JWT_SECRET: 'warbler_for-json#web$token',
// jwt 忽略默认验证的 path:全部忽略即可,需要登录验证的,用自己封装的 loginCheck
JWT_IGNORE_PATH: [/\//],
}
封装中间件
// src\middlewares\jwt.js
const jwtKoa = require('koa-jwt')
const { JWT_SECRET, JWT_IGNORE_PATH } = require('../config/constant')
const jwt = jwtKoa({
secret: JWT_SECRET, // jwt 秘钥
cookie: 'jwt_token', // 使用 cookie 存储 token
}).unless({
// 定义哪些路由忽略 jwt 验证
path: JWT_IGNORE_PATH,
})
module.exports = jwt
使用
// src\app.js
const jwt = require('./middlewares/jwt')
// 配置 jwt 中间件
app.use(jwt)
// src\utils\jwt.js
const util = require('util')
const jwt = require('jsonwebtoken')
// jwt 密钥
const { JWT_SECRET } = require('../config/constant')
// jwt 过期时间
const { jwtExpiresIn } = require('../config/index')
const verify = util.promisify(jwt.verify)
/**
* jwt verify 解密
* @param {string} token token
*/
async function jwtVerify(token) {
const data = await verify(token.split(' ')[1], JWT_SECRET) // 去掉前面的 Bearer
return data
}
/**
* jwt sign 加密
* @param {Object} data data
*/
function jwtSign(data) {
const token = jwt.sign(data, JWT_SECRET, { expiresIn: jwtExpiresIn })
return token
}
// 导出解密,加密两个方法
module.exports = {
jwtVerify,
jwtSign,
}
封装数据模型
// src\res-model\index.js
/**
* 基础模型,包括 errno data 和 message
*/
class BaseRes {
constructor({ errno, data, message }) {
this.errno = errno
if (data) {
this.data = data
}
if (message) {
this.message = message
}
}
}
/**
* 执行失败的数据模型
*/
class ErrorRes extends BaseRes {
constructor({ errno = -1, message = '', data }, addMessage = '') {
super({
errno,
message: addMessage
? `${message} - ${addMessage}` // 有追加信息
: message,
data,
})
}
}
/**
* 执行成功的数据模型
*/
class SuccessRes extends BaseRes {
constructor(data = {}) {
super({
errno: 0,
data,
})
}
}
module.exports = {
ErrorRes,
SuccessRes,
}
封装错误信息集合
// src\res-model\failInfo\error.js
module.exports = {
// 统一错误处理
serverErrorFailInfo: {
errno: -1,
message: '运行错误',
},
// 404
notFoundFailInfo: {
errno: -2,
message: '404 Not Found',
},
}
封装中间件
// src\middlewares\loginCheck.js
// 解密
const { jwtVerify } = require('../utils/jwt')
// 执行失败的数据模型
const { ErrorRes } = require('../res-model/index')
// 错误信息集合
const { loginCheckFailInfo } = require('../res-model/failInfo/index')
/**
* 登录校验
* @param {Object} ctx ctx
* @param {function} next next
*/
module.exports = async function loginCheck(ctx, next) {
// 失败信息
const errRes = new ErrorRes(loginCheckFailInfo)
// 获取 token
const token = ctx.header.authorization
if (!token) {
ctx.body = errRes
return
}
let flag = true
try {
const userInfo = await jwtVerify(token)
delete userInfo.password // 屏蔽密码
// 验证成功,获取 userInfo
ctx.userInfo = userInfo
} catch (ex) {
flag = false
ctx.body = errRes
}
if (flag) {
// 继续下一步
await next()
}
}
SSO
单点登录OAuth2
第三方鉴权简单的,如果业务系统都在同一主域名下,比如 wenku.baidu.com
、 tieba.baidu.com
,就可以直接把 cookie domain
设置为主域名 baidu.com
,百度也就是这么干的
SSO
是 OAuth2
的实际案例,其他常见的还有微信登录,github
登录等,当涉及到第三方用户登录校验时,都会使用 OAuth2
标准。