内容概括
主要产出
- 了解
Web常用的登录鉴权方式
主要内容
cookie和sessionJWTSSO和OAuth2
关于短信验证码
- 用户体验好,无需注册,无需记住密码
- 验证码需要收费,所以需要防止恶意共计,恶意刷接口
介绍 Session 登录
cookie 做登录校验的过程
- 前端输入用户名密码,传给后端
- 后端验证成功,返回信息时
set-cookie - 接下来的所有接口访问,都自动带上
cookie
为何会有 Session
cookie只存储userId,不暴露用户信息- 用户信息存储在
session中
Session 的优点
- 原理简单,易于学习
- 用户信息存储在服务端,可以快速封禁某个登录的用户
Session 的缺点
- 占用服务端内存,有硬件成本
- 多进程,多服务器时,不好同步,一般使用第三方
redis存储,成本高 - 跨域传递
cookie,需要特殊配置
介绍 JWT 登录
JWT 的全称是 JSON Web Token
JWT 的过程
- 前端输入用户名密码,传递给后端
- 后端验证成功,返回一段
token字符串,将用户信息加密之后得到的结果 - 前端获取
token之后,存储下来 - 以后访问接口,都在
header中携带这段token
JWT 的优点
- 不占用服务器内存
- 多进程,多服务器,不受影响
- 不受跨域限制
JWT 的缺点
- 无法快速封禁登录的用户
JWT 和 Session 的重要区别
JWT用户信息存储在客户端Session用户信息存储在服务器端
为何选择 JWT
- 没有快速封禁登录用户的需求
JWT成本低,维护简单- 需要考虑跨域的扩展性
代码演示
- 安装
npm插件,koa-jwt、jsonwebjson - 封装
jwt中间件,并使用 - 封装
loginCheck中间件 - 相关的配置项,构造函数等
安装插件
代码语言:javascript复制npm i koa-jwt jsonwebtoken -S封装 jwt 中间件
增加配置
代码语言:javascript复制// srcconfigconstant.js
module.exports = {
// jwt 秘钥
JWT_SECRET: 'warbler_for-json#web$token',
// jwt 忽略默认验证的 path:全部忽略即可,需要登录验证的,用自己封装的 loginCheck
JWT_IGNORE_PATH: [///],
}封装中间件
代码语言:javascript复制// srcmiddlewaresjwt.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使用
代码语言:javascript复制// srcapp.js
const jwt = require('./middlewares/jwt')
// 配置 jwt 中间件
app.use(jwt)封装 JWT 工具
代码语言:javascript复制// srcutilsjwt.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,
}封装 loginCheck 中间件
封装数据模型
代码语言:javascript复制// srcres-modelindex.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,
}封装错误信息集合
代码语言:javascript复制// srcres-modelfailInfoerror.js
module.exports = {
// 统一错误处理
serverErrorFailInfo: {
errno: -1,
message: '运行错误',
},
// 404
notFoundFailInfo: {
errno: -2,
message: '404 Not Found',
},
}封装中间件
代码语言:javascript复制// srcmiddlewaresloginCheck.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
SSO单点登录OAuth2第三方鉴权
使用 cookie 实现单点登录
简单的,如果业务系统都在同一主域名下,比如 wenku.baidu.com 、 tieba.baidu.com ,就可以直接把 cookie domain 设置为主域名 baidu.com ,百度也就是这么干的
SSO

OAuth2
SSO 是 OAuth2 的实际案例,其他常见的还有微信登录,github 登录等,当涉及到第三方用户登录校验时,都会使用 OAuth2 标准。


