使用路由导航守卫结合 token 处理视图访问拦截

    title: 基于 Token 的验证流程 participant 客户端 as client participant 服务器 as server client -> server: 用户名+密码 server --> client: Token 令牌 note over client: 将 Token 存储到本地

    在 中登陆成功,将服务器下发的 token 保存到本地存储:

    src/router/index.js 中,添加全局路由导航守卫对非登陆请求进行登陆权限判定:

    1. // 其它代码...
    2. const router = new Router({
    3. // ...
    4. })
    5. router.beforeEach((to, from, next) => {
    6. const {path} = to
    7. if (path !== '/login') { // 如果请求的不是 /login 则校验登陆状态
    8. const token = window.localStorage.getItem('token')
    9. if (!token) { // 如果没有 token 则让其跳转到 /login
    10. next('/login')
    11. } else { // 有 token,让其通过
    12. next()
    13. }
    14. } else {
    15. // 如果用户请求的就是 /login 则直接调用 next() 放行
    16. next()
    17. }
    18. })
    19. export default router

    安装依赖:

    1. # 或者 npm install element-ui
    2. yarn add element-ui

    src/main.js 中加载并配置:

    1. import Vue from 'vue'
    2. import App from './App'
    3. import router from './router'
    4. import ElementUI from 'element-ui'
    5. import 'element-ui/lib/theme-chalk/index.css'
    6. Vue.use(ElementUI)
    7. Vue.config.productionTip = false
    8. /* eslint-disable no-new */
    9. new Vue({
    10. el: '#app',
    11. router,
    12. components: { App },
    13. template: '<App/>'
    14. })

    布局登陆组件

    参考 Element 的 Form表单组件文档,我们先来个最简单的登陆表单。

    src/components/login/template.html 文件内容替换为:

    1. <div>
    2. <el-form :model="loginForm">
    3. <el-form-item>
    4. <el-input v-model="loginForm.username" placeholder="用户名"></el-input>
    5. </el-form-item>
    6. <el-form-item>
    7. <el-input type="password" v-model="loginForm.password" placeholder="密码"></el-input>
    8. </el-form-item>
    9. <el-form-item>
    10. <el-button type="primary" @click="handleLogin">登陆</el-button>
    11. </el-form-item>
    12. </el-form>
    13. </div>

    接下来我们开始调整登陆页面的样式。

    首先把公共样式写到 src/assets/css/index.css 文件中。

    然后在 src/main.js 加载:

    1. // 代码略...
    2. // 引入我们的公共样式
    3. import './assets/css/index.css'
    4. // 代码略...

    :

    1. <div class="login-wrap">
    2. <div class="login-form">
    3. <el-form :model="loginForm">
    4. <el-form-item>
    5. <el-input v-model="loginForm.username" placeholder="用户名"></el-input>
    6. </el-form-item>
    7. <el-form-item>
    8. <el-input type="password" v-model="loginForm.password" placeholder="密码"></el-input>
    9. </el-form-item>
    10. <el-form-item>
    11. <el-button class="login-submit" type="primary" @click="handleLogin">登陆</el-button>
    12. </el-form-item>
    13. </el-form>
    14. </div>
    15. </div>

    src/components/login/style.css:

    1. .login-wrap {
    2. width: 100%;
    3. height: 100%;
    4. background-color: #2d434c;
    5. display: flex;
    6. justify-content: center;
    7. align-items: center;
    8. }
    9. .login-wrap .login-form {
    10. background-color: #fff;
    11. padding: 50px 50px 20px 50px;
    12. width: 25%;
    13. }
    14. .login-wrap .login-form .login-submit {
    15. width: 100%;
    16. }
    1. 为表单中需要验证的表单项 el-form-item 声明 prop 属性,属性值给一个有意义的名称
    1. <div class="login-wrap">
    2. <div class="login-form">
    3. <el-form :model="loginForm">
    4. <el-form-item prop="username">
    5. <el-input v-model="loginForm.username" placeholder="用户名"></el-input>
    6. </el-form-item>
    7. <el-form-item prop="password">
    8. <el-input type="password" v-model="loginForm.password" placeholder="密码"></el-input>
    9. </el-form-item>
    10. <el-form-item>
    11. <el-button class="login-submit" type="primary" @click="handleLogin">登陆</el-button>
    12. </el-form-item>
    13. </el-form>
    14. </div>
    15. </div>
    1. 在组件的 data 中增加一个属性对象 loginFormRule 配置 prop 字段属性的验证规则
    1. 在登陆组件的模板中为 el-form 表单组件绑定 rules 属性到 data 中定义的 loginFormRule
    1. <div class="login-wrap">
    2. <div class="login-form">
    3. <el-form :model="loginForm" :rules="loginFormRule">
    4. ... 代码略
    1. 测试验证是否成功

    我们在前面做的 1 - 4 步已经完成了基本的表单验证功能。接下来我们要在表单提交登陆发起请求的时候使用 JavaScript 校验是否通过表单验证,表单验证通过再去提交表单。

    首先为登陆组件模板的 el-form 组件声明 ref 属性,属性值给一个有意义的名字。

    1. <div class="login-wrap">
    2. <div class="login-form">
    3. ... 代码略

    然后在表单提交的时候调用 JavaScript 判断表单验证是否通过,通过再发起登陆请求。

    1. export default {
    2. // ... 代码略
    3. methods: {
    4. handleLogin () {
    5. // ['form'] 中的 form 就是 el-form 标签 ref 属性值
    6. this.$refs['form'].validate((valid) => {
    7. if (!valid) {
    8. return
    9. }
    10. axios.post('http://localhost:8888/api/private/v1/login', this.loginForm)
    11. .then(res => {
    12. const {data, meta} = res.data
    13. const {msg, status} = meta
    14. if (status === 200) {
    15. // 将凭证放到到本地存储(会在路由守卫那里使用)
    16. window.localStorage.setItem('token', data.token)
    17. // 跳转到首页
    18. this.$router.push('/')
    19. } else if (status === 400) {
    20. window.alert(msg)
    21. }
    22. })
    23. })
    24. }
    25. }
    26. // ... 代码略
    27. }

    使用 Message 消息提示给出操作反馈

    1. import axios from 'axios'
    2. export default {
    3. // ... 代码略
    4. methods: {
    5. handleLogin () {
    6. // ['form'] 中的 form 就是 el-form 标签 ref 属性值
    7. this.$refs['form'].validate((valid) => {
    8. if (!valid) {
    9. return
    10. }
    11. axios.post('http://localhost:8888/api/private/v1/login', this.loginForm)
    12. .then(res => {
    13. const {data, meta} = res.data
    14. const {msg, status} = meta
    15. if (status === 200) {
    16. // 将凭证放到到本地存储(会在路由守卫那里使用)
    17. window.localStorage.setItem('token', data.token)
    18. // 跳转到首页
    19. this.$router.push('/')
    20. this.$message({
    21. message: '登陆成功',
    22. type: 'success'
    23. })
    24. } else if (status === 400) {
    25. this.$message.error(msg)
    26. }
    27. })
    28. })
    29. }
    30. }
    31. // ... 代码略
    32. }