亚欧色一区w666天堂,色情一区二区三区免费看,少妇特黄A片一区二区三区,亚洲人成网站999久久久综合,国产av熟女一区二区三区

  • 發布文章
  • 消息中心
點贊
收藏
評論
分享
原創

前端實現用戶無感知頁面刷新

2023-05-04 08:52:02
83
0

需求

當(dang)前前后端普遍使用(yong)token進行鑒權,當(dang)token過期(qi)后,用(yong)戶(hu)需要重(zhong)新登錄,輸入用(yong)戶(hu)名和(he)密碼以獲取新的(de)token。一般token過期(qi)時間設置很短,對于(yu)比(bi)較活躍的(de)用(yong)戶(hu)體驗感非常差。那么如(ru)何(he)解(jie)決(jue)這個問題呢?

這里引(yin)入兩個新(xin)的名(ming)詞access_token和refresh_token。access_token為授(shou)權令牌,用(yong)(yong)于驗證用(yong)(yong)戶身份(fen),是在調用(yong)(yong)API時需要(yao)傳入的header請求參數。當access_token過期(qi)后,需要(yao)刷新(xin),但(dan)是每次(ci)刷新(xin)都需要(yao)填寫(xie)用(yong)(yong)戶名(ming)密碼,非常(chang)繁瑣。而refresh_token可以解決(jue)這個問題(ti),顧名(ming)思義,refresh_token為刷新(xin)token,用(yong)(yong)來刷新(xin)access_token,無需用(yong)(yong)戶進行附加操作。

  1. 當access_token過期(qi),前端拿著refresh_token去獲(huo)取新(xin)的access_token,再重新(xin)發起請求,此(ci)時需要做到(dao)用(yong)戶無(wu)感知。
  2. 當用戶(hu)同時(shi)發起多個(ge)請(qing)求時(shi),第(di)一(yi)個(ge)請(qing)求會去調(diao)(diao)用刷新token接(jie)(jie)口(kou),當該接(jie)(jie)口(kou)還沒返回(hui)時(shi),其(qi)他的請(qing)求也去調(diao)(diao)用了刷新token接(jie)(jie)口(kou),此時(shi)會產生多個(ge)請(qing)求,前端需要避免這種情況(kuang)出現。

方案

利用axios的響應攔截(jie)器對返回的數據做處理。不(bu)需(xu)要(yao)解析access_token和refresh_token拿(na)到各(ge)自的過期時間去做判(pan)斷,但是需(xu)要(yao)多發送一(yi)次http請求。

實現

  • @/utils/cookies
import Cookies from 'js-cookie'
export default Cookies

// Token
const tokenKey = 'access_token'
export const getToken = (): string | undefined => Cookies.get(tokenKey)
export const setToken = (token: string): unknown => Cookies.set(tokenKey, token)
export const removeToken = (): unknown => Cookies.remove(tokenKey)

// refreshToken
const refreshTokenKey = 'refresh_token'
export const getRefreshToken = (): unknown => Cookies.get(refreshTokenKey)
export const setRefreshToken = (token: string): unknown => Cookies.set(refreshTokenKey, token)
export const removeRefreshToken = (): unknown => Cookies.remove(refreshTokenKey)

const usernameKey = 'username'
export const getUsername = (): unknown => Cookies.get(usernameKey)
export const setUsername = (token: string): unknown => Cookies.set(usernameKey, token)
export const removeUsername = (): unknown => Cookies.remove(usernameKey)

export const clearToken = (): void => {
  removeToken()
  removeRefreshToken()
  removeUsername()
}
  • @/utils/request.js
/** 創建axios實例 */
const service = axios.create({
  baseURL: settings.apiBaseUrl,
  timeout: 5 * 3600 * 1000,
})

/** 響應攔截器攔截器 */
service.interceptors.response.use(
  (response: BaseAxiosResponse) => {
    const { config, data } = response
    if (data.code === 0) {
      return Promise.resolve(response)
    } else if (data.code === 400005) {
    	// token 過期處理邏輯
    } else {
      return Promise.reject(response?.data)
    }
  },
  error => {
    return Promise.reject(error)
  }
)

響(xiang)應攔截器改造。當返回(hui)的code值為400005時,表示access_token無效。此時調用刷新接口重(zhong)新獲取access_token,并重(zhong)新發起原(yuan)請求。

/** 創建axios實例 */
const service = axios.create({
  baseURL: settings.apiBaseUrl,
  timeout: 5 * 3600 * 1000,
})

/** 響應攔截器攔截器 */
service.interceptors.response.use(
  (response: BaseAxiosResponse) => {
    const { config, data } = response
    if (data.code === 0) {
      return Promise.resolve(response)
    } else if (data.code === 400005) {
    	// token 過期處理邏輯
      const token = getRefreshToken()
      return refreshToken({ oldRefreshToken: token })
        .then(res => {
          const { accessToken, refreshToken } = res.data
          setToken(accessToken)
          setRefreshToken(refreshToken)
          config.headers.Authorization = accessToken
          return service(config)
        })
        .catch(err => {
          console.log('抱歉,您的登錄狀態已失效,請重新登錄!')
          return Promise.reject(err)
        })
    } else {
      // refreshToken 失效
      if (config.url.includes(REFRESH_URL)) {
        clearToken()
        router.push('/login').catch(err => {
          console.log(err)
        })
      }
      return Promise.reject(response?.data)
    }
  },
  error => {
    return Promise.reject(error)
  }
)

當用戶同時(shi)發起多個(ge)請(qing)求時(shi),可能存在多次調用刷(shua)新(xin)token的接(jie)口,因(yin)此需要定(ding)義一個(ge)標記(ji)來判斷當前(qian)是否處于刷(shua)新(xin)狀態,如果處于刷(shua)新(xin)狀態,則禁止其(qi)他請(qing)求調用刷(shua)新(xin)接(jie)口。

/** 創建axios實例 */
const service = axios.create({
  baseURL: settings.apiBaseUrl,
  timeout: 5 * 3600 * 1000,
})

let isRefreshing = false // 標記是否正在刷新 token

/** 響應攔截器攔截器 */
service.interceptors.response.use(
  (response: BaseAxiosResponse) => {
    const { config, data } = response
    if (data.code === 0) {
      return Promise.resolve(response)
    } else if (data.code === 400005) {
    	// token 過期處理邏輯
      if (!isRefreshing) {
        isRefreshing = true
        const token = getRefreshToken()
        return refreshToken({ oldRefreshToken: token })
          .then(res => {
            const { accessToken, refreshToken } = res.data
            setToken(accessToken)
            setRefreshToken(refreshToken)
            config.headers.Authorization = accessToken
            return service(config)
          })
          .catch(err => {
            router.push('/login').catch(err => {
              console.log(err)
            })
            return Promise.reject(err)
          })
          .finally(() => {
            isRefreshing = false
          })
    } else {
      // refreshToken 失效
      if (config.url.includes(REFRESH_URL)) {
        clearToken()
        router.push('/login').catch(err => {
          console.log(err)
        })
      }
      return Promise.reject(response?.data)
    }
  },
  error => {
    return Promise.reject(error)
  }
)

上(shang)述(shu)同(tong)時發起多個請求(qiu)的(de)方法可以進一(yi)步(bu)優化(hua)。

當(dang)發(fa)起(qi)多個(ge)請(qing)求,第一個(ge)請(qing)求進入刷新(xin)token的流(liu)程,需(xu)要將(jiang)其他請(qing)求掛起(qi),當(dang)token更新(xin)之(zhi)后(hou)在重新(xin)發(fa)起(qi)請(qing)求。這(zhe)里定義一個(ge)requests數組(zu),暫存掛起(qi)的請(qing)求。

/** 創建axios實例 */
const service = axios.create({
  baseURL: settings.apiBaseUrl,
  timeout: 5 * 3600 * 1000,
})

let isRefreshing = false // 標記是否正在刷新 token
const requests = [] // 存儲待重發請求的數組

/** 響應攔截器攔截器 */
service.interceptors.response.use(
  (response: BaseAxiosResponse) => {
    const { config, data } = response
    if (data.code === 0) {
      return Promise.resolve(response)
    } else if (data.code === 400005) {
    	// token 過期處理邏輯
      if (!isRefreshing) {
        isRefreshing = true
        const token = getRefreshToken()
        return refreshToken({ oldRefreshToken: token })
          .then(res => {
            const { accessToken, refreshToken } = res.data
            setToken(accessToken)
            setRefreshToken(refreshToken)
            config.headers.Authorization = accessToken
            // token 刷新后將數組的方法重新執行
            requests.forEach(cb => cb(accessToken))
            return service(config)
          })
          .catch(err => {
            router.push('/login').catch(err => {
              console.log(err)
            })
            return Promise.reject(err)
          })
          .finally(() => {
            isRefreshing = false
          })
      }else {
        // 返回未執行 resolve 的 Promise
        return new Promise(resolve => {
          // 用函數形式將 resolve 存入,刷新token之后回調執行
          requests.push(token => {
            config.headers.Authorization = token
            resolve(service(config))
          })
        })
      }
    } else {
      // refreshToken 失效
      if (config.url.includes(REFRESH_URL)) {
        clearToken()
        router.push('/login').catch(err => {
          console.log(err)
        })
      }
      return Promise.reject(response?.data)
    }
  },
  error => {
    return Promise.reject(error)
  }
)
0條評論
0 / 1000
張****翔
4文章數
0粉絲數
張****翔
4 文章 | 0 粉絲
張****翔
4文(wen)章數
0粉(fen)絲數
張****翔
4 文章 | 0 粉(fen)絲(si)
原創(chuang)

前端實現用戶無感知頁面刷新

2023-05-04 08:52:02
83
0

需求(qiu)

當前前后端(duan)普(pu)遍(bian)使(shi)用token進行(xing)鑒權,當token過期后,用戶(hu)(hu)需要重新登錄,輸(shu)入用戶(hu)(hu)名和(he)密碼(ma)以獲取(qu)新的(de)token。一般token過期時間設置很短,對(dui)于比較活躍的(de)用戶(hu)(hu)體驗感(gan)非(fei)常差(cha)。那(nei)么如何解決這個問題呢?

這里引入(ru)兩個新的名詞access_token和refresh_token。access_token為(wei)授(shou)權令牌,用(yong)(yong)于驗證用(yong)(yong)戶身份,是在(zai)調用(yong)(yong)API時需(xu)要傳入(ru)的header請求參(can)數(shu)。當access_token過(guo)期后,需(xu)要刷(shua)新,但是每次刷(shua)新都需(xu)要填寫用(yong)(yong)戶名密(mi)碼,非常繁(fan)瑣。而refresh_token可(ke)以解(jie)決這個問(wen)題(ti),顧名思義,refresh_token為(wei)刷(shua)新token,用(yong)(yong)來刷(shua)新access_token,無需(xu)用(yong)(yong)戶進行(xing)附加操作。

  1. 當access_token過期,前端拿(na)著refresh_token去(qu)獲(huo)取新的access_token,再重新發起請求,此時需要做到(dao)用(yong)戶(hu)無感知(zhi)。
  2. 當用(yong)戶同時發(fa)起多個請求(qiu)(qiu)時,第一(yi)個請求(qiu)(qiu)會去調用(yong)刷新token接(jie)口(kou),當該接(jie)口(kou)還沒(mei)返回時,其他的請求(qiu)(qiu)也去調用(yong)了刷新token接(jie)口(kou),此時會產生(sheng)多個請求(qiu)(qiu),前端需要避免這種情(qing)況出現。

方案

利用axios的(de)響(xiang)應(ying)攔截器對返回(hui)的(de)數據做處理。不需(xu)要(yao)解析access_token和refresh_token拿(na)到各自的(de)過期時間去做判斷(duan),但(dan)是需(xu)要(yao)多發(fa)送一次http請求。

實現(xian)

  • @/utils/cookies
import Cookies from 'js-cookie'
export default Cookies

// Token
const tokenKey = 'access_token'
export const getToken = (): string | undefined => Cookies.get(tokenKey)
export const setToken = (token: string): unknown => Cookies.set(tokenKey, token)
export const removeToken = (): unknown => Cookies.remove(tokenKey)

// refreshToken
const refreshTokenKey = 'refresh_token'
export const getRefreshToken = (): unknown => Cookies.get(refreshTokenKey)
export const setRefreshToken = (token: string): unknown => Cookies.set(refreshTokenKey, token)
export const removeRefreshToken = (): unknown => Cookies.remove(refreshTokenKey)

const usernameKey = 'username'
export const getUsername = (): unknown => Cookies.get(usernameKey)
export const setUsername = (token: string): unknown => Cookies.set(usernameKey, token)
export const removeUsername = (): unknown => Cookies.remove(usernameKey)

export const clearToken = (): void => {
  removeToken()
  removeRefreshToken()
  removeUsername()
}
  • @/utils/request.js
/** 創建axios實例 */
const service = axios.create({
  baseURL: settings.apiBaseUrl,
  timeout: 5 * 3600 * 1000,
})

/** 響應攔截器攔截器 */
service.interceptors.response.use(
  (response: BaseAxiosResponse) => {
    const { config, data } = response
    if (data.code === 0) {
      return Promise.resolve(response)
    } else if (data.code === 400005) {
    	// token 過期處理邏輯
    } else {
      return Promise.reject(response?.data)
    }
  },
  error => {
    return Promise.reject(error)
  }
)

響應(ying)攔截器(qi)改造。當(dang)返回的code值為400005時,表示access_token無效(xiao)。此時調用刷新接口(kou)重新獲(huo)取access_token,并重新發起原請求(qiu)。

/** 創建axios實例 */
const service = axios.create({
  baseURL: settings.apiBaseUrl,
  timeout: 5 * 3600 * 1000,
})

/** 響應攔截器攔截器 */
service.interceptors.response.use(
  (response: BaseAxiosResponse) => {
    const { config, data } = response
    if (data.code === 0) {
      return Promise.resolve(response)
    } else if (data.code === 400005) {
    	// token 過期處理邏輯
      const token = getRefreshToken()
      return refreshToken({ oldRefreshToken: token })
        .then(res => {
          const { accessToken, refreshToken } = res.data
          setToken(accessToken)
          setRefreshToken(refreshToken)
          config.headers.Authorization = accessToken
          return service(config)
        })
        .catch(err => {
          console.log('抱歉,您的登錄狀態已失效,請重新登錄!')
          return Promise.reject(err)
        })
    } else {
      // refreshToken 失效
      if (config.url.includes(REFRESH_URL)) {
        clearToken()
        router.push('/login').catch(err => {
          console.log(err)
        })
      }
      return Promise.reject(response?.data)
    }
  },
  error => {
    return Promise.reject(error)
  }
)

當用戶(hu)同(tong)時發起多個請求時,可能存在多次調(diao)用刷(shua)新(xin)token的接(jie)口(kou),因此需要定義一個標記來判斷當前(qian)是否(fou)處于(yu)刷(shua)新(xin)狀(zhuang)(zhuang)態,如(ru)果處于(yu)刷(shua)新(xin)狀(zhuang)(zhuang)態,則(ze)禁止(zhi)其(qi)他請求調(diao)用刷(shua)新(xin)接(jie)口(kou)。

/** 創建axios實例 */
const service = axios.create({
  baseURL: settings.apiBaseUrl,
  timeout: 5 * 3600 * 1000,
})

let isRefreshing = false // 標記是否正在刷新 token

/** 響應攔截器攔截器 */
service.interceptors.response.use(
  (response: BaseAxiosResponse) => {
    const { config, data } = response
    if (data.code === 0) {
      return Promise.resolve(response)
    } else if (data.code === 400005) {
    	// token 過期處理邏輯
      if (!isRefreshing) {
        isRefreshing = true
        const token = getRefreshToken()
        return refreshToken({ oldRefreshToken: token })
          .then(res => {
            const { accessToken, refreshToken } = res.data
            setToken(accessToken)
            setRefreshToken(refreshToken)
            config.headers.Authorization = accessToken
            return service(config)
          })
          .catch(err => {
            router.push('/login').catch(err => {
              console.log(err)
            })
            return Promise.reject(err)
          })
          .finally(() => {
            isRefreshing = false
          })
    } else {
      // refreshToken 失效
      if (config.url.includes(REFRESH_URL)) {
        clearToken()
        router.push('/login').catch(err => {
          console.log(err)
        })
      }
      return Promise.reject(response?.data)
    }
  },
  error => {
    return Promise.reject(error)
  }
)

上述同時發(fa)起多個請求的方法(fa)可以進一步優化。

當發起(qi)多個請求,第一個請求進(jin)入刷新(xin)token的流程(cheng),需(xu)要將其他(ta)請求掛(gua)起(qi),當token更新(xin)之(zhi)后在重新(xin)發起(qi)請求。這里定義(yi)一個requests數(shu)組,暫存掛(gua)起(qi)的請求。

/** 創建axios實例 */
const service = axios.create({
  baseURL: settings.apiBaseUrl,
  timeout: 5 * 3600 * 1000,
})

let isRefreshing = false // 標記是否正在刷新 token
const requests = [] // 存儲待重發請求的數組

/** 響應攔截器攔截器 */
service.interceptors.response.use(
  (response: BaseAxiosResponse) => {
    const { config, data } = response
    if (data.code === 0) {
      return Promise.resolve(response)
    } else if (data.code === 400005) {
    	// token 過期處理邏輯
      if (!isRefreshing) {
        isRefreshing = true
        const token = getRefreshToken()
        return refreshToken({ oldRefreshToken: token })
          .then(res => {
            const { accessToken, refreshToken } = res.data
            setToken(accessToken)
            setRefreshToken(refreshToken)
            config.headers.Authorization = accessToken
            // token 刷新后將數組的方法重新執行
            requests.forEach(cb => cb(accessToken))
            return service(config)
          })
          .catch(err => {
            router.push('/login').catch(err => {
              console.log(err)
            })
            return Promise.reject(err)
          })
          .finally(() => {
            isRefreshing = false
          })
      }else {
        // 返回未執行 resolve 的 Promise
        return new Promise(resolve => {
          // 用函數形式將 resolve 存入,刷新token之后回調執行
          requests.push(token => {
            config.headers.Authorization = token
            resolve(service(config))
          })
        })
      }
    } else {
      // refreshToken 失效
      if (config.url.includes(REFRESH_URL)) {
        clearToken()
        router.push('/login').catch(err => {
          console.log(err)
        })
      }
      return Promise.reject(response?.data)
    }
  },
  error => {
    return Promise.reject(error)
  }
)
文章來自個人專欄
文(wen)章 | 訂閱
0條評論
0 / 1000
請輸入你的評論
1
0