1、需求说明
在前后端分离开发中,前端使用Vue.js框架,通过axios发送异步HTTP请求,服务端就无法使用session的方式保存用户的登录信息,因为客户端的每一次异步请求在服务端都会被认为是一个新的session。我们可以使用jwt(jsonwebtoken)的方式实现用户的校验。
2、安装相关依赖
服务端:
本文中使用Express作为服务端框架,需要在服务端安装以下JWT依赖:
cnpm i jsonwebtoken --save
cnpm i express-jwt --save
前端:
在前端使用Vue.js框架,需要安装axios用于发送HTTP请求:
cnpm i axios --save
3、代码实现
3.1、服务端代码
app.js入口文件引入 express-jwt
,示例代码如下:
var express = require('express');
var expressJwt = require('express-jwt');
var app = express();
app.use(expressJwt({
credentialsRequired:false,
secret: 'helloworld', //密钥
algorithms: ['HS256'] //没有此配置项,在jwt6.0.0版本会报错:algorithms should be set
}).unless({
path: ['/login'] //设置不需要token验证的路由
}))
express-jwt
会自动验证请求头中的 token
信息。
在 routes/index.js 路由文件中创建用于登录和用户查询的路由,示例代码如下:
var express = require('express');
var router = express.Router();
var jwt = require('jsonwebtoken');
//用户登录的路由,此路由不会进行token校验
router.post('/login', function(req, res, next) {
let {
username,pwd} = req.body
//模拟数据库查询账号密码
if(username === 'admin' && pwd === '123456'){
//生成Token信息jwt.sign(payload, secretOrPrivateKey, [options, callback])
//payload参数为保存到客户端的用户信息,
//secretOrPrivateKey 为密钥,要和 app.js 文件中的 secret 的值保持一致
//option为配置项,expiresIn是token的有效时长,单位为秒,值也可以为字符串,例如 '2d' 表示2天
let Token = jwt.sign({
name: username,role: 1},'helloworld',{
expiresIn: 60})
res.json({
code: 200,
token: 'Bearer '+Token //向客户端响应的token前面必须添加 ’Bearer ’ 前缀
})
}else{
res.json({
code: 500
})
}
});
//查询用户信息的路由,此路由会校验token
router.get('/api/user/find', function(req,res,next){
res.json({
code: 200,
result: ['tom','jack','lily']
})
})
module.exports = router;
jwt.sign()
方法也可以使用异步加密的方式,示例代码如下:
//使用异步的方式生成token
jwt.sign(
{
name:username,role:1},
'helloworld',
{
expiresIn:60},
function(err,token){
console.log(token)
}
)
3.2、前端代码
views/Login.vue 用户登录组件,示例代码如下:
<template>
<div>
<input type="text" v-model="username" placeholder="用户名"></input>
<input type="password" v-model="pwd" placeholder="密码"></input>
<button @click="login">登录</button>
</div>
</template>
<script>
import axiost from 'axios'
export default {
data(){
return {
username: '',
pwd: ''
}
},
methods: {
login(){
//登录按钮点击事件
axios.post("/login",{
username: this.username,
pwd: this.pwd
}).then(res=>{
if(res.data.code === 200){
//保存token信息
localStorage.token = res.data.token
}
})
}
}
}
</script>
前端校验token的方法有以下几种:
(1)使用Vue的路由守卫校验
// 在路由全局前置守卫中判断 token 是否存在
router.beforeEach((to , from, next) => {
// 获取 token
if (localStorage.token)) {
if (to.name === 'login') {
// 如果用户在login页面
next('/');
} else {
next();
}
} else {
router.push('/login')
}
});
(2)在axios请求拦截器中添加token信息
// axios请求拦截器
axios.interceptors.request.use(
config => {
if (localStorage.token) {
// 判断是否存在token
config.headers.authorization = localStorage.token;
}
return config;
},
err => {
return Promise.reject(err);
});
//axios响应拦截器
axios.interceptors.response.use(res => {
return res;
}, err=> {
if (err.response.status === 401) {
//token校验失败,没有访问权限
//输出授权失败错误信息
} else {
//输出其他错误信息
}
return Promise.reject(err);
}
);
也可以直接在 axios
实例函数中设置 headers
,示例代码如下:
let instance = axios.create({
baseURL: 'http://localhost:3000',
headers:{
authorization: localStorage.getItem('token')
}
})
instance.get('/xxx')
3.3、服务端校验token并获取用户信息
在服务端可以使用 express-jwt
自动完成校验,也可以手动完成校验,我们还可以在服务端获取保存到客户端的用户信息。在服务端的 routes/index.js
路由文件中编写一个用于token验证的测试路由,示例代码如下:
var express = require('express');
var router = express.Router();
var jwt = require('jsonwebtoken');
//用于验证token并获取用户信息
router.get('/yz', function(req,res,next){
//从请求头中获取token内容
let token = req.headers.authorization
//由于token中包含 'Bearer '前缀,需要把前缀去掉获得token值
token = token.replace('Bearer ','')
let result = null;
try{
//jwt.verify(token,secretOrPublicKey,[options,callback]) 验证token的合法性
// secretOrPublicKey 参数为密钥,要和生成token的密钥保持一致
result = jwt.verify(token,'helloworld')
console.log(result)
}catch(err){
console.error(err)
}
res.json({
result
})
})
module.exports = router;
在控制台打印的结果为如下图所示:
jwt.verify()
方法返回的值为 jwt.sign()
方法中的 payload
对象参数,JWT 规定了7个官方字段,供选用,分别是:
- iss (issuer):签发人
- exp (expiration time):过期时间
- sub (subject):主题
- aud (audience):受众
- nbf (Not Before):生效时间
- iat (Issued At):签发时间
- jti (JWT ID):编号
转载:https://blog.csdn.net/p445098355/article/details/116211716