vue项目登录及token验证 vue-ant
在前后端完全分离的情况下,Vue项目中实现token验证大致思路如下:
1、第一次登录的时候,前端调后端的登陆接口,发送用户名和密码
2、后端收到请求,验证用户名和密码,验证成功,就给前端返回一个token
3、前端拿到token,将token存储到localStorage和vuex中,并跳转路由页面
4、前端每次跳转路由,就判断 localStroage 中有无 token ,没有就跳转到登录页面,有则跳转到对应路由页面
5、每次调后端接口,都要在请求头中加token
6、后端判断请求头中有无token,有token,就拿到token并验证token,验证成功就返回数据,验证失败(例如:token过期)就返回401,请求头中没有token也返回401
7、如果前端拿到状态码为401,就清除token信息并跳转到登录页面
.
.
.
.
实操代码:
1.login页(账号密码登录完成后,将后台返回的token储存到本地);最后附上了登录页完整代码
handleSubmit(e) {
var that = this;
this.userName = this.userName.trim();
this.password = this.password.trim();
if (this.userName === "" && this.password === "") {
that.$message.warning("账号和密码无内容");
return;
}
if (this.userName === "") {
that.$message.warning("账号无内容");
return;
}
if (this.password === "") {
that.$message.warning("密码无内容");
return;
}
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
e.preventDefault();
this.form.validateFields((err, values) => {
//这一步将用户名储存在vuex ||||||||||目前域账号和用户名都是写一样了||||||||||||||||||||||||||||||||||||||||||||||||
axios
.post(this.$store.state.windowCONTENT + "sysConfig/getUserRole", {
loginUser: this.userName,
})
.then((res) => {
if (res.data.success == 0) {
//有权限
this.$store.state.loginUser = this.userName;
this.$store.state.userName = this.userName;
this.$store.state.roleCode = res.data.data.roleCode;
//存入token
localStorage.setItem("token",JSON.stringify(res.data.data.token));
// console.log(res.data.data,this.$store.state.roleCode);
if (!err) {
this.$router.push("/layout");
this.$message.success("登陆成功");
}
} else {
//无权限
this.$message.error(res.data.data);
}
});
});
},
2.marn.js(配置请求拦截器,每次请求携带token,后端进行验证;配置响应拦截器,根据后端验证返回的结果,判断token是否过期)
import "babel-polyfill";
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import { Button, message } from 'ant-design-vue';
import Antd from "ant-design-vue";
import "ant-design-vue/dist/antd.css";
import zh_CN from "ant-design-vue/lib/locale-provider/zh_CN";
import moment from "moment";
import "moment/locale/zh-cn";
import "./assets/iconfont/iconfont";
import axios from "axios";
Vue.component(Button.name, Button);
Vue.config.productionTip = false;
Vue.use(Antd);
Vue.prototype.$message = message;
// 设置axios全局默认的BASE-URL, 只要设置了全局的默认base_url,以后的请求会自动拼接上base_url
//axios.defaults.baseURL = 'http://localhost:8888/api/private/v1/'
// 配置axios的请求拦截器-(每次在请求头上携带后台分配的token-后台判断token是否有效等问题)
axios.interceptors.request.use(
function(config) {
// 在发送请求之前做些什么
// console.log('请求到了哟', config.headers.Authorization)
// 统一的给config设置 token
config.headers.Authorization = JSON.parse(localStorage.getItem("token"));
config.headers['Token'] = JSON.parse(localStorage.getItem("token"));
return config;
},
function(error) {
// 对请求错误做些什么
return Promise.reject(error);
}
);
//响应拦截器 与后端定义状态是100时候是错误 跳转到登录界面
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
console.log(response)
//当返回信息为未登录或者登录失效的时候重定向为登录页面
if (response.data.status == 100 || response.data.message == '用户未登录或登录超时,请登录!') {
router.push({
path: "/login",
querry: { redirect: router.currentRoute.fullPath }//从哪个页面跳转
})
message.warning(response.data.message);
}
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error)
})
moment.locale("zh-cn");
new Vue({
zh_CN,
router,
watch: {
// 监听路由变化
"$route.path": function(newVal, oldVal) {
console.log(`new_path = ${ newVal}, old_path = ${ oldVal}`);
},
},
store,
render: (h) => h(App),
}).$mount("#app");
3.router.js(配置导航守卫,有token或者是去登录页就通过,否则定向到登录页)
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Layout from '../components/Layout.vue'
import Login from '../components/Login.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
redirect: 'login'
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/layout',
name: 'Layout',
component: Layout
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
routes
})
// to 到哪去
// from 从哪来
// next 是否放行 next() 放行 next('/login') 拦截到登录
// 如果准备去登录, 不需要拦截
// 如果已经登录过了, 有token, 不需要拦截
// 如果不是去登陆, 且没有 token, 拦截到登录页
router.beforeEach((to, from, next) => {
const token = JSON.parse(localStorage.getItem('token'));
console.log(token);
// console.log(to)
if (to.path === '/login' || token) {
next()
} else {
next('/login')
}
})
export default router
4.退出:清除本地储存的token
//退出登录
signOut() {
console.log("点击了退出", this.$route.query.sessionId);
localStorage.clear("token");
this.$route.push('/')
//将vuex的数据初始化和清除默认的数据
},
.
.
.
.
.
.
完整登录页
<template>
<div class="login">
<div class="logo">
<img :src="logoUrl" alt />
</div>
<div class="welcome">WELCOME</div>
<div class="platform">欢迎来到XX平台</div>
<div class="box">
<a-form
id="components-form-demo-normal-login"
:form="form"
class="login-form"
@submit.prevent="handleSubmit"
>
<a-form-item>
<div class="l-bor">登录</div>
</a-form-item>
<a-form-item>
<div class="account">账号</div>
</a-form-item>
<a-form-item>
<a-input v-model="userName" allowClear class="user-name" placeholder="请输入">
<!-- v-decorator="[
'userName',
{ rules: [{ required: true, message: '请输入账号名!' }] },
]"-->
<a-icon slot="prefix" type="user" style="color: rgba(0,0,0,.25)" />
</a-input>
</a-form-item>
<a-form-item>
<div class="p-box">密码</div>
</a-form-item>
<a-form-item>
<!-- <a-input
v-decorator="[
'password',
{ rules: [{ required: true, message: 'Please input your Password!' }] },
]"
type="password"
placeholder="Password"
>
<a-icon slot="prefix" type="lock" style="color: rgba(0,0,0,.25)" />
</a-input>-->
<a-input-password
v-model="password"
allowClear
class="password"
type="password"
placeholder="请输入"
>
<!-- v-decorator="[
'password',
{ rules: [{ required: true, message: '请输入密码!' }] },
]"-->
<a-icon slot="prefix" type="lock" style="color: rgba(0,0,0,.25)" />
</a-input-password>
</a-form-item>
<a-form-item>
<!-- <a-checkbox
v-decorator="[
'remember',
{
valuePropName: 'checked',
initialValue: true,
},
]"
>Remember me</a-checkbox>-->
<!-- <a class="login-form-forgot" href>Forgot password</a> -->
<a-button
:style="{opacity: (btnFlag ? .5 : 1)}"
type="primary"
html-type="submit"
class="login-form-button"
>
<span
style="width:58px;
height:24px;
font-size:17px;
font-family:PingFang-SC-Light,PingFang-SC;
font-weight:300;
color:rgba(255,255,255,1);
line-height:24px;
text-shadow:0px 2px 3px rgba(62,32,201,0.1);"
>登录</span>
</a-button>
<!-- Or -->
<!-- <a href>register now!</a> -->
</a-form-item>
</a-form>
</div>
<div class="bottom"></div>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
logoUrl: require("../assets/logo.svg"),
userName: "",
password: "",
btnFlag: true,
flagColor: {
opacity: 0.5,
},
};
},
beforeCreate() {
this.form = this.$form.createForm(this, { name: "normal_login" });
},
created() {
},
methods: {
handleSubmit(e) {
var that = this;
this.userName = this.userName.trim();
this.password = this.password.trim();
if (this.userName === "" && this.password === "") {
that.$message.warning("账号和密码无内容");
return;
}
if (this.userName === "") {
that.$message.warning("账号无内容");
return;
}
if (this.password === "") {
that.$message.warning("密码无内容");
return;
}
// jucenter.submit({name:this.userName,pwd:this.password});
e.preventDefault();
this.form.validateFields((err, values) => {
//这一步将用户名储存在vuex ||||||||||目前域账号和用户名都是写一样了||||||||||||||||||||||||||||||||||||||||||||||||
console.log(this.userName, this.password);
axios.post(
this.$store.state.windowCONTENT + "sysConfig/getUserRole",
{ loginUser: this.userName}
).then(res => {
if (res.data.success == 0) { //有权限
this.$store.state.loginUser = this.loginUser;
this.$store.state.userName = res.data.data.userName;
this.$store.state.roleCode = res.data.data.roleCode;
//存入token
localStorage.setItem("token",JSON.stringify(res.data.data.token));
if (!err) {
this.$router.push("/layout");
console.log(111);
this.$message.success('登陆成功')
}
} else { //无权限
this.$message.error(res.data.data)
}
})
});
},
},
watch: {
userName: function (v1, v2) {
var that = this;
if (v1 && this.password) {
this.btnFlag = false;
console.log(this.btnFlag);
} else {
this.btnFlag = true;
console.log(this.btnFlag);
}
},
password: function (v1, v2) {
var that = this;
if (v1 && this.userName) {
this.btnFlag = false;
console.log(this.btnFlag);
} else {
this.btnFlag = true;
console.log(this.btnFlag);
}
},
},
};
</script>
<style lang="less">
.login {
position: relative;
text-align: left;
height: 100%;
background: linear-gradient(
154deg,
rgba(119, 182, 244, 1) 0%,
rgba(66, 106, 229, 1) 100%
);
// background-color: pink!important;
// overflow: hidden;
.logo {
position: absolute;
top: 21px;
left: 35px;
}
.welcome {
position: absolute;
bottom: 60%;
left: 10%;
width: 416px;
height: 100px;
font-size: 72px;
font-family: PingFang-SC-Regular, PingFang-SC;
font-weight: 400;
color: rgba(255, 255, 255, 1);
line-height: 100px;
letter-spacing: 6px;
}
.platform {
position: absolute;
bottom: 53%;
left: 10%;
width: 313px;
height: 33px;
font-size: 24px;
font-family: PingFang-SC-Light, PingFang-SC;
font-weight: 300;
color: rgba(255, 255, 255, 1);
line-height: 33px;
letter-spacing: 2px;
}
.box {
position: absolute;
bottom: 10%;
right: 0;
// margin: 0 auto;
// margin-bottom: 151px; //定位高度-----↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
margin-right: 21%;
width: 370px;
height: 500px;
border-radius: 10px;
background: rgba(252, 251, 255, 1);
padding: 0px 53px;
//去除默认下边距
.ant-form-item {
margin-bottom: 0;
}
//输入框
.ant-input-affix-wrapper .ant-input:not(:last-child) {
height: 36px;
font-size: 14px;
font-family: PingFang-SC-Regular, PingFang-SC;
font-weight: 400;
color: rgba(53, 58, 64, 1);
// background-color: pink;
}
//登录按钮
#components-form-demo-normal-login .login-form-button {
width: 264px;
height: 44px;
background: linear-gradient(
90deg,
rgba(60, 163, 247, 1) 0%,
rgba(28, 106, 235, 1) 100%
);
box-shadow: 0px 2px 3px 0px rgba(62, 32, 201, 0.1);
border-radius: 6px;
}
.l-bor {
margin-left: -53px;
margin-top: 50px;
line-height: 25px;
border-left: 6px solid rgba(28, 106, 235, 1);
padding-left: 47px;
font-size: 18px;
font-family: PingFang-SC-Medium, PingFang-SC;
font-weight: 500;
color: rgba(53, 58, 64, 1);
letter-spacing: 1px;
}
// .account{
// margin-top: 58px;
// width:27px;
// height:17px;
// font-size:12px;
// font-family:PingFang-SC-Light,PingFang-SC;
// font-weight:300;
// color:rgba(53,58,64,1);
// line-height:17px;
// letter-spacing:1px;
// }
.ant-form-item:nth-child(2) {
position: absolute;
top: 133px;
.account {
width: 27px;
height: 17px;
font-size: 12px;
font-family: PingFang-SC-Light, PingFang-SC;
font-weight: 300;
color: rgba(53, 58, 64, 1);
line-height: 17px;
letter-spacing: 1px;
}
}
.ant-form-item:nth-child(3) {
position: absolute;
top: 160px;
width: 264px;
}
.ant-form-item:nth-child(4) {
position: absolute;
top: 230px;
width: 264px;
.p-box {
width: 27px;
height: 17px;
font-size: 12px;
font-family: PingFang-SC-Light, PingFang-SC;
font-weight: 300;
color: rgba(53, 58, 64, 1);
line-height: 17px;
letter-spacing: 1px;
}
}
.ant-form-item:nth-child(5) {
position: absolute;
top: 257px;
width: 264px;
}
.ant-form-item:nth-child(6) {
position: absolute;
top: 376px;
width: 264px;
}
}
.bottom {
position: absolute;
bottom: 0;
width: 100%;
// height: 151px; //固定高度-----↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
height: 10%; //固定高度-----↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
background: linear-gradient(
360deg,
rgba(75, 118, 232, 0) 0%,
rgba(57, 98, 224, 0.41) 100%
);
}
}
#components-form-demo-normal-login .login-form {
max-width: 300px;
}
#components-form-demo-normal-login .login-form-forgot {
float: right;
}
#components-form-demo-normal-login .login-form-button {
width: 100%;
}
</style>
还没有评论,来说两句吧...