Commit 4f52d1367be6ca504adff544cf069ef9f38456d8

Authored by YunaiV
1 parent ebee4ddb

实现管理后台登录时,使用 OAuth2 的 access token

Showing 30 changed files with 626 additions and 357 deletions
yudao-framework/yudao-spring-boot-starter-security/pom.xml
@@ -44,18 +44,11 @@ @@ -44,18 +44,11 @@
44 <artifactId>spring-boot-starter-security</artifactId> 44 <artifactId>spring-boot-starter-security</artifactId>
45 </dependency> 45 </dependency>
46 46
47 - <!-- TODO 芋艿: --> 47 + <!-- 业务组件 -->
48 <dependency> 48 <dependency>
49 - <groupId>org.activiti</groupId>  
50 - <artifactId>activiti-engine</artifactId>  
51 - <version>7.1.0.M6</version>  
52 - <exclusions>  
53 - <exclusion>  
54 - <groupId>*</groupId>  
55 - <artifactId>*</artifactId>  
56 - </exclusion>  
57 - </exclusions>  
58 - <optional>true</optional> 49 + <groupId>cn.iocoder.boot</groupId>
  50 + <artifactId>yudao-module-system-api</artifactId> <!-- 需要使用它,进行 Token 的校验 -->
  51 + <version>${revision}</version>
59 </dependency> 52 </dependency>
60 </dependencies> 53 </dependencies>
61 54
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.security.core.filter.TokenAuthenticationFilter @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.security.core.filter.TokenAuthenticationFilter
6 import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl; 6 import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl;
7 import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl; 7 import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl;
8 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; 8 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
  9 +import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi;
9 import org.springframework.beans.factory.config.MethodInvokingFactoryBean; 10 import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
10 import org.springframework.boot.context.properties.EnableConfigurationProperties; 11 import org.springframework.boot.context.properties.EnableConfigurationProperties;
11 import org.springframework.context.annotation.Bean; 12 import org.springframework.context.annotation.Bean;
@@ -72,8 +73,9 @@ public class YudaoSecurityAutoConfiguration { @@ -72,8 +73,9 @@ public class YudaoSecurityAutoConfiguration {
72 * Token 认证过滤器 Bean 73 * Token 认证过滤器 Bean
73 */ 74 */
74 @Bean 75 @Bean
75 - public TokenAuthenticationFilter authenticationTokenFilter(GlobalExceptionHandler globalExceptionHandler) {  
76 - return new TokenAuthenticationFilter(securityProperties, globalExceptionHandler); 76 + public TokenAuthenticationFilter authenticationTokenFilter(GlobalExceptionHandler globalExceptionHandler,
  77 + OAuth2TokenApi oauth2TokenApi) {
  78 + return new TokenAuthenticationFilter(securityProperties, globalExceptionHandler, oauth2TokenApi);
77 } 79 }
78 80
79 /** 81 /**
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java
1 package cn.iocoder.yudao.framework.security.core.filter; 1 package cn.iocoder.yudao.framework.security.core.filter;
2 2
  3 +import cn.hutool.core.util.ObjectUtil;
3 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; 6 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
@@ -8,7 +9,10 @@ import cn.iocoder.yudao.framework.security.core.LoginUser; @@ -8,7 +9,10 @@ import cn.iocoder.yudao.framework.security.core.LoginUser;
8 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; 9 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
9 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; 10 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
10 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; 11 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
  12 +import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi;
  13 +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO;
11 import lombok.RequiredArgsConstructor; 14 import lombok.RequiredArgsConstructor;
  15 +import org.springframework.security.access.AccessDeniedException;
12 import org.springframework.web.filter.OncePerRequestFilter; 16 import org.springframework.web.filter.OncePerRequestFilter;
13 17
14 import javax.servlet.FilterChain; 18 import javax.servlet.FilterChain;
@@ -30,6 +34,8 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { @@ -30,6 +34,8 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
30 34
31 private final GlobalExceptionHandler globalExceptionHandler; 35 private final GlobalExceptionHandler globalExceptionHandler;
32 36
  37 + private final OAuth2TokenApi oauth2TokenApi;
  38 +
33 @Override 39 @Override
34 @SuppressWarnings("NullableProblems") 40 @SuppressWarnings("NullableProblems")
35 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) 41 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
@@ -39,11 +45,21 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { @@ -39,11 +45,21 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
39 Integer userType = WebFrameworkUtils.getLoginUserType(request); 45 Integer userType = WebFrameworkUtils.getLoginUserType(request);
40 try { 46 try {
41 // 验证 token 有效性 47 // 验证 token 有效性
42 - LoginUser loginUser = null; // TODO 芋艿:待实现 48 + OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token);
  49 + if (accessToken != null && ObjectUtil.notEqual(accessToken.getUserType(), userType)) { // 用户类型不匹配,无权限
  50 + throw new AccessDeniedException("错误的用户类型");
  51 + }
  52 + LoginUser loginUser = null;
  53 + if (accessToken != null) { // 如果不为空,说明认证通过,则转换成登录用户
  54 + loginUser = new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
  55 + .setTenantId(accessToken.getTenantId());
  56 + }
  57 +
43 // 模拟 Login 功能,方便日常开发调试 58 // 模拟 Login 功能,方便日常开发调试
44 if (loginUser == null) { 59 if (loginUser == null) {
45 loginUser = mockLoginUser(request, token, userType); 60 loginUser = mockLoginUser(request, token, userType);
46 } 61 }
  62 +
47 // 设置当前用户 63 // 设置当前用户
48 if (loginUser != null) { 64 if (loginUser != null) {
49 SecurityFrameworkUtils.setLoginUser(loginUser, request); 65 SecurityFrameworkUtils.setLoginUser(loginUser, request);
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthFrameworkService.java deleted
1 -package cn.iocoder.yudao.framework.security.core.service;  
2 -  
3 -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;  
4 -import cn.iocoder.yudao.framework.security.core.LoginUser;  
5 -import org.springframework.security.core.userdetails.UserDetailsService;  
6 -  
7 -/**  
8 - * Security 框架 Auth Service 接口,定义不同用户类型的 {@link UserTypeEnum} 需要实现的方法  
9 - *  
10 - * @author 芋道源码  
11 - */  
12 -public interface SecurityAuthFrameworkService extends UserDetailsService {  
13 -  
14 - /**  
15 - * 校验 token 的有效性,并获取用户信息  
16 - * 通过后,刷新 token 的过期时间  
17 - *  
18 - * @param token token  
19 - * @return 用户信息  
20 - */  
21 - LoginUser verifyTokenAndRefresh(String token);  
22 -  
23 - /**  
24 - * 获得用户类型。每个用户类型,对应一个 SecurityAuthFrameworkService 实现类。  
25 - *  
26 - * @return 用户类型  
27 - */  
28 - UserTypeEnum getUserType();  
29 -  
30 -}  
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java
@@ -84,8 +84,8 @@ public class WebFrameworkUtils { @@ -84,8 +84,8 @@ public class WebFrameworkUtils {
84 } 84 }
85 // 1. 优先,从 Attribute 中获取 85 // 1. 优先,从 Attribute 中获取
86 Integer userType = (Integer) request.getAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE); 86 Integer userType = (Integer) request.getAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE);
87 - if (userType == null) {  
88 - return null; 87 + if (userType != null) {
  88 + return userType;
89 } 89 }
90 // 2. 其次,基于 URL 前缀的约定 90 // 2. 其次,基于 URL 前缀的约定
91 if (request.getRequestURI().startsWith(properties.getAdminApi().getPrefix())) { 91 if (request.getRequestURI().startsWith(properties.getAdminApi().getPrefix())) {
yudao-module-system/yudao-module-system-api/pom.xml
@@ -29,13 +29,6 @@ @@ -29,13 +29,6 @@
29 <optional>true</optional> 29 <optional>true</optional>
30 </dependency> 30 </dependency>
31 31
32 - <!-- 用户信息 -->  
33 - <dependency>  
34 - <groupId>cn.iocoder.boot</groupId>  
35 - <artifactId>yudao-spring-boot-starter-security</artifactId>  
36 - <optional>true</optional>  
37 - </dependency>  
38 -  
39 </dependencies> 32 </dependencies>
40 33
41 </project> 34 </project>
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2Api.java deleted
1 -package cn.iocoder.yudao.module.system.api.auth;  
2 -  
3 -/**  
4 - * OAuth2.0 API 接口  
5 - *  
6 - * @author 芋道源码  
7 - */  
8 -public interface OAuth2Api {  
9 -  
10 -  
11 -  
12 -}  
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApi.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.auth;
  2 +
  3 +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO;
  4 +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO;
  5 +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO;
  6 +
  7 +import javax.validation.Valid;
  8 +
  9 +/**
  10 + * OAuth2.0 Token API 接口
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +public interface OAuth2TokenApi {
  15 +
  16 + OAuth2AccessTokenRespDTO createAccessToken(@Valid OAuth2AccessTokenCreateReqDTO reqDTO);
  17 +
  18 + OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken);
  19 +
  20 +// void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO);
  21 +
  22 +}
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/UserSessionApi.java deleted
1 -package cn.iocoder.yudao.module.system.api.auth;  
2 -  
3 -import cn.iocoder.yudao.framework.security.core.LoginUser;  
4 -  
5 -import javax.validation.constraints.NotEmpty;  
6 -import javax.validation.constraints.NotNull;  
7 -  
8 -/**  
9 - * 在线用户 Session API 接口  
10 - *  
11 - * @author 芋道源码  
12 - */  
13 -public interface UserSessionApi {  
14 -  
15 - /**  
16 - * 创建在线用户 Session  
17 - *  
18 - * @param loginUser 登录用户  
19 - * @param userIp 用户 IP  
20 - * @param userAgent 用户 UA  
21 - * @return Token 令牌  
22 - */  
23 - String createUserSession(@NotNull(message = "登录用户不能为空") LoginUser loginUser, String userIp, String userAgent);  
24 -  
25 - /**  
26 - * 刷新在线用户 Session 的更新时间  
27 - *  
28 - * @param token Token 令牌  
29 - * @param loginUser 登录用户  
30 - */  
31 - void refreshUserSession(@NotEmpty(message = "Token 令牌不能为空") String token,  
32 - @NotNull(message = "登录用户不能为空") LoginUser loginUser);  
33 -  
34 - /**  
35 - * 删除在线用户 Session  
36 - *  
37 - * @param token Token 令牌  
38 - */  
39 - void deleteUserSession(String token);  
40 -  
41 - /**  
42 - * 获得 Token 令牌对应的在线用户  
43 - *  
44 - * @param token Token 令牌  
45 - * @return 在线用户  
46 - */  
47 - LoginUser getLoginUser(String token);  
48 -  
49 - /**  
50 - * 获得 Session 超时时间,单位:毫秒  
51 - *  
52 - * @return 超时时间  
53 - */  
54 - Long getSessionTimeoutMillis();  
55 -  
56 -}  
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCheckRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.auth.dto;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.io.Serializable;
  6 +
  7 +/**
  8 + * OAuth2.0 访问令牌的校验 Response DTO
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +@Data
  13 +public class OAuth2AccessTokenCheckRespDTO implements Serializable {
  14 +
  15 + /**
  16 + * 用户编号
  17 + */
  18 + private Long userId;
  19 + /**
  20 + * 用户类型
  21 + */
  22 + private Integer userType;
  23 + /**
  24 + * 租户编号
  25 + */
  26 + private Long tenantId;
  27 +
  28 +}
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCreateReqDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.auth.dto;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import lombok.Data;
  6 +import lombok.experimental.Accessors;
  7 +
  8 +import javax.validation.constraints.NotNull;
  9 +import java.io.Serializable;
  10 +
  11 +/**
  12 + * OAuth2.0 访问令牌创建 Request DTO
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@Data
  17 +@Accessors(chain = true)
  18 +public class OAuth2AccessTokenCreateReqDTO implements Serializable {
  19 +
  20 + /**
  21 + * 用户编号
  22 + */
  23 + @NotNull(message = "用户编号不能为空")
  24 + private Integer userId;
  25 + /**
  26 + * 用户类型
  27 + */
  28 + @NotNull(message = "用户类型不能为空")
  29 + @InEnum(value = UserTypeEnum.class, message = "用户类型必须是 {value}")
  30 + private Integer userType;
  31 + /**
  32 + * 客户端编号
  33 + */
  34 + @NotNull(message = "客户端编号不能为空")
  35 + private Long clientId;
  36 +
  37 +}
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.auth.dto;
  2 +
  3 +import lombok.Data;
  4 +import lombok.experimental.Accessors;
  5 +
  6 +import java.io.Serializable;
  7 +import java.util.Date;
  8 +
  9 +/**
  10 + * OAuth2.0 访问令牌的信息 Response DTO
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@Data
  15 +@Accessors(chain = true)
  16 +public class OAuth2AccessTokenRespDTO implements Serializable {
  17 +
  18 + /**
  19 + * 访问令牌
  20 + */
  21 + private String accessToken;
  22 + /**
  23 + * 刷新令牌
  24 + */
  25 + private String refreshToken;
  26 + /**
  27 + * 用户编号
  28 + */
  29 + private Integer userId;
  30 + /**
  31 + * 用户类型
  32 + */
  33 + private Integer userType;
  34 + /**
  35 + * 过期时间
  36 + */
  37 + private Date expiresTime;
  38 +
  39 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.auth;
  2 +
  3 +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO;
  4 +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO;
  5 +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO;
  6 +import cn.iocoder.yudao.module.system.convert.auth.UserSessionConvert;
  7 +import cn.iocoder.yudao.module.system.service.auth.OAuth2TokenService;
  8 +import org.springframework.stereotype.Service;
  9 +
  10 +import javax.annotation.Resource;
  11 +
  12 +/**
  13 + * OAuth2.0 Token API 实现类
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Service
  18 +public class OAuth2TokenApiImpl implements OAuth2TokenApi {
  19 +
  20 + @Resource
  21 + private OAuth2TokenService oauth2TokenService;
  22 +
  23 + @Override
  24 + public OAuth2AccessTokenRespDTO createAccessToken(OAuth2AccessTokenCreateReqDTO reqDTO) {
  25 + return null;
  26 + }
  27 +
  28 + @Override
  29 + public OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken) {
  30 + return UserSessionConvert.INSTANCE.convert(oauth2TokenService.checkAccessToken(accessToken));
  31 + }
  32 +
  33 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/UserSessionApiImpl.java deleted
1 -package cn.iocoder.yudao.module.system.api.auth;  
2 -  
3 -import cn.iocoder.yudao.framework.security.core.LoginUser;  
4 -import cn.iocoder.yudao.module.system.service.auth.UserSessionService;  
5 -import org.springframework.stereotype.Service;  
6 -import org.springframework.validation.annotation.Validated;  
7 -  
8 -import javax.annotation.Resource;  
9 -  
10 -/**  
11 - * 在线用户 Session API 实现类  
12 - *  
13 - * @author 芋道源码  
14 - */  
15 -@Service  
16 -@Validated  
17 -public class UserSessionApiImpl implements UserSessionApi {  
18 -  
19 - @Resource  
20 - private UserSessionService userSessionService;  
21 -  
22 - @Override  
23 - public String createUserSession(LoginUser loginUser, String userIp, String userAgent) {  
24 - return userSessionService.createUserSession(loginUser, userIp, userAgent);  
25 - }  
26 -  
27 - @Override  
28 - public void refreshUserSession(String token, LoginUser loginUser) {  
29 - userSessionService.refreshUserSession(token, loginUser);  
30 - }  
31 -  
32 - @Override  
33 - public void deleteUserSession(String token) {  
34 - userSessionService.deleteUserSession(token);  
35 - }  
36 -  
37 - @Override  
38 - public LoginUser getLoginUser(String token) {  
39 - return userSessionService.getLoginUser(token);  
40 - }  
41 -  
42 - @Override  
43 - public Long getSessionTimeoutMillis() {  
44 - return userSessionService.getSessionTimeoutMillis();  
45 - }  
46 -  
47 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/UserSessionConvert.java
1 package cn.iocoder.yudao.module.system.convert.auth; 1 package cn.iocoder.yudao.module.system.convert.auth;
2 2
  3 +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO;
3 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageItemRespVO; 4 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageItemRespVO;
  5 +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
4 import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; 6 import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO;
5 import org.mapstruct.Mapper; 7 import org.mapstruct.Mapper;
6 import org.mapstruct.factory.Mappers; 8 import org.mapstruct.factory.Mappers;
@@ -12,4 +14,6 @@ public interface UserSessionConvert { @@ -12,4 +14,6 @@ public interface UserSessionConvert {
12 14
13 UserSessionPageItemRespVO convert(UserSessionDO session); 15 UserSessionPageItemRespVO convert(UserSessionDO session);
14 16
  17 + OAuth2AccessTokenCheckRespDTO convert(OAuth2AccessTokenDO bean);
  18 +
15 } 19 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2AccessTokenDO.java
1 package cn.iocoder.yudao.module.system.dal.dataobject.auth; 1 package cn.iocoder.yudao.module.system.dal.dataobject.auth;
2 2
3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4 -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 4 +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
  5 +import com.baomidou.mybatisplus.annotation.TableId;
5 import com.baomidou.mybatisplus.annotation.TableName; 6 import com.baomidou.mybatisplus.annotation.TableName;
6 import lombok.Data; 7 import lombok.Data;
7 import lombok.EqualsAndHashCode; 8 import lombok.EqualsAndHashCode;
@@ -12,23 +13,31 @@ import java.util.Date; @@ -12,23 +13,31 @@ import java.util.Date;
12 /** 13 /**
13 * OAuth2 访问令牌 DO 14 * OAuth2 访问令牌 DO
14 * 15 *
  16 + * 如下字段,暂时未使用,暂时不支持:
  17 + * user_name、authentication(用户信息)
  18 + *
15 * @author 芋道源码 19 * @author 芋道源码
16 */ 20 */
17 @TableName("system_oauth2_access_token") 21 @TableName("system_oauth2_access_token")
18 @Data 22 @Data
19 @EqualsAndHashCode(callSuper = true) 23 @EqualsAndHashCode(callSuper = true)
20 @Accessors(chain = true) 24 @Accessors(chain = true)
21 -public class OAuth2AccessTokenDO extends BaseDO { 25 +public class OAuth2AccessTokenDO extends TenantBaseDO {
22 26
23 /** 27 /**
24 * 编号,数据库递增 28 * 编号,数据库递增
25 */ 29 */
  30 + @TableId
26 private Long id; 31 private Long id;
27 /** 32 /**
28 * 访问令牌 33 * 访问令牌
29 */ 34 */
30 private String accessToken; 35 private String accessToken;
31 /** 36 /**
  37 + * 刷新令牌
  38 + */
  39 + private String refreshToken;
  40 + /**
32 * 用户编号 41 * 用户编号
33 */ 42 */
34 private Long userId; 43 private Long userId;
@@ -39,11 +48,11 @@ public class OAuth2AccessTokenDO extends BaseDO { @@ -39,11 +48,11 @@ public class OAuth2AccessTokenDO extends BaseDO {
39 */ 48 */
40 private Integer userType; 49 private Integer userType;
41 /** 50 /**
42 - * 应用编号 51 + * 客户端编号
43 * 52 *
44 - * 关联 {@link OAuth2ApplicationDO#getId()} 53 + * 关联 {@link OAuth2ClientDO#getId()}
45 */ 54 */
46 - private Long applicationId; 55 + private Long clientId;
47 /** 56 /**
48 * 过期时间 57 * 过期时间
49 */ 58 */
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2ApplicationDO.java renamed to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2ClientDO.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.dal.dataobject.auth; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.dal.dataobject.auth;
2 2
3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import com.baomidou.mybatisplus.annotation.TableId;
5 import com.baomidou.mybatisplus.annotation.TableName; 6 import com.baomidou.mybatisplus.annotation.TableName;
6 import lombok.Data; 7 import lombok.Data;
7 import lombok.EqualsAndHashCode; 8 import lombok.EqualsAndHashCode;
@@ -12,12 +13,8 @@ import java.util.List; @@ -12,12 +13,8 @@ import java.util.List;
12 /** 13 /**
13 * OAuth2 客户端 DO 14 * OAuth2 客户端 DO
14 * 15 *
15 - * 为什么不使用 Client 作为表名?  
16 - * 1. clientId 字段被占用,导致表的 id 无法有合适的缩写  
17 - * 2. 大多数 Github、Gitee 等平台,都会习惯称为第三方接入应用  
18 - *  
19 * 如下字段,考虑到使用相对不是很高频,主要是一些开关,暂时不支持: 16 * 如下字段,考虑到使用相对不是很高频,主要是一些开关,暂时不支持:
20 - * authorized_grant_types、authorities、access_token_validity、refresh_token_validity、additional_information、autoapprove、resource_ids、scope 17 + * authorized_grant_types、authorities、additional_information、autoapprove、resource_ids、scope
21 * 18 *
22 * @author 芋道源码 19 * @author 芋道源码
23 */ 20 */
@@ -25,24 +22,19 @@ import java.util.List; @@ -25,24 +22,19 @@ import java.util.List;
25 @Data 22 @Data
26 @EqualsAndHashCode(callSuper = true) 23 @EqualsAndHashCode(callSuper = true)
27 @Accessors(chain = true) 24 @Accessors(chain = true)
28 -public class OAuth2ApplicationDO extends BaseDO { 25 +public class OAuth2ClientDO extends BaseDO {
29 26
30 /** 27 /**
31 - * 编号,数据库递增  
32 - */  
33 - private Long id;  
34 - /**  
35 * 客户端编号 28 * 客户端编号
  29 + *
  30 + * 由于 SQL Server 在存储 String 主键有点问题,所以暂时使用 Long 类型
36 */ 31 */
37 - private String clientId; 32 + @TableId
  33 + private Long id;
38 /** 34 /**
39 * 客户端密钥 35 * 客户端密钥
40 */ 36 */
41 - private String clientSecret;  
42 - /**  
43 - * 可重定向的 URI 地址  
44 - */  
45 - private List<String> redirectUris; 37 + private String secret;
46 /** 38 /**
47 * 应用名 39 * 应用名
48 */ 40 */
@@ -61,5 +53,17 @@ public class OAuth2ApplicationDO extends BaseDO { @@ -61,5 +53,17 @@ public class OAuth2ApplicationDO extends BaseDO {
61 * 枚举 {@link CommonStatusEnum} 53 * 枚举 {@link CommonStatusEnum}
62 */ 54 */
63 private Integer status; 55 private Integer status;
  56 + /**
  57 + * 访问令牌的有效期
  58 + */
  59 + private Integer accessTokenValiditySeconds;
  60 + /**
  61 + * 刷新令牌的有效期
  62 + */
  63 + private Integer refreshTokenValiditySeconds;
  64 + /**
  65 + * 可重定向的 URI 地址
  66 + */
  67 + private List<String> redirectUris;
64 68
65 } 69 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2CodeDO.java
@@ -39,11 +39,11 @@ public class OAuth2CodeDO extends BaseDO { @@ -39,11 +39,11 @@ public class OAuth2CodeDO extends BaseDO {
39 */ 39 */
40 private Integer userType; 40 private Integer userType;
41 /** 41 /**
42 - * 应用编号 42 + * 客户端编号
43 * 43 *
44 - * 关联 {@link OAuth2ApplicationDO#getId()} 44 + * 关联 {@link OAuth2ClientDO#getId()}
45 */ 45 */
46 - private Long applicationId; 46 + private Long clientId;
47 /** 47 /**
48 * 刷新令牌 48 * 刷新令牌
49 * 49 *
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2RefreshTokenDO.java
@@ -12,6 +12,7 @@ import java.util.Date; @@ -12,6 +12,7 @@ import java.util.Date;
12 /** 12 /**
13 * OAuth2 刷新令牌 13 * OAuth2 刷新令牌
14 * 14 *
  15 + * @author 芋道源码
15 */ 16 */
16 @TableName("system_oauth2_refresh_token") 17 @TableName("system_oauth2_refresh_token")
17 @Data 18 @Data
@@ -30,7 +31,7 @@ public class OAuth2RefreshTokenDO extends BaseDO { @@ -30,7 +31,7 @@ public class OAuth2RefreshTokenDO extends BaseDO {
30 /** 31 /**
31 * 用户编号 32 * 用户编号
32 */ 33 */
33 - private Integer userId; 34 + private Long userId;
34 /** 35 /**
35 * 用户类型 36 * 用户类型
36 * 37 *
@@ -38,12 +39,14 @@ public class OAuth2RefreshTokenDO extends BaseDO { @@ -38,12 +39,14 @@ public class OAuth2RefreshTokenDO extends BaseDO {
38 */ 39 */
39 private Integer userType; 40 private Integer userType;
40 /** 41 /**
41 - * 过期时间 42 + * 客户端编号
  43 + *
  44 + * 关联 {@link OAuth2ClientDO#getId()}
42 */ 45 */
43 - private Date expiresTime; 46 + private Long clientId;
44 /** 47 /**
45 - * 创建 IP 48 + * 过期时间
46 */ 49 */
47 - private String createIp; 50 + private Date expiresTime;
48 51
49 } 52 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.dal.mysql.auth;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
  5 +import org.apache.ibatis.annotations.Mapper;
  6 +
  7 +@Mapper
  8 +public interface OAuth2AccessTokenMapper extends BaseMapperX<OAuth2AccessTokenDO> {
  9 +
  10 + default OAuth2AccessTokenDO selectByAccessToken(String accessToken) {
  11 + return selectOne(OAuth2AccessTokenDO::getAccessToken, accessToken);
  12 + }
  13 +
  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 +// }
  31 +
  32 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2RefreshTokenMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.dal.mysql.auth;
  2 +
  3 +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO;
  4 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  5 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  6 +import org.apache.ibatis.annotations.Mapper;
  7 +
  8 +@Mapper
  9 +public interface OAuth2RefreshTokenMapper extends BaseMapper<OAuth2RefreshTokenDO> {
  10 +
  11 + default int deleteByUserIdAndUserType(Integer userId, Integer userType) {
  12 + return delete(new QueryWrapper<OAuth2RefreshTokenDO>()
  13 + .eq("user_id", userId).eq("user_type", userType));
  14 + }
  15 +
  16 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.dal.redis; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.dal.redis;
2 2
3 import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; 3 import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
4 import cn.iocoder.yudao.framework.security.core.LoginUser; 4 import cn.iocoder.yudao.framework.security.core.LoginUser;
  5 +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
5 6
6 import java.time.Duration; 7 import java.time.Duration;
7 8
@@ -22,6 +23,10 @@ public interface RedisKeyConstants { @@ -22,6 +23,10 @@ public interface RedisKeyConstants {
22 "login_user:%s", // 参数为 token 令牌 23 "login_user:%s", // 参数为 token 令牌
23 STRING, LoginUser.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); 24 STRING, LoginUser.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC);
24 25
  26 + RedisKeyDefine OAUTH2_ACCESS_TOKEN = new RedisKeyDefine("访问令牌的缓存",
  27 + "oauth2_access_token:%s", // 参数为访问令牌 token
  28 + STRING, OAuth2AccessTokenDO.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC);
  29 +
25 RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交登陆的 state", // 注意,它是被 JustAuth 的 justauth.type.prefix 使用到 30 RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交登陆的 state", // 注意,它是被 JustAuth 的 justauth.type.prefix 使用到
26 "social_auth_state:%s", // 参数为 state 31 "social_auth_state:%s", // 参数为 state
27 STRING, String.class, Duration.ofHours(24)); // 值为 state 32 STRING, String.class, Duration.ofHours(24)); // 值为 state
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/auth/OAuth2AccessTokenRedisDAO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.dal.redis.auth;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  4 +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
  5 +import org.springframework.data.redis.core.StringRedisTemplate;
  6 +import org.springframework.stereotype.Repository;
  7 +
  8 +import javax.annotation.Resource;
  9 +import java.util.concurrent.TimeUnit;
  10 +
  11 +import static cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN;
  12 +
  13 +/**
  14 + * {@link OAuth2AccessTokenDO} 的 RedisDAO
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +@Repository
  19 +public class OAuth2AccessTokenRedisDAO {
  20 +
  21 + @Resource
  22 + private StringRedisTemplate stringRedisTemplate;
  23 +
  24 + public OAuth2AccessTokenDO get(String accessToken) {
  25 + String redisKey = formatKey(accessToken);
  26 + return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), OAuth2AccessTokenDO.class);
  27 + }
  28 +
  29 + public void set(OAuth2AccessTokenDO accessTokenDO) {
  30 + String redisKey = formatKey(accessTokenDO.getAccessToken());
  31 + // 清理多余字段,避免缓存
  32 + accessTokenDO.setUpdater(null).setUpdateTime(null).setCreateTime(null).setCreator(null).setDeleted(null);
  33 + stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(accessTokenDO),
  34 + accessTokenDO.getExpiresTime().getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
  35 + }
  36 +
  37 + public void delete(String accessToken) {
  38 + String redisKey = formatKey(accessToken);
  39 + stringRedisTemplate.delete(redisKey);
  40 + }
  41 +
  42 + private static String formatKey(String accessToken) {
  43 + return String.format(OAUTH2_ACCESS_TOKEN.getKeyTemplate(), accessToken);
  44 + }
  45 +
  46 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
@@ -49,6 +49,8 @@ public class AdminAuthServiceImpl implements AdminAuthService { @@ -49,6 +49,8 @@ public class AdminAuthServiceImpl implements AdminAuthService {
49 @Resource 49 @Resource
50 private UserSessionService userSessionService; 50 private UserSessionService userSessionService;
51 @Resource 51 @Resource
  52 + private OAuth2TokenService oauth2TokenService;
  53 + @Resource
52 private SocialUserService socialUserService; 54 private SocialUserService socialUserService;
53 55
54 @Resource 56 @Resource
@@ -207,8 +209,12 @@ public class AdminAuthServiceImpl implements AdminAuthService { @@ -207,8 +209,12 @@ public class AdminAuthServiceImpl implements AdminAuthService {
207 LoginLogTypeEnum logType, String userIp, String userAgent) { 209 LoginLogTypeEnum logType, String userIp, String userAgent) {
208 // 插入登陆日志 210 // 插入登陆日志
209 createLoginLog(loginUser.getId(), username, logType, LoginResultEnum.SUCCESS); 211 createLoginLog(loginUser.getId(), username, logType, LoginResultEnum.SUCCESS);
210 - // 缓存登录用户到 Redis 中,返回 Token 令牌  
211 - return userSessionService.createUserSession(loginUser, userIp, userAgent); 212 + // 创建访问令牌
  213 + // TODO userIp、userAgent
  214 + // TODO clientId
  215 + return oauth2TokenService.createAccessToken(loginUser.getId(), getUserType().getValue(), 1L)
  216 + .getAccessToken();
  217 +// return userSessionService.createUserSession(loginUser, userIp, userAgent);
212 } 218 }
213 219
214 @Override 220 @Override
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.auth;
  2 +
  3 +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
  4 +
  5 +/**
  6 + * OAuth2.0 Client Service 接口
  7 + *
  8 + * 从功能上,和 JdbcClientDetailsService 的功能,提供客户端的操作
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +public interface OAuth2ClientService {
  13 +
  14 + /**
  15 + * 从缓存中,校验客户端是否合法
  16 + *
  17 + * @param id 客户端编号
  18 + * @return 客户端
  19 + */
  20 + OAuth2ClientDO validOAuthClientFromCache(Long id);
  21 +
  22 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.auth;
  2 +
  3 +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
  4 +import org.springframework.stereotype.Service;
  5 +
  6 +/**
  7 + * OAuth2.0 Client Service 实现类
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +@Service
  12 +public class OAuth2ClientServiceImpl implements OAuth2ClientService {
  13 +
  14 + @Override
  15 + public OAuth2ClientDO validOAuthClientFromCache(Long id) {
  16 + return new OAuth2ClientDO().setId(id)
  17 + .setAccessTokenValiditySeconds(60 * 30)
  18 + .setRefreshTokenValiditySeconds(60 * 60 * 24 * 30);
  19 + }
  20 +
  21 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ServiceImpl.java deleted
1 -package cn.iocoder.yudao.module.system.service.auth;  
2 -  
3 -import org.springframework.stereotype.Service;  
4 -  
5 -/**  
6 - * OAuth2.0 Service 实现类  
7 - *  
8 - *  
9 - *  
10 - * @author 芋道源码  
11 - */  
12 -@Service  
13 -public class OAuth2ServiceImpl implements OAuth2TokenService {  
14 -  
15 -// @Autowired  
16 -// private SystemBizProperties systemBizProperties;  
17 -//  
18 -// @Autowired  
19 -// private OAuth2AccessTokenMapper oauth2AccessTokenMapper;  
20 -// @Autowired  
21 -// private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper;  
22 -//  
23 -// @Autowired  
24 -// private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO;  
25 -//  
26 -// @Override  
27 -// @Transactional  
28 -// public OAuth2AccessTokenRespDTO createAccessToken(OAuth2CreateAccessTokenReqDTO createAccessTokenDTO) {  
29 -// // 创建刷新令牌 + 访问令牌  
30 -// OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(createAccessTokenDTO.getUserId(),  
31 -// createAccessTokenDTO.getUserType(), createAccessTokenDTO.getCreateIp());  
32 -// OAuth2AccessTokenDO accessTokenDO = createOAuth2AccessToken(refreshTokenDO, createAccessTokenDTO.getCreateIp());  
33 -// // 返回访问令牌  
34 -// return OAuth2Convert.INSTANCE.convert(accessTokenDO);  
35 -// }  
36 -//  
37 -// @Override  
38 -// @Transactional  
39 -// public OAuth2AccessTokenRespDTO checkAccessToken(String accessToken) {  
40 -// OAuth2AccessTokenDO accessTokenDO = this.getOAuth2AccessToken(accessToken);  
41 -// if (accessTokenDO == null) { // 不存在  
42 -// throw ServiceExceptionUtil.exception(OAUTH2_ACCESS_TOKEN_NOT_FOUND);  
43 -// }  
44 -// if (accessTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期  
45 -// throw ServiceExceptionUtil.exception(OAUTH2_ACCESS_TOKEN_TOKEN_EXPIRED);  
46 -// }  
47 -// // 返回访问令牌  
48 -// return OAuth2Convert.INSTANCE.convert(accessTokenDO);  
49 -// }  
50 -//  
51 -// @Override  
52 -// @Transactional  
53 -// public OAuth2AccessTokenRespDTO refreshAccessToken(OAuth2RefreshAccessTokenReqDTO refreshAccessTokenDTO) {  
54 -// OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectById(refreshAccessTokenDTO.getRefreshToken());  
55 -// // 校验刷新令牌是否合法  
56 -// if (refreshTokenDO == null) { // 不存在  
57 -// throw ServiceExceptionUtil.exception(OAUTH2_REFRESH_TOKEN_NOT_FOUND);  
58 -// }  
59 -// if (refreshTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期  
60 -// throw ServiceExceptionUtil.exception(OAUTH_REFRESH_TOKEN_EXPIRED);  
61 -// }  
62 -//  
63 -// // 标记 refreshToken 对应的 accessToken 都不合法  
64 -// // 这块的实现,参考了 Spring Security OAuth2 的代码  
65 -// List<OAuth2AccessTokenDO> accessTokenDOs = oauth2AccessTokenMapper.selectListByRefreshToken(refreshAccessTokenDTO.getRefreshToken());  
66 -// accessTokenDOs.forEach(accessTokenDO -> deleteOAuth2AccessToken(accessTokenDO.getId()));  
67 -//  
68 -// // 创建访问令牌  
69 -// OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(refreshTokenDO, refreshAccessTokenDTO.getCreateIp());  
70 -// // 返回访问令牌  
71 -// return OAuth2Convert.INSTANCE.convert(oauth2AccessTokenDO);  
72 -// }  
73 -//  
74 -// @Override  
75 -// @Transactional  
76 -// public void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO) {  
77 -// // 删除 Access Token  
78 -// OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByUserIdAndUserType(  
79 -// removeTokenDTO.getUserId(), removeTokenDTO.getUserType());  
80 -// if (accessTokenDO != null) {  
81 -// this.deleteOAuth2AccessToken(accessTokenDO.getId());  
82 -// }  
83 -//  
84 -// // 删除 Refresh Token  
85 -// oauth2RefreshTokenMapper.deleteByUserIdAndUserType(removeTokenDTO.getUserId(), removeTokenDTO.getUserType());  
86 -// }  
87 -//  
88 -// private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, String createIp) {  
89 -// OAuth2AccessTokenDO accessToken = new OAuth2AccessTokenDO()  
90 -// .setId(generateAccessToken())  
91 -// .setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType())  
92 -// .setRefreshToken(refreshTokenDO.getId())  
93 -// .setExpiresTime(new Date(System.currentTimeMillis() + systemBizProperties.getAccessTokenExpireTimeMillis()))  
94 -// .setCreateIp(createIp);  
95 -// oauth2AccessTokenMapper.insert(accessToken);  
96 -// return accessToken;  
97 -// }  
98 -//  
99 -// private OAuth2RefreshTokenDO createOAuth2RefreshToken(Integer userId, Integer userType, String createIp) {  
100 -// OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO()  
101 -// .setId(generateRefreshToken())  
102 -// .setUserId(userId).setUserType(userType)  
103 -// .setExpiresTime(new Date(System.currentTimeMillis() + systemBizProperties.getRefreshTokenExpireTimeMillis()))  
104 -// .setCreateIp(createIp);  
105 -// oauth2RefreshTokenMapper.insert(refreshToken);  
106 -// return refreshToken;  
107 -// }  
108 -//  
109 -// private OAuth2AccessTokenDO getOAuth2AccessToken(String accessToken) {  
110 -// // 优先从 Redis 中获取  
111 -// OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenRedisDAO.get(accessToken);  
112 -// if (accessTokenDO != null) {  
113 -// return accessTokenDO;  
114 -// }  
115 -//  
116 -// // 获取不到,从 MySQL 中获取  
117 -// accessTokenDO = oauth2AccessTokenMapper.selectById(accessToken);  
118 -// // 如果在 MySQL 存在,则往 Redis 中写入  
119 -// if (accessTokenDO != null) {  
120 -// oauth2AccessTokenRedisDAO.set(accessTokenDO);  
121 -// }  
122 -// return accessTokenDO;  
123 -// }  
124 -//  
125 -// /**  
126 -// * 删除 accessToken 的 MySQL 与 Redis 的数据  
127 -// *  
128 -// * @param accessToken 访问令牌  
129 -// */  
130 -// private void deleteOAuth2AccessToken(String accessToken) {  
131 -// // 删除 MySQL  
132 -// oauth2AccessTokenMapper.deleteById(accessToken);  
133 -// // 删除 Redis  
134 -// oauth2AccessTokenRedisDAO.delete(accessToken);  
135 -// }  
136 -//  
137 -// private static String generateAccessToken() {  
138 -// return StringUtils.uuid(true);  
139 -// }  
140 -//  
141 -// private static String generateRefreshToken() {  
142 -// return StringUtils.uuid(true);  
143 -// }  
144 -  
145 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java
1 package cn.iocoder.yudao.module.system.service.auth; 1 package cn.iocoder.yudao.module.system.service.auth;
2 2
  3 +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
  4 +
3 /** 5 /**
4 * OAuth2.0 Token Service 接口 6 * OAuth2.0 Token Service 接口
5 * 7 *
6 - * 从功能上,和 Spring Security OAuth 的 JdbcTokenStore 的功能,提供访问令牌、刷新令牌的操作 8 + * 从功能上,和 Spring Security OAuth 的 DefaultTokenServices + JdbcTokenStore 的功能,提供访问令牌、刷新令牌的操作
7 * 9 *
8 * @author 芋道源码 10 * @author 芋道源码
9 */ 11 */
10 public interface OAuth2TokenService { 12 public interface OAuth2TokenService {
11 13
12 -// OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String createIp);  
13 -//  
14 -// OAuth2AccessTokenRespDTO checkAccessToken(String accessToken);  
15 -//  
16 -// OAuth2AccessTokenRespDTO refreshAccessToken(OAuth2RefreshAccessTokenReqDTO refreshAccessTokenDTO);  
17 -//  
18 -// void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO); 14 + /**
  15 + * 创建访问令牌
  16 + * 注意:该流程中,会包含创建刷新令牌的创建
  17 + *
  18 + * 参考 DefaultTokenServices 的 createAccessToken 方法
  19 + *
  20 + * @param userId 用户编号
  21 + * @param userType 用户类型
  22 + * @param clientId 客户端编号
  23 + * @return 访问令牌的信息
  24 + */
  25 + OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, Long clientId);
  26 +
  27 + /**
  28 + * 刷新访问令牌
  29 + *
  30 + * 参考 DefaultTokenServices 的 refreshAccessToken 方法
  31 + *
  32 + * @param refreshToken 刷新令牌
  33 + * @return 访问令牌的信息
  34 + */
  35 + OAuth2AccessTokenDO refreshAccessToken(String refreshToken);
  36 +
  37 + /**
  38 + * 获得访问令牌
  39 + *
  40 + * 参考 DefaultTokenServices 的 getAccessToken 方法
  41 + *
  42 + * @param accessToken 访问令牌
  43 + * @return 访问令牌的信息
  44 + */
  45 + OAuth2AccessTokenDO getAccessToken(String accessToken);
  46 +
  47 + /**
  48 + * 校验访问令牌
  49 + *
  50 + * @param accessToken 访问令牌
  51 + * @return 访问令牌的信息
  52 + */
  53 + OAuth2AccessTokenDO checkAccessToken(String accessToken);
  54 +
  55 + /**
  56 + * 移除访问令牌
  57 + * 注意:该流程中,会移除相关的刷新令牌
  58 + *
  59 + * 参考 DefaultTokenServices 的 revokeToken 方法
  60 + *
  61 + * @param accessToken 刷新令牌
  62 + * @return 是否移除到
  63 + */
  64 + boolean removeAccessToken(String accessToken);
19 65
20 } 66 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.auth;
  2 +
  3 +import cn.hutool.core.util.IdUtil;
  4 +import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
  5 +import cn.iocoder.yudao.framework.common.util.date.DateUtils;
  6 +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
  7 +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
  9 +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO;
  10 +import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2AccessTokenMapper;
  11 +import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2RefreshTokenMapper;
  12 +import cn.iocoder.yudao.module.system.dal.redis.auth.OAuth2AccessTokenRedisDAO;
  13 +import org.springframework.stereotype.Service;
  14 +import org.springframework.transaction.annotation.Transactional;
  15 +
  16 +import javax.annotation.Resource;
  17 +import java.util.Calendar;
  18 +
  19 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  20 +
  21 +/**
  22 + * OAuth2.0 Token Service 实现类
  23 + *
  24 + * @author 芋道源码
  25 + */
  26 +@Service
  27 +public class OAuth2TokenServiceImpl implements OAuth2TokenService {
  28 +
  29 + @Resource
  30 + private OAuth2AccessTokenMapper oauth2AccessTokenMapper;
  31 + @Resource
  32 + private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper;
  33 +
  34 + @Resource
  35 + private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO;
  36 +
  37 + @Resource
  38 + private OAuth2ClientService oauth2ClientService;
  39 +
  40 + @Override
  41 + @Transactional
  42 + public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, Long clientId) {
  43 + OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
  44 + // 创建刷新令牌
  45 + OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO);
  46 + // 创建访问令牌
  47 + OAuth2AccessTokenDO accessTokenDO = createOAuth2AccessToken(refreshTokenDO, clientDO);
  48 + // 记录到 Redis 中
  49 + oauth2AccessTokenRedisDAO.set(accessTokenDO);
  50 + return accessTokenDO;
  51 + }
  52 +
  53 + @Override
  54 + public OAuth2AccessTokenDO refreshAccessToken(String refreshToken) {
  55 + return null;
  56 + }
  57 +
  58 + @Override
  59 + public OAuth2AccessTokenDO getAccessToken(String accessToken) {
  60 + // 优先从 Redis 中获取
  61 + OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenRedisDAO.get(accessToken);
  62 + if (accessTokenDO != null) {
  63 + return accessTokenDO;
  64 + }
  65 +
  66 + // 获取不到,从 MySQL 中获取
  67 + accessTokenDO = oauth2AccessTokenMapper.selectById(accessToken);
  68 + // 如果在 MySQL 存在,则往 Redis 中写入
  69 + if (accessTokenDO != null && !DateUtils.isExpired(accessTokenDO.getExpiresTime())) {
  70 + oauth2AccessTokenRedisDAO.set(accessTokenDO);
  71 + }
  72 + return accessTokenDO;
  73 + }
  74 +
  75 + @Override
  76 + public OAuth2AccessTokenDO checkAccessToken(String accessToken) {
  77 + OAuth2AccessTokenDO accessTokenDO = getAccessToken(accessToken);
  78 + if (accessTokenDO == null) {
  79 + throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "Token 不存在");
  80 + }
  81 + if (DateUtils.isExpired(accessTokenDO.getExpiresTime())) {
  82 + throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "Token 已过期");
  83 + }
  84 + return accessTokenDO;
  85 + }
  86 +
  87 + @Override
  88 + public boolean removeAccessToken(String accessToken) {
  89 + return false;
  90 + }
  91 +
  92 +// @Override
  93 +// @Transactional
  94 +// public OAuth2AccessTokenRespDTO checkAccessToken(String accessToken) {
  95 +// OAuth2AccessTokenDO accessTokenDO = this.getOAuth2AccessToken(accessToken);
  96 +// if (accessTokenDO == null) { // 不存在
  97 +// throw ServiceExceptionUtil.exception(OAUTH2_ACCESS_TOKEN_NOT_FOUND);
  98 +// }
  99 +// if (accessTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期
  100 +// throw ServiceExceptionUtil.exception(OAUTH2_ACCESS_TOKEN_TOKEN_EXPIRED);
  101 +// }
  102 +// // 返回访问令牌
  103 +// return OAuth2Convert.INSTANCE.convert(accessTokenDO);
  104 +// }
  105 +
  106 +// @Override
  107 +// @Transactional
  108 +// public OAuth2AccessTokenRespDTO refreshAccessToken(OAuth2RefreshAccessTokenReqDTO refreshAccessTokenDTO) {
  109 +// OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectById(refreshAccessTokenDTO.getRefreshToken());
  110 +// // 校验刷新令牌是否合法
  111 +// if (refreshTokenDO == null) { // 不存在
  112 +// throw ServiceExceptionUtil.exception(OAUTH2_REFRESH_TOKEN_NOT_FOUND);
  113 +// }
  114 +// if (refreshTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期
  115 +// throw ServiceExceptionUtil.exception(OAUTH_REFRESH_TOKEN_EXPIRED);
  116 +// }
  117 +//
  118 +// // 标记 refreshToken 对应的 accessToken 都不合法
  119 +// // 这块的实现,参考了 Spring Security OAuth2 的代码
  120 +// List<OAuth2AccessTokenDO> accessTokenDOs = oauth2AccessTokenMapper.selectListByRefreshToken(refreshAccessTokenDTO.getRefreshToken());
  121 +// accessTokenDOs.forEach(accessTokenDO -> deleteOAuth2AccessToken(accessTokenDO.getId()));
  122 +//
  123 +// // 创建访问令牌
  124 +// OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(refreshTokenDO, refreshAccessTokenDTO.getCreateIp());
  125 +// // 返回访问令牌
  126 +// return OAuth2Convert.INSTANCE.convert(oauth2AccessTokenDO);
  127 +// }
  128 +//
  129 +// @Override
  130 +// @Transactional
  131 +// public void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO) {
  132 +// // 删除 Access Token
  133 +// OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByUserIdAndUserType(
  134 +// removeTokenDTO.getUserId(), removeTokenDTO.getUserType());
  135 +// if (accessTokenDO != null) {
  136 +// this.deleteOAuth2AccessToken(accessTokenDO.getId());
  137 +// }
  138 +//
  139 +// // 删除 Refresh Token
  140 +// oauth2RefreshTokenMapper.deleteByUserIdAndUserType(removeTokenDTO.getUserId(), removeTokenDTO.getUserType());
  141 +// }
  142 +
  143 + private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
  144 + OAuth2AccessTokenDO accessToken = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
  145 + .setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType()).setClientId(clientDO.getId())
  146 + .setRefreshToken(refreshTokenDO.getRefreshToken())
  147 + .setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getAccessTokenValiditySeconds()));
  148 + accessToken.setTenantId(TenantContextHolder.getTenantId()); // 手动设置租户编号,避免缓存到 Redis 的时候,无对应的租户编号
  149 + oauth2AccessTokenMapper.insert(accessToken);
  150 + return accessToken;
  151 + }
  152 +
  153 + private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long userId, Integer userType, OAuth2ClientDO clientDO) {
  154 + OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setRefreshToken(generateRefreshToken())
  155 + .setUserId(userId).setUserType(userType).setClientId(clientDO.getId())
  156 + .setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getRefreshTokenValiditySeconds()));
  157 + oauth2RefreshTokenMapper.insert(refreshToken);
  158 + return refreshToken;
  159 + }
  160 +
  161 +
  162 +// /**
  163 +// * 删除 accessToken 的 MySQL 与 Redis 的数据
  164 +// *
  165 +// * @param accessToken 访问令牌
  166 +// */
  167 +// private void deleteOAuth2AccessToken(String accessToken) {
  168 +// // 删除 MySQL
  169 +// oauth2AccessTokenMapper.deleteById(accessToken);
  170 +// // 删除 Redis
  171 +// oauth2AccessTokenRedisDAO.delete(accessToken);
  172 +// }
  173 +//
  174 + private static String generateAccessToken() {
  175 + return IdUtil.fastSimpleUUID();
  176 + }
  177 +
  178 + private static String generateRefreshToken() {
  179 + return IdUtil.fastSimpleUUID();
  180 + }
  181 +
  182 +}
yudao-server/pom.xml
@@ -21,11 +21,11 @@ @@ -21,11 +21,11 @@
21 <url>https://github.com/YunaiV/ruoyi-vue-pro</url> 21 <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
22 22
23 <dependencies> 23 <dependencies>
24 - <dependency>  
25 - <groupId>cn.iocoder.boot</groupId>  
26 - <artifactId>yudao-module-member-biz</artifactId>  
27 - <version>${revision}</version>  
28 - </dependency> 24 +<!-- <dependency>-->
  25 +<!-- <groupId>cn.iocoder.boot</groupId>-->
  26 +<!-- <artifactId>yudao-module-member-biz</artifactId>-->
  27 +<!-- <version>${revision}</version>-->
  28 +<!-- </dependency>-->
29 <dependency> 29 <dependency>
30 <groupId>cn.iocoder.boot</groupId> 30 <groupId>cn.iocoder.boot</groupId>
31 <artifactId>yudao-module-system-biz</artifactId> 31 <artifactId>yudao-module-system-biz</artifactId>