当前位置: 首页 > 后端技术 > Node.js

个人博客的token验证实践(基于Node)

时间:2023-04-03 23:38:38 Node.js

前言为什么使用tokenHTTP是无状态协议,即HTTP无法保存客户端信息,无法区分每次请求的区别。想象这样一个场景,A和B同时修改个人文章,服务器同时收到两个post请求,但是浏览器不知道哪个请求是A哪个请求是B,需要一个标识符(token)标记一串信息,并在请求时带上token。什么是令牌?服务器生成的一串字符作为客户端请求的令牌。首次登录后,服务端会将Tonken字符串分发给客户端。后续的请求,客户端只需要带上这个Token,服务端就知道是用户的访问了。个人理解就是一串被服务器加密过的个人信息,比如下面这个:acess_token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1ZWRhMTBjOTI0NThmNDAwMmFjZDEyMTAiLCJuaWNrX25hbWUiOiLlsI_osaoxOTk2IiwiY3JlYXRlZF90aW1lIjoiMjAyMC0wNi0wNVQwOTozMDo0OS42MThaIiwidXBkYXRlZF90aW1lIjoiMjAyMC0wNi0wNVQwOToyOToyMC4wNzlaIiwiaWF0IjoxNTkxMzQ5NDY4LCJleHAiOjE1OTE5NTQyNjh9.GmUJRXHed7M1xJyPaFFgaQKJoS-w8-l3N_PQFPiwwTE服务器通过秘钥解密从而获得当前请求者的信息技术栈前端:vue+ssr后端:egg(anodeframework)+tsdatabase:redis+mongodeployment:Dockerconstruction:Jenkinsbloghasbeenbasicallycompleted,andssrrenderingisusedTherequestdoesnotperformtokenverification,andotherrequeststhatmodifyresourcessuchasPOSTandPUTwillperformtokenverification.Itcanbedisassembledintothefollowing3steps.Clientuserlogin,theservergeneratesatokenbasedonuserinformationandstorestheclientpersistentlyontheclient.Request,bringthetokenservertoverifythetoken,ifitfails,itwilldirectlyreturntheerrorstatus1.Thefrontend(vue.js)usestheaxioslibrary,andinsertsthetokenintotherequestheaderintherequestinterceptor,anduniformlychecksintheresponseinterceptorAftertheerrorstatuscodepromptsglobally,writethetokenintothebrowsercacheaftersuccessfullogin,andimportVuefrom'vue';importaxios,{AxiosRequestConfig,AxiosResponse}from'axios';axios.interceptors.request.use(async(config:AxiosRequestConfig)=>{constacess_token=awaitVue.prototype.$getCacheData('acess_token');#从缓存中读取令牌if(acess_token){config.headers.acess_token=acess_token;}返回配置;},(err:any)=>Promise.reject(err),);axios.interceptors.response.use((response:AxiosResponse)=>{if(response.data.ret===200){返回响应;}else{Vue.prototype.$global_fail(response.data.content);returnPromise.reject(response);}},(err:any)=>{console.log(err);if(err.code==='ECONNABORTED'&&err.message.indexOf('timeout')!==-1){Vue.prototype.$global_error('请求超时,请联系管理员');}if(err.response){Vue.prototype.$global_error(decodeURI(err.response.data.msg||err.response.data.message));}returnPromise.reject(err);},);关于前端缓存,这里推荐一个localForage库,非常实用。localForage是一个JavaScript库,可以存储多种类型的数据,而不仅仅是字符串localForage有一个优雅的回退策略,如果浏览器不支持IndexedDB或WebSQL,则使用localStorage。但是注意它的操作是异步的,可以自己封装一下,改成同步importVuefrom'vue';从“localforage”导入localForage;Vue.prototype.$setCacheData=async(key:string,data:any):Promise=>awaitlocalForage.setItem(key,data);Vue.prototype.$getCacheData=async(key:string):Promise<字符串|null>=>awaitlocalForage.getItem(key)||null;Vue.prototype.$clearCache=()=>localForage.clear();2.后端(egg.js)生成用户登录的token,使用jsonwebtoken生成token。jsonwebtoken详解请点击:node-jsonwebtoken加密解密也用到了比较简单,直接给UserService方法,其中secret为秘钥,可以设置token过期时间#/app/service/user.tsimport*asjwtfrom'jsonwebtoken';exportdefaultclassUserServiceextendsService{privatesecret='Hello__World';#密钥asynccreateToken(user:User):Promise{constpayload={_id:user._id,nick_name:user.nick_name,created_time:user.created_time,updated_time:user.updated_time,};返回jwt.sign(payload,this.secret,{expiresIn:'7d'});#过期时间}checkToken(token:string):User{try{#根据秘钥解密tokenreturnjwt.verify(token,this.secret);}catch(e){抛出“无效令牌”;}}}在中间件中进行token校验,如果失败则直接返回中间件校验启用verify中间件,只校验具体的POST请求:启用中间件#/config/config.default.tsconfig.middleware=['verify'];config.verify={enable:true,#只验证POST请求match(ctx){returnctx.request.method==='POST';},};在verify中调用checkToken方法验证token#/app/middleware/verify.tsmodule.exports=()=>{returnasync(ctx,next)=>{if(ctx.path.startsWith('/api/user/login')||ctx.path.startsWith('/api/user/sendCode')||ctx.path.startsWith('/api/user/register')){returnawaitnext();}try{constacess_token:string=ctx.request.header.访问令牌;if(!acess_token){throw'请登录';}else{awaitctx.service.user.checkToken(acess_token);#验证令牌returnawaitnext();}}catch(e){#令牌验证如果验证失败,会到这里返回自定义状态码console.log(e);ctx.body={ret:304,内容:`${e}`,};}};};token校验至此结束,如有不足欢迎指出END