Node.js应用:Koa2使用JWT进行鉴权

在前后端分离的开发中,通过 Restful API 进行数据交互时,如果没有对 API 进行保护,那么别人就可以很容易地获取并调用这些 API 进行操作。那么服务器端要如何进行鉴权呢?

成都做网站、成都网站设计,成都做网站公司-创新互联公司已向上千家企业提供了,网站设计,网站制作,网络营销等服务!设计与技术结合,多年网站推广经验,合理的价格为您打造企业品质网站。

Json Web Token 简称为 JWT,它定义了一种用于简洁、自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法。JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名。

说得好像跟真的一样,那么到底要怎么进行认证呢?

首先用户登录时,输入用户名和密码后请求服务器登录接口,服务器验证用户名密码正确后,生成token并返回给前端,前端存储token,并在后面的请求中把token带在请求头中传给服务器,服务器验证token有效,返回正确数据。

既然服务器端使用 Koa2 框架进行开发,除了要使用到 jsonwebtoken 库之外,还要使用一个 koa-jwt 中间件,该中间件针对 Koa 对 jsonwebtoken 进行了封装,使用起来更加方便。下面就来看看是如何使用的。

生成token

这里注册了个 /login 的路由,用于用户登录时获取token。

 
 
 
 
  1. const router = require('koa-router')(); 
  2. const jwt = require('jsonwebtoken'); 
  3. const userModel = require('../models/userModel.js'); 
  4. router.post('/login', async (ctx) => { 
  5.     const data = ctx.request.body; 
  6.     if(!data.name || !data.password){ 
  7.         return ctx.body = { 
  8.             code: '000002', 
  9.             data: null, 
  10.             msg: '参数不合法' 
  11.         } 
  12.     } 
  13.     const result = await userModel.findOne({ 
  14.         name: data.name, 
  15.         password: data.password 
  16.     }) 
  17.     if(result !== null){ 
  18.         const token = jwt.sign({ 
  19.             name: result.name, 
  20.             _id: result._id 
  21.         }, 'my_token', { expiresIn: '2h' }); 
  22.         return ctx.body = { 
  23.             code: '000001', 
  24.             data: token, 
  25.             msg: '登录成功' 
  26.         } 
  27.     }else{ 
  28.         return ctx.body = { 
  29.             code: '000002', 
  30.             data: null, 
  31.             msg: '用户名或密码错误' 
  32.         } 
  33.     } 
  34. }); 
  35. module.exports = router; 

在验证了用户名密码正确之后,调用 jsonwebtoken 的 sign() 方法来生成token,接收三个参数,第一个是载荷,用于编码后存储在 token 中的数据,也是验证 token 后可以拿到的数据;第二个是密钥,自己定义的,验证的时候也是要相同的密钥才能解码;第三个是options,可以设置 token 的过期时间。

获取token

接下来就是前端获取 token,这里是在 vue.js 中使用 axios 进行请求,请求成功之后拿到 token 保存到 localStorage 中。这里登录成功后,还把当前时间存了起来,除了判断 token 是否存在之外,还可以再简单的判断一下当前 token 是否过期,如果过期,则跳登录页面

 
 
 
 
  1. submit(){ 
  2.     axios.post('/login', { 
  3.         name: this.username, 
  4.         password: this.password 
  5.     }).then(res => { 
  6.         if(res.code === '000001'){ 
  7.             localStorage.setItem('token', res.data); 
  8.             localStorage.setItem('token_exp', new Date().getTime()); 
  9.             this.$router.push('/'); 
  10.         }else{ 
  11.             alert(res.msg); 
  12.         } 
  13.     }) 

然后请求服务器端API的时候,把 token 带在请求头中传给服务器进行验证。每次请求都要获取 localStorage 中的 token,这样很麻烦,这里使用了 axios 的请求拦截器,对每次请求都进行了取 token 放到 headers 中的操作。

 
 
 
 
  1. axios.interceptors.request.use(config => { 
  2.     const token = localStorage.getItem('token'); 
  3.     config.headers.common['Authorization'] = 'Bearer ' + token; 
  4.     return config; 
  5. }) 

验证token

通过 koa-jwt 中间件来进行验证,用法也非常简单

 
 
 
 
  1. const koa = require('koa'); 
  2. const koajwt = require('koa-jwt'); 
  3. const app = new koa(); 
  4. // 错误处理 
  5. app.use((ctx, next) => { 
  6.     return next().catch((err) => { 
  7.         if(err.status === 401){ 
  8.             ctx.status = 401; 
  9.             ctx.body = 'Protected resource, use Authorization header to get access\n'; 
  10.         }else{ 
  11.             throw err; 
  12.         } 
  13.     }) 
  14. }) 
  15. app.use(koajwt({ 
  16.     secret: 'my_token' 
  17. }).unless({ 
  18.     path: [/\/user\/login/] 
  19. })); 

通过 app.use 来调用该中间件,并传入密钥 {secret: 'my_token'},unless 可以指定哪些 URL 不需要进行 token 验证。token 验证失败的时候会抛出401错误,因此需要添加错误处理,而且要放在 app.use(koajwt()) 之前,否则不执行。

如果请求时没有token或者token过期,则会返回401。

解析koa-jwt

我们上面使用 jsonwebtoken 的 sign() 方法来生成 token 的,那么 koa-jwt 做了些什么帮我们来验证 token。

resolvers/auth-header.js

 
 
 
 
  1. module.exports = function resolveAuthorizationHeader(ctx, opts) { 
  2.     if (!ctx.header || !ctx.header.authorization) { 
  3.         return; 
  4.     } 
  5.     const parts = ctx.header.authorization.split(' '); 
  6.     if (parts.length === 2) { 
  7.         const scheme = parts[0]; 
  8.         const credentials = parts[1]; 
  9.         if (/^Bearer$/i.test(scheme)) { 
  10.             return credentials; 
  11.         } 
  12.     } 
  13.     if (!opts.passthrough) { 
  14.         ctx.throw(401, 'Bad Authorization header format. Format is "Authorization: Bearer "'); 
  15.     } 
  16. }; 

在 auth-header.js 中,判断请求头中是否带了 authorization,如果有,将 token 从 authorization 中分离出来。如果没有 authorization,则代表了客户端没有传 token 到服务器,这时候就抛出 401 错误状态。

verify.js

 
 
 
 
  1. const jwt = require('jsonwebtoken'); 
  2. module.exports = (...args) => { 
  3.     return new Promise((resolve, reject) => { 
  4.         jwt.verify(...args, (error, decoded) => { 
  5.             error ? reject(error) : resolve(decoded); 
  6.         }); 
  7.     }); 
  8. }; 

在 verify.js 中,使用 jsonwebtoken 提供的 verify() 方法进行验证返回结果。jsonwebtoken 的 sign() 方法来生成 token 的,而 verify() 方法则是用来认证和解析 token。如果 token 无效,则会在此方法被验证出来。

index.js

 
 
 
 
  1. const decodedToken = await verify(token, secret, opts); 
  2. if (isRevoked) { 
  3.     const tokenRevoked = await isRevoked(ctx, decodedToken, token); 
  4.     if (tokenRevoked) { 
  5.         throw new Error('Token revoked'); 
  6.     } 
  7. ctx.state[key] = decodedToken;  // 这里的key = 'user' 
  8. if (tokenKey) { 
  9.     ctx.state[tokenKey] = token; 

在 index.js 中,调用 verify.js 的方法进行验证并解析 token,拿到上面进行 sign() 的数据 {name: result.name, _id: result._id},并赋值给 ctx.state.user,在控制器中便可以直接通过 ctx.state.user 拿到 name 和 _id。

安全性

如果 JWT 的加密密钥泄露的话,那么就可以通过密钥生成 token,随意的请求 API 了。因此密钥绝对不能存在前端代码中,不然很容易就能被找到。

在 HTTP 请求中,token 放在 header 中,中间者很容易可以通过抓包工具抓取到 header 里的数据。而 HTTPS 即使能被抓包,但是它是加密传输的,所以也拿不到 token,就会相对安全了。

总结

这上面就是 jwt 基本的流程,这或许不是最完美的,但在大多数登录中使用已经足够了。

上面的代码可能不够具体,这里使用 Koa + mongoose + vue.js 实现的一个例子 : jwt-demo,可以做为参考。

【本文为专栏作者“林鑫”的原创稿件,转载请通过微信公众号联系作者获取授权】

本文名称:Node.js应用:Koa2使用JWT进行鉴权
文章位置:http://www.shufengxianlan.com/qtweb/news38/490038.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联