koa2学习笔记(七)使用JWT鉴权

在前段时间,遇到一个需求,使用token进行单一登陆,网上都资料也是比较久远了,不知道是否有效,在我一个一个都尝试下,终于找到一个实测有效的demo。

什么是token登陆

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

认证过程

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

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

生成token

注册一个/login的路由,用户登陆时获取token

app.js

const Koa = require('koa')
const jwt = require('jsonwebtoken')
const Router = require('koa-router')

const app = new Koa()
const router = new Router()

router.get('/login',async (ctx,next)=>{
    const data = ctx.request.body;
    if(!data.name || !data.password){
        return ctx.body = {
            code: '000002',
			data: null,
			msg: '参数不合法'
        }
    }
    
    // 因为要涉及到数据库,我这里模拟假数据来进行登陆
    const userInfo = {
        name: 'cbq123',
        password: '123456'
    }
    
    // 登陆校验
    if(data.name == userInfo.name && data.password == userInfo.password){
        const token = jwt.sign({
			name: result.name,
			_id: result._id
		}, 'my_token', { expiresIn: '2h' });
		return ctx.body = {
			code: '000001',
			data: token,
			msg: '登录成功'
		}
    }else{
        return ctx.body = {
			code: '000002',
			data: null,
			msg: '用户名或密码错误'
		}
    }
    
})

// koa-router启用,不太了解到话,可以看看这里https://www.npmjs.com/package/koa-router
app.use(router.routes())
   .use(router.allowedMethods())
   
app.listen(3000,()=>{
    console.log('服务已启动在3000端口')   
})
复制代码

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

获取token

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

一个登陆的提交方法

submit(){
	axios.post('/login', {
		name: this.name,
		password: this.password
	}).then(res => {
		if(res.code === '000001'){
			localStorage.setItem('token', res.data);
			localStorage.setItem('token_exp', new Date().getTime());
			this.$router.push('/');
		}else{
			alert(res.msg);
		}
	})
}
复制代码

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

axios.interceptors.request.use(config => {
    const token = localStorage.getItem('token');
    config.headers.common['Authorization'] = 'Bearer ' + token;
    return config;
})
复制代码

验证token

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

const koa = require('koa');
const koajwt = require('koa-jwt');
const app = new koa();
// 错误处理
app.use((ctx, next) => {
    return next().catch((err) => {
        if(err.status === 401){
            ctx.status = 401;
      		ctx.body = 'Protected resource, use Authorization header to get access\n';
        }else{
            throw err;
        }
    })
})
app.use(koajwt({
	secret: 'my_token'
}).unless({
	path: [/\/user\/login/]
}));
复制代码

通过 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

module.exports = function resolveAuthorizationHeader(ctx, opts) {
    if (!ctx.header || !ctx.header.authorization) {
        return;
    }
    const parts = ctx.header.authorization.split(' ');
    if (parts.length === 2) {
        const scheme = parts[0];
        const credentials = parts[1];
        if (/^Bearer$/i.test(scheme)) {
            return credentials;
        }
    }
    if (!opts.passthrough) {
        ctx.throw(401, 'Bad Authorization header format. Format is "Authorization: Bearer <token>"');
    }
};
复制代码

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

// verify.js

const jwt = require('jsonwebtoken');
module.exports = (...args) => {
    return new Promise((resolve, reject) => {
        jwt.verify(...args, (error, decoded) => {
            error ? reject(error) : resolve(decoded);
        });
    });
};
复制代码

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

// index.js
const decodedToken = await verify(token, secret, opts);
if (isRevoked) {
	const tokenRevoked = await isRevoked(ctx, decodedToken, token);
	if (tokenRevoked) {
		throw new Error('Token revoked');
	}
}
ctx.state[key] = decodedToken;  // 这里的key = 'user'
if (tokenKey) {
	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,就会相对安全了。

最后

以上内容要感谢林鑫博客,原文地址在Koa2 使用 JWT 进行鉴权

小强前端交流群QQ群:724179055

定时分析技术和资料,欢迎大家进来一起交流。

往期回顾地址:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值