This series will create a project from scratch and articles will continue to be updated

Project code: https://github.com/no-pear/edu-fed.git

Login and Authentication

A login,

1) Basic layout of the page

2) Login interface encapsulation

import request from '@/utils/request'
import qs from 'qs'
// import store from '@/store'

interface User {
    phone: string;
    password: string;
}

/ / login
export const login = (form: User) = > {
  return request({
    method: 'POST'.url: '/front/user/login'.// headers: { 'content-type': 'application/x-www-form-urlencoded' },
    /** * Content-type is application/json if data is a common object * if data is qs.stringfity(data) after conversion: Key =value&key=value, then content-type will be set to Application /x-www-form-urlencoded * If data is FormData, Then the content-type is multipart/form-data */
    data: qs.stringify(form) // Axios sends application/ JSON data by default})}Copy the code

3) Login processing

  • Form validation
  • Verify that the form is submitted
  • Processing request results
    • Success: Jump to home page
    • Failure: give a hint

src/login/index.vue

<template> <div class="login"> <! -- :model="ruleForm" :rules="rules" ref="ruleForm" --> <el-form ref="form" :model="form" :rules="rules" Label-width ="80px" label-position='top'> <el-form-item label=" prop "="phone"> < EL-input V-model ="form.phone"></el-input> </el-form-item> <el-form-item label=" password" prop="password"> <el-input v-model="form.password" type="password"></el-input> </el-form-item> <el-form-item> <el-button :loading='isLoginLoading' Type ="primary" @click="onSubmit"> login </el-button> </el-form-item> </el-form> </div> </template> <script lang="ts"> import Vue from 'vue' // import request from '@/utils/request' // import qs from 'qs' import { Form } from 'element-ui' import { login } from '@/services/user' export default Vue.extend({ name: 'LoginIndex', data () { return { form: { phone: '15510792995', password: '111111' }, isLoginLoading: false, rules: { phone: [ { required: true, message: 'Please input mobile phone number ', trigger: 'blur'}, {pattern: /^1\ D {10}$/, message:' Please input correct mobile phone number ', trigger: 'blur'}], Password: [{required: True, message: 'Please enter password ', trigger: 'blur'}, {min: 6, Max: 18, message:' length between 6 and 18 characters ', trigger: 'blur' } ] } } }, methods: { async onSubmit () { try { // 1. The form validates await (this.$refs.form as form).validate() // const {data} = await request({// method: 'POST', // url: '/front/user/login', // headers: { 'content-type': 'application/x-www-form-urlencoded' }, // data: qs.stringify(this.form) // }) const { data } = await login(this.form) // console.log(data) // 3. Processing request result // Success: jump to home page // failure: prompt if (data.state! This.$message.error(data.message)} else {$code.store.mit ('setUser', // this.$router. Push ({// name: 'home' / /}) / / login successful jump to the home page or login to jump to want to go to the page before this. $router. Push (this. $route. Query. Redirect the as string | | '/') IsLoginLoading = false} Catch (error) {console.log(' format failed ', false) } } } }) </script> <style lang="scss" scoped> .login { background-color: $body-bg; height: 100vh; display: flex; justify-content: center; align-items: center; .el-form { background: #fff; padding: 20px; border-radius: 5px; width: 300px; .el-button { width: 100%; } } } </style>Copy the code

Second, the certification

For some pages, you must log in to access them, so the server returns an access_token when you log in. The access_token can be added to the request header for each subsequent access

When the validity period expires, refresh_token is used to obtain the Access_token again

1) routing

/** * Global front guard * to: Route: destination Route object to be entered * from: Route: current navigation Route to leave * next: Function: Must call this method to resolve this hook. The execution depends on the call parameters of the next method. * /
router.beforeEach((to, from, next) = > {
  // console.log('to--->', to)
  // console.log('from--->', from)
  // To.matched is an array (matched routing information)
  if (to.matched.some(record= > record.meta.requiresAuth)) {
    if(! store.state.user) { next({path: '/login'.query: { // Pass the query string argument through the URL
          redirect: to.fullPath // Tell the login page which page to return to successfully login}})}else {
      next()
    }
  } else {
    next()
  }
})
Copy the code

2) Request interceptor

request.interceptors.request.use(function (config) {
  // Do something before request is sent
  // console.log(config)
  const { user } = store.state
  if (user && user.access_token) {
    config.headers.Authorization = user.access_token
  }

  return config
}, function (error) {
  // Do something with request error
  return Promise.reject(error)
})
Copy the code

3) Response interceptor

// Response interceptor
let isRefreshing = false // Controls the refreshing of the token status
let requests: any[] = [] // Stores 401 requests that come during token refresh
request.interceptors.response.use(function (response) { // 2xx status code
  // console.log('response--->', response)

  return response
}, async function (error) { // The status code exceeds the 2xx range
  // console.log('error--->', error)
  if (error.response) { // The request is sent and the response is received, but the status code is outside the 2xx range
    const { status } = error.response
    if (status === 400) {
      Message.error('Request parameter error')}else if (status === 401) {
      /** * Token invalid * If refresh_token is available, try to obtain a new token using refresh_token * if not, jump directly to the login page */

      if(! store.state.user) { redirectLogin()return Promise.reject(error)
      }

      / / refresh token
      if(! isRefreshing) { isRefreshing =true // Enable the refresh state

        return refreshToken().then(res= > {
          if(! res.data.success) {throw new Error('Failed to refresh token')}// Succeeded in refreshing the token
          store.commit('setUser', res.data.content)
          // Send requests from the Requests queue
          requests.forEach(cb= > {
            cb()
          })
          / / reset requests
          requests = []
          // console.log('error config-->', error. Config) // Failed request configuration information
          return request(error.config) // Resend the first request
        }).catch(err= > {
          console.log('err-->', err)
          // Clear the status of the current login user
          store.commit('setUser'.null)
          // Return to the login page
          redirectLogin()
          return Promise.reject(error)
        }).finally(() = > {
          isRefreshing = false // Resets the refresh state
        })

        return
      }

      // In the flush state, suspend requests into the Requests array
      return new Promise(resolve= > {
        requests.push(() = > {
          resolve(request(error.config))
        })
      })

      // try {
      // // tries to obtain a new token
      // const { data } = await axios.create()({
      // method: 'POST',
      // url: '/front/user/refresh_token',
      // data: qs.stringify({
      // refreshtoken: store.state.user.refresh_token
      / /})
      / /})
      // console.log('data:', data)
      / / / * *
      // * Update the new refresh_token to the container and local storage
      // * Succeeded in obtaining a new token and resend the failed request
      / / * /
      // store.commit('setUser', data.content)
      // console.log('error config-->', error. Config) // Failed request configuration information
      // return request(error.config)
      // } catch (error) {
      // // Clear the status of the current user
      // store.commit('setUser', null)
      // // return to the login page
      // redirectLogin()
      // return Promise.reject(error)
      // }
    } else if (status === 403) {
      Message.error('No permission, please contact administrator')}else if (status === 404) {
      Message.error('Requested resource not found')}else if (status >= 500) {
      Message.error('Server error')}}else if (error.request) { // The request was sent and no response was received
    Message.error('Request response failed')}else { // Something happened during the setup request that triggered an error
    Message.error('Request failed:${error.message}`)}return Promise.reject(error)
})
Copy the code