egg-jwt登录鉴权
1、安装 egg-jwt
npm install egg-jwt -S
1
2、配置 plugin.js
jwt: {
enable: true,
package: 'egg-jwt',
}
1
2
3
4
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
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
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
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
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
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
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
2
3
4
5
6
7
8
9
10
11
12
至此,登录鉴权工作已完成。
| <!-- | 表头 | 表头 |
|---|---|---|
| 单元格 | 单元格 | |
| 单元格 | 单元格 |
| 姓名 | 年龄 | |
|---|---|---|
| 张三 | 28 | --> |
编辑 (opens new window)
上次更新: 7/2/2024, 11:06:45 AM