Commit a3687132b68188f5ffd4c53ebbff3a1835d3e291

Authored by YunaiV
1 parent 86e6c04e

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

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;
6 6 import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
7 7 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
8 8 import cn.iocoder.yudao.framework.security.config.SecurityProperties;
9   -import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
10 9 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*;
11 10 import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
12 11 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
... ... @@ -34,6 +33,7 @@ import java.util.Set;
34 33  
35 34 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
36 35 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  36 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.obtainAuthorization;
37 37 import static java.util.Collections.singleton;
38 38  
39 39 @Api(tags = "管理后台 - 认证")
... ... @@ -68,13 +68,20 @@ public class AuthController {
68 68 @ApiOperation("登出系统")
69 69 @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
70 70 public CommonResult<Boolean> logout(HttpServletRequest request) {
71   - String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader());
  71 + String token = obtainAuthorization(request, securityProperties.getTokenHeader());
72 72 if (StrUtil.isNotBlank(token)) {
73 73 authService.logout(token);
74 74 }
75 75 return success(true);
76 76 }
77 77  
  78 + @PostMapping("/refresh-token")
  79 + @ApiOperation("刷新令牌")
  80 + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 TODO 接口文档
  81 + public CommonResult<AuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
  82 + return success(authService.refreshToken(refreshToken));
  83 + }
  84 +
78 85 @GetMapping("/get-permission-info")
79 86 @ApiOperation("获取登录用户的权限信息")
80 87 public CommonResult<AuthPermissionInfoRespVO> getPermissionInfo() {
... ...
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;
4 4 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
5 5 import org.apache.ibatis.annotations.Mapper;
6 6  
  7 +import java.util.List;
  8 +
7 9 @Mapper
8 10 public interface OAuth2AccessTokenMapper extends BaseMapperX<OAuth2AccessTokenDO> {
9 11  
... ... @@ -11,22 +13,8 @@ public interface OAuth2AccessTokenMapper extends BaseMapperX&lt;OAuth2AccessTokenDO
11 13 return selectOne(OAuth2AccessTokenDO::getAccessToken, accessToken);
12 14 }
13 15  
14   -// default OAuth2AccessTokenDO selectByUserIdAndUserType(Integer userId, Integer userType) {
15   -// return selectOne(new QueryWrapper<OAuth2AccessTokenDO>()
16   -// .eq("user_id", userId).eq("user_type", userType));
17   -// }
18   -//
19   -// default int deleteByUserIdAndUserType(Integer userId, Integer userType) {
20   -// return delete(new QueryWrapper<OAuth2AccessTokenDO>()
21   -// .eq("user_id", userId).eq("user_type", userType));
22   -// }
23   -//
24   -// default int deleteByRefreshToken(String refreshToken) {
25   -// return delete(new QueryWrapper<OAuth2AccessTokenDO>().eq("refresh_token", refreshToken));
26   -// }
27   -//
28   -// default List<OAuth2AccessTokenDO> selectListByRefreshToken(String refreshToken) {
29   -// return selectList(new QueryWrapper<OAuth2AccessTokenDO>().eq("refresh_token", refreshToken));
30   -// }
  16 + default List<OAuth2AccessTokenDO> selectListByRefreshToken(String refreshToken) {
  17 + return selectList(OAuth2AccessTokenDO::getRefreshToken, refreshToken);
  18 + }
31 19  
32 20 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2RefreshTokenMapper.java
1 1 package cn.iocoder.yudao.module.system.dal.mysql.auth;
2 2  
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
3 4 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
4 5 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO;
5   -import com.baomidou.mybatisplus.core.mapper.BaseMapper;
6 6 import org.apache.ibatis.annotations.Mapper;
7 7  
8 8 @Mapper
9   -public interface OAuth2RefreshTokenMapper extends BaseMapper<OAuth2RefreshTokenDO> {
  9 +public interface OAuth2RefreshTokenMapper extends BaseMapperX<OAuth2RefreshTokenDO> {
10 10  
11 11 default int deleteByRefreshToken(String refreshToken) {
12 12 return delete(new LambdaQueryWrapperX<OAuth2RefreshTokenDO>()
13 13 .eq(OAuth2RefreshTokenDO::getRefreshToken, refreshToken));
14 14 }
15 15  
  16 + default OAuth2RefreshTokenDO selectByRefreshToken(String refreshToken) {
  17 + return selectOne(OAuth2RefreshTokenDO::getRefreshToken, refreshToken);
  18 + }
  19 +
16 20 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/auth/OAuth2AccessTokenRedisDAO.java
1 1 package cn.iocoder.yudao.module.system.dal.redis.auth;
2 2  
  3 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
3 4 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
4 5 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
5 6 import org.springframework.data.redis.core.StringRedisTemplate;
6 7 import org.springframework.stereotype.Repository;
7 8  
8 9 import javax.annotation.Resource;
  10 +import java.util.Collection;
  11 +import java.util.List;
9 12 import java.util.concurrent.TimeUnit;
10 13  
11 14 import static cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN;
... ... @@ -39,6 +42,11 @@ public class OAuth2AccessTokenRedisDAO {
39 42 stringRedisTemplate.delete(redisKey);
40 43 }
41 44  
  45 + public void deleteList(Collection<String> accessTokens) {
  46 + List<String> redisKeys = CollectionUtils.convertList(accessTokens, OAuth2AccessTokenRedisDAO::formatKey);
  47 + stringRedisTemplate.delete(redisKeys);
  48 + }
  49 +
42 50 private static String formatKey(String accessToken) {
43 51 return String.format(OAUTH2_ACCESS_TOKEN.getKeyTemplate(), accessToken);
44 52 }
... ...
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 {
21 21 // 登录的接口
22 22 registry.antMatchers(buildAdminApi("/system/auth/login")).permitAll();
23 23 registry.antMatchers(buildAdminApi("/system/auth/logout")).permitAll();
  24 + registry.antMatchers(buildAdminApi("/system/auth/refresh-token")).permitAll();
24 25 // 社交登陆的接口
25 26 registry.antMatchers(buildAdminApi("/system/auth/social-auth-redirect")).permitAll();
26 27 registry.antMatchers(buildAdminApi("/system/auth/social-quick-login")).permitAll();
... ...
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 {
59 59 */
60 60 AuthLoginRespVO socialBindLogin(@Valid AuthSocialBindLoginReqVO reqVO);
61 61  
  62 + /**
  63 + * 刷新访问令牌
  64 + *
  65 + * @param refreshToken 刷新令牌
  66 + * @return 登录结果
  67 + */
  68 + AuthLoginRespVO refreshToken(String refreshToken);
  69 +
62 70 }
... ...
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 {
192 192 return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
193 193 }
194 194  
  195 + @Override
  196 + public AuthLoginRespVO refreshToken(String refreshToken) {
  197 + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientIdEnum.DEFAULT.getId());
  198 + return AuthConvert.INSTANCE.convert(accessTokenDO);
  199 + }
  200 +
195 201 private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) {
196 202 // 插入登陆日志
197 203 createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);
... ...
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 {
30 30 * 参考 DefaultTokenServices 的 refreshAccessToken 方法
31 31 *
32 32 * @param refreshToken 刷新令牌
  33 + * @param clientId 客户端编号
33 34 * @return 访问令牌的信息
34 35 */
35   - OAuth2AccessTokenDO refreshAccessToken(String refreshToken);
  36 + OAuth2AccessTokenDO refreshAccessToken(String refreshToken, Long clientId);
36 37  
37 38 /**
38 39 * 获得访问令牌
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java
1 1 package cn.iocoder.yudao.module.system.service.auth;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.util.IdUtil;
  5 +import cn.hutool.core.util.ObjectUtil;
4 6 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
5 7 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
6 8 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
... ... @@ -15,8 +17,10 @@ import org.springframework.transaction.annotation.Transactional;
15 17  
16 18 import javax.annotation.Resource;
17 19 import java.util.Calendar;
  20 +import java.util.List;
18 21  
19 22 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  23 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
20 24  
21 25 /**
22 26 * OAuth2.0 Token Service 实现类
... ... @@ -44,15 +48,38 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
44 48 // 创建刷新令牌
45 49 OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO);
46 50 // 创建访问令牌
47   - OAuth2AccessTokenDO accessTokenDO = createOAuth2AccessToken(refreshTokenDO, clientDO);
48   - // 记录到 Redis 中
49   - oauth2AccessTokenRedisDAO.set(accessTokenDO);
50   - return accessTokenDO;
  51 + return createOAuth2AccessToken(refreshTokenDO, clientDO);
51 52 }
52 53  
53 54 @Override
54   - public OAuth2AccessTokenDO refreshAccessToken(String refreshToken) {
55   - return null;
  55 + public OAuth2AccessTokenDO refreshAccessToken(String refreshToken, Long clientId) {
  56 + // 查询访问令牌
  57 + OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectByRefreshToken(refreshToken);
  58 + if (refreshTokenDO == null) {
  59 + throw exception(GlobalErrorCodeConstants.BAD_REQUEST, "无效的刷新令牌");
  60 + }
  61 +
  62 + // 校验 Client 匹配
  63 + OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
  64 + if (ObjectUtil.notEqual(clientId, refreshTokenDO.getClientId())) {
  65 + throw exception(GlobalErrorCodeConstants.BAD_REQUEST, "刷新令牌的客户端编号不正确");
  66 + }
  67 +
  68 + // 移除相关的访问令牌
  69 + List<OAuth2AccessTokenDO> accessTokenDOs = oauth2AccessTokenMapper.selectListByRefreshToken(refreshToken);
  70 + if (CollUtil.isNotEmpty(accessTokenDOs)) {
  71 + oauth2AccessTokenMapper.deleteBatchIds(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getId));
  72 + oauth2AccessTokenRedisDAO.deleteList(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getAccessToken));
  73 + }
  74 +
  75 + // 已过期的情况下,删除刷新令牌
  76 + if (DateUtils.isExpired(refreshTokenDO.getExpiresTime())) {
  77 + oauth2AccessTokenMapper.deleteById(refreshTokenDO.getId());
  78 + throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "刷新令牌已过期");
  79 + }
  80 +
  81 + // 创建访问令牌
  82 + return createOAuth2AccessToken(refreshTokenDO, clientDO);
56 83 }
57 84  
58 85 @Override
... ... @@ -76,10 +103,10 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
76 103 public OAuth2AccessTokenDO checkAccessToken(String accessToken) {
77 104 OAuth2AccessTokenDO accessTokenDO = getAccessToken(accessToken);
78 105 if (accessTokenDO == null) {
79   - throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "Token 不存在");
  106 + throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "访问令牌不存在");
80 107 }
81 108 if (DateUtils.isExpired(accessTokenDO.getExpiresTime())) {
82   - throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "Token 已过期");
  109 + throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "访问令牌已过期");
83 110 }
84 111 return accessTokenDO;
85 112 }
... ... @@ -98,38 +125,16 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
98 125 return accessTokenDO;
99 126 }
100 127  
101   -
102   -// @Override
103   -// @Transactional
104   -// public OAuth2AccessTokenRespDTO refreshAccessToken(OAuth2RefreshAccessTokenReqDTO refreshAccessTokenDTO) {
105   -// OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectById(refreshAccessTokenDTO.getRefreshToken());
106   -// // 校验刷新令牌是否合法
107   -// if (refreshTokenDO == null) { // 不存在
108   -// throw ServiceExceptionUtil.exception(OAUTH2_REFRESH_TOKEN_NOT_FOUND);
109   -// }
110   -// if (refreshTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期
111   -// throw ServiceExceptionUtil.exception(OAUTH_REFRESH_TOKEN_EXPIRED);
112   -// }
113   -//
114   -// // 标记 refreshToken 对应的 accessToken 都不合法
115   -// // 这块的实现,参考了 Spring Security OAuth2 的代码
116   -// List<OAuth2AccessTokenDO> accessTokenDOs = oauth2AccessTokenMapper.selectListByRefreshToken(refreshAccessTokenDTO.getRefreshToken());
117   -// accessTokenDOs.forEach(accessTokenDO -> deleteOAuth2AccessToken(accessTokenDO.getId()));
118   -//
119   -// // 创建访问令牌
120   -// OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(refreshTokenDO, refreshAccessTokenDTO.getCreateIp());
121   -// // 返回访问令牌
122   -// return OAuth2Convert.INSTANCE.convert(oauth2AccessTokenDO);
123   -// }
124   -
125 128 private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
126   - OAuth2AccessTokenDO accessToken = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
  129 + OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
127 130 .setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType()).setClientId(clientDO.getId())
128 131 .setRefreshToken(refreshTokenDO.getRefreshToken())
129 132 .setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getAccessTokenValiditySeconds()));
130   - accessToken.setTenantId(TenantContextHolder.getTenantId()); // 手动设置租户编号,避免缓存到 Redis 的时候,无对应的租户编号
131   - oauth2AccessTokenMapper.insert(accessToken);
132   - return accessToken;
  133 + accessTokenDO.setTenantId(TenantContextHolder.getTenantId()); // 手动设置租户编号,避免缓存到 Redis 的时候,无对应的租户编号
  134 + oauth2AccessTokenMapper.insert(accessTokenDO);
  135 + // 记录到 Redis 中
  136 + oauth2AccessTokenRedisDAO.set(accessTokenDO);
  137 + return accessTokenDO;
133 138 }
134 139  
135 140 private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long userId, Integer userType, OAuth2ClientDO clientDO) {
... ...
yudao-ui-admin/src/api/login.js
1 1 import request from '@/utils/request'
  2 +import {getRefreshToken} from "@/utils/auth";
  3 +import service from "@/utils/request";
2 4  
3 5 // 登录方法
4 6 export function login(username, password, code, uuid) {
... ... @@ -99,3 +101,11 @@ export function smsLogin(mobile, code) {
99 101 }
100 102 })
101 103 }
  104 +
  105 +// 刷新访问令牌
  106 +export function refreshToken() {
  107 + return service({
  108 + url: '/system/auth/refresh-token?refreshToken=' + getRefreshToken(),
  109 + method: 'post'
  110 + })
  111 +}
... ...
yudao-ui-admin/src/store/modules/user.js
... ... @@ -3,8 +3,6 @@ import {getAccessToken, setToken, removeToken, getRefreshToken} from &#39;@/utils/au
3 3  
4 4 const user = {
5 5 state: {
6   - accessToken: getAccessToken(),
7   - refreshToken: getRefreshToken(),
8 6 id: 0, // 用户编号
9 7 name: '',
10 8 avatar: '',
... ... @@ -16,10 +14,6 @@ const user = {
16 14 SET_ID: (state, id) => {
17 15 state.id = id
18 16 },
19   - SET_TOKEN: (state, token) => {
20   - state.accessToken = token.accessToken
21   - state.refreshToken = token.refreshToken
22   - },
23 17 SET_NAME: (state, name) => {
24 18 state.name = name
25 19 },
... ... @@ -46,7 +40,6 @@ const user = {
46 40 res = res.data;
47 41 // 设置 token
48 42 setToken(res)
49   - commit('SET_TOKEN', res)
50 43 resolve()
51 44 }).catch(error => {
52 45 reject(error)
... ... @@ -64,7 +57,6 @@ const user = {
64 57 res = res.data;
65 58 // 设置 token
66 59 setToken(res)
67   - commit('SET_TOKEN', res)
68 60 resolve()
69 61 }).catch(error => {
70 62 reject(error)
... ... @@ -84,7 +76,6 @@ const user = {
84 76 res = res.data;
85 77 // 设置 token
86 78 setToken(res)
87   - commit('SET_TOKEN', res)
88 79 resolve()
89 80 }).catch(error => {
90 81 reject(error)
... ... @@ -100,7 +91,6 @@ const user = {
100 91 res = res.data;
101 92 // 设置 token
102 93 setToken(res)
103   - commit('SET_TOKEN', res)
104 94 resolve()
105 95 }).catch(error => {
106 96 reject(error)
... ... @@ -148,7 +138,6 @@ const user = {
148 138 LogOut({ commit, state }) {
149 139 return new Promise((resolve, reject) => {
150 140 logout(state.token).then(() => {
151   - commit('SET_TOKEN', '')
152 141 commit('SET_ROLES', [])
153 142 commit('SET_PERMISSIONS', [])
154 143 removeToken()
... ...
yudao-ui-admin/src/utils/auth.js
... ... @@ -8,7 +8,7 @@ export function getAccessToken() {
8 8 }
9 9  
10 10 export function getRefreshToken() {
11   - return Cookies.get(AccessTokenKey)
  11 + return Cookies.get(RefreshTokenKey)
12 12 }
13 13  
14 14 export function setToken(token) {
... ...
yudao-ui-admin/src/utils/request.js
1 1 import axios from 'axios'
2 2 import { Notification, MessageBox, Message } from 'element-ui'
3 3 import store from '@/store'
4   -import { getAccessToken } from '@/utils/auth'
  4 +import {getAccessToken, getRefreshToken, setToken} from '@/utils/auth'
5 5 import errorCode from '@/utils/errorCode'
6 6 import Cookies from "js-cookie";
7 7 import {getPath, getTenantEnable} from "@/utils/ruoyi";
  8 +import {refreshToken} from "@/api/login";
8 9  
9 10 // 是否显示重新登录
10 11 export let isRelogin = { show: false };
  12 +// Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 实现
  13 +// 请求队列
  14 +let requestList = []
  15 +// 是否正在刷新中
  16 +let isRefreshToken = false
11 17  
12 18 axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
13 19 // 创建axios实例
... ... @@ -60,29 +66,43 @@ service.interceptors.request.use(config =&gt; {
60 66 })
61 67  
62 68 // 响应拦截器
63   -service.interceptors.response.use(res => {
  69 +service.interceptors.response.use( async res => {
64 70 // 未设置状态码则默认成功状态
65 71 const code = res.data.code || 200;
66 72 // 获取错误信息
67 73 const msg = errorCode[code] || res.data.msg || errorCode['default']
68 74 if (code === 401) {
69   - if (!isRelogin.show) {
70   - isRelogin.show = true;
71   - MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
72   - confirmButtonText: '重新登录',
73   - cancelButtonText: '取消',
74   - type: 'warning'
75   - }
76   - ).then(() => {
77   - isRelogin.show = false;
78   - store.dispatch('LogOut').then(() => {
79   - location.href = getPath('/index');
  75 + // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
  76 + if (!isRefreshToken) {
  77 + isRefreshToken = true;
  78 + // 1. 如果获取不到刷新令牌,则只能执行登出操作
  79 + if (!getRefreshToken()) {
  80 + return handleAuthorized();
  81 + }
  82 + // 2. 进行刷新访问令牌
  83 + try {
  84 + const refreshTokenRes = await refreshToken()
  85 + // 2.1 刷新成功,则回放队列的请求 + 当前请求
  86 + setToken(refreshTokenRes.data)
  87 + requestList.forEach(cb => cb())
  88 + return service(res.config)
  89 + } catch (e) {
  90 + // 2.1 刷新失败,则只能执行登出操作
  91 + return handleAuthorized();
  92 + } finally {
  93 + requestList = []
  94 + isRefreshToken = false
  95 + }
  96 + } else {
  97 + // 添加到队列,等待刷新获取到新的令牌
  98 + return new Promise(resolve => {
  99 + requestList.push(() => {
  100 + config.headers['Authorization'] = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
  101 + resolve(service(config))
80 102 })
81   - }).catch(() => {
82   - isRelogin.show = false;
83   - });
  103 + })
84 104 }
85   - return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
  105 + return handleAuthorized();
86 106 } else if (code === 500) {
87 107 Message({
88 108 message: msg,
... ... @@ -138,4 +158,24 @@ export function getBaseHeader() {
138 158 }
139 159 }
140 160  
  161 +function handleAuthorized() {
  162 + if (!isRelogin.show) {
  163 + isRelogin.show = true;
  164 + MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
  165 + confirmButtonText: '重新登录',
  166 + cancelButtonText: '取消',
  167 + type: 'warning'
  168 + }
  169 + ).then(() => {
  170 + isRelogin.show = false;
  171 + store.dispatch('LogOut').then(() => {
  172 + location.href = getPath('/index');
  173 + })
  174 + }).catch(() => {
  175 + isRelogin.show = false;
  176 + });
  177 + }
  178 + return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
  179 +}
  180 +
141 181 export default service
... ...