什么是 JWT
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准((RFC 7519).该 token 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 token 也可直接被用于认证,也可被加密。JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。它提供基于 JSON 格式的 Token 来做安全认证。
JWT 鉴权在 ThinkJS 中的实践
公共配置
/src/config/config.js
1 2 3 4 5 6 7 8
| module.exports = { jwt: { secret: 'ConstOwn', cookie: 'jwt-token', expire: 30, }, }
|
因为这三个参数在不同的位置会用到,为了统一管理我们提取到了公共的 config 中。
中间件配置
/src/config/middleware.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const jwt = require('koa-jwt') const isDev = think.env === 'development'
module.exports = [ { handle: jwt, options: { cookie: think.config('jwt')['cookie'], secret: think.config('jwt')['secret'], passthrough: true, }, }, ]
|
在 think.Controller 上使用
封装 base.js
通常情况下,Controller 中都继承于 base.js
,我们可以把鉴权相关的方法封装在 base.js
中
src/controller/base.js
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
| const jsonwebtoken = require('jsonwebtoken')
module.exports = class extends think.Controller { async __before() { this.header('Access-Control-Allow-Origin', '*') this.header('Access-Control-Allow-Headers', 'x-requested-with') this.header('Access-Control-Request-Method', 'GET,POST,PUT,DELETE') this.header('Access-Control-Allow-Credentials', 'true') } authFail() { this.json({ state: false, message: '身份校验失败,请重新登录', }) return false } checkAuth() { const token = this.ctx.headers['x-token'] const { secret } = this.config('jwt') try { var tokenObj = token ? jsonwebtoken.verify(token, secret) : {} this.ctx.state.username = tokenObj.name } catch (error) { return this.authFail() } if (!tokenObj.name) { return this.authFail() } this.updateAuth(token.name) } updateAuth(userName) { const userInfo = { name: userName, } const { secret, cookie, expire } = this.config('jwt') const token = jsonwebtoken.sign(userInfo, secret, { expiresIn: expire }) this.cookie(cookie, token) this.header('authorization', token) return 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
| const Base = require('../base.js')
module.exports = class extends Base { __before() {} indexAction() { return this.json({ msg: 'login page' }) }
async loginAction() { if (this.method === 'POST') { const username = this.post('username') const password = this.post('password') const user = await this.model('member') .where({ username: username }) .find() if (user.password === this.verifyPassword(password)) { const token = this.updateAuth(username) this.json({ state: '登陆成功', token: token }) } else { return this.json({ state: '登陆失败' }) } } } logoutAction() { this.updateAuth(null) return this.success('退出登录成功') } verifyPassword(password) { return think.md5( think.md5('ConstOwn') + think.md5(password) + think.md5('nwOtsnoC') ) } }
|
路由验证
如果进入路由需要验证权限的话,直接在 __before 中调用 checkAuth
方法即可:
1 2 3 4 5 6
| module.exports = class extends Base { __before() { return this.checkAuth() } }
|
Logic 权限验证
/src/logic/jwt1.js
1 2 3 4 5 6 7
| const { checkAuth } = think.Controller.prototype module.exports = class extends think.Logic { @checkAuth userAction() { } }
|
这样一个验证就完成了! 如果该 Logic 中的所有 action 都需要进行验证,只需要给 __before 加 decorator 就可以了,其他的 action 就不用加了!