diff --git a/yudao-framework/yudao-spring-boot-starter-security/pom.xml b/yudao-framework/yudao-spring-boot-starter-security/pom.xml
index ba33598c5..4e32a6c7c 100644
--- a/yudao-framework/yudao-spring-boot-starter-security/pom.xml
+++ b/yudao-framework/yudao-spring-boot-starter-security/pom.xml
@@ -44,18 +44,11 @@
spring-boot-starter-security
-
+
- org.activiti
- activiti-engine
- 7.1.0.M6
-
-
- *
- *
-
-
- true
+ cn.iocoder.boot
+ yudao-module-system-api
+ ${revision}
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java
index 087edc32f..4933969bc 100644
--- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java
+++ b/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
import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl;
import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl;
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
+import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@@ -72,8 +73,9 @@ public class YudaoSecurityAutoConfiguration {
* Token 认证过滤器 Bean
*/
@Bean
- public TokenAuthenticationFilter authenticationTokenFilter(GlobalExceptionHandler globalExceptionHandler) {
- return new TokenAuthenticationFilter(securityProperties, globalExceptionHandler);
+ public TokenAuthenticationFilter authenticationTokenFilter(GlobalExceptionHandler globalExceptionHandler,
+ OAuth2TokenApi oauth2TokenApi) {
+ return new TokenAuthenticationFilter(securityProperties, globalExceptionHandler, oauth2TokenApi);
}
/**
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java
index b27863f60..75caa59ff 100644
--- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java
+++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.security.core.filter;
+import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
@@ -8,7 +9,10 @@ import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
+import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi;
+import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO;
import lombok.RequiredArgsConstructor;
+import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
@@ -30,6 +34,8 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
private final GlobalExceptionHandler globalExceptionHandler;
+ private final OAuth2TokenApi oauth2TokenApi;
+
@Override
@SuppressWarnings("NullableProblems")
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
@@ -39,11 +45,21 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
Integer userType = WebFrameworkUtils.getLoginUserType(request);
try {
// 验证 token 有效性
- LoginUser loginUser = null; // TODO 芋艿:待实现
+ OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token);
+ if (accessToken != null && ObjectUtil.notEqual(accessToken.getUserType(), userType)) { // 用户类型不匹配,无权限
+ throw new AccessDeniedException("错误的用户类型");
+ }
+ LoginUser loginUser = null;
+ if (accessToken != null) { // 如果不为空,说明认证通过,则转换成登录用户
+ loginUser = new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
+ .setTenantId(accessToken.getTenantId());
+ }
+
// 模拟 Login 功能,方便日常开发调试
if (loginUser == null) {
loginUser = mockLoginUser(request, token, userType);
}
+
// 设置当前用户
if (loginUser != null) {
SecurityFrameworkUtils.setLoginUser(loginUser, request);
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthFrameworkService.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthFrameworkService.java
deleted file mode 100644
index 3afef7acf..000000000
--- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthFrameworkService.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package cn.iocoder.yudao.framework.security.core.service;
-
-import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
-import cn.iocoder.yudao.framework.security.core.LoginUser;
-import org.springframework.security.core.userdetails.UserDetailsService;
-
-/**
- * Security 框架 Auth Service 接口,定义不同用户类型的 {@link UserTypeEnum} 需要实现的方法
- *
- * @author 芋道源码
- */
-public interface SecurityAuthFrameworkService extends UserDetailsService {
-
- /**
- * 校验 token 的有效性,并获取用户信息
- * 通过后,刷新 token 的过期时间
- *
- * @param token token
- * @return 用户信息
- */
- LoginUser verifyTokenAndRefresh(String token);
-
- /**
- * 获得用户类型。每个用户类型,对应一个 SecurityAuthFrameworkService 实现类。
- *
- * @return 用户类型
- */
- UserTypeEnum getUserType();
-
-}
diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java
index b3a7f7ecb..f5ac676fa 100644
--- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java
+++ b/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 {
}
// 1. 优先,从 Attribute 中获取
Integer userType = (Integer) request.getAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE);
- if (userType == null) {
- return null;
+ if (userType != null) {
+ return userType;
}
// 2. 其次,基于 URL 前缀的约定
if (request.getRequestURI().startsWith(properties.getAdminApi().getPrefix())) {
diff --git a/yudao-module-system/yudao-module-system-api/pom.xml b/yudao-module-system/yudao-module-system-api/pom.xml
index 81b129916..655db05a7 100644
--- a/yudao-module-system/yudao-module-system-api/pom.xml
+++ b/yudao-module-system/yudao-module-system-api/pom.xml
@@ -29,13 +29,6 @@
true
-
-
- cn.iocoder.boot
- yudao-spring-boot-starter-security
- true
-
-
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2Api.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2Api.java
deleted file mode 100644
index 973466e55..000000000
--- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2Api.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package cn.iocoder.yudao.module.system.api.auth;
-
-/**
- * OAuth2.0 API 接口
- *
- * @author 芋道源码
- */
-public interface OAuth2Api {
-
-
-
-}
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApi.java
new file mode 100644
index 000000000..44c9079f5
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApi.java
@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.system.api.auth;
+
+import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO;
+import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO;
+import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO;
+
+import javax.validation.Valid;
+
+/**
+ * OAuth2.0 Token API 接口
+ *
+ * @author 芋道源码
+ */
+public interface OAuth2TokenApi {
+
+ OAuth2AccessTokenRespDTO createAccessToken(@Valid OAuth2AccessTokenCreateReqDTO reqDTO);
+
+ OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken);
+
+// void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO);
+
+}
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/UserSessionApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/UserSessionApi.java
deleted file mode 100644
index e7fdcb982..000000000
--- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/UserSessionApi.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package cn.iocoder.yudao.module.system.api.auth;
-
-import cn.iocoder.yudao.framework.security.core.LoginUser;
-
-import javax.validation.constraints.NotEmpty;
-import javax.validation.constraints.NotNull;
-
-/**
- * 在线用户 Session API 接口
- *
- * @author 芋道源码
- */
-public interface UserSessionApi {
-
- /**
- * 创建在线用户 Session
- *
- * @param loginUser 登录用户
- * @param userIp 用户 IP
- * @param userAgent 用户 UA
- * @return Token 令牌
- */
- String createUserSession(@NotNull(message = "登录用户不能为空") LoginUser loginUser, String userIp, String userAgent);
-
- /**
- * 刷新在线用户 Session 的更新时间
- *
- * @param token Token 令牌
- * @param loginUser 登录用户
- */
- void refreshUserSession(@NotEmpty(message = "Token 令牌不能为空") String token,
- @NotNull(message = "登录用户不能为空") LoginUser loginUser);
-
- /**
- * 删除在线用户 Session
- *
- * @param token Token 令牌
- */
- void deleteUserSession(String token);
-
- /**
- * 获得 Token 令牌对应的在线用户
- *
- * @param token Token 令牌
- * @return 在线用户
- */
- LoginUser getLoginUser(String token);
-
- /**
- * 获得 Session 超时时间,单位:毫秒
- *
- * @return 超时时间
- */
- Long getSessionTimeoutMillis();
-
-}
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCheckRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCheckRespDTO.java
new file mode 100644
index 000000000..5acdc1ea8
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCheckRespDTO.java
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.system.api.auth.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * OAuth2.0 访问令牌的校验 Response DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class OAuth2AccessTokenCheckRespDTO implements Serializable {
+
+ /**
+ * 用户编号
+ */
+ private Long userId;
+ /**
+ * 用户类型
+ */
+ private Integer userType;
+ /**
+ * 租户编号
+ */
+ private Long tenantId;
+
+}
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCreateReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCreateReqDTO.java
new file mode 100644
index 000000000..bf814f888
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCreateReqDTO.java
@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.module.system.api.auth.dto;
+
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * OAuth2.0 访问令牌创建 Request DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+@Accessors(chain = true)
+public class OAuth2AccessTokenCreateReqDTO implements Serializable {
+
+ /**
+ * 用户编号
+ */
+ @NotNull(message = "用户编号不能为空")
+ private Integer userId;
+ /**
+ * 用户类型
+ */
+ @NotNull(message = "用户类型不能为空")
+ @InEnum(value = UserTypeEnum.class, message = "用户类型必须是 {value}")
+ private Integer userType;
+ /**
+ * 客户端编号
+ */
+ @NotNull(message = "客户端编号不能为空")
+ private Long clientId;
+
+}
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenRespDTO.java
new file mode 100644
index 000000000..429fb071d
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenRespDTO.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.system.api.auth.dto;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * OAuth2.0 访问令牌的信息 Response DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+@Accessors(chain = true)
+public class OAuth2AccessTokenRespDTO implements Serializable {
+
+ /**
+ * 访问令牌
+ */
+ private String accessToken;
+ /**
+ * 刷新令牌
+ */
+ private String refreshToken;
+ /**
+ * 用户编号
+ */
+ private Integer userId;
+ /**
+ * 用户类型
+ */
+ private Integer userType;
+ /**
+ * 过期时间
+ */
+ private Date expiresTime;
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java
new file mode 100644
index 000000000..e8537b0dc
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java
@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.system.api.auth;
+
+import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO;
+import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO;
+import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO;
+import cn.iocoder.yudao.module.system.convert.auth.UserSessionConvert;
+import cn.iocoder.yudao.module.system.service.auth.OAuth2TokenService;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+/**
+ * OAuth2.0 Token API 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+public class OAuth2TokenApiImpl implements OAuth2TokenApi {
+
+ @Resource
+ private OAuth2TokenService oauth2TokenService;
+
+ @Override
+ public OAuth2AccessTokenRespDTO createAccessToken(OAuth2AccessTokenCreateReqDTO reqDTO) {
+ return null;
+ }
+
+ @Override
+ public OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken) {
+ return UserSessionConvert.INSTANCE.convert(oauth2TokenService.checkAccessToken(accessToken));
+ }
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/UserSessionApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/UserSessionApiImpl.java
deleted file mode 100644
index 63226c125..000000000
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/UserSessionApiImpl.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package cn.iocoder.yudao.module.system.api.auth;
-
-import cn.iocoder.yudao.framework.security.core.LoginUser;
-import cn.iocoder.yudao.module.system.service.auth.UserSessionService;
-import org.springframework.stereotype.Service;
-import org.springframework.validation.annotation.Validated;
-
-import javax.annotation.Resource;
-
-/**
- * 在线用户 Session API 实现类
- *
- * @author 芋道源码
- */
-@Service
-@Validated
-public class UserSessionApiImpl implements UserSessionApi {
-
- @Resource
- private UserSessionService userSessionService;
-
- @Override
- public String createUserSession(LoginUser loginUser, String userIp, String userAgent) {
- return userSessionService.createUserSession(loginUser, userIp, userAgent);
- }
-
- @Override
- public void refreshUserSession(String token, LoginUser loginUser) {
- userSessionService.refreshUserSession(token, loginUser);
- }
-
- @Override
- public void deleteUserSession(String token) {
- userSessionService.deleteUserSession(token);
- }
-
- @Override
- public LoginUser getLoginUser(String token) {
- return userSessionService.getLoginUser(token);
- }
-
- @Override
- public Long getSessionTimeoutMillis() {
- return userSessionService.getSessionTimeoutMillis();
- }
-
-}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/UserSessionConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/UserSessionConvert.java
index d30dfdcbb..9138795fd 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/UserSessionConvert.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/UserSessionConvert.java
@@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.system.convert.auth;
+import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO;
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageItemRespVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@@ -12,4 +14,6 @@ public interface UserSessionConvert {
UserSessionPageItemRespVO convert(UserSessionDO session);
+ OAuth2AccessTokenCheckRespDTO convert(OAuth2AccessTokenDO bean);
+
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2AccessTokenDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2AccessTokenDO.java
index b8a63c1e1..aaaa8152c 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2AccessTokenDO.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2AccessTokenDO.java
@@ -1,7 +1,8 @@
package cn.iocoder.yudao.module.system.dal.dataobject.auth;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
+import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -12,22 +13,30 @@ import java.util.Date;
/**
* OAuth2 访问令牌 DO
*
+ * 如下字段,暂时未使用,暂时不支持:
+ * user_name、authentication(用户信息)
+ *
* @author 芋道源码
*/
@TableName("system_oauth2_access_token")
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
-public class OAuth2AccessTokenDO extends BaseDO {
+public class OAuth2AccessTokenDO extends TenantBaseDO {
/**
* 编号,数据库递增
*/
+ @TableId
private Long id;
/**
* 访问令牌
*/
private String accessToken;
+ /**
+ * 刷新令牌
+ */
+ private String refreshToken;
/**
* 用户编号
*/
@@ -39,11 +48,11 @@ public class OAuth2AccessTokenDO extends BaseDO {
*/
private Integer userType;
/**
- * 应用编号
+ * 客户端编号
*
- * 关联 {@link OAuth2ApplicationDO#getId()}
+ * 关联 {@link OAuth2ClientDO#getId()}
*/
- private Long applicationId;
+ private Long clientId;
/**
* 过期时间
*/
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2ApplicationDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2ClientDO.java
similarity index 68%
rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2ApplicationDO.java
rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2ClientDO.java
index 20a2b6087..88db45149 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2ApplicationDO.java
+++ b/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;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -12,12 +13,8 @@ import java.util.List;
/**
* OAuth2 客户端 DO
*
- * 为什么不使用 Client 作为表名?
- * 1. clientId 字段被占用,导致表的 id 无法有合适的缩写
- * 2. 大多数 Github、Gitee 等平台,都会习惯称为第三方接入应用
- *
* 如下字段,考虑到使用相对不是很高频,主要是一些开关,暂时不支持:
- * authorized_grant_types、authorities、access_token_validity、refresh_token_validity、additional_information、autoapprove、resource_ids、scope
+ * authorized_grant_types、authorities、additional_information、autoapprove、resource_ids、scope
*
* @author 芋道源码
*/
@@ -25,24 +22,19 @@ import java.util.List;
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
-public class OAuth2ApplicationDO extends BaseDO {
+public class OAuth2ClientDO extends BaseDO {
- /**
- * 编号,数据库递增
- */
- private Long id;
/**
* 客户端编号
+ *
+ * 由于 SQL Server 在存储 String 主键有点问题,所以暂时使用 Long 类型
*/
- private String clientId;
+ @TableId
+ private Long id;
/**
* 客户端密钥
*/
- private String clientSecret;
- /**
- * 可重定向的 URI 地址
- */
- private List redirectUris;
+ private String secret;
/**
* 应用名
*/
@@ -61,5 +53,17 @@ public class OAuth2ApplicationDO extends BaseDO {
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
+ /**
+ * 访问令牌的有效期
+ */
+ private Integer accessTokenValiditySeconds;
+ /**
+ * 刷新令牌的有效期
+ */
+ private Integer refreshTokenValiditySeconds;
+ /**
+ * 可重定向的 URI 地址
+ */
+ private List redirectUris;
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2CodeDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2CodeDO.java
index 293bd0a41..7a851b01d 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2CodeDO.java
+++ b/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 {
*/
private Integer userType;
/**
- * 应用编号
+ * 客户端编号
*
- * 关联 {@link OAuth2ApplicationDO#getId()}
+ * 关联 {@link OAuth2ClientDO#getId()}
*/
- private Long applicationId;
+ private Long clientId;
/**
* 刷新令牌
*
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2RefreshTokenDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2RefreshTokenDO.java
index 42824f297..1f0aa3032 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2RefreshTokenDO.java
+++ b/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;
/**
* OAuth2 刷新令牌
*
+ * @author 芋道源码
*/
@TableName("system_oauth2_refresh_token")
@Data
@@ -30,20 +31,22 @@ public class OAuth2RefreshTokenDO extends BaseDO {
/**
* 用户编号
*/
- private Integer userId;
+ private Long userId;
/**
* 用户类型
*
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
+ /**
+ * 客户端编号
+ *
+ * 关联 {@link OAuth2ClientDO#getId()}
+ */
+ private Long clientId;
/**
* 过期时间
*/
private Date expiresTime;
- /**
- * 创建 IP
- */
- private String createIp;
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java
new file mode 100644
index 000000000..76d04d71a
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java
@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.system.dal.mysql.auth;
+
+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;
+
+@Mapper
+public interface OAuth2AccessTokenMapper extends BaseMapperX {
+
+ default OAuth2AccessTokenDO selectByAccessToken(String accessToken) {
+ return selectOne(OAuth2AccessTokenDO::getAccessToken, accessToken);
+ }
+
+// default OAuth2AccessTokenDO selectByUserIdAndUserType(Integer userId, Integer userType) {
+// return selectOne(new QueryWrapper()
+// .eq("user_id", userId).eq("user_type", userType));
+// }
+//
+// default int deleteByUserIdAndUserType(Integer userId, Integer userType) {
+// return delete(new QueryWrapper()
+// .eq("user_id", userId).eq("user_type", userType));
+// }
+//
+// default int deleteByRefreshToken(String refreshToken) {
+// return delete(new QueryWrapper().eq("refresh_token", refreshToken));
+// }
+//
+// default List selectListByRefreshToken(String refreshToken) {
+// return selectList(new QueryWrapper().eq("refresh_token", refreshToken));
+// }
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2RefreshTokenMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2RefreshTokenMapper.java
new file mode 100644
index 000000000..e5206d242
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2RefreshTokenMapper.java
@@ -0,0 +1,16 @@
+package cn.iocoder.yudao.module.system.dal.mysql.auth;
+
+import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface OAuth2RefreshTokenMapper extends BaseMapper {
+
+ default int deleteByUserIdAndUserType(Integer userId, Integer userType) {
+ return delete(new QueryWrapper()
+ .eq("user_id", userId).eq("user_type", userType));
+ }
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java
index b17c6fb4d..f70b546fc 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java
+++ b/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;
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
import cn.iocoder.yudao.framework.security.core.LoginUser;
+import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
import java.time.Duration;
@@ -22,6 +23,10 @@ public interface RedisKeyConstants {
"login_user:%s", // 参数为 token 令牌
STRING, LoginUser.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC);
+ RedisKeyDefine OAUTH2_ACCESS_TOKEN = new RedisKeyDefine("访问令牌的缓存",
+ "oauth2_access_token:%s", // 参数为访问令牌 token
+ STRING, OAuth2AccessTokenDO.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC);
+
RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交登陆的 state", // 注意,它是被 JustAuth 的 justauth.type.prefix 使用到
"social_auth_state:%s", // 参数为 state
STRING, String.class, Duration.ofHours(24)); // 值为 state
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/auth/OAuth2AccessTokenRedisDAO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/auth/OAuth2AccessTokenRedisDAO.java
new file mode 100644
index 000000000..cb7fba538
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/auth/OAuth2AccessTokenRedisDAO.java
@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.system.dal.redis.auth;
+
+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.concurrent.TimeUnit;
+
+import static cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN;
+
+/**
+ * {@link OAuth2AccessTokenDO} 的 RedisDAO
+ *
+ * @author 芋道源码
+ */
+@Repository
+public class OAuth2AccessTokenRedisDAO {
+
+ @Resource
+ private StringRedisTemplate stringRedisTemplate;
+
+ public OAuth2AccessTokenDO get(String accessToken) {
+ String redisKey = formatKey(accessToken);
+ return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), OAuth2AccessTokenDO.class);
+ }
+
+ public void set(OAuth2AccessTokenDO accessTokenDO) {
+ String redisKey = formatKey(accessTokenDO.getAccessToken());
+ // 清理多余字段,避免缓存
+ accessTokenDO.setUpdater(null).setUpdateTime(null).setCreateTime(null).setCreator(null).setDeleted(null);
+ stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(accessTokenDO),
+ accessTokenDO.getExpiresTime().getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
+ }
+
+ public void delete(String accessToken) {
+ String redisKey = formatKey(accessToken);
+ stringRedisTemplate.delete(redisKey);
+ }
+
+ private static String formatKey(String accessToken) {
+ return String.format(OAUTH2_ACCESS_TOKEN.getKeyTemplate(), accessToken);
+ }
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
index d049d9edb..69fd38e07 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
+++ b/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 {
@Resource
private UserSessionService userSessionService;
@Resource
+ private OAuth2TokenService oauth2TokenService;
+ @Resource
private SocialUserService socialUserService;
@Resource
@@ -207,8 +209,12 @@ public class AdminAuthServiceImpl implements AdminAuthService {
LoginLogTypeEnum logType, String userIp, String userAgent) {
// 插入登陆日志
createLoginLog(loginUser.getId(), username, logType, LoginResultEnum.SUCCESS);
- // 缓存登录用户到 Redis 中,返回 Token 令牌
- return userSessionService.createUserSession(loginUser, userIp, userAgent);
+ // 创建访问令牌
+ // TODO userIp、userAgent
+ // TODO clientId
+ return oauth2TokenService.createAccessToken(loginUser.getId(), getUserType().getValue(), 1L)
+ .getAccessToken();
+// return userSessionService.createUserSession(loginUser, userIp, userAgent);
}
@Override
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientService.java
new file mode 100644
index 000000000..71211f410
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientService.java
@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.system.service.auth;
+
+import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
+
+/**
+ * OAuth2.0 Client Service 接口
+ *
+ * 从功能上,和 JdbcClientDetailsService 的功能,提供客户端的操作
+ *
+ * @author 芋道源码
+ */
+public interface OAuth2ClientService {
+
+ /**
+ * 从缓存中,校验客户端是否合法
+ *
+ * @param id 客户端编号
+ * @return 客户端
+ */
+ OAuth2ClientDO validOAuthClientFromCache(Long id);
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImpl.java
new file mode 100644
index 000000000..9416eb353
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImpl.java
@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.module.system.service.auth;
+
+import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
+import org.springframework.stereotype.Service;
+
+/**
+ * OAuth2.0 Client Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+public class OAuth2ClientServiceImpl implements OAuth2ClientService {
+
+ @Override
+ public OAuth2ClientDO validOAuthClientFromCache(Long id) {
+ return new OAuth2ClientDO().setId(id)
+ .setAccessTokenValiditySeconds(60 * 30)
+ .setRefreshTokenValiditySeconds(60 * 60 * 24 * 30);
+ }
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ServiceImpl.java
deleted file mode 100644
index da08b0a57..000000000
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ServiceImpl.java
+++ /dev/null
@@ -1,145 +0,0 @@
-package cn.iocoder.yudao.module.system.service.auth;
-
-import org.springframework.stereotype.Service;
-
-/**
- * OAuth2.0 Service 实现类
- *
- *
- *
- * @author 芋道源码
- */
-@Service
-public class OAuth2ServiceImpl implements OAuth2TokenService {
-
-// @Autowired
-// private SystemBizProperties systemBizProperties;
-//
-// @Autowired
-// private OAuth2AccessTokenMapper oauth2AccessTokenMapper;
-// @Autowired
-// private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper;
-//
-// @Autowired
-// private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO;
-//
-// @Override
-// @Transactional
-// public OAuth2AccessTokenRespDTO createAccessToken(OAuth2CreateAccessTokenReqDTO createAccessTokenDTO) {
-// // 创建刷新令牌 + 访问令牌
-// OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(createAccessTokenDTO.getUserId(),
-// createAccessTokenDTO.getUserType(), createAccessTokenDTO.getCreateIp());
-// OAuth2AccessTokenDO accessTokenDO = createOAuth2AccessToken(refreshTokenDO, createAccessTokenDTO.getCreateIp());
-// // 返回访问令牌
-// return OAuth2Convert.INSTANCE.convert(accessTokenDO);
-// }
-//
-// @Override
-// @Transactional
-// public OAuth2AccessTokenRespDTO checkAccessToken(String accessToken) {
-// OAuth2AccessTokenDO accessTokenDO = this.getOAuth2AccessToken(accessToken);
-// if (accessTokenDO == null) { // 不存在
-// throw ServiceExceptionUtil.exception(OAUTH2_ACCESS_TOKEN_NOT_FOUND);
-// }
-// if (accessTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期
-// throw ServiceExceptionUtil.exception(OAUTH2_ACCESS_TOKEN_TOKEN_EXPIRED);
-// }
-// // 返回访问令牌
-// return OAuth2Convert.INSTANCE.convert(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 accessTokenDOs = oauth2AccessTokenMapper.selectListByRefreshToken(refreshAccessTokenDTO.getRefreshToken());
-// accessTokenDOs.forEach(accessTokenDO -> deleteOAuth2AccessToken(accessTokenDO.getId()));
-//
-// // 创建访问令牌
-// OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(refreshTokenDO, refreshAccessTokenDTO.getCreateIp());
-// // 返回访问令牌
-// return OAuth2Convert.INSTANCE.convert(oauth2AccessTokenDO);
-// }
-//
-// @Override
-// @Transactional
-// public void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO) {
-// // 删除 Access Token
-// OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByUserIdAndUserType(
-// removeTokenDTO.getUserId(), removeTokenDTO.getUserType());
-// if (accessTokenDO != null) {
-// this.deleteOAuth2AccessToken(accessTokenDO.getId());
-// }
-//
-// // 删除 Refresh Token
-// oauth2RefreshTokenMapper.deleteByUserIdAndUserType(removeTokenDTO.getUserId(), removeTokenDTO.getUserType());
-// }
-//
-// private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, String createIp) {
-// OAuth2AccessTokenDO accessToken = new OAuth2AccessTokenDO()
-// .setId(generateAccessToken())
-// .setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType())
-// .setRefreshToken(refreshTokenDO.getId())
-// .setExpiresTime(new Date(System.currentTimeMillis() + systemBizProperties.getAccessTokenExpireTimeMillis()))
-// .setCreateIp(createIp);
-// oauth2AccessTokenMapper.insert(accessToken);
-// return accessToken;
-// }
-//
-// private OAuth2RefreshTokenDO createOAuth2RefreshToken(Integer userId, Integer userType, String createIp) {
-// OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO()
-// .setId(generateRefreshToken())
-// .setUserId(userId).setUserType(userType)
-// .setExpiresTime(new Date(System.currentTimeMillis() + systemBizProperties.getRefreshTokenExpireTimeMillis()))
-// .setCreateIp(createIp);
-// oauth2RefreshTokenMapper.insert(refreshToken);
-// return refreshToken;
-// }
-//
-// private OAuth2AccessTokenDO getOAuth2AccessToken(String accessToken) {
-// // 优先从 Redis 中获取
-// OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenRedisDAO.get(accessToken);
-// if (accessTokenDO != null) {
-// return accessTokenDO;
-// }
-//
-// // 获取不到,从 MySQL 中获取
-// accessTokenDO = oauth2AccessTokenMapper.selectById(accessToken);
-// // 如果在 MySQL 存在,则往 Redis 中写入
-// if (accessTokenDO != null) {
-// oauth2AccessTokenRedisDAO.set(accessTokenDO);
-// }
-// return accessTokenDO;
-// }
-//
-// /**
-// * 删除 accessToken 的 MySQL 与 Redis 的数据
-// *
-// * @param accessToken 访问令牌
-// */
-// private void deleteOAuth2AccessToken(String accessToken) {
-// // 删除 MySQL
-// oauth2AccessTokenMapper.deleteById(accessToken);
-// // 删除 Redis
-// oauth2AccessTokenRedisDAO.delete(accessToken);
-// }
-//
-// private static String generateAccessToken() {
-// return StringUtils.uuid(true);
-// }
-//
-// private static String generateRefreshToken() {
-// return StringUtils.uuid(true);
-// }
-
-}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java
index 7e6bed380..65e2ddb6f 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java
@@ -1,20 +1,66 @@
package cn.iocoder.yudao.module.system.service.auth;
+import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
+
/**
* OAuth2.0 Token Service 接口
*
- * 从功能上,和 Spring Security OAuth 的 JdbcTokenStore 的功能,提供访问令牌、刷新令牌的操作
+ * 从功能上,和 Spring Security OAuth 的 DefaultTokenServices + JdbcTokenStore 的功能,提供访问令牌、刷新令牌的操作
*
* @author 芋道源码
*/
public interface OAuth2TokenService {
-// OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String createIp);
-//
-// OAuth2AccessTokenRespDTO checkAccessToken(String accessToken);
-//
-// OAuth2AccessTokenRespDTO refreshAccessToken(OAuth2RefreshAccessTokenReqDTO refreshAccessTokenDTO);
-//
-// void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO);
+ /**
+ * 创建访问令牌
+ * 注意:该流程中,会包含创建刷新令牌的创建
+ *
+ * 参考 DefaultTokenServices 的 createAccessToken 方法
+ *
+ * @param userId 用户编号
+ * @param userType 用户类型
+ * @param clientId 客户端编号
+ * @return 访问令牌的信息
+ */
+ OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, Long clientId);
+
+ /**
+ * 刷新访问令牌
+ *
+ * 参考 DefaultTokenServices 的 refreshAccessToken 方法
+ *
+ * @param refreshToken 刷新令牌
+ * @return 访问令牌的信息
+ */
+ OAuth2AccessTokenDO refreshAccessToken(String refreshToken);
+
+ /**
+ * 获得访问令牌
+ *
+ * 参考 DefaultTokenServices 的 getAccessToken 方法
+ *
+ * @param accessToken 访问令牌
+ * @return 访问令牌的信息
+ */
+ OAuth2AccessTokenDO getAccessToken(String accessToken);
+
+ /**
+ * 校验访问令牌
+ *
+ * @param accessToken 访问令牌
+ * @return 访问令牌的信息
+ */
+ OAuth2AccessTokenDO checkAccessToken(String accessToken);
+
+ /**
+ * 移除访问令牌
+ * 注意:该流程中,会移除相关的刷新令牌
+ *
+ * 参考 DefaultTokenServices 的 revokeToken 方法
+ *
+ * @param accessToken 刷新令牌
+ * @return 是否移除到
+ */
+ boolean removeAccessToken(String accessToken);
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java
new file mode 100644
index 000000000..a9940c482
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java
@@ -0,0 +1,182 @@
+package cn.iocoder.yudao.module.system.service.auth;
+
+import cn.hutool.core.util.IdUtil;
+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;
+import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO;
+import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2AccessTokenMapper;
+import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2RefreshTokenMapper;
+import cn.iocoder.yudao.module.system.dal.redis.auth.OAuth2AccessTokenRedisDAO;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.util.Calendar;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+
+/**
+ * OAuth2.0 Token Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+public class OAuth2TokenServiceImpl implements OAuth2TokenService {
+
+ @Resource
+ private OAuth2AccessTokenMapper oauth2AccessTokenMapper;
+ @Resource
+ private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper;
+
+ @Resource
+ private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO;
+
+ @Resource
+ private OAuth2ClientService oauth2ClientService;
+
+ @Override
+ @Transactional
+ public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, Long clientId) {
+ OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
+ // 创建刷新令牌
+ OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO);
+ // 创建访问令牌
+ OAuth2AccessTokenDO accessTokenDO = createOAuth2AccessToken(refreshTokenDO, clientDO);
+ // 记录到 Redis 中
+ oauth2AccessTokenRedisDAO.set(accessTokenDO);
+ return accessTokenDO;
+ }
+
+ @Override
+ public OAuth2AccessTokenDO refreshAccessToken(String refreshToken) {
+ return null;
+ }
+
+ @Override
+ public OAuth2AccessTokenDO getAccessToken(String accessToken) {
+ // 优先从 Redis 中获取
+ OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenRedisDAO.get(accessToken);
+ if (accessTokenDO != null) {
+ return accessTokenDO;
+ }
+
+ // 获取不到,从 MySQL 中获取
+ accessTokenDO = oauth2AccessTokenMapper.selectById(accessToken);
+ // 如果在 MySQL 存在,则往 Redis 中写入
+ if (accessTokenDO != null && !DateUtils.isExpired(accessTokenDO.getExpiresTime())) {
+ oauth2AccessTokenRedisDAO.set(accessTokenDO);
+ }
+ return accessTokenDO;
+ }
+
+ @Override
+ public OAuth2AccessTokenDO checkAccessToken(String accessToken) {
+ OAuth2AccessTokenDO accessTokenDO = getAccessToken(accessToken);
+ if (accessTokenDO == null) {
+ throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "Token 不存在");
+ }
+ if (DateUtils.isExpired(accessTokenDO.getExpiresTime())) {
+ throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "Token 已过期");
+ }
+ return accessTokenDO;
+ }
+
+ @Override
+ public boolean removeAccessToken(String accessToken) {
+ return false;
+ }
+
+// @Override
+// @Transactional
+// public OAuth2AccessTokenRespDTO checkAccessToken(String accessToken) {
+// OAuth2AccessTokenDO accessTokenDO = this.getOAuth2AccessToken(accessToken);
+// if (accessTokenDO == null) { // 不存在
+// throw ServiceExceptionUtil.exception(OAUTH2_ACCESS_TOKEN_NOT_FOUND);
+// }
+// if (accessTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期
+// throw ServiceExceptionUtil.exception(OAUTH2_ACCESS_TOKEN_TOKEN_EXPIRED);
+// }
+// // 返回访问令牌
+// return OAuth2Convert.INSTANCE.convert(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 accessTokenDOs = oauth2AccessTokenMapper.selectListByRefreshToken(refreshAccessTokenDTO.getRefreshToken());
+// accessTokenDOs.forEach(accessTokenDO -> deleteOAuth2AccessToken(accessTokenDO.getId()));
+//
+// // 创建访问令牌
+// OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(refreshTokenDO, refreshAccessTokenDTO.getCreateIp());
+// // 返回访问令牌
+// return OAuth2Convert.INSTANCE.convert(oauth2AccessTokenDO);
+// }
+//
+// @Override
+// @Transactional
+// public void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO) {
+// // 删除 Access Token
+// OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByUserIdAndUserType(
+// removeTokenDTO.getUserId(), removeTokenDTO.getUserType());
+// if (accessTokenDO != null) {
+// this.deleteOAuth2AccessToken(accessTokenDO.getId());
+// }
+//
+// // 删除 Refresh Token
+// oauth2RefreshTokenMapper.deleteByUserIdAndUserType(removeTokenDTO.getUserId(), removeTokenDTO.getUserType());
+// }
+
+ private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
+ OAuth2AccessTokenDO accessToken = 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;
+ }
+
+ private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long userId, Integer userType, OAuth2ClientDO clientDO) {
+ OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setRefreshToken(generateRefreshToken())
+ .setUserId(userId).setUserType(userType).setClientId(clientDO.getId())
+ .setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getRefreshTokenValiditySeconds()));
+ oauth2RefreshTokenMapper.insert(refreshToken);
+ return refreshToken;
+ }
+
+
+// /**
+// * 删除 accessToken 的 MySQL 与 Redis 的数据
+// *
+// * @param accessToken 访问令牌
+// */
+// private void deleteOAuth2AccessToken(String accessToken) {
+// // 删除 MySQL
+// oauth2AccessTokenMapper.deleteById(accessToken);
+// // 删除 Redis
+// oauth2AccessTokenRedisDAO.delete(accessToken);
+// }
+//
+ private static String generateAccessToken() {
+ return IdUtil.fastSimpleUUID();
+ }
+
+ private static String generateRefreshToken() {
+ return IdUtil.fastSimpleUUID();
+ }
+
+}
diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml
index 4480f88a5..e243290d5 100644
--- a/yudao-server/pom.xml
+++ b/yudao-server/pom.xml
@@ -21,11 +21,11 @@
https://github.com/YunaiV/ruoyi-vue-pro
-
- cn.iocoder.boot
- yudao-module-member-biz
- ${revision}
-
+
+
+
+
+
cn.iocoder.boot
yudao-module-system-biz