网站开发难点,设计感超强的公司名字,毕设如何做网站,做网站用什么网名好node后端vue前端实现接口请求时携带authorization验证
我们在写web项目时#xff0c;后端写好接口#xff0c;前端想要调用后端接口时#xff0c;除了登录注册页面#xff0c;所有的请求都需要携带authorization#xff0c;这样是为了避免随意通过接口调取数据的现象发生…node后端vue前端实现接口请求时携带authorization验证
我们在写web项目时后端写好接口前端想要调用后端接口时除了登录注册页面所有的请求都需要携带authorization这样是为了避免随意通过接口调取数据的现象发生。这是写web项目时最基础的点但是也挺麻烦的涉及前后端好几个地方的编码经常忘记怎么写的现在记录一下。
总体流程如下
后端使用中间件开启接口请求验证除登录/注册外所有接口的请求都需要携带验证参数才能正确发起请求前端登录时存储验证消息也就是token请求拦截器中设置请求头写入authorization
大体就这么几个步骤下面细化
一、后端开启接口请求验证
我是用node写的后端请求验证写在后端入口程序app.js中完整代码如下
const express require(express);
const cors require(cors);
const bodyParser require(body-parser);
const multer require(multer);
const upload multer({ dest: ./public/upload });
const morgan require(morgan);
const fs require(fs);const app express();// 创建一个写入流将日志写入access.log文件
const accessLogStream fs.createWriteStream(./access.log, { flags: a });// 使用Morgan中间件将日志写入控制台和文件
app.use(morgan(combined, { stream: accessLogStream }));app.use(cors());app.use(express.urlencoded({ extended: false }));
app.use(bodyParser.json());// 托管静态文件
app.use(upload.any());
app.use(express.static(./public));// 处理错误的中间件
app.use((req, res, next) {res.cc (err, status 1) {res.send({status,message: err instanceof Error ? err.message : err,});};next();
});const jwtconfig require(./jwt_config/index);
const { expressjwt: jwt } require(express-jwt);
app.use(jwt({secret: jwtconfig.jwtSecretKey,algorithms: [HS256],}).unless({path: [/^\/api\/user\/.*$/],})
);const userManagerRouter require(./router/user);
app.use(/api/user, userManagerRouter);const userInfoManageRouter require(./router/userinfo);
app.use(/api/userinfo, userInfoManageRouter);const settingRouter require(./router/setting);
app.use(/api/setting, settingRouter);const productRouter require(./router/product);
app.use(/api/product, productRouter);const messageRouter require(./router/message);
app.use(/api/message, messageRouter);const filesRouter require(./router/files);
app.use(/api/files, filesRouter);const logRouter require(./router/log);
app.use(/api/log, logRouter);const overviewRouter require(./router/overview)
app.use(/api/overview, overviewRouter)// 用户消息读取情况
const dmMsgRouter require(./router/department_msg)
app.use(/api/dm, dmMsgRouter)// 对不符合joi规则的情况进行报错
// app.use((err, req, res, next) {
// if (err instanceof Joi.ValidationError) return res.cc(err.details[0].message);
// else res.cc(err);
// });app.listen(3088, () {console.log(api server running at http://127.0.0.1:3088);
});
这个程序太长相关的代码如下
const jwtconfig require(./jwt_config/index);
const { expressjwt: jwt } require(express-jwt);
app.use(jwt({secret: jwtconfig.jwtSecretKey,algorithms: [HS256],}).unless({path: [/^\/api\/user\/.*$/],})
);其实这个写法相对来说是固定的首先导入自己写好的jwt验证规则也叫秘钥其实就是一个jwtSecretKey./jwt_config目录下的index.js文件如下
module.exports {jwtSecretKey: xxx // 改成自己的秘钥
}然后导入express-jwt接下来就是使用中间件来设定接口路由规则了unless方法里面写的是排除的接口地址是用正则表达式来排除的/^\/api\/user\/.*$/这个正则表达式的意思是排除所有以/api/user/开头的接口
可以看上面的完整代码app.use(/api/user, userManagerRouter);这里以/api/user/开头的接口都是给用户登录和注册相关的接口
后端按这个思路写就行了
二、登录存储token
这里有两种存储方式一种是把token存储在localstorage中另外一种是存储在全局数据管理工具中也就是vuex或者pinia中这里设计前后端联调
1、后端写登录接口向前端传递token
先看看我的完整的登录接口处理函数
exports.login (req, res) {// res.send(login);const userInfo req.body;const sql select * from users where account ?;db.query(sql, userInfo.account, (err, results) {if (err) return res.cc(err);if (results.length ! 1) return res.cc(用户不存在);const compareResult bcrypt.compareSync(userInfo.password,results[0].password);if (!compareResult) return res.cc(密码错误);// 判断账号是否冻结if (results[0].status 1) return res.cc(账号被冻结);const user {...results[0],password: ,imageUrl: ,create_time: ,update_time: ,};const tokenStr jwt.sign(user, jwt_config.jwtSecretKey, {expiresIn: 10h,});res.send({status: 0,results: results[0],message: 登录成功,token: Bearer tokenStr})});
};相关的代码如下
const tokenStr jwt.sign(user, jwt_config.jwtSecretKey, {expiresIn: 10h,
});
res.send({status: 0,results: results[0],message: 登录成功,token: Bearer tokenStr
})这里其实很简单就是后端通过秘钥生成一个有效期为10小时的token这里的秘钥也是上面提到的然后向前端发送这个token
2、前端登录时存储token
我用的vue3数据存储在pinia中看看我的前端登录代码
import { useUserStore } from /stores/user
const userStore useUserStore()const loginCB async () {const { account, password } form.valueconst data { account, password }loginFormRef.value.validate(async valid {if (valid) {try {await userStore.getUserInfo(data)// console.log(userStore.userInfo)if (!userStore.userInfo.token) return ElMessage.error(用户名或密码错误)// console.log(userStore.vue3ManageUserInfo)router.push(/)// console.log(results)} catch (error) {ElMessage.error(用户名或密码错误)console.log(error)}} else {ElMessage.error(没通过校验)}})
}上面这段代码不是完整的loginCB是登录按钮的回调函数从回调中看到其实我的登录是写在pinia的getUserInfo方法中的继续看看这个store中的写法
import { ref } from vue;
import { defineStore } from pinia;
import { loginAPI } from /apis/user;
import { getUserInfoAPI } from /apis/userinfo;
import { loginLogAPI } from /apis/log;export const useUserStore defineStore(user,() {const userInfo ref({});const getUserInfo async (data) {const res await loginAPI(data);// console.log(res);userInfo.value {account: res.results.account,token: res.token,avatar: res.results.image_url,id: res.results.id,name: res.results.name,sex: res.results.sex,email: res.results.email,department: res.results.department,identity: res.results.identity};// 登录日志await loginLogAPI({account: res.results.account,name: res.results.name,email: res.results.email,});// console.log(userInfo.value)};// 修改头像const changeAvatar (url) {userInfo.avatar url;};// 修改姓名const changeName (name) {userInfo.name name;};// 解决刷新页面丢失store信息的问题const clearUserInfo () {userInfo.value {};};return {userInfo,getUserInfo,clearUserInfo,changeAvatar,changeName,};},{persist: true,}
);pinia中我用的是组合式api的写法其实也是比较流行的写法逻辑和语法都比较清楚
可以看到我定义了一个userInfo的state登录需要调用loginAPI这个接口对应后端的login函数请求到数据后将接口返回的token写入到userInfo.token中这样全局数据管理store中就存储了一个包含token的名为userInfo的state真的绕。。。
注意组合式api需要将state return出去 {persist: true,}上面这个是持久化存储我在“vue3pinia用户信息持久缓存token)的问题”这篇博客中有记录目的是将userInfo存储到localstorage中
这样登录时要做的工作就做完了
三、请求拦截器中携带token
使用axios发起接口请求对axios进行二次封装封装代码如下
import axios from axios;
import { ElMessage } from element-plus;
import { useUserStore } from /stores/user;// 创建axios实例
const http axios.create({baseURL: http://127.0.0.1:xxxx/api/, // 改成自己的端口timeout: 5000,
});// axios请求拦截器
http.interceptors.request.use((config) {const userStore useUserStore();const token userStore.userInfo.token;if (token) {config.headers.Authorization token;}return config;},(e) Promise.reject(e)
);// axios响应式拦截器
http.interceptors.response.use((res) res.data,(e) {ElMessage.warning(接口响应出错);console.log(e);return Promise.reject(e);}
);export default http;注意看请求拦截器中的代码用到了刚刚上面提到的pinia中的userInfo这个state首先获取userInfo中的token判断是否存在token存在的话就把它写到请求头中去关键的代码就是下面这行
config.headers.Authorization token;
这样每次向后端发起数据请求的时候都会携带这个token了除了登录和注册因为不存在后端的中间件也能通过验证了