Pārlūkot izejas kodu

增加 refresh token 接口,并接入到前端项目

YunaiV 2 gadi atpakaļ
vecāks
revīzija
a3687132b6
13 mainītis faili ar 154 papildinājumiem un 87 dzēšanām
  1. 9 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java
  2. 5 17
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java
  3. 6 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2RefreshTokenMapper.java
  4. 8 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/auth/OAuth2AccessTokenRedisDAO.java
  5. 1 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java
  6. 8 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java
  7. 6 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
  8. 2 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java
  9. 41 36
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java
  10. 10 0
      yudao-ui-admin/src/api/login.js
  11. 0 11
      yudao-ui-admin/src/store/modules/user.js
  12. 1 1
      yudao-ui-admin/src/utils/auth.js
  13. 57 17
      yudao-ui-admin/src/utils/request.js

+ 9 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java

@@ -6,7 +6,6 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
 import cn.iocoder.yudao.framework.security.config.SecurityProperties;
-import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*;
 import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
@@ -34,6 +33,7 @@ import java.util.Set;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.obtainAuthorization;
 import static java.util.Collections.singleton;
 
 @Api(tags = "管理后台 - 认证")
@@ -68,13 +68,20 @@ public class AuthController {
     @ApiOperation("登出系统")
     @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
     public CommonResult<Boolean> logout(HttpServletRequest request) {
-        String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader());
+        String token = obtainAuthorization(request, securityProperties.getTokenHeader());
         if (StrUtil.isNotBlank(token)) {
             authService.logout(token);
         }
         return success(true);
     }
 
+    @PostMapping("/refresh-token")
+    @ApiOperation("刷新令牌")
+    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 TODO 接口文档
+    public CommonResult<AuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
+        return success(authService.refreshToken(refreshToken));
+    }
+
     @GetMapping("/get-permission-info")
     @ApiOperation("获取登录用户的权限信息")
     public CommonResult<AuthPermissionInfoRespVO> getPermissionInfo() {

+ 5 - 17
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java

@@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 @Mapper
 public interface OAuth2AccessTokenMapper extends BaseMapperX<OAuth2AccessTokenDO> {
 
@@ -11,22 +13,8 @@ public interface OAuth2AccessTokenMapper extends BaseMapperX<OAuth2AccessTokenDO
         return selectOne(OAuth2AccessTokenDO::getAccessToken, accessToken);
     }
 
-//    default OAuth2AccessTokenDO selectByUserIdAndUserType(Integer userId, Integer userType) {
-//        return selectOne(new QueryWrapper<OAuth2AccessTokenDO>()
-//                .eq("user_id", userId).eq("user_type", userType));
-//    }
-//
-//    default int deleteByUserIdAndUserType(Integer userId, Integer userType) {
-//        return delete(new QueryWrapper<OAuth2AccessTokenDO>()
-//                .eq("user_id", userId).eq("user_type", userType));
-//    }
-//
-//    default int deleteByRefreshToken(String refreshToken) {
-//        return delete(new QueryWrapper<OAuth2AccessTokenDO>().eq("refresh_token", refreshToken));
-//    }
-//
-//    default List<OAuth2AccessTokenDO> selectListByRefreshToken(String refreshToken) {
-//        return selectList(new QueryWrapper<OAuth2AccessTokenDO>().eq("refresh_token", refreshToken));
-//    }
+    default List<OAuth2AccessTokenDO> selectListByRefreshToken(String refreshToken) {
+        return selectList(OAuth2AccessTokenDO::getRefreshToken, refreshToken);
+    }
 
 }

+ 6 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2RefreshTokenMapper.java

@@ -1,16 +1,20 @@
 package cn.iocoder.yudao.module.system.dal.mysql.auth;
 
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
 
 @Mapper
-public interface OAuth2RefreshTokenMapper extends BaseMapper<OAuth2RefreshTokenDO> {
+public interface OAuth2RefreshTokenMapper extends BaseMapperX<OAuth2RefreshTokenDO> {
 
     default int deleteByRefreshToken(String refreshToken) {
         return delete(new LambdaQueryWrapperX<OAuth2RefreshTokenDO>()
                 .eq(OAuth2RefreshTokenDO::getRefreshToken, refreshToken));
     }
 
+    default OAuth2RefreshTokenDO selectByRefreshToken(String refreshToken) {
+        return selectOne(OAuth2RefreshTokenDO::getRefreshToken, refreshToken);
+    }
+
 }

+ 8 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/auth/OAuth2AccessTokenRedisDAO.java

@@ -1,11 +1,14 @@
 package cn.iocoder.yudao.module.system.dal.redis.auth;
 
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Repository;
 
 import javax.annotation.Resource;
+import java.util.Collection;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import static cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN;
@@ -39,6 +42,11 @@ public class OAuth2AccessTokenRedisDAO {
         stringRedisTemplate.delete(redisKey);
     }
 
+    public void deleteList(Collection<String> accessTokens) {
+        List<String> redisKeys = CollectionUtils.convertList(accessTokens, OAuth2AccessTokenRedisDAO::formatKey);
+        stringRedisTemplate.delete(redisKeys);
+    }
+
     private static String formatKey(String accessToken) {
         return String.format(OAUTH2_ACCESS_TOKEN.getKeyTemplate(), accessToken);
     }

+ 1 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java

@@ -21,6 +21,7 @@ public class SecurityConfiguration {
                 // 登录的接口
                 registry.antMatchers(buildAdminApi("/system/auth/login")).permitAll();
                 registry.antMatchers(buildAdminApi("/system/auth/logout")).permitAll();
+                registry.antMatchers(buildAdminApi("/system/auth/refresh-token")).permitAll();
                 // 社交登陆的接口
                 registry.antMatchers(buildAdminApi("/system/auth/social-auth-redirect")).permitAll();
                 registry.antMatchers(buildAdminApi("/system/auth/social-quick-login")).permitAll();

+ 8 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java

@@ -59,4 +59,12 @@ public interface AdminAuthService {
      */
     AuthLoginRespVO socialBindLogin(@Valid AuthSocialBindLoginReqVO reqVO);
 
+    /**
+     * 刷新访问令牌
+     *
+     * @param refreshToken 刷新令牌
+     * @return 登录结果
+     */
+    AuthLoginRespVO refreshToken(String refreshToken);
+
 }

+ 6 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java

@@ -192,6 +192,12 @@ public class AdminAuthServiceImpl implements AdminAuthService {
         return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
     }
 
+    @Override
+    public AuthLoginRespVO refreshToken(String refreshToken) {
+        OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientIdEnum.DEFAULT.getId());
+        return AuthConvert.INSTANCE.convert(accessTokenDO);
+    }
+
     private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) {
         // 插入登陆日志
         createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);

+ 2 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java

@@ -30,9 +30,10 @@ public interface OAuth2TokenService {
      * 参考 DefaultTokenServices 的 refreshAccessToken 方法
      *
      * @param refreshToken 刷新令牌
+     * @param clientId 客户端编号
      * @return 访问令牌的信息
      */
-    OAuth2AccessTokenDO refreshAccessToken(String refreshToken);
+    OAuth2AccessTokenDO refreshAccessToken(String refreshToken, Long clientId);
 
     /**
      * 获得访问令牌

+ 41 - 36
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java

@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.module.system.service.auth;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.ObjectUtil;
 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
@@ -15,8 +17,10 @@ import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
 import java.util.Calendar;
+import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
 
 /**
  * OAuth2.0 Token Service 实现类
@@ -44,15 +48,38 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
         // 创建刷新令牌
         OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO);
         // 创建访问令牌
-        OAuth2AccessTokenDO accessTokenDO = createOAuth2AccessToken(refreshTokenDO, clientDO);
-        // 记录到 Redis 中
-        oauth2AccessTokenRedisDAO.set(accessTokenDO);
-        return accessTokenDO;
+        return createOAuth2AccessToken(refreshTokenDO, clientDO);
     }
 
     @Override
-    public OAuth2AccessTokenDO refreshAccessToken(String refreshToken) {
-        return null;
+    public OAuth2AccessTokenDO refreshAccessToken(String refreshToken, Long clientId) {
+        // 查询访问令牌
+        OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectByRefreshToken(refreshToken);
+        if (refreshTokenDO == null) {
+            throw exception(GlobalErrorCodeConstants.BAD_REQUEST, "无效的刷新令牌");
+        }
+
+        // 校验 Client 匹配
+        OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
+        if (ObjectUtil.notEqual(clientId, refreshTokenDO.getClientId())) {
+            throw exception(GlobalErrorCodeConstants.BAD_REQUEST, "刷新令牌的客户端编号不正确");
+        }
+
+        // 移除相关的访问令牌
+        List<OAuth2AccessTokenDO> accessTokenDOs = oauth2AccessTokenMapper.selectListByRefreshToken(refreshToken);
+        if (CollUtil.isNotEmpty(accessTokenDOs)) {
+            oauth2AccessTokenMapper.deleteBatchIds(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getId));
+            oauth2AccessTokenRedisDAO.deleteList(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getAccessToken));
+        }
+
+        // 已过期的情况下,删除刷新令牌
+        if (DateUtils.isExpired(refreshTokenDO.getExpiresTime())) {
+            oauth2AccessTokenMapper.deleteById(refreshTokenDO.getId());
+            throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "刷新令牌已过期");
+        }
+
+        // 创建访问令牌
+        return createOAuth2AccessToken(refreshTokenDO, clientDO);
     }
 
     @Override
@@ -76,10 +103,10 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
     public OAuth2AccessTokenDO checkAccessToken(String accessToken) {
         OAuth2AccessTokenDO accessTokenDO = getAccessToken(accessToken);
         if (accessTokenDO == null) {
-            throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "Token 不存在");
+            throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "访问令牌不存在");
         }
         if (DateUtils.isExpired(accessTokenDO.getExpiresTime())) {
-            throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "Token 已过期");
+            throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "访问令牌已过期");
         }
         return accessTokenDO;
     }
@@ -98,38 +125,16 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
         return accessTokenDO;
     }
 
-
-//    @Override
-//    @Transactional
-//    public OAuth2AccessTokenRespDTO refreshAccessToken(OAuth2RefreshAccessTokenReqDTO refreshAccessTokenDTO) {
-//        OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectById(refreshAccessTokenDTO.getRefreshToken());
-//        // 校验刷新令牌是否合法
-//        if (refreshTokenDO == null) { // 不存在
-//            throw ServiceExceptionUtil.exception(OAUTH2_REFRESH_TOKEN_NOT_FOUND);
-//        }
-//        if (refreshTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期
-//            throw ServiceExceptionUtil.exception(OAUTH_REFRESH_TOKEN_EXPIRED);
-//        }
-//
-//        // 标记 refreshToken 对应的 accessToken 都不合法
-//        // 这块的实现,参考了 Spring Security OAuth2 的代码
-//        List<OAuth2AccessTokenDO> accessTokenDOs = oauth2AccessTokenMapper.selectListByRefreshToken(refreshAccessTokenDTO.getRefreshToken());
-//        accessTokenDOs.forEach(accessTokenDO -> deleteOAuth2AccessToken(accessTokenDO.getId()));
-//
-//        // 创建访问令牌
-//        OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(refreshTokenDO, refreshAccessTokenDTO.getCreateIp());
-//        // 返回访问令牌
-//        return OAuth2Convert.INSTANCE.convert(oauth2AccessTokenDO);
-//    }
-
     private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
-        OAuth2AccessTokenDO accessToken = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
+        OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
                 .setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType()).setClientId(clientDO.getId())
                 .setRefreshToken(refreshTokenDO.getRefreshToken())
                 .setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getAccessTokenValiditySeconds()));
-        accessToken.setTenantId(TenantContextHolder.getTenantId()); // 手动设置租户编号,避免缓存到 Redis 的时候,无对应的租户编号
-        oauth2AccessTokenMapper.insert(accessToken);
-        return accessToken;
+        accessTokenDO.setTenantId(TenantContextHolder.getTenantId()); // 手动设置租户编号,避免缓存到 Redis 的时候,无对应的租户编号
+        oauth2AccessTokenMapper.insert(accessTokenDO);
+        // 记录到 Redis 中
+        oauth2AccessTokenRedisDAO.set(accessTokenDO);
+        return accessTokenDO;
     }
 
     private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long userId, Integer userType, OAuth2ClientDO clientDO) {

+ 10 - 0
yudao-ui-admin/src/api/login.js

@@ -1,4 +1,6 @@
 import request from '@/utils/request'
+import {getRefreshToken} from "@/utils/auth";
+import service from "@/utils/request";
 
 // 登录方法
 export function login(username, password, code, uuid) {
@@ -99,3 +101,11 @@ export function smsLogin(mobile, code) {
     }
   })
 }
+
+// 刷新访问令牌
+export function refreshToken() {
+  return service({
+    url: '/system/auth/refresh-token?refreshToken=' + getRefreshToken(),
+    method: 'post'
+  })
+}

+ 0 - 11
yudao-ui-admin/src/store/modules/user.js

@@ -3,8 +3,6 @@ import {getAccessToken, setToken, removeToken, getRefreshToken} from '@/utils/au
 
 const user = {
   state: {
-    accessToken: getAccessToken(),
-    refreshToken: getRefreshToken(),
     id: 0, // 用户编号
     name: '',
     avatar: '',
@@ -16,10 +14,6 @@ const user = {
     SET_ID: (state, id) => {
       state.id = id
     },
-    SET_TOKEN: (state, token) => {
-      state.accessToken = token.accessToken
-      state.refreshToken = token.refreshToken
-    },
     SET_NAME: (state, name) => {
       state.name = name
     },
@@ -46,7 +40,6 @@ const user = {
           res = res.data;
           // 设置 token
           setToken(res)
-          commit('SET_TOKEN', res)
           resolve()
         }).catch(error => {
           reject(error)
@@ -64,7 +57,6 @@ const user = {
           res = res.data;
           // 设置 token
           setToken(res)
-          commit('SET_TOKEN', res)
           resolve()
         }).catch(error => {
           reject(error)
@@ -84,7 +76,6 @@ const user = {
           res = res.data;
           // 设置 token
           setToken(res)
-          commit('SET_TOKEN', res)
           resolve()
         }).catch(error => {
           reject(error)
@@ -100,7 +91,6 @@ const user = {
           res = res.data;
           // 设置 token
           setToken(res)
-          commit('SET_TOKEN', res)
           resolve()
         }).catch(error => {
           reject(error)
@@ -148,7 +138,6 @@ const user = {
     LogOut({ commit, state }) {
       return new Promise((resolve, reject) => {
         logout(state.token).then(() => {
-          commit('SET_TOKEN', '')
           commit('SET_ROLES', [])
           commit('SET_PERMISSIONS', [])
           removeToken()

+ 1 - 1
yudao-ui-admin/src/utils/auth.js

@@ -8,7 +8,7 @@ export function getAccessToken() {
 }
 
 export function getRefreshToken() {
-  return Cookies.get(AccessTokenKey)
+  return Cookies.get(RefreshTokenKey)
 }
 
 export function setToken(token) {

+ 57 - 17
yudao-ui-admin/src/utils/request.js

@@ -1,13 +1,19 @@
 import axios from 'axios'
 import { Notification, MessageBox, Message } from 'element-ui'
 import store from '@/store'
-import { getAccessToken } from '@/utils/auth'
+import {getAccessToken, getRefreshToken, setToken} from '@/utils/auth'
 import errorCode from '@/utils/errorCode'
 import Cookies from "js-cookie";
 import {getPath, getTenantEnable} from "@/utils/ruoyi";
+import {refreshToken} from "@/api/login";
 
 // 是否显示重新登录
 export let isRelogin = { show: false };
+// Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 实现
+// 请求队列
+let requestList = []
+// 是否正在刷新中
+let isRefreshToken = false
 
 axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
 // 创建axios实例
@@ -60,29 +66,43 @@ service.interceptors.request.use(config => {
 })
 
 // 响应拦截器
-service.interceptors.response.use(res => {
+service.interceptors.response.use( async res => {
     // 未设置状态码则默认成功状态
     const code = res.data.code || 200;
     // 获取错误信息
     const msg = errorCode[code] || res.data.msg || errorCode['default']
     if (code === 401) {
-      if (!isRelogin.show) {
-        isRelogin.show = true;
-        MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
-            confirmButtonText: '重新登录',
-            cancelButtonText: '取消',
-            type: 'warning'
-          }
-        ).then(() => {
-          isRelogin.show = false;
-          store.dispatch('LogOut').then(() => {
-            location.href = getPath('/index');
+      // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
+      if (!isRefreshToken) {
+        isRefreshToken = true;
+        // 1. 如果获取不到刷新令牌,则只能执行登出操作
+        if (!getRefreshToken()) {
+          return handleAuthorized();
+        }
+        // 2. 进行刷新访问令牌
+        try {
+          const refreshTokenRes = await refreshToken()
+          // 2.1 刷新成功,则回放队列的请求 + 当前请求
+          setToken(refreshTokenRes.data)
+          requestList.forEach(cb => cb())
+          return service(res.config)
+        } catch (e) {
+          // 2.1 刷新失败,则只能执行登出操作
+          return handleAuthorized();
+        } finally {
+          requestList = []
+          isRefreshToken = false
+        }
+      } else {
+        // 添加到队列,等待刷新获取到新的令牌
+        return new Promise(resolve => {
+          requestList.push(() => {
+            config.headers['Authorization'] = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
+            resolve(service(config))
           })
-        }).catch(() => {
-          isRelogin.show = false;
-        });
+        })
       }
-      return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
+      return handleAuthorized();
     } else if (code === 500) {
       Message({
         message: msg,
@@ -138,4 +158,24 @@ export function getBaseHeader() {
   }
 }
 
+function handleAuthorized() {
+  if (!isRelogin.show) {
+    isRelogin.show = true;
+    MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
+        confirmButtonText: '重新登录',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }
+    ).then(() => {
+      isRelogin.show = false;
+      store.dispatch('LogOut').then(() => {
+        location.href = getPath('/index');
+      })
+    }).catch(() => {
+      isRelogin.show = false;
+    });
+  }
+  return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
+}
+
 export default service