Vue+node.js 实现 jwt token验证
<p>项目架构:vue+node.js</p><p>jwt 验证流程</p>
<p>1、客户端访问登陆接口(不带token),请求服务器验证</p>
<p>2、服务器验证通过,通过jwt返回给客户端一个token</p>
<p>3、客户端请求其他接口时带上从服务器获取的token</p>
<p>4、服务器验证客户端的token,验证通过后,返回给客户端访问接口数据</p>
<p>阮一峰 JSON Web Token:</p>
<p>http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html</p>
<p> </p>
<p><strong>服务端node.js</strong></p>
<p>1、安装依赖</p>
<p><span style="color: rgba(136, 136, 136, 1)"> npm install jsonwebtoken –save</span></p>
<p><span style="color: rgba(136, 136, 136, 1)"> npm install koa-jwt –save</span></p>
<p>2、中间件请求token</p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)">app.use(async (ctx, next) => {</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> // let token = ctx.header.authorization;</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> return next().catch((err) => {</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> if (err.status === 404) {</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> ctx.status = 404;</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> ctx.body = {</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> code: 404,</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> msg: err.message</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> }</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> } else {</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> throw err;</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> }</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> })</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)">});</span></p>
<p> </p>
<p>3、排除不验证的请求</p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)">app.use(koajwt({ secret: secret }).unless({</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> // 登录接口不需要验证</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> path: [/^\/api\/login/]</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)">}));</span></p>
<p> </p>
<p>4、登陆签发token</p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)">app.use(async (ctx, next) => {</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> if(ctx.request.method==='POST'&&ctx.request.url==='/api/login'){</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> const postData = ctx.request.body.user</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> const {login_name,login_password}=JSON.parse(postData)</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> //判断账号和密码是否正确</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> //xxx</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> //登陆成功返回token</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> const token=sign({login_name},secret,{expiresIn:'1h'}) </span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> ctx.body=token</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> }else{</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> //继续执行api请求</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> await next()</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)"> }</span></p>
<p align="left"><span style="color: rgba(136, 136, 136, 1)">});</span></p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_7dd5b6aa-fe98-43d7-8cf8-5bc75d323ef2" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_7dd5b6aa-fe98-43d7-8cf8-5bc75d323ef2" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_7dd5b6aa-fe98-43d7-8cf8-5bc75d323ef2" class="cnblogs_code_hide">
<pre>
<span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* @Author: wuyongxian
* @Date: 2019-11-04 17:52:58
* @Last Modified by:
* @Last Modified time: 2019-11-04 17:53:23
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
const Koa </span>= require('koa'<span style="color: rgba(0, 0, 0, 1)">);
const app </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Koa();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">jwt token验证</span>
<span style="color: rgba(0, 0, 0, 1)">
const { sign } </span>= require('jsonwebtoken'<span style="color: rgba(0, 0, 0, 1)">);
const secret </span>= 'xxx'<span style="color: rgba(0, 0, 0, 1)">;
const koajwt </span>= require('koa-jwt'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 中间件对token进行验证</span>
<span style="color: rgba(0, 0, 0, 1)">
app.use(async (ctx, next) </span>=><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> let token = ctx.header.authorization;</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> next().<span style="color: rgba(0, 0, 255, 1)">catch</span>((err) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (err.status === 404<span style="color: rgba(0, 0, 0, 1)">) {
ctx.status </span>= 404<span style="color: rgba(0, 0, 0, 1)">;
ctx.body </span>=<span style="color: rgba(0, 0, 0, 1)"> {
code: </span>404<span style="color: rgba(0, 0, 0, 1)">,
msg: err.message
}
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)"> err;
}
})
});
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">排除不验证的请求</span>
<span style="color: rgba(0, 0, 0, 1)">
app.use(koajwt({ secret: secret }).unless({
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 登录接口不需要验证</span>
<span style="color: rgba(0, 0, 0, 1)">
path: [</span>/^\/api\/login/<span style="color: rgba(0, 0, 0, 1)">]
}));
app.use(async (ctx, next) </span>=><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(ctx.request.method==='POST'&&ctx.request.url==='/api/login'<span style="color: rgba(0, 0, 0, 1)">){
const postData </span>=<span style="color: rgba(0, 0, 0, 1)"> ctx.request.body.user
const {login_name,login_password}</span>=<span style="color: rgba(0, 0, 0, 1)">JSON.parse(postData)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">判断账号和密码是否正确</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">xxx</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">登陆成功返回token</span>
<span style="color: rgba(0, 0, 0, 1)">
const token</span>=sign({login_name},secret,{expiresIn:'1h'<span style="color: rgba(0, 0, 0, 1)">})
ctx.body</span>=<span style="color: rgba(0, 0, 0, 1)">token
}</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">继续执行api请求</span>
<span style="color: rgba(0, 0, 0, 1)">
await next()
}
});
app.listen(</span>3000);</pre>
</div>
<span class="cnblogs_code_collapse">node.js 完整代码</span></div>
<p>参考:https://www.jianshu.com/p/663520bd7e95</p>
<p> </p>
<p><strong>客户端vue:</strong></p>
<p>1、登陆验证获取token</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_f4fed023-d54c-4045-aed8-7f4b18eb310c" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_f4fed023-d54c-4045-aed8-7f4b18eb310c" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_f4fed023-d54c-4045-aed8-7f4b18eb310c" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 0, 1)"> loginAuth() {
let params </span>=<span style="color: rgba(0, 0, 0, 1)"> {
login_name: </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.ruleForm.username,
login_password: md5(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.ruleForm.password)
}
let that </span>= <span style="color: rgba(0, 0, 255, 1)">this</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">axios 请求后台接口</span>
<span style="color: rgba(0, 0, 0, 1)">
api.login(params).then(res </span>=><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (res.code == 0<span style="color: rgba(0, 0, 0, 1)">) {
that.MSG.success(res.msg)
that.loginSuccess(res)
} </span><span style="color: rgba(0, 0, 255, 1)">else</span> {<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">result.state==1||reuslt.state==2</span>
<span style="color: rgba(0, 0, 0, 1)">
that.MSG.warning(res.msg)
}
})
},
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">登陆成功</span>
<span style="color: rgba(0, 0, 0, 1)">
loginSuccess(result) {
sessionStorage.setItem(</span>'token'<span style="color: rgba(0, 0, 0, 1)">, result.token)
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.$router.push({ path: '/index'<span style="color: rgba(0, 0, 0, 1)"> })
}</span></pre>
</div>
<span class="cnblogs_code_collapse">登陆验证</span></div>
<p>获取到的token可以放到sessionStorage、Vuex.Store获取其他存储介质中,方便下次调用接口使用</p>
<p> </p>
<p>2、在main.js文件,在请求拦截方法中,为请求接口加入token</p>
<p>需要注意的是token格式为:‘Bearer ’+token,token参数需要赋值给请求头的Authorization</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_b0b76151-5b17-4517-82d1-10338298bfe3" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_b0b76151-5b17-4517-82d1-10338298bfe3" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_b0b76151-5b17-4517-82d1-10338298bfe3" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 0, 1)">axios.interceptors.request.use(
config </span>=><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">除登陆接口意外的接口都要加上token,才可以请求接口</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (!config.url.endsWith('/api/login'<span style="color: rgba(0, 0, 0, 1)">)) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(sessionStorage.getItem('token')!=<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">){
const token</span>='Bearer '+sessionStorage.getItem('token'<span style="color: rgba(0, 0, 0, 1)">)
config.headers.common[</span>'Authorization']=<span style="color: rgba(0, 0, 0, 1)">token
}
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> config;
},
error </span>=><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Promise.reject(error);
}
);</span></pre>
</div>
<span class="cnblogs_code_collapse">axios请求头加token</span></div>
<p>请求头参数格式:</p>
<p> <img src="https://img2020.cnblogs.com/blog/451237/202007/451237-20200717104720537-855692043.png" alt="" loading="lazy"></p><br><br>
来源:https://www.cnblogs.com/eye-like/p/13328535.html
頁:
[1]