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>
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值