This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

The identity authentication

  • The login is complete, but now the user can access it without logging in (the corresponding URL), which makes the login meaningless.
  • To make logging in meaningful:
    1. You should generate a token (token) for the user after successful login and save the token.
    2. Validate the token when the user accesses any page (component) that needs to be logged in;
    3. In this way, users can be identified whether they are logged in or have access to corresponding functions.

    A. If yes, access the component. B. If a failure occurs, a message is displayed.

  • How can you make the data in the LOGIN component accessible to any other component? You can use Vuex, Vue’s official state management tool.

Vuex

  • Vuex documentation: vuex.vuejs.org/zh/

Introduction to the

  • Vuex is a state management library designed specifically for vue.js
  • Centrally store data that needs to be shared
  • It’s essentially a JavaScript library
  • Used to solve complex component communication, data sharing problems

Vuex helps us manage shared state and comes with more concepts and frameworks. This requires a trade-off between short-term and long-term benefits. Using Vuex can be tedious and redundant if you don’t plan to develop large, single-page applications. That’s true — if your application is simple, you’d better not use Vuex. A simple Store pattern (opens New Window) is all you need. However, if you need to build a medium to large single-page application, and you’re probably thinking about how to better manage state outside of components, Vuex would be a natural choice.

How do I determine whether I need Vuex?

  • Multiple views depend on the same state
  • Behaviors from different views need to change to the same state

Vuex use

Install Vuex

  • npm install vuex -S
  • When creating a project using the Vue CLI, you can select Vuex from the project options to install it

use

  • becauseVuex, like VueRouter, is a plug-in for Vue, so it needs to be usedvue.use(Vuex)

State

  • Data stored in components that need to be shared

Mutation

  • The only way to change the state in Vuex’s store is to commit mutation
    • Reason: Easy maintenance
  • Commit mutation for useStore.mit (' method name ')
    • A mutation is triggered
  • Have a historical record
    • Each operation is recorded in a detailed list
  • Must be a synchronization function

Payload submission

  • Pass an additional parameter, the mutation payload, to store.mit
  • In most cases, the payload should be an object, which can contain multiple fields and the mutation record will be more readable

Action

  • Action is similar to mutation, except that:
    • The Action commits mutation rather than a direct state change
    • Action can contain any asynchronous operation, and mutation cannot have asynchronous operations.
    • For example, only synchronous operations can exist in mutation. If asynchronous operations are written in mutation, the true operation order cannot be recorded in DevTools.

conclusion

  • The asynchronous operation is written in actions, the synchronous operation is written in mutations, and the final call is actions with asynchronous function

Module

  • Because of the use of a single state tree, all the states of an application are grouped into one large object. As the application becomes more complex, the Store object can become quite bloated.
  • To solve these problems, Vuex allows us to split the Store into modules. Each module has its own state, mutation, action, getter, and even nested submodules.
  • Code demo
const moduleA = {
  state: () = >({... }),mutations: {... },actions: {... },getters: {...}
}
Copy the code

getters

  • Vuex allows you to define “getters” (which you can think of as computed properties of a store) in a store. Just like evaluating properties, the return value of a getter is cached based on its dependency and is recalculated only if its dependency value changes.
  • Code demo
const store = new Vuex.store({
  state: {todos: [{id: 1.text: '... '.done: true},
      {id: 2.text: '... '.done: false}},getters: {
    doneTodos: state= > {
      return state.todos.filter(todo= > todo.done)
    }
  }
})
Copy the code

Authentication: Login state storage

  • In order to be able to access user login information in any component, the state is stored in Vuex.

Verify page access permissions

  • Before redirecting a route, verify the login status and perform subsequent processing based on the result.
  • Use Vue Router’s navguard beforeEach to detect login status when task navigation is triggered.

Implementation method

  • Directly set a route, in which case all internal child routes need to be authenticated (including themselves)
  • Verify that the login user information is stored in Vuex store
  • All background pages need to be in the login state. However, if only some pages need to be in the login state, how to determine and handle the problem? This can be set using the routing meta information function in the Vue Router.

Routing meta information

  • You need to verify login status to set some pages
  • This project sets the child routes of “/” to require authentication
    • Meta Is used to save custom routing data
    • RequiresAuth: whether authentication is required. True: Authentication is required

After login, the page you accessed last time is displayed

  • For example, I’m currently visiting AD Management/advertAt this point I want to access user management/user, but because there is no operation for a long time, the login status expires (manually request store emulation), it will automatically jump back/loginWhen I log in again, it will jump to/The home page.
  • What if you want to log in and jump to the page you visited last time?
  • In this case, the information about the current to destination route should be recorded each time the hop to /login is displayed. The information can be set using the query attribute of the hop route.

  • inlogin/index.vueIt reads:this.$router.push(this.$route.query.redirect || '/')

Matters needing attention

  • Difference between fullPath and PATH


  • r o u t e r with The router and
    The difference between the route

    • Former: jump operation, call method
    • Latter: indicates the routing information of the current route
  • To verify the page access permission, set the
    • Set the user object in Vuex to null
    • Delete the user object from the Local Storage in the console Application

User information and interface authentication

  • Use Postman to test the interface
  • The token string can be written directly globally during testing. That way, you don’t have to fill in every time you test a different interface.

Pay attention to

  • 401: Unauthorized (client error starting with 4)

Display user information

  • Encapsulates the request
// Basic user information is null because no parameters need to be passed
export const getUserInfo = () = > {
  return request({
    method: 'GET'.url: '/front/user/getInfo'
    headers: {
      // The token information needs to be fetched from the Vuex store
      Authorization: store.state.user.access_token
    }
  })
}
Copy the code
  • Data binding to the view
methods: {
    async loadUserInfo () {
      // Save the returned data to data
      const { data } = await getUserInfo()
      this.userInfo = data.content
    }
  }
Copy the code

The Token is set through a request interceptor

  • Note: you need to check whether there is a user object before user.access_token, be careful
// Create axios request interceptor, callback function
request.interceptors.request.use(function (config) {
  const { user } = store.state
  // Sets the token string in the request header
  if (user && user.access_token) {
    config.headers.Authorization = user.access_token
  }
  return config
})
Copy the code

User exit

  • The “Exit” setting click event is invalid
    • Analysis: The “exit” is a component instead of a button, and the events bound to the component are custom events, not native DOM events.
    • But now if you want to use native DOM events, you need to set the event modifier to the event.

Improve user experience: A dialog box is displayed

  • Remind the user whether to exit the MessageBox popup
handleLogout () {
// Contents of the message box The title of the message box
  this.$confirm('Are you sure? '.'Exit hint', {
    confirmButtonText: 'sure'.cancelButtonText: 'cancel'.type: 'warning'
  }).then(() = > {
    // messageBox is a combination of messageBox and message prompt
    this.$message({
      type: 'success'.message: 'Exit successful! '
    })
    // 1. Delete the user object stored in Vuex as null
    this.$store.commit('setUser'.null)
    // 2. The route is forwarded to /login
    this.$router.push('/login')
  }).catch(() = > {
    this.$message({
      type: 'info'.message: 'Cancelled exit'})})}Copy the code

Token Expiration Processing

  • The reason why “User information disappears” appears, but the page still stays on the home page is that the background page does not need to be authenticated by the server, but the data in the Vuex is verified locally on the client. However, the local Vuex cannot verify whether the token is expired
  • Back-end interface authentication is not always available and will expire. After the expiration, it can be processed

Token Expiration Analysis

  • The Token is used for interface authentication. However, the Token has an expiration time set on the back end. After the Token expires, interface data cannot be requested.
  • The expiration time of the backend is set to 24 hours. During the test, you can manually change the token value to make the token invalid
  • handling
    • Method 1: The user logs in again to obtain the new Token. However, if the expiration time is short, users have to log in again each time, resulting in poor experience.
      • To improve user information security, tokens expire within a short time. (Even if it does, it will expire after a while.)
    • Method 2: Based on the user information, a new Token is automatically generated for the user to reduce the retry times.
  • Looking at the previous function, the response information of the interface has three token-related information.
    • Access_token: Indicates the current Token used to access the interface that requires authorization
    • Expires_in: Expiration time of the access_token
    • Refresh_token: refreshes the access_token

Refreshing tokens (key)

  • Method 1: Intercept each request before it is initiated (request interceptor), determine whether token expires based on expires_in, and refresh the request interface after expiration.
    • Advantages: Interception before request processing, can save the number of requests.
    • Disadvantages: The backend needs to provide Token expiration time fields (such as expires_in), and needs to be combined with the local time of the computer, if the computer time is tampered (especially slower than the server time), interception will fail
  • Method 2: Intercept each request after the response (responder interceptor), and if the request fails (due to Token expiration), refresh your Token and request the interface again.
    • Advantages: The Token expiration time field is not required
    • Disadvantages: Consume one more request
  • Method 2 is recommended. Compared with method 1, method 2 is more stable and will not have unexpected problems.

Use Axios to respond to interceptors

  • The interceptor

  • The response interceptor is intercepted by the interceptor after the response is received and before the corresponding request is processed.
  • The response interceptor parameter Response holds information about the response.

Refreshing Token Operations

  • HTTP status code 401 means unauthorized, which can lead to a number of situations:
    • There is no Token
    • Token is invalid
    • Token expired
  • The judgment method is as follows:
    • Check if refresh_token exists (the back end usually limits refresh_token to one new Token per refresh_token)
      • If the request is successfully obtained, restart to send the request and request interface data
      • The login page is displayed
    • If not, go to the login page

Axios error handling

  • Check the official website for writing style

Handling Token Expiration

  • Array stores pending requests due to Token updates

Multiple requests repeatedly refresh Token processing

  • If there are multiple requests on the page (and most pages have more than one request), each request will refresh the Token if the Token expires, and there is no point in refreshing the Token multiple times, adding to the number of requests and causing additional problems
    • Each refresh_token can only be used once, and multiple requests will result in errors

  • To avoid multiple requests to refresh the Token, you can use a (let) variable isRefreshing to mark the Token refresh status
    • The default state is false and is checked before the refresh Token request is sent. It can only be sent when the state is false
    • Set the flag to true when sending the refresh request
    • Request completed, set the flag to false(should be written inside finally, whether the request failed or succeeded)
  • ** Problem: ** Although the refresh Token issue was resolved, only one of the two requests sent previously was successfully executed and the remaining requests were blocked.
  • ** Declare an array to store all pending requests and resend them when the Token is refreshed.
// The store should wait for pending requests
let requests = []
request.interceptors.response.use(function (response) {...if (status === 400) {
    errorMessage = 'Request parameter error'
  } else if (status === 401) {...if (isRefreshing) {
    // Add the current request to the array and return the array
    return requests.push(() = > {
      request(error.config)
    })
  }
  // If it is not updating, set the flag true
  isRefreshing = true
  // refresh_token Refreshes the token
  request({
    method: 'POST'.url: ' '.data: qs.stringify({
      refreshtoken: store.state.user.refresh_token
    })
  }).then({
    ......
    // Call other requests to execute
    requests.forEach(callback= > callback())
    // Clear all requests
    requests = []
    return request(error.config)
  }).catch({
    ......
  }).finally({
    isRefreshing = false})... }... }Copy the code

“Welcome to the discussion in the comments section. The nuggets will be giving away 100 nuggets in the comments section after the diggnation project. See the event article for details.”