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