设计内容
前端
- 配置全局前置路由守卫
- axios拦截器
- 登录页面和主页
后端
- JWT的封装
- 登录接口
- 中间件放行
- mysql数据库的连接
详细设计
路由设计
配置全局前置守卫,如果访问的是登录页面则放行,不是则进入判断是否有token,没有则拦截回到登录页面,有则放行访问。
router.beforeEach((to, from, next) => {
//如果是访问Login,则直接通过
if(to.name==='Login'){
next();
}else{
//如果没有token则进入登录页面
if(!localStorage.getItem("token")){
next({
path:'/login'
});
}else{
next();
}
}
});
axios拦截器
配置响应拦截器,拿到后端传来的token并保存到localStorage中,如果后端传回来了401错误(token失效),就会删除localStorage中的token并回到登录页面。
// 响应拦截
axios.interceptors.response.use(function (response) {
//拿到响应里的token
console.log(response);
const authorization = response.data.token;
console.log(authorization);
authorization && localStorage.setItem("token",authorization);
return response;
}, function (error) {
const { status } = error.response;
if(status===401){
localStorage.removeItem("token");
router.push("/login");
}
return Promise.reject(error);
});
配置请求拦截器,把localStorage中的token加到请求头中的Authorization中。
//请求拦截
axios.interceptors.request.use(function (config) {
const token = localStorage.getItem("token");
//请求时带上token,给后端认证
config.headers.Authorization = `${token}`;
return config;
}, function (error) {
return Promise.reject(error);
});
登录页面和主页
登录方法写得比较简单,请求登录接口,判断后端返回的结果。
LoginHandle(){
if(this.loginForm.password || this.loginForm.username){
axios.post("http://localhost:3000/login",this.loginForm).then(res=>{
if(res.data.status == "success"){
this.$router.push("/mainbox");
}else{
ElMessage.error('用户名或密码错误!');
}
})
}else{
ElMessage.error('请填写账号和密码!');
}
}
访问主页时会请求后端的接口,主页请求时所携带的token给后端处理,后端会判断 token是否过期,如果过期后端就回应401错误码,401错误码被axios的响应拦截器处理,跳回登录页面。
mounted(){
this.getIndex();
},
methods:{
getIndex(){
axios.get('http://localhost:3000/bill').then(res=>{
console.log(res.data);
})
}
}
JWT封装
JWT是JSON Web Token的缩写,jsonwebtoken这个模块有两个常用的方法,sign()和verify()作用分别是生成token和验证token,sign()方法需要3个基本的参数,1.加密内容,2.密钥,3.过期时间。verify()方法有2个基本参数,1.加密内容,2.密钥。
const jwt = require("jsonwebtoken");
const secret = "samrol";
const JWT = {
generate(value,expires){
return jwt.sign(value,secret,{expiresIn:expires});
},
verify(token){
try{
return jwt.verify(token,secret);
}catch(error){
return false;
}
}
}
module.exports = JWT;
登录接口
访问/login时后端会做:拿到前端请求带过来的账户和密码,连接数据库,查询登录信息是否正确,不正确则回应登录错误给前端,信息正确:生成token,把token添加到header的Authorization里,返回成功信息。
const express = require("express");
const router = express.Router();
const mysql2 = require("mysql2");
const JWT = require("../util/JWT");
const getDBConfig = require("../util/mysql");
router.post("/",async (req,res)=>{
const {username,password} = req.body;
const config = getDBConfig();
const promisePool = mysql2.createPool(config).promise();
var user = await promisePool.query(`select * from user where name=? and password=?`,[username,password]);
//登陆成功
if(user[0].length>0){
//生成token
const token = JWT.generate({username,password},"10s");
//设置头部
res.header("Authorization",token);
res.send({status:"success",message:"登录成功",token});
}else{
res.send({status:"error",message:"用户名或密码错误"});
}
})
module.exports = router;
补充一个数据库连接配置
function getDBConfig(){
return{
host:'127.0.0.1',
port:3306,
user:'root',
password:'',
database:'vue_test',
}
}
module.exports = getDBConfig;
接口拦截中间键
接收到的每次请求都需要通过这个中间件,如果是login接口则直接放行,其他的则需要通过验证前端携带的token是否过期来判断能否放行,如果过期则返回401错误码来提醒用户token过期需要重新登录。
app.use((req,res,next)=>{
if(req.url==="/login"){
next();
return;
}
const token = req.headers['authorization']//.split(" ")[1];
if(token){
var payload = JWT.verify(token);
if(JWT.verify(token)){
const newToken = JWT.generate({
username:payload.username,
password:payload.password,
},"10s");
res.header("Authorization",newToken);
next();
}else{
res.status(401).send({errCode:"-1",errorInfo:"token过期!"});
}
}
})