当前位置: 首页 > news >正文

页面登录功能的思路

一般用模块化的思想来单独封装请求,发送后获取token的数据,除了登录页面如果别的页面想实现数据请求一般都要带上token,所以将token存放在vuex中的state里,通过mutations来修改state中的数据。因为veux中存贮的数据是存在内存里的,刷新后就没有了,所以要把token存在本地,可以用localStorage或者cookie存储,在把本地存储的token赋值到vuex中。用请求拦截器给每次请求都加上请求头和token。但是token不是永久的,会有失效的问题,所以可以根据后端返回的状态码进行判断,如果不存在了,用Vue-Router中提供的钩子函数,实现拦截;如果存在,就正常放行,最后$router.push()实现跳转登录功能。

一     登录功能实现的大致步骤:

1.前端用户验证用户输入是否规范。

2.前端调用后端的接口,向后端发出登录请求。

3.后端收到请求,验查数据库中是否有相应的账号和密码是否正确。

4.验证通过后,将成功信息连同token一起发给前端。

5.前端将token存储起来,每次用户进入登录才能访问的页面时或者每次进网站时向后端发送那个token。

6.如果token过期了,则清除用户的信息,回到未登录的状态。

二     vue-admin-template框架配置登录接口

1.使用axios--配置基础路径

开发阶段基础路径--项目开发阶段使用的服务器:

  env.development='http://xx.xxx.xxx.xx:xxxx'

  产品环境路径--上线阶段后使用的服务器:

 env.production='http://xx.xxx.xxx.xx:xxxx'

   重启项目使配置生效

npm run dev

 2.真实对接后台登录接口

(api/user.js中)将修改url

修改前:

/vue-admin-template/user/login

修改后:

/user/login

3.修改真实对接用户数据

(login/index.vue中)

修改用户:

修改前:

 loginForm:{
                username:"admin",
                password:111111
            }

修改后:

loginForm:{
                username:"admin1",
                password:123321
            }

修改mock校验器限定输入得用户名 ,取消用户名验证

修改前:

 loginRules: {
                username: [{ required: true, trigger: 'blur', validator: validateUsername }],
                password: [{ required: true, trigger: 'blur', validator: validatePassword }]
            },    

修改后:

 loginRules: {
                username: [{ required: true, trigger: 'blur',  }],
                password: [{ required: true, trigger: 'blur', validator: validatePassword }]
            },  

4.登录时除了发送login 还发送info(getInfo)

 给每一个请求都携带token:

(utils/request.js中)

修改请求拦截器:

修改前:

 axios.interceptors.request.user(config=>{
            config.headers['x-Token']=getToken()
        })

修改后:

axios.interceptors.request.user(config=>{
            config.headers['Authorization']=getToken()
        })

修改响应拦截器 响应拦截器--当响应status不为200时候执行错误回调:

修改前:

axios.interceptors.response.user(config=>{
            if(res.code!==20000){
                callback(error)
            }
        })

 修改后:

 axios.interceptors.response.user(config=>{
            if(res.status!==200){
                callback(error)
            }
        })

可以自己封装get post 方法:

//封装get方法
  export function get(url,params){
          return service.get(url,{params})
        }
//封装数据格式为表单的post方法
  export function post(url,data){
          return service.post(url,qs.stringify(data))
        }
//封装装数据格式为json的post方法
   export function postJSON(url,data){
          return service.post(url,data)
        }

5.如何获取个人信息

(store/modules/user.js中)

        用户登录和获取个人信息getInfo维护在store中     

  const { username, userFace } = data  //--将后台真实字段从数据中解构出来

        commit('SET_NAME', username)

        commit('SET_AVATAR', userFace); //将用户名和头像提交到突变函数中

 6.使用getter映射状态机数据

       (store/modules/getters.js中)

username:state=>state.use.username

userFace:state=>state.use.userFace

三     登录的请求是怎么发出的:

1.表单验证(

在login文件下的index.vue部分代码)

export default {
  name: 'Login',
  data() {
    const validateUsername = (rule, value, callback) => {
      if (!validUsername(value)) {
        callback(new Error('Please enter the correct user name'))
      } else {
        callback()
      }
    }
    const validatePassword = (rule, value, callback) => {
      if (value.length < 6) {
        callback(new Error('The password can not be less than 6 digits'))
      } else {
        callback()
      }
    }
    return {
      loginForm: {
        username: 'admin1',
        password: '123321'
      },
      loginRules: {
        username: [{ required: true, trigger: 'blur' }],
        password: [{ required: true, trigger: 'blur', validator: validatePassword }]
      },
      loading: false,
      passwordType: 'password',
      redirect: undefined
    }
  },
  watch: {
    $route: {
      handler: function(route) {
        this.redirect = route.query && route.query.redirect
      },
      immediate: true
    }
  },
  methods: {
    showPwd() {
      if (this.passwordType === 'password') {
        this.passwordType = ''
      } else {
        this.passwordType = 'password'
      }
      this.$nextTick(() => {
        this.$refs.password.focus()
      })
    },
    handleLogin() {
      this.$refs.loginForm.validate(valid => {
        if (valid) {
          this.loading = true
          this.$store.dispatch('user/login', this.loginForm).then(() => {
            this.$router.push({ path: this.redirect || '/' })
            this.loading = false
          }).catch(() => {
            this.loading = false
          })
        } else {
          console.log('error submit!!')
          return false
        }
      })
    }
  }
}

  (utils/validate.js里)   设置校验规则:

 @param {string} path
 * @returns {Boolean}
 */
export function isExternal(path) {
  return /^(https?:|mailto:|tel:)/.test(path)
}

/**
 * @param {string} str
 * @returns {Boolean}
 */
export function validUsername(str) {
  const valid_map = ['admin', 'editor','admin1'];
  
  return valid_map.indexOf(str.trim()) >= 0
}

2.调用api/user.js中封装好的api

(utils/request.js部分代码)   创建axios实例,获取axios实例。  设置基地址, baseURL



// create an axios instance
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
  // withCredentials: true, // send cookies when cross-domain requests
  timeout: 5000 // request timeout
})



//导出实例 service
export default service



3.收集用户的参数,上传给api。(页面上收集页面的数据和接口中的一致)

(login/index.vue中部分代码)

<template>
  <div class="login-container">
    <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on" label-position="left">

      <div class="title-container">
        <h3 class="title">Login Form</h3>
      </div>

      <el-form-item prop="username">
        <span class="svg-container">
          <svg-icon icon-class="user" />
        </span>
        <el-input
          ref="username"
          v-model="loginForm.username"
          placeholder="Username"
          name="username"
          type="text"
          tabindex="1"
          auto-complete="on"
        />
      </el-form-item>

      <el-form-item prop="password">
        <span class="svg-container">
          <svg-icon icon-class="password" />
        </span>
        <el-input
          :key="passwordType"
          ref="password"
          v-model="loginForm.password"
          :type="passwordType"
          placeholder="Password"
          name="password"
          tabindex="2"
          auto-complete="on"
          @keyup.enter.native="handleLogin"
        />
        <span class="show-pwd" @click="showPwd">
          <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
        </span>
      </el-form-item>

      <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">Login</el-button>

      <div class="tips">
        <span style="margin-right:20px;">username: admin</span>
        <span> password: any</span>
      </div>

    </el-form>
  </div>
</template>

<script>

4.   经过请求拦截器,添加请求头(添加token)

(utils/request.js部分代码)

// request interceptor
service.interceptors.request.use(
  config => {
    // do something before request is sent

    if (store.getters.token) {
      // let each request carry token
      config.headers['Authorization'] = getToken()
    }
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

 5.经过响应拦截器的处理

简化获取有效的数据写法,(response.data),判断当前操作是否成功, 决定是否axios报错Promise.reject()     

(utils/request.js部分代码)

// response interceptor
service.interceptors.response.use(
  response => {
    const res = response.data

    if (res.status!==200 ) {
      Message({
        message: res.message || 'Error',
        type: 'error',
        duration: 5 * 1000
      })

      if (res.status==401 || res.status === 500) {
        // to re-login
        MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
          confirmButtonText: 'Re-Login',
          cancelButtonText: 'Cancel',
          type: 'warning'
        }).then(() => {
          store.dispatch('user/resetToken').then(() => {
            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)
  }
)

6.得到api调用的结果   

(login/index.vue中部分代码)

<template>
  <div class="login-container">
    <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on" label-position="left">

      <div class="title-container">
        <h3 class="title">Login Form</h3>
      </div>

      <el-form-item prop="username">
        <span class="svg-container">
          <svg-icon icon-class="user" />
        </span>
        <el-input
          ref="username"
          v-model="loginForm.username"
          placeholder="Username"
          name="username"
          type="text"
          tabindex="1"
          auto-complete="on"
        />
      </el-form-item>

      <el-form-item prop="password">
        <span class="svg-container">
          <svg-icon icon-class="password" />
        </span>
        <el-input
          :key="passwordType"
          ref="password"
          v-model="loginForm.password"
          :type="passwordType"
          placeholder="Password"
          name="password"
          tabindex="2"
          auto-complete="on"
          @keyup.enter.native="handleLogin"
        />
        <span class="show-pwd" @click="showPwd">
          <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
        </span>
      </el-form-item>

      <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">Login</el-button>

      <div class="tips">
        <span style="margin-right:20px;">username: admin</span>
        <span> password: any</span>
      </div>

    </el-form>
  </div>
</template>

<script>

7.保存token到vuex 

(store/modules/user.js部分代码)

const getDefaultState = () => {
  return {
    token: getToken(),
    username: '',
    userFace: ''
  }
}

const state = getDefaultState()

const mutations = {
  RESET_STATE: (state) => {
    Object.assign(state, getDefaultState())
  },
  SET_TOKEN: (state, token) => {
    state.token = token
  },
  SET_NAME: (state, username) => {
    state.username = username
  },
  SET_AVATAR: (state, userFace) => {
    state.userFace = userFace
  }
}

const actions = {
  // user login
  login({ commit }, userInfo) {
    const { username, password } = userInfo
    return new Promise((resolve, reject) => {
      login({ username: username.trim(), password: password }).then(response => {
        const { data } = response
        commit('SET_TOKEN', data.token)
        setToken(data.token)
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  },


}

8.token做到持久化。

(utils/auth.js)  本文使用的是cookie存

import Cookies from 'js-cookie'

const TokenKey = 'vue_admin_template_token'

export function getToken() {
  return Cookies.get(TokenKey)
}

export function setToken(token) {
  return Cookies.set(TokenKey, token)
}

export function removeToken() {
  return Cookies.remove(TokenKey)
}

 

相关文章:

  • ShardingSphere 5.2.0:分片审计功能拦截多分片场景下的不合理请求
  • 毕业设计 单片机stm32智能大棚监控护理系统 - lora 远程通信
  • 关于QCefView的一些事
  • b站pink老师JavaScript的jQuery 案例代码——电梯导航案例
  • Python快速实现简易飞机大战小游戏
  • PowerWorld仿真与电力系统潮流计算(牛顿拉夫逊法和高斯赛德尔法)(Matlab实现)
  • VSCode自动更新后关闭,重新打开后版本自动降级
  • Google Earth Engine(GEE)——如何处理阈值筛选后的结果没发生变化,以青藏高原NDBI为例
  • 数字验证学习笔记——SystemVerilog芯片验证4 ——数据类型
  • 行为树BT设计与实现
  • 基于神经网络的指纹识别,指纹比对技术何时出现
  • 【图像分割】基于差分进化算法优化模糊熵实现多级图像阈值分割附matlab代码
  • LeetCode 0817. 链表组件
  • 27、Java——超市会员管理系统(对象+IO流)
  • 嵌入式分享合集74
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • 【面试系列】之二:关于js原型
  • C++类中的特殊成员函数
  • Consul Config 使用Git做版本控制的实现
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • HTML中设置input等文本框为不可操作
  • HTTP中GET与POST的区别 99%的错误认识
  • Leetcode 27 Remove Element
  • MySQL QA
  • Nginx 通过 Lua + Redis 实现动态封禁 IP
  • SpiderData 2019年2月25日 DApp数据排行榜
  • vue.js框架原理浅析
  • 紧急通知:《观止-微软》请在经管柜购买!
  • 跨域
  • 蓝海存储开关机注意事项总结
  • 宾利慕尚创始人典藏版国内首秀,2025年前实现全系车型电动化 | 2019上海车展 ...
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • (1) caustics\
  • (C语言)二分查找 超详细
  • (备忘)Java Map 遍历
  • (接口自动化)Python3操作MySQL数据库
  • (论文阅读23/100)Hierarchical Convolutional Features for Visual Tracking
  • (一)UDP基本编程步骤
  • (原创)Stanford Machine Learning (by Andrew NG) --- (week 9) Anomaly DetectionRecommender Systems...
  • (原創) 未来三学期想要修的课 (日記)
  • (转)原始图像数据和PDF中的图像数据
  • * CIL library *(* CIL module *) : error LNK2005: _DllMain@12 already defined in mfcs120u.lib(dllmodu
  • .NET Compact Framework 多线程环境下的UI异步刷新
  • .NET delegate 委托 、 Event 事件
  • .NET 中选择合适的文件打开模式(CreateNew, Create, Open, OpenOrCreate, Truncate, Append)
  • @Documented注解的作用
  • @hook扩展分析
  • @RequestBody与@ModelAttribute
  • @Validated和@Valid校验参数区别
  • @取消转义
  • [Android Pro] AndroidX重构和映射
  • [Android]使用Git将项目提交到GitHub
  • [error] 17755#0: *58522 readv() failed (104: Connection reset by peer) while reading upstream