本文只介绍简单的应用,关于 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 方法用来生成 toeknsecret为自定义的秘钥,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 对象并且usernamevip 时将生成一个 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

<form>
  <input type="text" name="username">
  <button id="submit">提交</button>
</form>
<button id="get">获取</button>
<script src="/vendors/axios.min.js"></script>

首先添加登录的逻辑:

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查看并下载,如有疑问欢迎留言讨论。

03-05 21:50