前端开发搭建博客系统(二),jwt权鉴设计以及中途遇到的问题。献给前端

mac2026-04-03  5

废话部分

上次废话太多,估计有些小伙伴没看太清楚。这次会精简很多。只拿关键的出来讲。

个人属于前端,后端看到了扣下留情。希望本文能帮助大家

项目地址:

前端:https://github.com/ht-sauce/dream

后端:https://github.com/ht-sauce/dream-admin

一、权鉴选型

这块没太多可以说的,基本百度的结果就是jwt

而eggjs官方是egg-jwt,其本质也是jsonwebtoken

egg-jwt地址:https://www.npmjs.com/package/egg-jwt

jsonwebtoken地址:https://www.npmjs.com/package/jsonwebtoken

eggjs的插件配置方面我就不多说了。太简单了(再次赞美eggjs)。

二、先写登录接口,保证生成权鉴信息

1、先放完整的登录接口函数

// 登录接口 async login() { const { ctx, service } = this; const userBusiness = service.consumer.user; const query = ctx.request.body; // 接口请求数据 // 参数校验 const rule = { account: { type: 'string', required: true }, // 有format才能有message信息 password: { type: 'string', required: true }, }; try { await ctx.validate(rule, query); // 优先处理用户是否存在 const login = await userBusiness.userLogin(query); if (login) { // 登录之后生成口令,有效期24小时 const token = ctx.helper.generate_token(query.account); // 前端肯定会需要用户信息,返回给前端用户基本信息 const userInfo = { userInfo: await userBusiness.find(query), sign: token, }; ctx.body = ctx.helper.result(userInfo); } else { ctx.body = ctx.helper.result('', -1, '用户不存在请注册'); } } catch (e) { ctx.body = ctx.helper.result('', -1, e); } }

2、原理解析

主要是先判断用户名密码,然后再是用自己封装的helper(eggjs的扩展)来生成口令。

至于helper就是进行统一的封装函数处理。

然后一并丢给前端。

三、中间件解析口令,进行校验拦截

1、中间件代码

'use strict'; module.exports = (options, app) => { return async function(ctx, next) { const token = ctx.request.header.authorization; try { if (token) { // 验证当前token const decode = app.jwt.verify(token, options.secret); // 验证用户信息是否正常 if (!decode || !decode.account) { ctx.body = ctx.helper.result('', -1, '用户信息缺失', 1); } // 验证用户是否存在 const user = await ctx.model.Consumer.User.findOne( { where: { account: decode.account } } ); if (user) { // 如果口令有效期小于15分钟则发送新口令给前端 if (decode.exp - Date.now() / 1000 < 60 * 15) { const token = ctx.helper.generate_token(decode.account); ctx.set('Authorization', token); } // 当所有验证都通过之后,可以正常访问 await next(); } else { ctx.body = ctx.helper.result('', -1, '用户信息验证失败', 1); } } else { ctx.body = ctx.helper.result('', -1, '口令验证失败', 1); } } catch (e) { console.log(e); ctx.body = ctx.helper.result('', -1, e, 1); } }; };

2、原理解析

大家仔细看代码。在各种验证之后最后只有await next()部分才是最终正确代码执行下去的。代表权鉴没有问题。

从前端的角度来看,无非就是if else之后通过了。

3、口令刷新问题

上面的代码有一个部分

// 如果口令有效期小于15分钟则发送新口令给前端 if (decode.exp - Date.now() / 1000 < 60 * 15) { const token = ctx.helper.generate_token(decode.account); ctx.set('Authorization', token); }

这里比较关键,在于实现当用户不停操作之后能不会因为口令过期而突然退出登录。所以就需要校验口令的快过期时间。当快过期的时候发一个新的口令给前端。让前端刷新当前缓存的口令。注意不能时间太短。15分钟算一个比较合理的时间。

4、后端某些接口不校验口令直接过。

这个在后台管理的项目中基本不用太担心。但是开放式的博客就很有必要了。这里主要是看eggjs官方对于中间件的处理。

地址:https://eggjs.org/zh-cn/basics/middleware.html

我个人也对应进行配置

// 中间件配置 config.middleware = [ 'jwtAuthorize' ]; // 给jwtAuthorize中间件传入的参数 config.jwtAuthorize = { secret: 'daihaitian19940329', // 忽略指定路由 ignore: [ `${config.dreamCustom.prefix}/noauth` ], };

三、前端存储口令以及口令发送问题

1、登录接口

登录部分很简单,就是缓存用户信息就行了。主要核心在于ajax函数封装的地方

// 登录 login() { let data = { account: this.logindata.userName, password: userLoginPassword(this.logindata.password) }; this.logining = true; this.axios .ajax({ url: this.$api.consumer().user.login, data: data, method: "post" }) .then(e => { this.logining = false; // 存储用户数据到缓存 store.clearAll(); store.set("user_info", e.data); console.log(e.data); }) .catch(e => { this.logining = false; console.log(e); });

2、封装ajax函数,并且全局处理口令以及口令刷新的问题

代码放出核心封装部分

1、一个headers的封装。需要在前端有口令数据的情况将口令发送给后端

// 授权函数封装 const authorize = herders => { const user_info = store.get("user_info"); if (user_info && user_info.sign) { herders.Authorization = user_info.sign; return herders; } else { return herders; } };

2、口令刷新函数的封装

进行一系列的验证之后,如果后端返回的数据headers里面产生了口令需要及时刷新口令

// 刷新口令以及判断数据类型来判断是否退出登录 refresh_sign_or_out(res) { console.log(res); if (!res || !res.data) { this.logout(); return false; } // type类型为1必定退出登录 if (res.data.type === 1) { this.logout(); return false; } if (res.headers.authorization) { const user_info = store.get("user_info"); user_info.sign = res.headers.authorization; store.set("user_info", user_info); } return true; }

四、缺陷回顾

1、目前最大的缺陷是口令只要生成了,那么就可以验证通过。

2、如果解决上一个问题,让口令唯一的话,那么就需要解决并发情况下口令更新,保证后续接口不会因为口令刷新而报错

3、口令唯一化问题

个人:不处理了。目前这样就可以保证并发的问题,只要不口令唯一化,那么就不用担心上述问题。而且个人属于前端,不打算深入探究了。

 

最新回复(0)