diff --git a/kernel-d-auth/auth-api/src/main/java/cn/stylefeng/roses/kernel/auth/api/AuthServiceApi.java b/kernel-d-auth/auth-api/src/main/java/cn/stylefeng/roses/kernel/auth/api/AuthServiceApi.java index f669b30e0..dc1373d43 100644 --- a/kernel-d-auth/auth-api/src/main/java/cn/stylefeng/roses/kernel/auth/api/AuthServiceApi.java +++ b/kernel-d-auth/auth-api/src/main/java/cn/stylefeng/roses/kernel/auth/api/AuthServiceApi.java @@ -30,6 +30,7 @@ import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginResponse; import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser; import cn.stylefeng.roses.kernel.auth.api.pojo.payload.DefaultJwtPayload; import cn.stylefeng.roses.kernel.auth.api.pojo.sso.LoginBySsoTokenRequest; +import cn.stylefeng.roses.kernel.auth.api.pojo.sso.LogoutBySsoTokenRequest; /** * 认证服务的接口,包括基本的登录退出操作和校验token等操作 @@ -136,4 +137,14 @@ public interface AuthServiceApi { */ LoginUser createNewLoginInfo(String token, DefaultJwtPayload defaultJwtPayload); + /** + * 通过单点的CaToken将本系统的用户退出 + *

+ * 一般用在单点认证中心退出时,认证中心调用本系统退出接口 + * + * @author fengshuonan + * @since 2023/11/7 15:57 + */ + void logoutByCaToken(LogoutBySsoTokenRequest logoutBySsoTokenRequest); + } diff --git a/kernel-d-auth/auth-api/src/main/java/cn/stylefeng/roses/kernel/auth/api/pojo/sso/LogoutBySsoTokenRequest.java b/kernel-d-auth/auth-api/src/main/java/cn/stylefeng/roses/kernel/auth/api/pojo/sso/LogoutBySsoTokenRequest.java new file mode 100644 index 000000000..273e79c71 --- /dev/null +++ b/kernel-d-auth/auth-api/src/main/java/cn/stylefeng/roses/kernel/auth/api/pojo/sso/LogoutBySsoTokenRequest.java @@ -0,0 +1,25 @@ +package cn.stylefeng.roses.kernel.auth.api.pojo.sso; + +import cn.stylefeng.roses.kernel.rule.pojo.request.BaseRequest; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; + +/** + * 通过单点的token退出系统 + * + * @author fengshuonan + * @since 2023/11/7 16:09 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class LogoutBySsoTokenRequest extends BaseRequest { + + /** + * 从单点服务获取到的token + */ + @NotBlank(message = "CA Token不能为空") + private String caToken; + +} diff --git a/kernel-d-auth/auth-sdk/src/main/java/cn/stylefeng/roses/kernel/auth/auth/AuthServiceImpl.java b/kernel-d-auth/auth-sdk/src/main/java/cn/stylefeng/roses/kernel/auth/auth/AuthServiceImpl.java index 845b159be..62902e483 100644 --- a/kernel-d-auth/auth-sdk/src/main/java/cn/stylefeng/roses/kernel/auth/auth/AuthServiceImpl.java +++ b/kernel-d-auth/auth-sdk/src/main/java/cn/stylefeng/roses/kernel/auth/auth/AuthServiceImpl.java @@ -47,6 +47,7 @@ import cn.stylefeng.roses.kernel.auth.api.pojo.payload.DefaultJwtPayload; import cn.stylefeng.roses.kernel.auth.api.pojo.sso.DecryptCaLoginUser; import cn.stylefeng.roses.kernel.auth.api.pojo.sso.DecryptCaTokenInfo; import cn.stylefeng.roses.kernel.auth.api.pojo.sso.LoginBySsoTokenRequest; +import cn.stylefeng.roses.kernel.auth.api.pojo.sso.LogoutBySsoTokenRequest; import cn.stylefeng.roses.kernel.cache.api.CacheOperatorApi; import cn.stylefeng.roses.kernel.demo.expander.DemoConfigExpander; import cn.stylefeng.roses.kernel.jwt.api.JwtApi; @@ -121,7 +122,12 @@ public class AuthServiceImpl implements AuthServiceApi { // aes解密出用户信息 AES aesUtil = SecureUtil.aes(Base64.decode(AuthConfigExpander.getSsoDataDecryptSecret())); - String userInfoJson = aesUtil.decryptStr(encryptUserInfo, CharsetUtil.CHARSET_UTF_8); + String userInfoJson = null; + try { + userInfoJson = aesUtil.decryptStr(encryptUserInfo, CharsetUtil.CHARSET_UTF_8); + } catch (Exception e) { + throw new AuthException(AuthExceptionEnum.SSO_TOKEN_PARSE_ERROR, "sso token无法解析"); + } // 转化为实体类 DecryptCaTokenInfo decryptCaTokenInfo = JSON.parseObject(userInfoJson, DecryptCaTokenInfo.class); @@ -246,4 +252,19 @@ public class AuthServiceImpl implements AuthServiceApi { return loginUser; } + @Override + public void logoutByCaToken(LogoutBySsoTokenRequest logoutBySsoTokenRequest) { + + // 通过CaToken查询到本地是否有对应的会话 + String localGunsToken = caClientTokenCacheApi.get(logoutBySsoTokenRequest.getCaToken()); + + // 如果缓存不存在则直接返回 + if (ObjectUtil.isEmpty(localGunsToken)) { + return; + } + + // 如果缓存存在,则直接移除token + this.sessionManagerApi.removeSession(localGunsToken); + } + } diff --git a/kernel-d-config/config-business/src/main/java/cn/stylefeng/roses/kernel/config/modular/strategy/DefaultStrategyImpl.java b/kernel-d-config/config-business/src/main/java/cn/stylefeng/roses/kernel/config/modular/strategy/DefaultStrategyImpl.java index e49560f7d..2481546cb 100644 --- a/kernel-d-config/config-business/src/main/java/cn/stylefeng/roses/kernel/config/modular/strategy/DefaultStrategyImpl.java +++ b/kernel-d-config/config-business/src/main/java/cn/stylefeng/roses/kernel/config/modular/strategy/DefaultStrategyImpl.java @@ -44,6 +44,7 @@ public class DefaultStrategyImpl implements ConfigInitStrategyApi { configInitItems.add(new ConfigInitItem("系统默认密码", "SYS_DEFAULT_PASSWORD", "123456", "用在重置密码的默认密码")); configInitItems.add(new ConfigInitItem("系统发布版本", "SYS_RELEASE_VERSION", DateUtil.format(new Date(), "yyyyMMdd"), "系统发布的版本号")); configInitItems.add(new ConfigInitItem("数据库加密AES秘钥", "SYS_ENCRYPT_SECRET_KEY", RandomUtil.randomString(32), "对称加密秘钥,用在数据库数据加密")); + configInitItems.add(new ConfigInitItem("SSO服务端加密Token信息秘钥", "SYS_AUTH_SSO_DECRYPT_DATA_SECRET", RandomUtil.randomString(32), "SSO服务端加密Token信息秘钥,用在单点登录认证时候加密生成Token")); return configInitItems; } diff --git a/kernel-s-system/system-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/login/controller/SsoLoginController.java b/kernel-s-system/system-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/login/controller/SsoLoginController.java index ea9c434bb..d8159361a 100644 --- a/kernel-s-system/system-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/login/controller/SsoLoginController.java +++ b/kernel-s-system/system-business-permission/src/main/java/cn/stylefeng/roses/kernel/sys/modular/login/controller/SsoLoginController.java @@ -3,9 +3,11 @@ package cn.stylefeng.roses.kernel.sys.modular.login.controller; import cn.stylefeng.roses.kernel.auth.api.AuthServiceApi; import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginResponse; import cn.stylefeng.roses.kernel.auth.api.pojo.sso.LoginBySsoTokenRequest; +import cn.stylefeng.roses.kernel.auth.api.pojo.sso.LogoutBySsoTokenRequest; import cn.stylefeng.roses.kernel.rule.pojo.response.ResponseData; import cn.stylefeng.roses.kernel.rule.pojo.response.SuccessResponseData; import cn.stylefeng.roses.kernel.scanner.api.annotation.ApiResource; +import cn.stylefeng.roses.kernel.scanner.api.annotation.GetResource; import cn.stylefeng.roses.kernel.scanner.api.annotation.PostResource; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; @@ -29,15 +31,27 @@ public class SsoLoginController { private AuthServiceApi authServiceApi; /** - * 通过单带登录 + * 通过单点服务的CaToken进行登录 * * @author fengshuonan * @since 2023/11/7 14:12 */ - @PostResource(name = "系统登录接口", path = "/loginByCaToken", requiredLogin = false) - public ResponseData loginApi(@RequestBody @Validated LoginBySsoTokenRequest loginWithTokenRequest) { + @PostResource(name = "通过单点服务的CaToken进行登录", path = "/loginByCaToken", requiredLogin = false) + public ResponseData loginByCaToken(@RequestBody @Validated LoginBySsoTokenRequest loginWithTokenRequest) { LoginResponse loginResponse = authServiceApi.LoginByCaToken(loginWithTokenRequest); return new SuccessResponseData<>(loginResponse); } + /** + * 通过单点服务的CaToken进行退出本平台的会话 + * + * @author fengshuonan + * @since 2023/11/7 15:57 + */ + @GetResource(name = "通过单点服务的CaToken进行退出本平台的会话", path = "/logoutByCaToken", requiredLogin = false) + public ResponseData logoutByCaToken(@Validated LogoutBySsoTokenRequest logoutBySsoTokenRequest) { + authServiceApi.logoutByCaToken(logoutBySsoTokenRequest); + return new SuccessResponseData<>(); + } + }