leiwuhen-67's blog leiwuhen-67's blog
首页
    • 《Vue》笔记
    • 《React》笔记
    • 《NodeJs》笔记
    • 《CSS》笔记
    • 《Redis》笔记
    • 基础入门
    • 《Mock》笔记
    • 《MySQL》笔记
    • 《Git》相关
影音视听
收藏
关于
GitHub (opens new window)

首页
    • 《Vue》笔记
    • 《React》笔记
    • 《NodeJs》笔记
    • 《CSS》笔记
    • 《Redis》笔记
    • 基础入门
    • 《Mock》笔记
    • 《MySQL》笔记
    • 《Git》相关
影音视听
收藏
关于
GitHub (opens new window)
  • Express

  • Koa

  • Egg

    • 使用脚手架快速初始化Egg项目
    • 实现接口API并连接mysql数据库
    • 数据库的基本操作,增、删、改、查
    • egg-jwt登录鉴权
    • 上传图片到项目指定文件夹下
    • 上传图片到七牛云
    • 使用ApiDoc生成接口文档
  • Node相关

  • 《NodeJs》笔记
  • Egg
心欲无痕
2023-07-14

egg-jwt登录鉴权

1、安装 egg-jwt

npm install egg-jwt -S
1

2、配置 plugin.js

jwt: {
  enable: true,
  package: 'egg-jwt',
}
1
2
3
4

3、在 config.default.js 中配置

config.jwt = {
  secret: '123456', // secret为加密密钥
  // expiresIn为token过期时间,数值的话单位为秒,如果是字符串的话,除非提供"2 days", "10h", "7d"这种,否则单位为毫秒
  expiresIn: 10 * 60,  
}
1
2
3
4
5

准备工作完成,现在来分析登录鉴权的流程。

例如这样一个场景,用户输入手机号、密码登录后台,首先后端应该判断手机号、密码是否正确,如果正确,则根据手机号跟密码生成一个 token,假定 1 天,然后将该 token 下发到前端,前端存储该 token,然后进入到后台其他页面,访问接口时将 token 传给服务端,服务端判断该 token 是否过期,以及是否对应当前用户,如果都满足的话则正常返回数据,否则告诉用户 token 已过期,需要重新登录。

首先编写一个生成 token 的工具函数,在 /app/utils/token.js 中编写如下:

// 记住,这里使用require跟exports,如果使用import和export default的话会报错。
const jwt = require("jsonwebtoken")

// 生成token
exports.sign = function (data, secret, expireIn) {
	return jwt.sign(data, secret, { expiresIn: expireIn });
}
1
2
3
4
5
6
7

接着,在 /app/controller/login.js 中编写如下:

'use strict';

const { Controller } = require('egg')
const getToken = require('../utils/token')

class LoginController extends Controller {
	async login () {
		const { ctx } = this
		const secret = ctx.app.config.jwt.secret
		const expiresIn = ctx.app.config.jwt.expiresIn
		const { tel, password } = ctx.request.body	
		let userInfo = await ctx.service.user.findUser(tel, password)
		if (userInfo && userInfo.length) {
			const token = getToken.sign({tel, password}, secret, expiresIn)
			ctx.body = {
				status: 200,
				data: {
					tel: tel,
					userName: userInfo[0].user_name,
					token: token
				},
				msg: '成功'
			}
		} else {
			ctx.body = {
				status: 300,
				data: null,
				msg: '手机号或密码错误'
			}
		}
	}
}

module.exports = LoginController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

在 app/service/user.js 中代码如下:

const { Service } = require('egg')

class UserService extends Service {
	async find(uid) {
		if (uid) {
			const user = await this.app.mysql.select('user_list', {
				where: {
					user_id: uid
				}
			})
			if (user) {
				return user
			} else {
				return []
			}
		} else {
			const user = await this.app.mysql.select('user_list')
			// const user = await this.app.mysql.query(`select * from user_list`)
			if (user) {
				return user
			} else {
				return []
			}
		}
		// const user = await this.app.mysql.get('user_list', {user_id: uid})
		// return user
	}
	async findUser (tel, password) {
		const user = await this.app.mysql.select('user', {
			where: {tel: tel, password: password}
		})
		if (user) {
			return user
		} else {
			return []
		}
	}
}

module.exports = UserService
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

在 /app/router.js 中代码如下:

'use strict';

/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => {
  const { router, controller, middleware } = app;
  router.post('/', controller.login.login);
  router.post('/getList',  controller.list.getList)
};
1
2
3
4
5
6
7
8
9
10

至此,登录及访问列表页接口已经完成,但是此时还没有鉴权,不管用户访问接口时是否传 token 都能正常访问数据。

要完成鉴权工作,还需要通过中间件来完成。

在 /app/middleware/checkToken.js 中代码如下:

module.exports = (secret) => {
	return async function (ctx, next) {
		// 若是没有 token,返回的是 null 字符串,前端访问接口时需在header中携带token和userid
		const token = ctx.request.header.authorization;
		const userId = ctx.request.header.userid
		if (!userId) {
		  ctx.status = 200;
		  ctx.body = {
		    status: 401,
		    msg: 'userId不存在'
		  };
		  return false
		}
		if (token && token !== null) {
			// 有token需要校验
			try {
				let decode = ctx.app.jwt.verify(token, secret);
				if (decode.tel) {
				  // 去数据库查找用户
				  const user = await ctx.service.user.findUser(decode.tel, decode.password)
				  if (user && user.length) {
				    if (user[0].user_id != userId) {
				      ctx.status = 200;
				      ctx.body = {
				        status: 401,
				        msg: 'token已过期,请重新登录'
				      }
				    } else {
				      await next()
				    }
				  } else {
				    // token 不存在
				    ctx.status = 200;
				    ctx.body = {
				      status: 401,
				      msg: 'token不存在'
				    };
				  }
				}
			} catch (err) {
				ctx.status = 200;
				ctx.body = {
				  status: 401,
				  msg: 'token已过期,请重新登录'
				}
			}
		} else {
			// token 不存在
		  ctx.status = 200;
		  ctx.body = {
		    status: 401,
		    msg: 'token不存在'
		  };
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

中间件编写完成,得去路由中使用中间件了。

在 /app/router.js 中代码修改如下:

'use strict';

/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => {
  const { router, controller, middleware } = app;
  // 传入加密字符串
  const checkToken = middleware.checkToken(app.config.jwt.secret)
  router.post('/', controller.login.login);
  router.post('/getList', checkToken,  controller.list.getList)
};
1
2
3
4
5
6
7
8
9
10
11
12

至此,登录鉴权工作已完成。

<!-- 表头 表头
单元格 单元格
单元格 单元格
姓名 年龄
张三 28 -->
编辑 (opens new window)
上次更新: 7/2/2024, 11:06:45 AM
数据库的基本操作,增、删、改、查
上传图片到项目指定文件夹下

← 数据库的基本操作,增、删、改、查 上传图片到项目指定文件夹下→

Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式