1、封装登录接口 api,新建 src/api/auth.js
,添加如下代码:
import request from "@/utils/request";
// 登录
export function userLogin(data) {
return request({
url: "/admin/auth/sign_in",
method: "post",
data,
});
}
2、准备登录路由,在 router/index.js
中添加登录的路由
.
.
{
path: "/login",
name: "Login",
component: LoginView,
},
.
.
3、新建 components/auth/LoginView.vue
组件并请求登录接口,添加代码:
<template>
<div class="sign-in">
<el-row>
<el-col :span="12" :offset="6">
<h1>长乐未央后台</h1>
<el-form
:model="user"
:rules="rules"
ref="ruleForm"
label-width="100px"
class="demo-ruleForm"
>
<el-form-item label="用户名" prop="username">
<el-input v-model="user.username"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="user.password" type="password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">
立即登录
</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
import { userLogin } from "@/api/auth";
export default {
data() {
return {
user: {
username: "",
password: "",
},
rules: {
username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
},
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
userLogin(this.user).then((res) => {
if (res.code === 20000) {
localStorage.token = res.data.token;
location.reload();
} else {
this.$message.error(res.message);
}
});
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
},
};
</script>
<style>
.sign-in {
padding-top: 100px;
}
h1 {
text-align: center;
}
</style>
4、封装 axios
拦截器,前台验证 token
在 utils/request.js
中,添加代码:
.
.
// request拦截器
service.interceptors.request.use(
(config) => {
// do something 在发送请求前
const token = localStorage.token;
if (token) {
// 让每个请求携带token
// ['X-Token']为自定义key
// 请根据实际情况自行修改
config.headers["token"] = token;
}
return config;
},
(error) => {
// do something 当请求错误
console.log(error); // for debug
return Promise.reject(error);
}
);
.
.
5、判断是否登录,在 App.vue
中修改代码:
<template>
<Login v-if="!token" />
<el-container v-else>
<el-header>
<img
src="https://images.clwy.cn/common/logo.png"
alt="长乐未央Logo"
class="logo"
/>
<h1>长乐未央后台管理</h1>
</el-header>
<el-container>
<el-aside width="200px">
<Menu />
</el-aside>
<el-container>
<el-main>
<router-view />
</el-main>
<el-footer>
Copyright 2013-2022 CLWY Inc. All Rights Reserved.
</el-footer>
</el-container>
</el-container>
</el-container>
</template>
<script>
import Menu from "@/components/layouts/MenuComponent";
import Login from "@/components/auth/LoginView";
export default {
components: {
Menu,
Login,
},
data() {
return {
token: "",
};
},
created() {
this.token = localStorage.token;
},
};
</script>
至此,前端验证 token
工作已完成,测试结果:未登录状态下,访问后台请求接口的页面是无法访问的。当用户登录成功后,即 token
值存在的情况下,可正常访问。
但是我们会发现一个严重的问题,只要 token
值存在,不管对错与否,都能登录后台,这样用户就可以恶意篡改 token
的值,网站不安全!
5、继续封装 axios
拦截器,后台验证 token
这里需要注意的是如果后端接口验证的中间件没写,要自行加上。
在 utils/request.js
中,添加代码:
.
.
import { MessageBox, Message } from "element-ui";
.
.
// response拦截器
service.interceptors.response.use(
/**
* 如果你想获取 http 信息,例如 headers 或 status
* 请 return response => response
*/
/**
* 下面的注释为通过response自定义code来标示请求状态,当code返回如下情况为权限有问题,登出并返回到登录页
* 如通过xmlhttprequest 状态码标识 逻辑可写在下面error中
*/
(response) => {
const res = response.data;
// 如果返回的自定义code不是20000, 认定为error。
if (res.code !== 20000) {
Message({
message: res.message || "Error",
type: "error",
duration: 5 * 1000,
});
// 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// 重新登录
MessageBox.confirm(
"你已被登出,可以取消继续留在该页面,或者重新登录",
"确定登出",
{
confirmButtonText: "重新登录",
cancelButtonText: "取消",
type: "warning",
}
).then(() => {
// 为了重新实例化vue-router对象 避免bug
localStorage.removeItem("token");
location.reload();
});
}
return Promise.reject(new Error(res.message || "Error"));
} else {
return res;
}
},
(error) => {
console.log("err" + error); // for debug
Message({
message: error.message,
type: "error",
duration: 5 * 1000,
});
return Promise.reject(error);
}
);
.
.
接下来测试,当用户在浏览器中修改了一个错误的 token
值,发现无法正常登录了,这就是后端验证了 token
是否正确。
6、假设现在是已登录状态,当用户直接在浏览器中输入登录的路由,直接就进入登录页面了,这样用户体验就不太好了
在 router/index.js
中
// 路由守卫
router.beforeEach((to, from, next) => {
const token = localStorage.token;
if (token) {
// 如果已经登录了,自己非要手动访问登录页,则自动跳转到首页
if (to.path === "/login") {
next({ path: "/" });
}
next();
} else {
next({
path: "/login",
});
}
});
sessionStorage
的用法
存储方式:sessionStorage.token = xxx,用法跟 localStorage 一样的。但是两者是有一定区别的!
localStorage可以永久存储数据,而sessionStorage当浏览器关闭后,数据丢失!
一张图看懂 token
的作用,如图所示:
总结:登录业务流程
1、在登录页输入用户名和密码
2、点击登录按钮,调用后台登录接口,请求成功后得到 token
,并存入 localStorage
中
3、根据后台的响应状态跳转到后台首页