本文只介绍简单的应用,关于 json web token
的具体介绍以及原理请参考阮一峰老师的 JSON Web Token 入门教程。
使用的 Node
框架是 koa2
,前端发送 ajax
请求使用 axios。
首先创建工程目录:
具体如何初始化一个 Koa2
工程我的 另一篇文章 中作了详细介绍,在此不再赘述。
然后安装必要的依赖项:
"dependencies": {
"@koa/router": "^8.0.8",
"ejs": "^3.1.3",
"jsonwebtoken": "^8.5.1",
"koa": "^2.12.0",
"koa-bodyparser": "^4.3.0",
"koa-jwt": "^4.0.0",
"koa-static": "^5.0.0",
"koa-views": "^6.2.2"
}
在 server.js
中添加代码来创建一个简单的后端程序:
const koa = require('koa');
const app = new koa();
const bodyParser = require('koa-bodyparser');
const router = require('@koa/router')();
const views = require('koa-views');
const static = require('koa-static');
const path = require('path');
app.use(bodyParser());
app.use(views(__dirname + '/views', {
map: { html: 'ejs' }
}));
app.use(static(path.join(__dirname, '/static')));
router.get('/', ctx => ctx.render('index'));
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(8080, () => {
console.log('server is running at port 8080');
});
在 const path = require('path');
后添加代码:
const { sign } = require('jsonwebtoken');
const secret = 'demo';
const jwt = require('koa-jwt')({ secret });
sign
方法用来生成 toekn
, secret
为自定义的秘钥,jwt
提供路有权限控制的功能,它会对需要限制的资源请求进行检查。
创建路由 login
:
router.post('/login', ctx => {
const { user } = ctx.request.body;
if (user && user.username === 'vip') {
let { username } = user;
const token = sign({ username }, secret, { expiresIn: '1h' });
ctx.body = {
message: 'GET TOKEN SUCCESS',
status: 200,
token
}
} else {
ctx.body = {
message: 'GET TOKEN FAILED',
status: 500
}
}
});
如代码所示,当前端发送的请求体中包含一个 user
对象并且username
为 vip
时将生成一个 token
返回给前端,这里用到了前文提到的 sign
方法,第一个参数是用户信息,第二个参数是自定义的 key
,第三个参数是个 option
,此处只定义了过期时间。
再创建路由 info
:
router.get('/info', jwt, ctx => {
ctx.body = { message: `Welcome ${ctx.state.user.username}!`};
});
与平时看到的路由代码稍有不同,这里增加了一个 jwt
中间件,用来对权限进行控制,如果无法通过验证,则不会执行之后的代码。在前文生成 token
后,会把用户名存入 ctx.state.user
中,在这里可以直接获取。
这时在控制台中输入 node server
启动该项目
打开 index.html
文件,添加一个简单的表单和一个按钮,并引入 axios
:
首先添加登录的逻辑:
document.querySelector('#submit').addEventListener('click', e => {
e.preventDefault();
const username = document.querySelector('input[name="username"]').value;
axios.post('/login', { user: { username } })
.then(response => {
response = response.data;
const { status, token, message } = response;
if (status === 200) {
localStorage.setItem('token', token);
}
alert(message);
})
.catch(error => alert(error.toString()));
});
这里将服务端生成的 token
存入 localStorage
以便下次使用。
当在输入框中输入 vip
并点击提交按钮,后端返回如下格式的信息:
message: "GET TOKEN SUCCESS"
status: 200
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InZpcCIsImlhdCI6MTU5MDMyOTkxOSwiZXhwIjoxNTkwMzMzNTE5fQ.PsyLYmr-pDxxdtrBEvMccVtBr9-xtOAHdZKen4FP34c"
然后再添加获取逻辑:
document.querySelector('#get').addEventListener('click', e => {
e.preventDefault();
const instance = axios.create({ headers: { authorization: `Bearer ${localStorage.getItem('token')}` } });
instance.get('/info')
.then(response => {
response = response.data;
console.log(response)
alert(response.message);
})
.catch(error => alert(error.toString()));
});
此处用到了 axios.create
方法,该方法可以在请求头中添加 token
信息。用 localStorage
中获取 token
并拼成形如 authorization: Bearer token
的形式,然后再用示例发送 get
请求。
此时再点击获取按钮,会提示:
证明 token
是有效的。
这时去 Application
中把 localStorage
记录清除掉,再点击获取按钮,提示:
证明拦截成功。
完整示例可以去我的GitHub查看并下载,如有疑问欢迎留言讨论。