diff --git a/README.md b/README.md index be0a7de3..9876dd2c 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,7 @@ application.yml文件依次修改以下配置 ## 加入社群 -![](https://user-images.githubusercontent.com/30397655/216215736-815861e7-8890-4c62-a496-e69c9c6ee216.jpg) +![](https://user-images.githubusercontent.com/30397655/217441678-f6499558-77d6-422d-92a4-13a439c0faa6.jpg) ## 参与贡献 @@ -228,4 +228,13 @@ application.yml文件依次修改以下配置 - console-public-base-url (后台前端服务域名,可配置IP+端口) - portal-public-base-url (门户前台服务域名,可配置IP+端口) - openapi-public-base-url (后台服务域名,可配置IP+端口) -- synchronizer-public-base-url (同步认证源服务域名,可配置IP+端口) \ No newline at end of file +- synchronizer-public-base-url (同步认证源服务域名,可配置IP+端口) + +2、提示 cn.topiam.employee.common.entity.* 相关包缺失 + +因项目用到了 `QueryDSL` 查询框架,需要进行编译。 + +解决方案: +- 使用命令 `mvn compile`之后,自动生成代码,对应的代码目标在target/generated-sources目录下。 +- 通过 IDEA 选中其目录,Mark Directory as -> generated sources root 。 +- 通过 IDEA 工具类中File -> Invalidate Caches 清理缓存(可选操作) \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..4e4a0674 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,62 @@ +version: '3' +services: + eiam-console: + build: + context: ./eiam-console/ + dockerfile: Dockerfile + environment: + MYSQL_HOST: 192.168.56.107 + MYSQL_USER: root + MYSQL_PASSWORD: admin + ES_HOST: 192.168.56.107 + REDIS_HOST: 192.168.56.107 + REDIS_PASSWORD: 12345678 + ports: + - 1898:1898 + image: eiam-console + restart: always + eiam-openapi: + build: + context: ./eiam-openapi/ + dockerfile: Dockerfile + environment: + MYSQL_HOST: 192.168.56.107 + MYSQL_USER: root + MYSQL_PASSWORD: admin + ES_HOST: 192.168.56.107 + REDIS_HOST: 192.168.56.107 + REDIS_PASSWORD: 12345678 + image: eiam-openapi + restart: always + ports: + - 1988:1988 + eiam-portal: + build: + context: ./eiam-portal/ + dockerfile: Dockerfile + environment: + MYSQL_HOST: 192.168.56.107 + MYSQL_USER: root + MYSQL_PASSWORD: admin + ES_HOST: 192.168.56.107 + REDIS_HOST: 192.168.56.107 + REDIS_PASSWORD: 12345678 + image: eiam-portal + restart: always + ports: + - 1989:1989 + eiam-synchronizer: + build: + context: ./eiam-synchronizer/ + dockerfile: Dockerfile + environment: + MYSQL_HOST: 192.168.56.107 + MYSQL_USER: root + MYSQL_PASSWORD: admin + ES_HOST: 192.168.56.107 + REDIS_HOST: 192.168.56.107 + REDIS_PASSWORD: 12345678 + image: eiam-synchronizer + restart: always + ports: + - 1986:1986 diff --git a/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/AbstractCasApplicationService.java b/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/AbstractCasApplicationService.java index c1b3ac19..fd9570e5 100644 --- a/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/AbstractCasApplicationService.java +++ b/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/AbstractCasApplicationService.java @@ -22,10 +22,9 @@ import org.slf4j.LoggerFactory; import org.springframework.transaction.annotation.Transactional; import cn.topiam.employee.application.AbstractApplicationService; -import cn.topiam.employee.application.CasApplicationService; +import cn.topiam.employee.application.cas.model.CasSsoModel; import cn.topiam.employee.common.entity.app.po.AppCasConfigPO; import cn.topiam.employee.common.repository.app.*; -import cn.topiam.employee.core.protocol.CasSsoModel; /** * CAS 应用配置 @@ -58,8 +57,8 @@ public abstract class AbstractCasApplicationService extends AbstractApplicationS @Override public CasSsoModel getSsoModel(Long appId) { - AppCasConfigPO appCasConfigPO = appCasConfigRepository.getByAppId(appId); - return CasSsoModel.builder().ssoCallbackUrl(appCasConfigPO.getSpCallbackUrl()).build(); + AppCasConfigPO appCasConfigPo = appCasConfigRepository.getByAppId(appId); + return CasSsoModel.builder().clientServiceUrl(appCasConfigPo.getClientServiceUrl()).build(); } /** diff --git a/eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/CasApplicationService.java b/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/CasApplicationService.java similarity index 81% rename from eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/CasApplicationService.java rename to eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/CasApplicationService.java index 7752258f..6397a9ab 100644 --- a/eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/CasApplicationService.java +++ b/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/CasApplicationService.java @@ -1,5 +1,5 @@ /* - * eiam-application-core - Employee Identity and Access Management Program + * eiam-application-cas - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,9 +15,10 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.application; +package cn.topiam.employee.application.cas; -import cn.topiam.employee.core.protocol.CasSsoModel; +import cn.topiam.employee.application.ApplicationService; +import cn.topiam.employee.application.cas.model.CasSsoModel; /** * @author TopIAM diff --git a/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/CasStandardApplicationServiceImpl.java b/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/CasStandardApplicationServiceImpl.java index 39851e2e..f0b45ffd 100644 --- a/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/CasStandardApplicationServiceImpl.java +++ b/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/CasStandardApplicationServiceImpl.java @@ -28,26 +28,19 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; -import cn.topiam.employee.application.cas.model.AppCasStandardConfigGetResult; -import cn.topiam.employee.application.cas.model.AppCasStandardSaveConfigParam; +import cn.topiam.employee.application.cas.converter.AppCasStandardConfigConverter; +import cn.topiam.employee.application.cas.pojo.AppCasStandardSaveConfigParam; import cn.topiam.employee.application.exception.AppNotExistException; import cn.topiam.employee.audit.context.AuditContext; -import cn.topiam.employee.common.constants.ProtocolConstants; import cn.topiam.employee.common.entity.app.AppCasConfigEntity; import cn.topiam.employee.common.entity.app.AppEntity; import cn.topiam.employee.common.entity.app.po.AppCasConfigPO; -import cn.topiam.employee.common.enums.app.AppProtocol; -import cn.topiam.employee.common.enums.app.AppType; -import cn.topiam.employee.common.enums.app.AuthorizationType; -import cn.topiam.employee.common.enums.app.InitLoginType; +import cn.topiam.employee.common.enums.app.*; import cn.topiam.employee.common.repository.app.*; -import cn.topiam.employee.core.context.ServerContextHelp; import cn.topiam.employee.support.exception.TopIamException; import cn.topiam.employee.support.validation.ValidationHelp; import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; -import static cn.topiam.employee.common.constants.ProtocolConstants.APP_CODE_VARIABLE; - /** * Cas 用户应用 * @@ -56,23 +49,7 @@ import static cn.topiam.employee.common.constants.ProtocolConstants.APP_CODE_VAR */ @Component public class CasStandardApplicationServiceImpl extends AbstractCasApplicationService { - private final Logger logger = LoggerFactory - .getLogger(CasStandardApplicationServiceImpl.class); - - /** - * AppCasConfigRepository - */ - protected final AppCasConfigRepository appCasConfigRepository; - - public CasStandardApplicationServiceImpl(AppCertRepository appCertRepository, - AppAccountRepository appAccountRepository, - AppAccessPolicyRepository appAccessPolicyRepository, - AppRepository appRepository, - AppCasConfigRepository appCasConfigRepository) { - super(appCertRepository, appAccountRepository, appAccessPolicyRepository, appRepository, - appCasConfigRepository); - this.appCasConfigRepository = appCasConfigRepository; - } + private final Logger logger = LoggerFactory.getLogger(CasStandardApplicationServiceImpl.class); /** * 更新应用配置 @@ -119,7 +96,9 @@ public class CasStandardApplicationServiceImpl extends AbstractCasApplicationSer throw new AppNotExistException(); } AppCasConfigEntity entity = cas.get(); - entity.setSpCallbackUrl(model.getSpCallbackUrl()); + entity.setClientServiceUrl(model.getClientServiceUrl()); + entity.setUserIdentityType(model.getUserIdentityType()); + entity.setServiceTicketExpireTime(model.getServiceTicketExpireTime()); appCasConfigRepository.save(entity); } @@ -133,18 +112,7 @@ public class CasStandardApplicationServiceImpl extends AbstractCasApplicationSer @Override public Object getConfig(String appId) { AppCasConfigPO po = appCasConfigRepository.getByAppId(Long.valueOf(appId)); - AppCasStandardConfigGetResult result = new AppCasStandardConfigGetResult(); - result.setAuthorizationType(po.getAuthorizationType()); - result.setInitLoginType(po.getInitLoginType()); - result.setInitLoginUrl(po.getInitLoginUrl()); - result.setSpCallbackUrl(po.getSpCallbackUrl()); - - String baseUrl = ServerContextHelp.getPortalPublicBaseUrl(); - // 服务端URL配置前缀 - result.setServerUrlPrefix( - baseUrl + ProtocolConstants.CasEndpointConstants.CAS_AUTHORIZE_BASE_PATH - .replace(APP_CODE_VARIABLE, po.getAppCode())); - return result; + return casStandardConfigConverter.entityConverterToCasConfigResult(po); } /** @@ -236,16 +204,36 @@ public class CasStandardApplicationServiceImpl extends AbstractCasApplicationSer appEntity.setProtocol(getProtocol()); appEntity.setClientId(idGenerator.generateId().toString().replace("-", "")); appEntity.setClientSecret(idGenerator.generateId().toString().replace("-", "")); - appEntity.setInitLoginType(InitLoginType.PORTAL_OR_APP); + appEntity.setInitLoginType(InitLoginType.APP); appEntity.setAuthorizationType(AuthorizationType.AUTHORIZATION); appEntity.setRemark(remark); appRepository.save(appEntity); AppCasConfigEntity casEntity = new AppCasConfigEntity(); casEntity.setAppId(appEntity.getId()); - casEntity.setSpCallbackUrl(""); + casEntity.setUserIdentityType(CasUserIdentityType.USER_USERNAME); + casEntity.setServiceTicketExpireTime(30); appCasConfigRepository.save(casEntity); return appEntity.getId().toString(); } + private final AppCasStandardConfigConverter casStandardConfigConverter; + + /** + * AppCasConfigRepository + */ + protected final AppCasConfigRepository appCasConfigRepository; + + public CasStandardApplicationServiceImpl(AppCertRepository appCertRepository, + AppAccountRepository appAccountRepository, + AppAccessPolicyRepository appAccessPolicyRepository, + AppRepository appRepository, + AppCasConfigRepository appCasConfigRepository, + AppCasStandardConfigConverter casStandardConfigConverter) { + super(appCertRepository, appAccountRepository, appAccessPolicyRepository, appRepository, + appCasConfigRepository); + this.appCasConfigRepository = appCasConfigRepository; + this.casStandardConfigConverter = casStandardConfigConverter; + } + } diff --git a/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/converter/AppCasStandardConfigConverter.java b/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/converter/AppCasStandardConfigConverter.java index 82e67f8c..391c7184 100644 --- a/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/converter/AppCasStandardConfigConverter.java +++ b/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/converter/AppCasStandardConfigConverter.java @@ -19,6 +19,13 @@ package cn.topiam.employee.application.cas.converter; import org.mapstruct.Mapper; +import cn.topiam.employee.application.cas.pojo.AppCasProtocolEndpoint; +import cn.topiam.employee.application.cas.pojo.AppCasStandardConfigGetResult; +import cn.topiam.employee.common.constants.ProtocolConstants; +import cn.topiam.employee.common.entity.app.po.AppCasConfigPO; +import cn.topiam.employee.core.context.ServerContextHelp; +import static cn.topiam.employee.common.constants.ProtocolConstants.APP_CODE_VARIABLE; + /** * 配置转换 * @@ -27,4 +34,35 @@ import org.mapstruct.Mapper; */ @Mapper(componentModel = "spring") public interface AppCasStandardConfigConverter { + + /** + * 实体转CAS配置 + * + * @param po {@link AppCasConfigPO} + * @return {@link AppCasStandardConfigGetResult} + */ + default AppCasStandardConfigGetResult entityConverterToCasConfigResult(AppCasConfigPO po) { + AppCasStandardConfigGetResult result = new AppCasStandardConfigGetResult(); + result.setAuthorizationType(po.getAuthorizationType()); + result.setAppId(String.valueOf(po.getAppId())); + result.setInitLoginType(po.getInitLoginType()); + result.setInitLoginUrl(po.getInitLoginUrl()); + result.setClientServiceUrl(po.getClientServiceUrl()); + result.setUserIdentityType(po.getUserIdentityType()); + result.setServiceTicketExpireTime(po.getServiceTicketExpireTime()); + + //封装端点信息 + //@formatter:off + AppCasProtocolEndpoint protocolEndpoint = new AppCasProtocolEndpoint(); + String baseUrl = ServerContextHelp.getPortalPublicBaseUrl(); + protocolEndpoint.setCasServerUrlPrefix(baseUrl+ProtocolConstants.CasEndpointConstants.CAS_AUTHORIZE_BASE_PATH.replace(APP_CODE_VARIABLE, po.getAppCode())); + protocolEndpoint.setCasSsoEndpoint(baseUrl + ProtocolConstants.CasEndpointConstants.CAS_LOGIN_PATH.replace(APP_CODE_VARIABLE, po.getAppCode())); + protocolEndpoint.setCasSloEndpoint(baseUrl + ProtocolConstants.CasEndpointConstants.CAS_LOGOUT_PATH.replace(APP_CODE_VARIABLE, po.getAppCode())); + protocolEndpoint.setCasValidateEndpoint(baseUrl + ProtocolConstants.CasEndpointConstants.CAS_VALIDATE_V1_PATH.replace(APP_CODE_VARIABLE, po.getAppCode())); + protocolEndpoint.setCasValidateV2Endpoint(baseUrl + ProtocolConstants.CasEndpointConstants.CAS_VALIDATE_V2_PATH.replace(APP_CODE_VARIABLE, po.getAppCode())); + protocolEndpoint.setCasValidateV3Endpoint(baseUrl + ProtocolConstants.CasEndpointConstants.CAS_VALIDATE_V3_PATH.replace(APP_CODE_VARIABLE, po.getAppCode())); + result.setProtocolEndpoint(protocolEndpoint); + //@formatter:on + return result; + } } diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/protocol/CasSsoModel.java b/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/model/CasSsoModel.java similarity index 82% rename from eiam-core/src/main/java/cn/topiam/employee/core/protocol/CasSsoModel.java rename to eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/model/CasSsoModel.java index 14719eed..2c00928e 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/protocol/CasSsoModel.java +++ b/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/model/CasSsoModel.java @@ -1,5 +1,5 @@ /* - * eiam-core - Employee Identity and Access Management Program + * eiam-application-cas - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.core.protocol; +package cn.topiam.employee.application.cas.model; import java.io.Serializable; @@ -30,6 +30,9 @@ import lombok.Data; @Builder public class CasSsoModel implements Serializable { - private String ssoCallbackUrl; + /** + * 客户端服务URL + */ + private String clientServiceUrl; } diff --git a/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/pojo/AppCasProtocolEndpoint.java b/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/pojo/AppCasProtocolEndpoint.java new file mode 100644 index 00000000..7f6e4653 --- /dev/null +++ b/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/pojo/AppCasProtocolEndpoint.java @@ -0,0 +1,73 @@ +/* + * eiam-application-cas - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.application.cas.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 协议端点域 +* +* @author TopIAM +* Created by support@topiam.cn on 2022/6/4 23:37 +*/ +@Data +@Schema(description = "协议端点") +public class AppCasProtocolEndpoint implements Serializable { + + @Serial + private static final long serialVersionUID = -2261602995152894964L; + /** + * CAS URL前缀 + */ + @Schema(description = "CAS URL前缀") + private String casServerUrlPrefix; + + /** + * CAS SSO 端点 + */ + @Schema(description = "CAS SSO 端点") + private String casSsoEndpoint; + + /** + * CAS SLO 端点 + */ + @Schema(description = "CAS SLO 端点") + private String casSloEndpoint; + + /** + * CAS 校验端点 + */ + @Schema(description = "CAS 校验端点") + private String casValidateEndpoint; + + /** + * CAS v2 校验端点 + */ + @Schema(description = "CAS V2 校验端点") + private String casValidateV2Endpoint; + + /** + * CAS v3 校验端点 + */ + @Schema(description = "CAS V3 校验端点") + private String casValidateV3Endpoint; +} diff --git a/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/pojo/AppCasStandardConfigGetResult.java b/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/pojo/AppCasStandardConfigGetResult.java new file mode 100644 index 00000000..807ce0e8 --- /dev/null +++ b/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/pojo/AppCasStandardConfigGetResult.java @@ -0,0 +1,81 @@ +/* + * eiam-application-cas - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.application.cas.pojo; + +import cn.topiam.employee.common.enums.app.AuthorizationType; +import cn.topiam.employee.common.enums.app.CasUserIdentityType; +import cn.topiam.employee.common.enums.app.InitLoginType; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * @author TopIAM + * Created by support@topiam.cn on 2023/1/2 22:23 + */ +@Data +@Schema(description = "CAS 配置返回结果") +public class AppCasStandardConfigGetResult { + + /** + * 应用id + */ + @Schema(description = "应用id") + private String appId; + + /** + * SSO 发起方 + */ + @Parameter(description = "SSO 发起方") + private InitLoginType initLoginType; + + /** + * SSO 登录链接 + */ + @Parameter(description = "SSO 登录链接") + private String initLoginUrl; + + /** + * 授权范围 + */ + @Parameter(description = "SSO 授权范围") + private AuthorizationType authorizationType; + + /** + * 客户端服务URL + */ + @Schema(name = "客户端服务URL") + private String clientServiceUrl; + + /** + * 用户身份类型标识 + */ + @Schema(name = "用户身份类型标识") + private CasUserIdentityType userIdentityType; + + /** + * serviceTicket 过期时间(秒) + */ + @Schema(name = "serviceTicket 过期时间(秒)") + private Integer serviceTicketExpireTime; + /** + * CAS 协议端点 + */ + @Schema(name = "CAS 协议端点") + private AppCasProtocolEndpoint protocolEndpoint; +} diff --git a/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/model/AppCasStandardSaveConfigParam.java b/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/pojo/AppCasStandardSaveConfigParam.java similarity index 66% rename from eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/model/AppCasStandardSaveConfigParam.java rename to eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/pojo/AppCasStandardSaveConfigParam.java index 0ea31a7a..89581305 100644 --- a/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/model/AppCasStandardSaveConfigParam.java +++ b/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/pojo/AppCasStandardSaveConfigParam.java @@ -15,19 +15,17 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.application.cas.model; +package cn.topiam.employee.application.cas.pojo; + +import cn.topiam.employee.common.enums.app.AuthorizationType; +import cn.topiam.employee.common.enums.app.CasUserIdentityType; +import cn.topiam.employee.common.enums.app.InitLoginType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; import java.io.Serial; import java.io.Serializable; -import cn.topiam.employee.common.enums.app.AuthorizationType; -import cn.topiam.employee.common.enums.app.InitLoginType; - -import lombok.Data; - -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Schema; - /** * @author TopIAM * Created by support@topiam.cn on 2023/1/2 22:27 @@ -35,29 +33,41 @@ import io.swagger.v3.oas.annotations.media.Schema; @Data public class AppCasStandardSaveConfigParam implements Serializable { @Serial - private static final long serialVersionUID = 1881187724713984421L; + private static final long serialVersionUID = 1881187724713984421L; /** * 应用ID */ @Schema(description = "授权类型") - private AuthorizationType authorizationType; + private AuthorizationType authorizationType; /** * SSO 发起登录类型 */ @Schema(description = "SSO 发起登录类型") - private InitLoginType initLoginType; + private InitLoginType initLoginType; /** * SSO 发起登录URL */ @Schema(description = "SSO 发起登录URL") - private String initLoginUrl; + private String initLoginUrl; /** - * 单点登录 SP 回调地址 + * 客户端服务URL */ - @Parameter(name = "单点登录 sp Callback Url") - private String spCallbackUrl; + @Schema(name = "客户端服务URL") + private String clientServiceUrl; + + /** + * 用户身份类型标识 + */ + @Schema(name = "用户身份类型标识") + private CasUserIdentityType userIdentityType; + + /** + * serviceTicket 过期时间(秒) + */ + @Schema(name = "serviceTicket 过期时间(秒)") + private Integer serviceTicketExpireTime; } diff --git a/eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/AbstractApplicationService.java b/eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/AbstractApplicationService.java index f1b32495..e7e81d20 100644 --- a/eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/AbstractApplicationService.java +++ b/eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/AbstractApplicationService.java @@ -57,7 +57,7 @@ public abstract class AbstractApplicationService implements ApplicationService { * 创建证书 * * @param appId {@link Long} - * @param appCode {@link Long} + * @param appCode {@link Long} * @param usingType {@link AppCertUsingType} */ public void createCertificate(Long appId, String appCode, AppCertUsingType usingType) { @@ -120,7 +120,7 @@ public abstract class AbstractApplicationService implements ApplicationService { protected final AppAccountRepository appAccountRepository; /** - * AppAccessPolicyRepository + *AppAccessPolicyRepository */ protected final AppAccessPolicyRepository appAccessPolicyRepository; diff --git a/eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/ApplicationServiceLoader.java b/eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/ApplicationServiceLoader.java index ea299531..62445b34 100644 --- a/eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/ApplicationServiceLoader.java +++ b/eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/ApplicationServiceLoader.java @@ -44,6 +44,7 @@ public class ApplicationServiceLoader implements ApplicationContextAware { * 用于保存接口实现类名及对应的类 */ private Map loadMap = new HashMap<>(16); + private ApplicationContext applicationContext; /** * key: code,value:templateImpl */ @@ -64,9 +65,9 @@ public class ApplicationServiceLoader implements ApplicationContextAware { * @see BeanInitializationException */ @Override - public void setApplicationContext(org.springframework.context.ApplicationContext applicationContext) throws BeansException { + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; loadMap = applicationContext.getBeansOfType(ApplicationService.class); - getApplicationServiceList(); } /** @@ -101,4 +102,12 @@ public class ApplicationServiceLoader implements ApplicationContextAware { return impl; } + public void addApplicationService(List beanNameList) { + Map applicationServiceMap = new HashMap<>(16); + for (String beanName : beanNameList) { + applicationServiceMap.put(beanName, + applicationContext.getBean(beanName, ApplicationService.class)); + } + loadMap.putAll(applicationServiceMap); + } } diff --git a/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/AbstractFormApplicationService.java b/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/AbstractFormApplicationService.java index 9fe8f53a..3867036c 100644 --- a/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/AbstractFormApplicationService.java +++ b/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/AbstractFormApplicationService.java @@ -17,8 +17,11 @@ */ package cn.topiam.employee.application.form; -import cn.topiam.employee.application.ApplicationService; -import cn.topiam.employee.common.repository.app.AppCertRepository; +import org.springframework.util.AlternativeJdkIdGenerator; +import org.springframework.util.IdGenerator; + +import cn.topiam.employee.common.repository.app.AppAccountRepository; +import cn.topiam.employee.common.repository.app.AppFormConfigRepository; import cn.topiam.employee.common.repository.app.AppRepository; /** @@ -27,20 +30,40 @@ import cn.topiam.employee.common.repository.app.AppRepository; * @author TopIAM * Created by support@topiam.cn on 2022/8/23 20:58 */ -public abstract class AbstractFormApplicationService implements ApplicationService { +public abstract class AbstractFormApplicationService implements FormApplicationService { + + @Override + public void delete(String appId) { + //删除应用 + appRepository.deleteById(Long.valueOf(appId)); + //删除应用账户 + appAccountRepository.deleteAllByAppId(Long.valueOf(appId)); + // 删除应用配置 + appFormConfigRepository.deleteByAppId(Long.valueOf(appId)); + } - /** - * AppCertRepository - */ - protected final AppCertRepository appCertRepository; /** * ApplicationRepository */ - protected final AppRepository appRepository; + protected final AppRepository appRepository; - protected AbstractFormApplicationService(AppCertRepository appCertRepository, - AppRepository appRepository) { - this.appCertRepository = appCertRepository; + /** + * AppAccountRepository + */ + protected final AppAccountRepository appAccountRepository; + + protected final AppFormConfigRepository appFormConfigRepository; + + /** + * IdGenerator + */ + protected final IdGenerator idGenerator = new AlternativeJdkIdGenerator(); + + protected AbstractFormApplicationService(AppRepository appRepository, + AppAccountRepository appAccountRepository, + AppFormConfigRepository appFormConfigRepository) { this.appRepository = appRepository; + this.appAccountRepository = appAccountRepository; + this.appFormConfigRepository = appFormConfigRepository; } } diff --git a/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/FormApplicationService.java b/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/FormApplicationService.java new file mode 100644 index 00000000..8d105ce6 --- /dev/null +++ b/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/FormApplicationService.java @@ -0,0 +1,48 @@ +/* + * eiam-application-form - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.application.form; + +import cn.topiam.employee.application.ApplicationService; +import cn.topiam.employee.application.form.model.FormProtocolConfig; +import cn.topiam.employee.common.entity.app.AppAccountEntity; + +/** + * 应用接口 + * + * @author TopIAM + * Created by support@topiam.cn on 2022/8/20 23:20 + */ +public interface FormApplicationService extends ApplicationService { + + /** + * 获取协议配置 + * + * @param appCode {@link String} + * @return {@link FormProtocolConfig} + */ + FormProtocolConfig getProtocolConfig(String appCode); + + /** + * 获取应用用户信息 + * + * @param appId {@link Long} + * @param userId {@link Long} + * @return {@link FormProtocolConfig} + */ + AppAccountEntity getAppAccount(Long appId, Long userId); +} diff --git a/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/FormStandardApplicationServiceImpl.java b/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/FormStandardApplicationServiceImpl.java index 75e5dd82..ed24688d 100644 --- a/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/FormStandardApplicationServiceImpl.java +++ b/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/FormStandardApplicationServiceImpl.java @@ -17,15 +17,45 @@ */ package cn.topiam.employee.application.form; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; +import javax.validation.ConstraintViolationException; + +import org.apache.commons.text.StringSubstitutor; import org.springframework.stereotype.Component; -import cn.topiam.employee.common.enums.app.AppProtocol; -import cn.topiam.employee.common.enums.app.AppType; -import cn.topiam.employee.common.repository.app.AppCertRepository; +import com.fasterxml.jackson.databind.ObjectMapper; + +import cn.topiam.employee.application.exception.AppNotExistException; +import cn.topiam.employee.application.form.converter.AppFormConfigConverter; +import cn.topiam.employee.application.form.model.FormProtocolConfig; +import cn.topiam.employee.application.form.pojo.AppFormSaveConfigParam; +import cn.topiam.employee.audit.context.AuditContext; +import cn.topiam.employee.common.entity.app.AppAccountEntity; +import cn.topiam.employee.common.entity.app.AppEntity; +import cn.topiam.employee.common.entity.app.AppFormConfigEntity; +import cn.topiam.employee.common.entity.app.po.AppFormConfigPO; +import cn.topiam.employee.common.enums.app.*; +import cn.topiam.employee.common.exception.app.AppAccountNotExistException; +import cn.topiam.employee.common.repository.app.AppAccountRepository; +import cn.topiam.employee.common.repository.app.AppFormConfigRepository; import cn.topiam.employee.common.repository.app.AppRepository; +import cn.topiam.employee.core.context.ServerContextHelp; +import cn.topiam.employee.support.exception.TopIamException; +import cn.topiam.employee.support.util.BeanUtils; +import cn.topiam.employee.support.util.HttpUrlUtils; +import cn.topiam.employee.support.validation.ValidationHelp; + +import lombok.extern.slf4j.Slf4j; +import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; + +import static cn.topiam.employee.common.constants.ProtocolConstants.APP_CODE; +import static cn.topiam.employee.common.constants.ProtocolConstants.FormEndpointConstants.IDP_FORM_SSO_INITIATOR; +import static cn.topiam.employee.support.repository.domain.BaseEntity.LAST_MODIFIED_BY; +import static cn.topiam.employee.support.repository.domain.BaseEntity.LAST_MODIFIED_TIME; /** * Form 用户应用 @@ -33,6 +63,7 @@ import cn.topiam.employee.common.repository.app.AppRepository; * @author TopIAM * Created by support@topiam.cn on 2022/8/20 23:20 */ +@Slf4j @Component public class FormStandardApplicationServiceImpl extends AbstractFormApplicationService { @@ -44,6 +75,51 @@ public class FormStandardApplicationServiceImpl extends AbstractFormApplicationS */ @Override public void saveConfig(String appId, Map config) { + AppFormSaveConfigParam model; + try { + ObjectMapper mapper = new ObjectMapper(); + String value = mapper.writeValueAsString(config); + // 指定序列化输入的类型 + mapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false); + model = mapper.readValue(value, AppFormSaveConfigParam.class); + } catch (Exception e) { + throw new TopIamException(e.getMessage()); + } + //@formatter:off + ValidationHelp.ValidationResult validationResult = ValidationHelp.validateEntity(model); + if (validationResult.isHasErrors()) { + throw new ConstraintViolationException(validationResult.getConstraintViolations()); + } + //@formatter:on + //1、修改基本信息 + Optional optional = appRepository.findById(Long.valueOf(appId)); + if (optional.isEmpty()) { + AuditContext.setContent("保存配置失败,应用 [" + appId + "] 不存在!"); + log.error(AuditContext.getContent()); + throw new AppNotExistException(); + } + AppEntity appEntity = optional.get(); + appEntity.setAuthorizationType(model.getAuthorizationType()); + Map variables = new HashMap<>(16); + variables.put(APP_CODE, appEntity.getCode()); + StringSubstitutor sub = new StringSubstitutor(variables, "{", "}"); + appEntity.setInitLoginUrl(sub.replace(HttpUrlUtils + .format(ServerContextHelp.getPortalPublicBaseUrl() + IDP_FORM_SSO_INITIATOR))); + appEntity.setInitLoginType(model.getInitLoginType()); + appRepository.save(appEntity); + //2、修改 表单代填 配置 + Optional form = appFormConfigRepository + .findByAppId(Long.valueOf(appId)); + if (form.isEmpty()) { + AuditContext.setContent("保存配置失败,应用 [" + appId + "] 不存在!"); + log.error(AuditContext.getContent()); + throw new AppNotExistException(); + } + AppFormConfigEntity entity = form.get(); + AppFormConfigEntity formConfig = appFormConfigConverter + .appFormSaveConfigParamToEntity(model); + BeanUtils.merge(formConfig, entity, LAST_MODIFIED_BY, LAST_MODIFIED_TIME); + appFormConfigRepository.save(entity); } /** @@ -54,7 +130,8 @@ public class FormStandardApplicationServiceImpl extends AbstractFormApplicationS */ @Override public Object getConfig(String appId) { - return null; + AppFormConfigPO po = appFormConfigRepository.getByAppId(Long.valueOf(appId)); + return appFormConfigConverter.entityConverterToFormConfigResult(po); } /** @@ -64,7 +141,7 @@ public class FormStandardApplicationServiceImpl extends AbstractFormApplicationS */ @Override public String getCode() { - return "form"; + return AppProtocol.FORM.getCode(); } /** @@ -74,7 +151,7 @@ public class FormStandardApplicationServiceImpl extends AbstractFormApplicationS */ @Override public String getName() { - return "表单代填"; + return AppProtocol.FORM.getDesc(); } /** @@ -124,7 +201,7 @@ public class FormStandardApplicationServiceImpl extends AbstractFormApplicationS */ @Override public String getBase64Icon() { - return "data:image/svg+xml;base64,PHN2ZyB0PSIxNjYwOTc4MDk3OTkyIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjMxNDMiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNMjk0LjkxMiA0MTQuMjA4aDg2LjUyOHYzNDIuMDE2SDI5NC45MTJ6TTQyNC45NiA0MTQuMjA4aDMwMy4xMDR2MzQyLjAxNkg0MjQuOTZ6TTI5NC45MTIgMjY3LjI2NGg0MzMuMTUydjk3Ljc5MkgyOTQuOTEyeiIgZmlsbD0iIzI3QTNGRiIgcC1pZD0iMzE0NCI+PC9wYXRoPjxwYXRoIGQ9Ik05MjEuNiAwSDEwMi40YTEwMi40IDEwMi40IDAgMCAwLTEwMi40IDEwMi40djgxOS4yYTEwMi40IDEwMi40IDAgMCAwIDEwMi40IDEwMi40aDgxOS4yYTEwMi40IDEwMi40IDAgMCAwIDEwMi40LTEwMi40VjEwMi40YTEwMi40IDEwMi40IDAgMCAwLTEwMi40LTEwMi40eiBtLTE1MC4wMTYgNzgwLjhhMjMuMDQgMjMuMDQgMCAwIDEtMjEuNTA0IDI0LjA2NEgyNzMuNDA4YTIzLjA0IDIzLjA0IDAgMCAxLTIxLjUwNC0yNC4wNjRWMjQyLjY4OGEyMy4wNCAyMy4wNCAwIDAgMSAyMS41MDQtMjQuMDY0aDQ3Ni42NzJhMjMuMDQgMjMuMDQgMCAwIDEgMjEuNTA0IDI0LjA2NHoiIGZpbGw9IiMyN0EzRkYiIHAtaWQ9IjMxNDUiPjwvcGF0aD48L3N2Zz4="; + return "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjcyNDA3MTc3NTExIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQ5MjkiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCI+PHBhdGggZD0iTTQ2Ny4yNyA4NzAuMzNIMTgzLjczYy0yMC41OCAwLTM3LjMyLTE1LjE2LTM3LjMyLTMzLjhWMTg1Ljg2YzAtMTguNjMgMTYuNzQtMzMuNzkgMzcuMzItMzMuNzlINzE4YzIwLjU3IDAgMzcuMzEgMTUuMTYgMzcuMzEgMzMuNzl2MjYyLjU5YTI0IDI0IDAgMSAwIDQ4IDBWMTg1Ljg2YzAtNDUuMS0zOC4yNy04MS43OS04NS4zMS04MS43OUgxODMuNzNjLTQ3IDAtODUuMzIgMzYuNjktODUuMzIgODEuNzl2NjUwLjY3YzAgNDUuMSAzOC4yNyA4MS44IDg1LjMyIDgxLjhoMjgzLjU0YTI0IDI0IDAgMCAwIDAtNDh6IiBmaWxsPSIjNjc3Nzg3IiBwLWlkPSI0OTMwIj48L3BhdGg+PHBhdGggZD0iTTkyMi4zOSA2NzQuNThsLTAuMzQtMi4xOGE0Ni4zOCA0Ni4zOCAwIDAgMC0zOS41OC0zOC42bC0yLjE5LTAuMjlhMzcuMzcgMzcuMzcgMCAwIDEtMzIuNDEtMzdBMzYuNDQgMzYuNDQgMCAwIDEgODUwIDU4NS42bDAuNzYtMi4zMmE0Ni4xOSA0Ni4xOSAwIDAgMC0yMS40Ni01NC42N2wtNDEuNDMtMjNhNDYuMDggNDYuMDggMCAwIDAtNTMuNDQgNi4yNWwtMS41OCAxLjQzYTExMi4zNSAxMTIuMzUgMCAwIDEtMTAuNiA4LjcxYy04LjgxIDYuMTYtMTUuNDIgOC4zNC0xOC40IDguMzRzLTkuNzItMi4yMi0xOC40OC04LjQ1YTEwMS42MSAxMDEuNjEgMCAwIDEtMTAuMjYtOC40NGwtMS41Ni0xLjQ4YTQ2IDQ2IDAgMCAwLTU0LTdsLTQzLjE4IDIzLjY3YTQ2LjEgNDYuMSAwIDAgMC0yMS41OSA1NWwwLjc4IDIuMzRhMzUuNyAzNS43IDAgMCAxLTMwLjMzIDQ3LjYxbC0yLjE4IDAuMjlhNDYuMzggNDYuMzggMCAwIDAtMzkuNTcgMzguNTRsLTAuMzQgMi4xOGMtMS40OCA5LjM0LTMuMjMgMjMuMDUtMy4yMyAzNS4zMyAwIDEyLjA5IDEuNzUgMjUuODEgMy4yMiAzNS4ybDAuMzQgMi4xOGE0Ni4zOCA0Ni4zOCAwIDAgMCAzOS42MiAzOC41OWwyLjE5IDAuMjlhMzUuNzUgMzUuNzUgMCAwIDEgMzAuMjcgNDcuNjhsLTAuNzcgMi4zMUE0Ni4xNiA0Ni4xNiAwIDAgMCA1NzYuMDYgODkxbDQwLjI2IDIyLjUxYTQ1Ljg3IDQ1Ljg3IDAgMCAwIDU0LjQ1LTdsMS41OS0xLjUyYTEwMS4zNiAxMDEuMzYgMCAwIDEgMTAuNTEtOC44M2M5LTYuNiAxNS43NS04Ljk0IDE4LjgxLTguOTQgMS41NyAwIDcuNTYgMC42NiAxOS4wNyA5LjE4YTk3LjU3IDk3LjU3IDAgMCAxIDEwLjMzIDguODFsMS41NyAxLjU0YTQ2LjM4IDQ2LjM4IDAgMCAwIDU0LjU5IDcuNDlsNDItMjMuMTNhNDYuMTYgNDYuMTYgMCAwIDAgMjEuNTctNTQuNzNMODUwIDgzNGEzNi43OSAzNi43OSAwIDAgMS0yLjE0LTEwLjkzIDM3LjMyIDM3LjMyIDAgMCAxIDMyLjM2LTM3bDIuMi0wLjI5QTQ2LjM3IDQ2LjM3IDAgMCAwIDkyMiA3NDcuMjRsMC4zNS0yLjE3YzEuMTktNy42MiAzLjItMjIuMzMgMy4yLTM1LjIyIDAuMDQtMTIuMjUtMS43LTI1Ljk0LTMuMTYtMzUuMjd6IG0tNDcuNDIgNjNsLTAuMTMgMC43OS0wLjc5IDAuMWE4NS40NSA4NS40NSAwIDAgMC03NC4xOCA4NC41OCA4My43OSA4My43OSAwIDAgMCA0LjUyIDI1Ljg1bDAuMjggMC44NS0zOS4zOCAyMS43LTAuNTctMC41NmExNDcuNiAxNDcuNiAwIDAgMC0xNS40NC0xMy4xOGMtMTYuNjUtMTIuMzItMzIuNjctMTguNTctNDcuNi0xOC41N3MtMzAuNzEgNi4xMy00Ny4xNyAxOC4yMWExNTMuNiAxNTMuNiAwIDAgMC0xNS40MSAxMi45NGwtMC41NyAwLjU2LTM3LjY5LTIxLjA3IDAuMjgtMC44NGE4NC4xNSA4NC4xNSAwIDAgMCA0LjU1LTI1LjkgODUuNDIgODUuNDIgMCAwIDAtNzQuMTYtODQuNTdsLTAuOC0wLjExLTAuMTItMC43OWExOTMuMzMgMTkzLjMzIDAgMCAxLTIuNTktMjcuNzIgMTkxLjkgMTkxLjkgMCAwIDEgMi41OS0yNy44NWwwLjEzLTAuNzkgMC43OS0wLjExYTg1LjUgODUuNSAwIDAgMCA3NC4xNi04NC41NyA4My40NSA4My40NSAwIDAgMC00LjUyLTI1Ljc3bC0wLjI5LTAuODYgNDAuNi0yMi4yNSAwLjU3IDAuNTRhMTQ2LjE5IDE0Ni4xOSAwIDAgMCAxNS40NSAxMi43MmMxNi4yMiAxMS41NCAzMS44MiAxNy4zOSA0Ni4zNyAxNy4zOSAxNC4zNCAwIDI5LjgyLTUuNzQgNDYtMTcuMDdsMC4xMy0wLjA5YTE1Ny40MSAxNTcuNDEgMCAwIDAgMTUuMjMtMTIuMzZsMC41OC0wLjUyIDM4Ljg5IDIxLjU3LTAuMjggMC44NGE4My45IDgzLjkgMCAwIDAtNC41MiAyNS44NiA4NS41MSA4NS41MSAwIDAgMCA3NC4xOCA4NC41OGwwLjc5IDAuMSAwLjEzIDAuNzlhMTk2LjUyIDE5Ni41MiAwIDAgMSAyLjYyIDI3Ljg2IDE5My43MSAxOTMuNzEgMCAwIDEtMi42IDI3Ljc2eiIgZmlsbD0iIzY3Nzc4NyIgcC1pZD0iNDkzMSI+PC9wYXRoPjxwYXRoIGQ9Ik03MDIuNzcgNjMzLjQ1YTc2LjEzIDc2LjEzIDAgMSAwIDc2LjEzIDc2LjEzIDc2LjIxIDc2LjIxIDAgMCAwLTc2LjEzLTc2LjEzeiBtMCAxMDQuMjZhMjguMTMgMjguMTMgMCAxIDEgMjguMTMtMjguMTMgMjguMTYgMjguMTYgMCAwIDEtMjguMTMgMjguMTN6TTU1OS4yMyA0ODguNDVhMjQgMjQgMCAwIDAtMjQtMjRIMjM3Ljc0YTI0IDI0IDAgMSAwIDAgNDhoMjk3LjQ5YTI0IDI0IDAgMCAwIDI0LTI0ek02ODIuMzMgMzA3LjIxYTI0IDI0IDAgMCAwLTI0LTI0SDIzNy43NGEyNCAyNCAwIDAgMCAwIDQ4aDQyMC41OWEyNCAyNCAwIDAgMCAyNC0yNHpNMjQzLjIxIDYzOC45NGEyNCAyNCAwIDAgMCAwIDQ4aDE4My45NGEyNCAyNCAwIDEgMCAwLTQ4eiIgZmlsbD0iIzY3Nzc4NyIgcC1pZD0iNDkzMiI+PC9wYXRoPjwvc3ZnPg=="; } /** @@ -135,22 +212,50 @@ public class FormStandardApplicationServiceImpl extends AbstractFormApplicationS */ @Override public String create(String name, String remark) { - return ""; + //1、创建应用 + AppEntity appEntity = new AppEntity(); + appEntity.setName(name); + appEntity.setCode( + org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric(32).toLowerCase()); + appEntity.setTemplate(getCode()); + appEntity.setType(getType()); + appEntity.setEnabled(true); + appEntity.setProtocol(getProtocol()); + appEntity.setClientId(idGenerator.generateId().toString().replace("-", "")); + appEntity.setClientSecret(idGenerator.generateId().toString().replace("-", "")); + appEntity.setInitLoginType(InitLoginType.PORTAL_OR_APP); + appEntity.setAuthorizationType(AuthorizationType.AUTHORIZATION); + appEntity.setRemark(remark); + appRepository.save(appEntity); + + AppFormConfigEntity appFormConfig = new AppFormConfigEntity(); + appFormConfig.setAppId(appEntity.getId()); + //提交类型 + appFormConfig.setSubmitType(FormSubmitType.POST); + appFormConfigRepository.save(appFormConfig); + return String.valueOf(appEntity.getId()); } - /** - * 删除应用 - * - * @param appId {@link String} 应用ID - */ @Override - public void delete(String appId) { - + public FormProtocolConfig getProtocolConfig(String appCode) { + AppFormConfigPO configPo = appFormConfigRepository.findByAppCode(appCode); + return appFormConfigConverter.appFormEntityToConfig(configPo); } - protected FormStandardApplicationServiceImpl(AppCertRepository appCertRepository, - AppRepository appRepository) { - super(appCertRepository, appRepository); + @Override + public AppAccountEntity getAppAccount(Long appId, Long userId) { + return appAccountRepository.findByAppIdAndUserId(appId, userId) + .orElseThrow(AppAccountNotExistException::new); + } + + private final AppFormConfigConverter appFormConfigConverter; + + protected FormStandardApplicationServiceImpl(AppAccountRepository appAccountRepository, + AppFormConfigRepository appFormConfigRepository, + AppRepository appRepository, + AppFormConfigConverter appFormConfigConverter) { + super(appRepository, appAccountRepository, appFormConfigRepository); + this.appFormConfigConverter = appFormConfigConverter; } } diff --git a/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/converter/AppFormConfigConverter.java b/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/converter/AppFormConfigConverter.java new file mode 100644 index 00000000..c0df3a14 --- /dev/null +++ b/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/converter/AppFormConfigConverter.java @@ -0,0 +1,117 @@ +/* + * eiam-application-form - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.application.form.converter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.text.StringSubstitutor; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +import cn.topiam.employee.application.form.model.FormProtocolConfig; +import cn.topiam.employee.application.form.pojo.AppFormConfigGetResult; +import cn.topiam.employee.application.form.pojo.AppFormProtocolEndpoint; +import cn.topiam.employee.application.form.pojo.AppFormSaveConfigParam; +import cn.topiam.employee.common.entity.app.AppFormConfigEntity; +import cn.topiam.employee.common.entity.app.po.AppFormConfigPO; +import cn.topiam.employee.core.context.ServerContextHelp; +import static cn.topiam.employee.common.constants.ProtocolConstants.APP_CODE; +import static cn.topiam.employee.common.constants.ProtocolConstants.FormEndpointConstants.FORM_SSO_PATH; + +/** + * 应用映射 + * + * @author TopIAM + * Created by support@topiam.cn on 2020/8/14 22:45 + */ +@Mapper(componentModel = "spring") +public interface AppFormConfigConverter { + + /** + * save 转 entity + * + * @param config {@link AppFormSaveConfigParam} + * @return {@link AppFormConfigEntity} + */ + @Mapping(target = "updateTime", ignore = true) + @Mapping(target = "updateBy", ignore = true) + @Mapping(target = "remark", ignore = true) + @Mapping(target = "id", ignore = true) + @Mapping(target = "createTime", ignore = true) + @Mapping(target = "createBy", ignore = true) + @Mapping(target = "appId", ignore = true) + AppFormConfigEntity appFormSaveConfigParamToEntity(AppFormSaveConfigParam config); + + /** + * entity转config + * + * @param po {@link AppFormConfigPO} + * @return {@link FormProtocolConfig} + */ + FormProtocolConfig appFormEntityToConfig(AppFormConfigPO po); + + /** + * po 转 result + * + * @param po {@link AppFormConfigPO} + * @return {@link AppFormConfigGetResult} + */ + default AppFormConfigGetResult entityConverterToFormConfigResult(AppFormConfigPO po) { + if (po == null) { + return null; + } + AppFormConfigGetResult result = new AppFormConfigGetResult(); + if (po.getAppId() != null) { + result.setAppId(String.valueOf(po.getAppId())); + } + result.setInitLoginType(po.getInitLoginType()); + result.setInitLoginUrl(po.getInitLoginUrl()); + result.setAuthorizationType(po.getAuthorizationType()); + result.setLoginUrl(po.getLoginUrl()); + result.setUsernameField(po.getUsernameField()); + result.setPasswordField(po.getPasswordField()); + result.setSubmitType(po.getSubmitType()); + List list = po.getOtherField(); + if (list != null) { + result.setOtherField(new ArrayList<>(list)); + } + result.setProtocolEndpoint(getProtocolEndpointDomain(po.getAppCode())); + return result; + } + + /** + * 获取协议端点 + * + * @param appCode {@link String} + * @return {@link AppFormProtocolEndpoint} + */ + private AppFormProtocolEndpoint getProtocolEndpointDomain(String appCode) { + //@formatter:off + AppFormProtocolEndpoint domain = new AppFormProtocolEndpoint(); + Map variables = new HashMap<>(16); + variables.put(APP_CODE,appCode); + StringSubstitutor sub = new StringSubstitutor(variables, "{", "}"); + //IDP SSO 端点 + domain.setIdpSsoEndpoint(sub.replace(ServerContextHelp.getPortalPublicBaseUrl()+FORM_SSO_PATH)); + return domain; + //@formatter:on + } +} diff --git a/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/model/FormProtocolConfig.java b/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/model/FormProtocolConfig.java new file mode 100644 index 00000000..cab38d69 --- /dev/null +++ b/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/model/FormProtocolConfig.java @@ -0,0 +1,77 @@ +/* + * eiam-application-form - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.application.form.model; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +import cn.topiam.employee.common.entity.app.AppFormConfigEntity; +import cn.topiam.employee.common.enums.app.FormSubmitType; + +import lombok.Builder; +import lombok.Data; + +/** + * Form 协议配置 + * + * @author TopIAM + * Created by support@topiam.cn on 2022/8/28 21:43 + */ +@Data +@Builder +public class FormProtocolConfig implements Serializable { + + @Serial + private static final long serialVersionUID = -3671812647788723766L; + + /** + * APP ID + */ + private String appId; + + /** + * APP Code + */ + private String appCode; + + /** + * 登录URL + */ + private String loginUrl; + + /** + * 登录名属性名称 + */ + private String usernameField; + + /** + * 登录密码属性名称 + */ + private String passwordField; + + /** + * 登录提交方式 + */ + private FormSubmitType submitType; + + /** + * 登录其他信息 + */ + private List otherField; +} diff --git a/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/pojo/AppFormConfigGetResult.java b/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/pojo/AppFormConfigGetResult.java new file mode 100644 index 00000000..4e72f037 --- /dev/null +++ b/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/pojo/AppFormConfigGetResult.java @@ -0,0 +1,101 @@ +/* + * eiam-application-form - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.application.form.pojo; + +import java.io.Serializable; +import java.util.List; + +import cn.topiam.employee.common.entity.app.AppFormConfigEntity; +import cn.topiam.employee.common.enums.app.AuthorizationType; +import cn.topiam.employee.common.enums.app.FormSubmitType; +import cn.topiam.employee.common.enums.app.InitLoginType; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Form 配置返回 + * + * @author TopIAM + * Created by support@topiam.cn on 2022/5/31 22:46 + */ +@Data +@Schema(description = "Form 配置返回结果") +public class AppFormConfigGetResult implements Serializable { + /** + * 应用id + */ + @Schema(description = "应用id") + private String appId; + + /** + * SSO 发起方 + */ + @Parameter(description = "SSO 发起方") + private InitLoginType initLoginType; + + /** + * SSO 登录链接 + */ + @Parameter(description = "SSO 登录链接") + private String initLoginUrl; + + /** + * 授权范围 + */ + @Parameter(description = "SSO 授权范围") + private AuthorizationType authorizationType; + + /** + * 登录URL + */ + @Schema(description = "登录URL") + private String loginUrl; + + /** + * 登录名属性名称 + */ + @Schema(description = "登录名属性名称") + private String usernameField; + + /** + * 登录密码属性名称 + */ + @Schema(description = "登录密码属性名称") + private String passwordField; + + /** + * 登录提交方式 + */ + @Schema(description = "登录提交方式") + private FormSubmitType submitType; + + /** + * 登录其他信息 + */ + @Schema(description = "登录其他信息") + private List otherField; + + /** + * 协议端点 + */ + @Schema(description = "协议端点") + private AppFormProtocolEndpoint protocolEndpoint; +} diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/result/LoginMfaFactorResult.java b/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/pojo/AppFormProtocolEndpoint.java similarity index 58% rename from eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/result/LoginMfaFactorResult.java rename to eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/pojo/AppFormProtocolEndpoint.java index 87308dcc..eef01b69 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/result/LoginMfaFactorResult.java +++ b/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/pojo/AppFormProtocolEndpoint.java @@ -1,5 +1,5 @@ /* - * eiam-portal - Employee Identity and Access Management Program + * eiam-application-form - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,38 +15,32 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.portal.pojo.result; +package cn.topiam.employee.application.form.pojo; import java.io.Serial; import java.io.Serializable; -import cn.topiam.employee.common.enums.MfaFactor; - -import lombok.Builder; import lombok.Data; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; + /** - * Mfa 登录方式 - * - * @author TopIAM - * Created by support@topiam.cn on 2022/8/13 21:29 - */ -@Builder +* 协议端点域 +* +* @author TopIAM +* Created by support@topiam.cn on 2022/6/4 23:37 +*/ @Data -public class LoginMfaFactorResult implements Serializable { +@Schema(description = "协议端点") +public class AppFormProtocolEndpoint implements Serializable { @Serial - private static final long serialVersionUID = 7255002979319970337L; + private static final long serialVersionUID = -2261602995152894964L; + /** - * provider + * IDP SSO 端点 */ - private MfaFactor factor; - /** - * 可用 - */ - private Boolean usable; - /** - * 目标 - */ - private String target; + @Parameter(description = "IDP SSO 端点") + private String idpSsoEndpoint; } diff --git a/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/pojo/AppFormSaveConfigParam.java b/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/pojo/AppFormSaveConfigParam.java new file mode 100644 index 00000000..3ebad2c3 --- /dev/null +++ b/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/pojo/AppFormSaveConfigParam.java @@ -0,0 +1,93 @@ +/* + * eiam-application-form - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.application.form.pojo; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +import javax.validation.constraints.NotNull; + +import cn.topiam.employee.common.entity.app.AppFormConfigEntity; +import cn.topiam.employee.common.enums.app.AuthorizationType; +import cn.topiam.employee.common.enums.app.FormSubmitType; +import cn.topiam.employee.common.enums.app.InitLoginType; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * @author TopIAM + * Created by support@topiam.cn on 2022/12/13 22:45 + */ +@Data +@Schema(description = "保存 表单代填 应用配置参数") +public class AppFormSaveConfigParam implements Serializable { + + @Serial + private static final long serialVersionUID = 7257798528680745281L; + + /** + * SSO范围 + */ + @NotNull(message = "SSO范围不能为空") + @Schema(description = "SSO范围") + private AuthorizationType authorizationType; + + /** + * SSO发起方 + */ + @NotNull(message = "SSO发起方不能为空") + @Schema(description = "SSO发起方") + private InitLoginType initLoginType; + + /** + * 登录URL + */ + @NotNull(message = "登录URL不能为空") + @Schema(description = "登录URL") + private String loginUrl; + + /** + * 登录名属性名称 + */ + @NotNull(message = "登录名属性名称不能为空") + @Schema(description = "登录名属性名称") + private String usernameField; + + /** + * 登录密码属性名称 + */ + @NotNull(message = "登录密码属性名称不能为空") + @Schema(description = "登录密码属性名称") + private String passwordField; + + /** + * 登录提交方式 + */ + @NotNull(message = "登录提交方式不能为空") + @Schema(description = "登录提交方式") + private FormSubmitType submitType; + + /** + * 登录其他信息 + */ + @Schema(description = "登录其他信息") + private List otherField; +} diff --git a/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/AbstractOidcApplicationService.java b/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/AbstractOidcApplicationService.java index a01c4e72..7c320944 100644 --- a/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/AbstractOidcApplicationService.java +++ b/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/AbstractOidcApplicationService.java @@ -40,7 +40,7 @@ public abstract class AbstractOidcApplicationService extends AbstractApplication appAccountRepository.deleteAllByAppId(Long.valueOf(appId)); //删除应用权限策略 appAccessPolicyRepository.deleteAllByAppId(Long.valueOf(appId)); - //删除SAML2配置 + //删除OIDC配置 appOidcConfigRepository.deleteByAppId(Long.valueOf(appId)); } diff --git a/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/OidcStandardApplicationServiceImpl.java b/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/OidcStandardApplicationServiceImpl.java index 075a59c5..17993f0b 100644 --- a/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/OidcStandardApplicationServiceImpl.java +++ b/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/OidcStandardApplicationServiceImpl.java @@ -39,7 +39,7 @@ import com.google.common.collect.Sets; import cn.topiam.employee.application.exception.AppNotExistException; import cn.topiam.employee.application.oidc.converter.AppOidcStandardConfigConverter; -import cn.topiam.employee.application.oidc.model.AppOidcStandardSaveConfigParam; +import cn.topiam.employee.application.oidc.pojo.AppOidcStandardSaveConfigParam; import cn.topiam.employee.audit.context.AuditContext; import cn.topiam.employee.common.entity.app.AppEntity; import cn.topiam.employee.common.entity.app.AppOidcConfigEntity; diff --git a/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/converter/AppOidcStandardConfigConverter.java b/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/converter/AppOidcStandardConfigConverter.java index 91452748..d648f1e8 100644 --- a/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/converter/AppOidcStandardConfigConverter.java +++ b/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/converter/AppOidcStandardConfigConverter.java @@ -25,8 +25,9 @@ import org.apache.commons.text.StringSubstitutor; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import cn.topiam.employee.application.oidc.model.AppOidcStandardConfigGetResult; -import cn.topiam.employee.application.oidc.model.AppOidcStandardSaveConfigParam; +import cn.topiam.employee.application.oidc.pojo.AppOidcProtocolEndpoint; +import cn.topiam.employee.application.oidc.pojo.AppOidcStandardConfigGetResult; +import cn.topiam.employee.application.oidc.pojo.AppOidcStandardSaveConfigParam; import cn.topiam.employee.common.constants.ProtocolConstants; import cn.topiam.employee.common.entity.app.AppOidcConfigEntity; import cn.topiam.employee.common.entity.app.po.AppOidcConfigPO; @@ -88,6 +89,7 @@ public interface AppOidcStandardConfigConverter { * @param config {@link AppOidcConfigEntity} * @return {@link AppOidcConfigEntity} */ + @Mapping(target = "responseTypes", ignore = true) @Mapping(target = "updateTime", ignore = true) @Mapping(target = "updateBy", ignore = true) @Mapping(target = "remark", ignore = true) @@ -101,11 +103,11 @@ public interface AppOidcStandardConfigConverter { * 获取协议端点 * * @param appCode {@link String} - * @return {@link AppOidcStandardConfigGetResult.ProtocolEndpoint} + * @return {@link AppOidcProtocolEndpoint} */ - private AppOidcStandardConfigGetResult.ProtocolEndpoint getProtocolEndpointDomain(String appCode) { + private AppOidcProtocolEndpoint getProtocolEndpointDomain(String appCode) { //@formatter:off - AppOidcStandardConfigGetResult.ProtocolEndpoint domain = new AppOidcStandardConfigGetResult.ProtocolEndpoint(); + AppOidcProtocolEndpoint domain = new AppOidcProtocolEndpoint(); //issues Map variables = new HashMap<>(16); variables.put(APP_CODE,appCode); diff --git a/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/pojo/AppOidcProtocolEndpoint.java b/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/pojo/AppOidcProtocolEndpoint.java new file mode 100644 index 00000000..f5d5a6f6 --- /dev/null +++ b/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/pojo/AppOidcProtocolEndpoint.java @@ -0,0 +1,81 @@ +/* + * eiam-application-oidc - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.application.oidc.pojo; + +import java.io.Serial; +import java.io.Serializable; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * 协议端点域 + * + * @author TopIAM + * Created by support@topiam.cn on 2022/6/4 23:37 + */ +@Data +@Schema(description = "协议端点") +public class AppOidcProtocolEndpoint implements Serializable { + + @Serial + private static final long serialVersionUID = -2261602995152894964L; + /** + * oidcIssuer + */ + @Parameter(description = "Issuer") + private String issuer; + + /** + * discoveryEndpoint + */ + @Parameter(description = "Discovery Endpoint") + private String discoveryEndpoint; + + /** + * UserinfoEndpoint + */ + @Parameter(description = "UserInfo Endpoint") + private String userinfoEndpoint; + + /** + * jwksEndpoint + */ + @Parameter(description = "Jwks Endpoint") + private String jwksEndpoint; + + /** + * revokeEndpoint + */ + @Parameter(description = "Revoke Endpoint") + private String revokeEndpoint; + + /** + * tokenEndpoint + */ + @Parameter(description = "Token Endpoint") + private String tokenEndpoint; + + /** + * authorizationEndpoint + */ + @Parameter(description = "Authorization Endpoint") + private String authorizationEndpoint; +} diff --git a/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/model/AppOidcStandardConfigGetResult.java b/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/pojo/AppOidcStandardConfigGetResult.java similarity index 55% rename from eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/model/AppOidcStandardConfigGetResult.java rename to eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/pojo/AppOidcStandardConfigGetResult.java index ccea1df6..0ca3fd5a 100644 --- a/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/model/AppOidcStandardConfigGetResult.java +++ b/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/pojo/AppOidcStandardConfigGetResult.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.application.oidc.model; +package cn.topiam.employee.application.oidc.pojo; import java.io.Serial; import java.io.Serializable; @@ -40,163 +40,109 @@ import io.swagger.v3.oas.annotations.media.Schema; public class AppOidcStandardConfigGetResult implements Serializable { @Serial - private static final long serialVersionUID = 4177874005424703372L; + private static final long serialVersionUID = 4177874005424703372L; /** * APP ID */ @Parameter(description = "appId") - private Long appId; + private Long appId; /** * SSO 发起方 */ @Parameter(description = "SSO 发起方") - private InitLoginType initLoginType; + private InitLoginType initLoginType; /** * SSO 登录链接 */ @Parameter(description = "SSO 登录链接") - private String initLoginUrl; + private String initLoginUrl; /** * 授权范围 */ @Parameter(description = "SSO 授权范围") - private AuthorizationType authorizationType; + private AuthorizationType authorizationType; /** * authorizationGrantTypes */ @Parameter(description = "认证授权类型") - private Set authGrantTypes; + private Set authGrantTypes; /** * 客户端认证方式 */ @Parameter(description = "客户端认证方式") - private Set clientAuthMethods; + private Set clientAuthMethods; /** * 重定向URI */ @Parameter(description = "重定向URI") - private Set redirectUris; + private Set redirectUris; /** * scopes */ @Parameter(description = "授权范围") - private Set grantScopes; + private Set grantScopes; /** * 启用PKCE */ @Parameter(description = "启用PKCE") - private Boolean requireProofKey; + private Boolean requireProofKey; /** * 令牌 Endpoint 身份验证签名算法 */ @Parameter(description = "令牌 Endpoint 身份验证签名算法") - private String tokenEndpointAuthSigningAlgorithm; + private String tokenEndpointAuthSigningAlgorithm; /** * 是否需要授权同意 */ @Parameter(description = "是否需要授权同意") - private Boolean requireAuthConsent; + private Boolean requireAuthConsent; /** * 访问令牌有效时间 */ @Parameter(description = "访问令牌有效时间") - private String accessTokenTimeToLive; + private String accessTokenTimeToLive; /** * 刷新令牌有效时间 */ @Parameter(description = "刷新令牌有效时间") - private String refreshTokenTimeToLive; + private String refreshTokenTimeToLive; /** * ID token 有效时间 */ @Parameter(description = "ID 令牌有效时间") - private String idTokenTimeToLive; + private String idTokenTimeToLive; /** * id 令牌签名算法 */ @Parameter(description = "Id令牌签名算法") - private String idTokenSignatureAlgorithm; + private String idTokenSignatureAlgorithm; /** * 协议端点域 */ @Parameter(description = "协议端点域") - private ProtocolEndpoint protocolEndpoint; + private AppOidcProtocolEndpoint protocolEndpoint; /** * Access Token 格式 */ @Parameter(description = "Access Token 格式") - private String accessTokenFormat; + private String accessTokenFormat; /** * 是否重用刷新令牌 */ @Parameter(description = "是否重用刷新令牌") - private Boolean reuseRefreshToken; + private Boolean reuseRefreshToken; - /** - * 协议端点域 - * - * @author TopIAM - * Created by support@topiam.cn on 2022/6/4 23:37 - */ - @Data - @Schema(description = "协议端点") - public static class ProtocolEndpoint implements Serializable { - - @Serial - private static final long serialVersionUID = -2261602995152894964L; - /** - * oidcIssuer - */ - @Parameter(description = "Issuer") - private String issuer; - - /** - * discoveryEndpoint - */ - @Parameter(description = "Discovery Endpoint") - private String discoveryEndpoint; - - /** - * UserinfoEndpoint - */ - @Parameter(description = "UserInfo Endpoint") - private String userinfoEndpoint; - - /** - * jwksEndpoint - */ - @Parameter(description = "Jwks Endpoint") - private String jwksEndpoint; - - /** - * revokeEndpoint - */ - @Parameter(description = "Revoke Endpoint") - private String revokeEndpoint; - - /** - * tokenEndpoint - */ - @Parameter(description = "Token Endpoint") - private String tokenEndpoint; - - /** - * authorizationEndpoint - */ - @Parameter(description = "Authorization Endpoint") - private String authorizationEndpoint; - } } diff --git a/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/model/AppOidcStandardSaveConfigParam.java b/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/pojo/AppOidcStandardSaveConfigParam.java similarity index 98% rename from eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/model/AppOidcStandardSaveConfigParam.java rename to eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/pojo/AppOidcStandardSaveConfigParam.java index f66a60b4..4140309f 100644 --- a/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/model/AppOidcStandardSaveConfigParam.java +++ b/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/pojo/AppOidcStandardSaveConfigParam.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.application.oidc.model; +package cn.topiam.employee.application.oidc.pojo; import java.io.Serial; import java.io.Serializable; diff --git a/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/package-info.java b/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/pojo/package-info.java similarity index 85% rename from eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/package-info.java rename to eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/pojo/package-info.java index 24c3c14d..14b28282 100644 --- a/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/package-info.java +++ b/eiam-application/eiam-application-oidc/src/main/java/cn/topiam/employee/application/oidc/pojo/package-info.java @@ -1,5 +1,5 @@ /* - * eiam-application-saml2 - Employee Identity and Access Management Program + * eiam-application-oidc - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,4 +15,4 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.application; \ No newline at end of file +package cn.topiam.employee.application.oidc.pojo; \ No newline at end of file diff --git a/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/AbstractSamlAppService.java b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/AbstractSamlAppService.java index 9a369e5a..a05cc437 100644 --- a/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/AbstractSamlAppService.java +++ b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/AbstractSamlAppService.java @@ -26,11 +26,14 @@ import org.mapstruct.Mapping; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.AlternativeJdkIdGenerator; import org.springframework.util.CollectionUtils; +import org.springframework.util.IdGenerator; import cn.topiam.employee.application.AbstractApplicationService; -import cn.topiam.employee.application.Saml2ApplicationService; import cn.topiam.employee.application.exception.AppCertNotExistException; +import cn.topiam.employee.application.saml2.model.Saml2ProtocolConfig; +import cn.topiam.employee.application.saml2.model.Saml2SsoModel; import cn.topiam.employee.common.entity.account.UserEntity; import cn.topiam.employee.common.entity.app.AppAccountEntity; import cn.topiam.employee.common.entity.app.AppCertEntity; @@ -43,8 +46,6 @@ import cn.topiam.employee.common.exception.app.AppAccountNotExistException; import cn.topiam.employee.common.repository.account.UserRepository; import cn.topiam.employee.common.repository.app.*; import cn.topiam.employee.common.util.SamlKeyStoreProvider; -import cn.topiam.employee.core.protocol.Saml2ProtocolConfig; -import cn.topiam.employee.core.protocol.Saml2SsoModel; import cn.topiam.employee.core.security.util.SecurityUtils; import cn.topiam.employee.support.context.ApplicationContextHelp; import static cn.topiam.employee.common.enums.app.SamlNameIdValueType.*; @@ -134,6 +135,11 @@ public abstract class AbstractSamlAppService extends AbstractApplicationService */ protected final AppSaml2ConfigRepository appSaml2ConfigRepository; + /** + * IdGenerator + */ + protected final IdGenerator idGenerator; + protected AbstractSamlAppService(AppCertRepository appCertRepository, AppAccountRepository appAccountRepository, AppAccessPolicyRepository appAccessPolicyRepository, @@ -141,6 +147,7 @@ public abstract class AbstractSamlAppService extends AbstractApplicationService AppSaml2ConfigRepository appSaml2ConfigRepository) { super(appCertRepository, appAccountRepository, appAccessPolicyRepository, appRepository); this.appSaml2ConfigRepository = appSaml2ConfigRepository; + this.idGenerator = new AlternativeJdkIdGenerator(); } @Mapper(componentModel = "spring") diff --git a/eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/Saml2ApplicationService.java b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/Saml2ApplicationService.java similarity index 79% rename from eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/Saml2ApplicationService.java rename to eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/Saml2ApplicationService.java index 4f25df24..98e8b50d 100644 --- a/eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/Saml2ApplicationService.java +++ b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/Saml2ApplicationService.java @@ -1,5 +1,5 @@ /* - * eiam-application-core - Employee Identity and Access Management Program + * eiam-application-saml2 - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,10 +15,11 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.application; +package cn.topiam.employee.application.saml2; -import cn.topiam.employee.core.protocol.Saml2ProtocolConfig; -import cn.topiam.employee.core.protocol.Saml2SsoModel; +import cn.topiam.employee.application.ApplicationService; +import cn.topiam.employee.application.saml2.model.Saml2ProtocolConfig; +import cn.topiam.employee.application.saml2.model.Saml2SsoModel; /** * 应用接口 diff --git a/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/Saml2StandardApplicationServiceImpl.java b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/Saml2StandardApplicationServiceImpl.java index eb3e424c..6db7a640 100644 --- a/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/Saml2StandardApplicationServiceImpl.java +++ b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/Saml2StandardApplicationServiceImpl.java @@ -28,9 +28,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; + import cn.topiam.employee.application.exception.AppNotExistException; import cn.topiam.employee.application.saml2.converter.AppSaml2StandardConfigConverter; -import cn.topiam.employee.application.saml2.model.AppSaml2StandardSaveConfigParam; +import cn.topiam.employee.application.saml2.pojo.AppSaml2StandardSaveConfigParam; import cn.topiam.employee.audit.context.AuditContext; import cn.topiam.employee.common.entity.app.AppEntity; import cn.topiam.employee.common.entity.app.AppSaml2ConfigEntity; @@ -69,6 +71,7 @@ public class Saml2StandardApplicationServiceImpl extends AbstractSamlAppService public void saveConfig(String appId, Map config) { AppSaml2StandardSaveConfigParam model; try { + ObjectMapper mapper = new ObjectMapper(); String value = mapper.writeValueAsString(config); // 指定序列化输入的类型 mapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false); diff --git a/eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/SamlRamRoleNameValueType.java b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/SamlRamRoleNameValueType.java similarity index 91% rename from eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/SamlRamRoleNameValueType.java rename to eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/SamlRamRoleNameValueType.java index 0f18b798..e357d9c5 100644 --- a/eiam-application/eiam-application-core/src/main/java/cn/topiam/employee/application/SamlRamRoleNameValueType.java +++ b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/SamlRamRoleNameValueType.java @@ -1,5 +1,5 @@ /* - * eiam-application-core - Employee Identity and Access Management Program + * eiam-application-saml2 - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.application; +package cn.topiam.employee.application.saml2; import com.fasterxml.jackson.annotation.JsonValue; diff --git a/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/converter/AppSaml2StandardConfigConverter.java b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/converter/AppSaml2StandardConfigConverter.java index 053b1ccb..48eefa9b 100644 --- a/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/converter/AppSaml2StandardConfigConverter.java +++ b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/converter/AppSaml2StandardConfigConverter.java @@ -22,9 +22,9 @@ import java.util.List; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import cn.topiam.employee.application.saml2.model.AppSaml2StandardConfigGetResult; -import cn.topiam.employee.application.saml2.model.AppSaml2StandardSaveConfigParam; -import cn.topiam.employee.application.saml2.model.Saml2ConverterUtils; +import cn.topiam.employee.application.saml2.pojo.AppSaml2StandardConfigGetResult; +import cn.topiam.employee.application.saml2.pojo.AppSaml2StandardSaveConfigParam; +import cn.topiam.employee.application.saml2.pojo.Saml2ConverterUtils; import cn.topiam.employee.common.entity.app.AppSaml2ConfigEntity; import cn.topiam.employee.common.entity.app.po.AppSaml2ConfigPO; diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/protocol/Saml2ProtocolConfig.java b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/Saml2ProtocolConfig.java similarity index 97% rename from eiam-core/src/main/java/cn/topiam/employee/core/protocol/Saml2ProtocolConfig.java rename to eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/Saml2ProtocolConfig.java index 6e5f646a..7b945fda 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/protocol/Saml2ProtocolConfig.java +++ b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/Saml2ProtocolConfig.java @@ -1,5 +1,5 @@ /* - * eiam-core - Employee Identity and Access Management Program + * eiam-application-saml2 - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.core.protocol; +package cn.topiam.employee.application.saml2.model; import java.io.Serial; import java.io.Serializable; diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/protocol/Saml2SsoModel.java b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/Saml2SsoModel.java similarity index 97% rename from eiam-core/src/main/java/cn/topiam/employee/core/protocol/Saml2SsoModel.java rename to eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/Saml2SsoModel.java index 82d145a3..5b4b8041 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/protocol/Saml2SsoModel.java +++ b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/Saml2SsoModel.java @@ -1,5 +1,5 @@ /* - * eiam-core - Employee Identity and Access Management Program + * eiam-application-saml2 - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.core.protocol; +package cn.topiam.employee.application.saml2.model; import java.io.Serial; import java.io.Serializable; diff --git a/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/Saml2ProtocolEndpoint.java b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/pojo/AppSaml2ProtocolEndpoint.java similarity index 93% rename from eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/Saml2ProtocolEndpoint.java rename to eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/pojo/AppSaml2ProtocolEndpoint.java index 2e16338e..1adf4dad 100644 --- a/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/Saml2ProtocolEndpoint.java +++ b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/pojo/AppSaml2ProtocolEndpoint.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.application.saml2.model; +package cn.topiam.employee.application.saml2.pojo; import java.io.Serial; import java.io.Serializable; @@ -33,7 +33,7 @@ import io.swagger.v3.oas.annotations.media.Schema; */ @Data @Schema(description = "协议端点") -public class Saml2ProtocolEndpoint implements Serializable { +public class AppSaml2ProtocolEndpoint implements Serializable { @Serial private static final long serialVersionUID = -2261602995152894964L; diff --git a/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/AppSaml2StandardConfigGetResult.java b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/pojo/AppSaml2StandardConfigGetResult.java similarity index 97% rename from eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/AppSaml2StandardConfigGetResult.java rename to eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/pojo/AppSaml2StandardConfigGetResult.java index 02605901..24e90c86 100644 --- a/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/AppSaml2StandardConfigGetResult.java +++ b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/pojo/AppSaml2StandardConfigGetResult.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.application.saml2.model; +package cn.topiam.employee.application.saml2.pojo; import java.util.List; import java.util.Map; @@ -160,7 +160,7 @@ public class AppSaml2StandardConfigGetResult { * 协议端点域 */ @Parameter(description = "协议端点域") - private Saml2ProtocolEndpoint protocolEndpoint; + private AppSaml2ProtocolEndpoint protocolEndpoint; /** * 模版配置 diff --git a/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/AppSaml2StandardSaveConfigParam.java b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/pojo/AppSaml2StandardSaveConfigParam.java similarity index 98% rename from eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/AppSaml2StandardSaveConfigParam.java rename to eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/pojo/AppSaml2StandardSaveConfigParam.java index 8923844e..d36de479 100644 --- a/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/AppSaml2StandardSaveConfigParam.java +++ b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/pojo/AppSaml2StandardSaveConfigParam.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.application.saml2.model; +package cn.topiam.employee.application.saml2.pojo; import java.io.Serial; import java.io.Serializable; diff --git a/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/Saml2ConverterUtils.java b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/pojo/Saml2ConverterUtils.java similarity index 83% rename from eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/Saml2ConverterUtils.java rename to eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/pojo/Saml2ConverterUtils.java index ac618f1e..5e528d7c 100644 --- a/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/model/Saml2ConverterUtils.java +++ b/eiam-application/eiam-application-saml2/src/main/java/cn/topiam/employee/application/saml2/pojo/Saml2ConverterUtils.java @@ -15,10 +15,11 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.application.saml2.model; +package cn.topiam.employee.application.saml2.pojo; import cn.topiam.employee.core.context.ServerContextHelp; -import static cn.topiam.employee.common.constants.ProtocolConstants.*; +import static cn.topiam.employee.common.constants.ProtocolConstants.APP_CODE_VARIABLE; +import static cn.topiam.employee.common.constants.ProtocolConstants.Saml2EndpointConstants; /** * Saml2ConverterUtils @@ -31,10 +32,10 @@ public class Saml2ConverterUtils { * 应用ID * * @param appCode {@link String} - * @return {@link Saml2ProtocolEndpoint} + * @return {@link AppSaml2ProtocolEndpoint} */ - public static Saml2ProtocolEndpoint getProtocolEndpointDomain(String appCode) { - Saml2ProtocolEndpoint domain = new Saml2ProtocolEndpoint(); + public static AppSaml2ProtocolEndpoint getProtocolEndpointDomain(String appCode) { + AppSaml2ProtocolEndpoint domain = new AppSaml2ProtocolEndpoint(); //IDP String baseUrl = ServerContextHelp.getPortalPublicBaseUrl(); //元数据端点 diff --git a/eiam-audit/src/main/java/cn/topiam/employee/audit/context/AuditContext.java b/eiam-audit/src/main/java/cn/topiam/employee/audit/context/AuditContext.java index 667113b4..aee7f92b 100644 --- a/eiam-audit/src/main/java/cn/topiam/employee/audit/context/AuditContext.java +++ b/eiam-audit/src/main/java/cn/topiam/employee/audit/context/AuditContext.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import org.springframework.security.core.Authentication; import org.springframework.util.CollectionUtils; import com.alibaba.ttl.TransmittableThreadLocal; @@ -41,6 +42,11 @@ public class AuditContext { */ private static final TransmittableThreadLocal CONTENT = new TransmittableThreadLocal<>(); + /** + * Authentication + */ + private static final TransmittableThreadLocal AUTHENTICATION = new TransmittableThreadLocal<>(); + /** * 目标对象 */ @@ -122,6 +128,19 @@ public class AuditContext { ADDITIONAL_DATA.set(value); } + /** + * Get Authentication + * + * @return {@link Authentication} + */ + public static Authentication getAuthorization() { + return AUTHENTICATION.get(); + } + + public static void setAuthorization(Authentication authorization) { + AUTHENTICATION.set(authorization); + } + /** * Get Target * @@ -156,6 +175,13 @@ public class AuditContext { TARGET_LIST.remove(); } + /** + * Remove Authentication + */ + public static void removeAuthentication() { + AUTHENTICATION.remove(); + } + /** * remove */ @@ -182,5 +208,7 @@ public class AuditContext { removeAdditionalData(); removeContent(); removeTarget(); + removeAuthentication(); } + } diff --git a/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/Actor.java b/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/Actor.java index bbf4dd48..0935d4b4 100644 --- a/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/Actor.java +++ b/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/Actor.java @@ -40,6 +40,8 @@ public class Actor implements Serializable { public static final String ACTOR_ID = "actor.id"; public static final String ACTOR_TYPE = "actor.type"; + public static final String ACTOR_AUTH_TYPE = "actor.auth_type.keyword"; + @Serial private static final long serialVersionUID = -1144169992714000310L; /** @@ -54,4 +56,10 @@ public class Actor implements Serializable { @Field(type = FieldType.Keyword, name = "type") private UserType type; + /** + * 身份验证类型 + */ + @Field(type = FieldType.Keyword, name = "auth_type") + private String authType; + } diff --git a/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/AuditEntity.java b/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/AuditEntity.java index 1c2adfde..7da1b835 100644 --- a/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/AuditEntity.java +++ b/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/AuditEntity.java @@ -25,18 +25,23 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; import org.hibernate.annotations.Type; +import org.hibernate.annotations.Where; import cn.topiam.employee.audit.enums.EventStatus; import cn.topiam.employee.audit.enums.EventType; import cn.topiam.employee.common.enums.UserType; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * 审计 @@ -51,7 +56,10 @@ import lombok.experimental.Accessors; @Accessors(chain = true) @Entity @Table(name = "audit") -public class AuditEntity extends BaseEntity { +@SQLDelete(sql = "update audit set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update audit set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class AuditEntity extends LogicDeleteEntity { @Serial private static final long serialVersionUID = -3119319193111206582L; @@ -136,4 +144,10 @@ public class AuditEntity extends BaseEntity { */ @Column(name = "actor_type") private UserType actorType; + + /** + * 身份验证类型 + */ + @Column(name = "actor_auth_type") + private String actorAuthType; } diff --git a/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/Event.java b/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/Event.java index 6b7b7787..f145df1a 100644 --- a/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/Event.java +++ b/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/Event.java @@ -64,7 +64,7 @@ public class Event implements Serializable { /** * 事件内容 */ - @Field(type = FieldType.Text, name = "content") + @Field(type = FieldType.Object, name = "content") private String content; /** diff --git a/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/GeoLocation.java b/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/GeoLocation.java index 6d36b0fd..2d9ac4e4 100644 --- a/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/GeoLocation.java +++ b/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/GeoLocation.java @@ -41,7 +41,9 @@ import lombok.Data; public class GeoLocation implements Serializable { @Serial - private static final long serialVersionUID = -1144169992714000310L; + private static final long serialVersionUID = -1144169992714000310L; + + public static final String GEO_LOCATION_PROVINCE_CODE = "geo_location.province_code.keyword"; /** * IP diff --git a/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/Target.java b/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/Target.java index 054b1e64..9b143bec 100644 --- a/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/Target.java +++ b/eiam-audit/src/main/java/cn/topiam/employee/audit/entity/Target.java @@ -48,6 +48,12 @@ public class Target implements Serializable { */ @Field(type = FieldType.Keyword, name = "id") private String id; + + /** + * 目标名称 + */ + @Field(type = FieldType.Keyword, name = "name") + private String name; /** * * 目标类型 @@ -55,4 +61,9 @@ public class Target implements Serializable { @Field(type = FieldType.Keyword, name = "type") private TargetType type; + /** + * 目标类型名称 + */ + @Field(type = FieldType.Keyword, name = "type_name") + private String typeName; } diff --git a/eiam-audit/src/main/java/cn/topiam/employee/audit/event/AuditEvent.java b/eiam-audit/src/main/java/cn/topiam/employee/audit/event/AuditEvent.java index 93cce30b..138b1136 100644 --- a/eiam-audit/src/main/java/cn/topiam/employee/audit/event/AuditEvent.java +++ b/eiam-audit/src/main/java/cn/topiam/employee/audit/event/AuditEvent.java @@ -18,7 +18,7 @@ package cn.topiam.employee.audit.event; import java.io.Serial; -import java.util.*; +import java.util.List; import org.springframework.context.ApplicationEvent; diff --git a/eiam-audit/src/main/java/cn/topiam/employee/audit/event/AuditEventListener.java b/eiam-audit/src/main/java/cn/topiam/employee/audit/event/AuditEventListener.java index 5410e82f..af91a912 100644 --- a/eiam-audit/src/main/java/cn/topiam/employee/audit/event/AuditEventListener.java +++ b/eiam-audit/src/main/java/cn/topiam/employee/audit/event/AuditEventListener.java @@ -30,7 +30,7 @@ import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; import cn.topiam.employee.audit.entity.*; -import cn.topiam.employee.audit.repository.*; +import cn.topiam.employee.audit.repository.AuditRepository; import cn.topiam.employee.core.configuration.EiamSupportProperties; import static cn.topiam.employee.common.constants.AuditConstants.getAuditIndexPrefix; import static cn.topiam.employee.support.constant.EiamConstants.DEFAULT_DATE_FORMATTER_PATTERN; diff --git a/eiam-audit/src/main/java/cn/topiam/employee/audit/event/AuditEventPublish.java b/eiam-audit/src/main/java/cn/topiam/employee/audit/event/AuditEventPublish.java index f1ab28d9..20d2d3f6 100644 --- a/eiam-audit/src/main/java/cn/topiam/employee/audit/event/AuditEventPublish.java +++ b/eiam-audit/src/main/java/cn/topiam/employee/audit/event/AuditEventPublish.java @@ -35,6 +35,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import com.alibaba.fastjson2.JSONObject; +import com.google.common.collect.Maps; import cn.topiam.employee.audit.entity.*; import cn.topiam.employee.audit.enums.EventStatus; @@ -86,6 +87,35 @@ public class AuditEventPublish { //@formatter:on } + /** + * 发布 审计事件 + * + * @param eventType {@link EventType} + */ + public void publish(EventType eventType, Authentication authentication, EventStatus eventStatus, + List targets, String result) { + //@formatter:off + //封装操作事件 + Event event = Event.builder() + .type(eventType) + .time(Instant.now()) + .result(result) + .status(eventStatus).build(); + if (authentication.getPrincipal() instanceof UserDetails){ + String username = ((UserDetails) authentication.getPrincipal()).getUsername(); + event.setContent(username+":"+event.getType().getDesc()); + } + //封装地理位置 + GeoLocation geoLocationModal = getGeoLocation(); + //封装用户代理 + UserAgent userAgent = getUserAgent(); + //封装操作人 + Actor actor = getActor(authentication); + //Publish AuditEvent + applicationEventPublisher.publishEvent(new AuditEvent(TraceUtils.get(), ServletContextHelp.getSession().getId(), actor, event, userAgent, geoLocationModal, targets)); + //@formatter:on + } + /** * 发布 审计事件 * @@ -99,9 +129,12 @@ public class AuditEventPublish { .type(eventType) .time(Instant.now()) .status(eventStatus).build(); - if (authentication.getPrincipal() instanceof UserDetails){ - String username = ((UserDetails) authentication.getPrincipal()).getUsername(); - event.setContent(username+":"+event.getType().getDesc()); + if (authentication.getPrincipal() instanceof UserDetails principal){ + String username = principal.getUsername(); + Map content= Maps.newConcurrentMap(); + content.put("auth_type",principal.getAuthType()); + content.put("desc",username+":"+event.getType().getDesc()); + event.setContent(JSONObject.toJSONString(content)); } //封装地理位置 GeoLocation geoLocationModal = getGeoLocation(); @@ -209,10 +242,16 @@ public class AuditEventPublish { //@formatter:off SecurityContext securityContext = SecurityContextHolder.getContext(); Authentication authentication = securityContext.getAuthentication(); - return Actor.builder() + Object principal = authentication.getPrincipal(); + + Actor actor = Actor.builder() .id(getActorId(authentication)) .type(getActorType(authentication)) .build(); + if (principal instanceof UserDetails){ + actor.setAuthType(((UserDetails) principal).getAuthType()); + } + return actor; //@formatter:on } @@ -223,10 +262,15 @@ public class AuditEventPublish { */ private Actor getActor(Authentication authentication) { //@formatter:off - return Actor.builder() + Actor actor = Actor.builder() .id(getActorId(authentication)) .type(getActorType(authentication)) .build(); + Object principal = authentication.getPrincipal(); + if (principal instanceof UserDetails){ + actor.setAuthType(((UserDetails) principal).getAuthType()); + } + return actor; //@formatter:on } diff --git a/eiam-audit/src/main/java/cn/topiam/employee/audit/repository/AuditRepository.java b/eiam-audit/src/main/java/cn/topiam/employee/audit/repository/AuditRepository.java index 3b05e6cf..21615f80 100644 --- a/eiam-audit/src/main/java/cn/topiam/employee/audit/repository/AuditRepository.java +++ b/eiam-audit/src/main/java/cn/topiam/employee/audit/repository/AuditRepository.java @@ -21,11 +21,11 @@ import java.time.LocalDateTime; import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import cn.topiam.employee.audit.entity.AuditEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; /** * 行为审计repository @@ -34,7 +34,7 @@ import cn.topiam.employee.audit.entity.AuditEntity; * Created by support@topiam.cn on 2021/9/11 22:32 */ @Repository -public interface AuditRepository extends CrudRepository, +public interface AuditRepository extends LogicDeleteRepository, QuerydslPredicateExecutor { /** diff --git a/eiam-audit/src/main/java/cn/topiam/employee/audit/service/converter/AuditDataConverter.java b/eiam-audit/src/main/java/cn/topiam/employee/audit/service/converter/AuditDataConverter.java index e23eebe9..ffef737d 100644 --- a/eiam-audit/src/main/java/cn/topiam/employee/audit/service/converter/AuditDataConverter.java +++ b/eiam-audit/src/main/java/cn/topiam/employee/audit/service/converter/AuditDataConverter.java @@ -40,13 +40,33 @@ import com.google.common.collect.Lists; import cn.topiam.employee.audit.controller.pojo.AuditListQuery; import cn.topiam.employee.audit.controller.pojo.AuditListResult; -import cn.topiam.employee.audit.entity.*; +import cn.topiam.employee.audit.entity.Actor; +import cn.topiam.employee.audit.entity.AuditElasticSearchEntity; +import cn.topiam.employee.audit.entity.Event; +import cn.topiam.employee.audit.entity.Target; import cn.topiam.employee.audit.enums.EventType; +import cn.topiam.employee.audit.enums.TargetType; +import cn.topiam.employee.common.entity.account.OrganizationEntity; import cn.topiam.employee.common.entity.account.UserEntity; +import cn.topiam.employee.common.entity.account.UserGroupEntity; +import cn.topiam.employee.common.entity.app.AppEntity; +import cn.topiam.employee.common.entity.app.AppPermissionResourceEntity; +import cn.topiam.employee.common.entity.app.AppPermissionRoleEntity; +import cn.topiam.employee.common.entity.authentication.IdentityProviderEntity; +import cn.topiam.employee.common.entity.identitysource.IdentitySourceEntity; import cn.topiam.employee.common.entity.setting.AdministratorEntity; +import cn.topiam.employee.common.entity.setting.MailTemplateEntity; import cn.topiam.employee.common.enums.UserType; +import cn.topiam.employee.common.repository.account.OrganizationRepository; +import cn.topiam.employee.common.repository.account.UserGroupRepository; import cn.topiam.employee.common.repository.account.UserRepository; +import cn.topiam.employee.common.repository.app.AppPermissionResourceRepository; +import cn.topiam.employee.common.repository.app.AppPermissionRoleRepository; +import cn.topiam.employee.common.repository.app.AppRepository; +import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; +import cn.topiam.employee.common.repository.identitysource.IdentitySourceRepository; import cn.topiam.employee.common.repository.setting.AdministratorRepository; +import cn.topiam.employee.common.repository.setting.MailTemplateRepository; import cn.topiam.employee.support.context.ApplicationContextHelp; import cn.topiam.employee.support.repository.page.domain.Page; import cn.topiam.employee.support.repository.page.domain.PageModel; @@ -69,7 +89,7 @@ public interface AuditDataConverter { * searchHits 转审计列表 * * @param search {@link SearchHits} - * @param page {@link PageModel} + * @param page {@link PageModel} * @return {@link Page} */ default Page searchHitsConvertToAuditListResult(SearchHits search, @@ -94,6 +114,14 @@ public interface AuditDataConverter { //用户类型 result.setUserType(actor.getType().getCode()); //操作对象 + if (Objects.nonNull(content.getTargets())) { + for (Target target : content.getTargets()) { + if (Objects.nonNull(target.getId())) { + target.setName(getTargetName(target.getType(), target.getId())); + } + target.setTypeName(target.getType().getDesc()); + } + } result.setTargets(content.getTargets()); list.add(result); }); @@ -102,7 +130,7 @@ public interface AuditDataConverter { result.setPagination(Page.Pagination.builder() .total(search.getTotalHits()) .totalPages(Math.toIntExact(search.getTotalHits() / page.getPageSize())) - .current(page.getCurrent()+1) + .current(page.getCurrent() + 1) .build()); result.setList(list); //@formatter:on @@ -110,10 +138,9 @@ public interface AuditDataConverter { } /** - * * 获取用户名 * - * @param actorId {@link String} + * @param actorId {@link String} * @param actorType {@link UserType} * @return {@link String} */ @@ -140,7 +167,7 @@ public interface AuditDataConverter { * 审计列表请求到本机搜索查询 * * @param query {@link AuditListQuery} - * @param page {@link PageModel} + * @param page {@link PageModel} * @return {@link NativeSearchQuery} */ default NativeSearchQuery auditListRequestConvertToNativeSearchQuery(AuditListQuery query, @@ -201,4 +228,118 @@ public interface AuditDataConverter { //排序 .withSorts(fieldSortBuilders).build(); } + + /** + * 获取目标名称 + * + * @param targetType {@link TargetType} + * @param id {@link String} + * @return + */ + @SuppressWarnings("AlibabaMethodTooLong") + default String getTargetName(TargetType targetType, String id) { + String name = ""; + if (TargetType.USER.equals(targetType) || TargetType.USER_DETAIL.equals(targetType)) { + UserRepository userRepository = ApplicationContextHelp.getBean(UserRepository.class); + Optional user = userRepository.findByIdContainsDeleted(Long.valueOf(id)); + if (user.isPresent()) { + name = user.get().getUsername(); + } + } + + if (TargetType.USER_GROUP.equals(targetType)) { + UserGroupRepository userGroupRepository = ApplicationContextHelp + .getBean(UserGroupRepository.class); + Optional userGroup = userGroupRepository + .findByIdContainsDeleted(Long.valueOf(id)); + if (userGroup.isPresent()) { + name = userGroup.get().getName(); + } + } + + if (TargetType.IDENTITY_SOURCE.equals(targetType)) { + IdentitySourceRepository identitySourceRepository = ApplicationContextHelp + .getBean(IdentitySourceRepository.class); + Optional identitySource = identitySourceRepository + .findByIdContainsDeleted(Long.valueOf(id)); + if (identitySource.isPresent()) { + name = identitySource.get().getName(); + } + } + + if (TargetType.ORGANIZATION.equals(targetType)) { + OrganizationRepository organizationRepository = ApplicationContextHelp + .getBean(OrganizationRepository.class); + Optional organizationEntity = organizationRepository + .findByIdContainsDeleted(id); + if (organizationEntity.isPresent()) { + name = organizationEntity.get().getName(); + } + } + + if (TargetType.APPLICATION.equals(targetType)) { + AppRepository appRepository = ApplicationContextHelp.getBean(AppRepository.class); + Optional appEntity = appRepository.findByIdContainsDeleted(Long.valueOf(id)); + if (appEntity.isPresent()) { + name = appEntity.get().getName(); + } + } + + if (TargetType.APP_PERMISSION_RESOURCE.equals(targetType)) { + AppPermissionResourceRepository appPermissionResourceRepository = ApplicationContextHelp + .getBean(AppPermissionResourceRepository.class); + Optional appPermissionResourceEntity = appPermissionResourceRepository + .findByIdContainsDeleted(Long.valueOf(id)); + if (appPermissionResourceEntity.isPresent()) { + name = appPermissionResourceEntity.get().getName(); + } + } + + if (TargetType.APPLICATION_ACCOUNT.equals(targetType)) { + if (org.apache.commons.lang3.StringUtils.isNotBlank(id)) { + name = id; + } + } + + if (TargetType.APP_PERMISSION_ROLE.equals(targetType)) { + AppPermissionRoleRepository appPermissionResourceRepository = ApplicationContextHelp + .getBean(AppPermissionRoleRepository.class); + Optional appPermissionRoleEntity = appPermissionResourceRepository + .findByIdContainsDeleted(Long.valueOf(id)); + if (appPermissionRoleEntity.isPresent()) { + name = appPermissionRoleEntity.get().getName(); + } + } + + if (TargetType.ADMINISTRATOR.equals(targetType)) { + AdministratorRepository administratorRepository = ApplicationContextHelp + .getBean(AdministratorRepository.class); + Optional administratorEntity = administratorRepository + .findByIdContainsDeleted(Long.valueOf(id)); + if (administratorEntity.isPresent()) { + name = administratorEntity.get().getUsername(); + } + } + + if (TargetType.MAIL_TEMPLATE.equals(targetType)) { + MailTemplateRepository mailTemplateRepository = ApplicationContextHelp + .getBean(MailTemplateRepository.class); + Optional mailTemplateEntity = mailTemplateRepository + .findByIdContainsDeleted(Long.valueOf(id)); + if (mailTemplateEntity.isPresent()) { + name = mailTemplateEntity.get().getSender(); + } + } + + if (TargetType.IDENTITY_PROVIDER.equals(targetType)) { + IdentityProviderRepository identityProviderRepository = ApplicationContextHelp + .getBean(IdentityProviderRepository.class); + Optional identityProviderEntity = identityProviderRepository + .findByIdContainsDeleted(Long.valueOf(id)); + if (identityProviderEntity.isPresent()) { + name = identityProviderEntity.get().getName(); + } + } + return name; + } } diff --git a/eiam-audit/src/main/java/cn/topiam/employee/audit/service/impl/AuditServiceImpl.java b/eiam-audit/src/main/java/cn/topiam/employee/audit/service/impl/AuditServiceImpl.java index 18ae85c9..61563148 100644 --- a/eiam-audit/src/main/java/cn/topiam/employee/audit/service/impl/AuditServiceImpl.java +++ b/eiam-audit/src/main/java/cn/topiam/employee/audit/service/impl/AuditServiceImpl.java @@ -17,7 +17,10 @@ */ package cn.topiam.employee.audit.service.impl; -import java.util.*; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; diff --git a/eiam-authentication/eiam-authentication-all/pom.xml b/eiam-authentication/eiam-authentication-all/pom.xml index b861659c..940d36d0 100644 --- a/eiam-authentication/eiam-authentication-all/pom.xml +++ b/eiam-authentication/eiam-authentication-all/pom.xml @@ -63,6 +63,12 @@ eiam-authentication-wechatwork ${project.version} + + + cn.topiam + eiam-authentication-captcha + ${project.version} + cn.topiam diff --git a/eiam-authentication/eiam-authentication-captcha/pom.xml b/eiam-authentication/eiam-authentication-captcha/pom.xml new file mode 100644 index 00000000..a0cf577e --- /dev/null +++ b/eiam-authentication/eiam-authentication-captcha/pom.xml @@ -0,0 +1,42 @@ + + + + + eiam-authentication + cn.topiam + 1.0.0-beta1 + ../pom.xml + + 4.0.0 + + eiam-authentication-captcha + jar + + + + cn.topiam + eiam-authentication-core + ${project.version} + + + \ No newline at end of file diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/CaptchaValidator.java b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/CaptchaValidator.java similarity index 90% rename from eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/CaptchaValidator.java rename to eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/CaptchaValidator.java index 00a6da91..ddfb5f04 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/CaptchaValidator.java +++ b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/CaptchaValidator.java @@ -1,5 +1,5 @@ /* - * eiam-core - Employee Identity and Access Management Program + * eiam-authentication-captcha - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.core.security.captcha; +package cn.topiam.employee.authentication.captcha; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/NoneCaptchaProvider.java b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/NoneCaptchaProvider.java similarity index 90% rename from eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/NoneCaptchaProvider.java rename to eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/NoneCaptchaProvider.java index c27812f1..e12e6ac9 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/NoneCaptchaProvider.java +++ b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/NoneCaptchaProvider.java @@ -1,5 +1,5 @@ /* - * eiam-core - Employee Identity and Access Management Program + * eiam-authentication-captcha - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.core.security.captcha; +package cn.topiam.employee.authentication.captcha; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/configurer/package-info.java b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/configurer/package-info.java new file mode 100644 index 00000000..d30603cf --- /dev/null +++ b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/configurer/package-info.java @@ -0,0 +1,18 @@ +/* + * eiam-authentication-captcha - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.authentication.captcha.configurer; \ No newline at end of file diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/CaptchaValidatorFilter.java b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/filter/CaptchaValidatorFilter.java similarity index 95% rename from eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/CaptchaValidatorFilter.java rename to eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/filter/CaptchaValidatorFilter.java index a3c548ff..3027927a 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/CaptchaValidatorFilter.java +++ b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/filter/CaptchaValidatorFilter.java @@ -1,5 +1,5 @@ /* - * eiam-core - Employee Identity and Access Management Program + * eiam-authentication-captcha - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.core.security.captcha; +package cn.topiam.employee.authentication.captcha.filter; import java.io.IOException; import java.util.Objects; @@ -38,6 +38,7 @@ import org.springframework.web.filter.OncePerRequestFilter; import com.alibaba.fastjson2.JSONObject; +import cn.topiam.employee.authentication.captcha.CaptchaValidator; import cn.topiam.employee.common.constants.AuthorizeConstants; import cn.topiam.employee.support.result.ApiRestResult; import cn.topiam.employee.support.trace.TraceUtils; diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/geetest/package-info.java b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/filter/package-info.java similarity index 84% rename from eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/geetest/package-info.java rename to eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/filter/package-info.java index 7f596dcf..0800dc3b 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/geetest/package-info.java +++ b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/filter/package-info.java @@ -1,5 +1,5 @@ /* - * eiam-core - Employee Identity and Access Management Program + * eiam-authentication-captcha - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,4 +15,4 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.core.security.captcha.geetest; \ No newline at end of file +package cn.topiam.employee.authentication.captcha.filter; \ No newline at end of file diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/geetest/GeeTestCaptchaProviderConfig.java b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/geetest/GeeTestCaptchaProviderConfig.java similarity index 91% rename from eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/geetest/GeeTestCaptchaProviderConfig.java rename to eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/geetest/GeeTestCaptchaProviderConfig.java index 498f065f..276bede4 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/geetest/GeeTestCaptchaProviderConfig.java +++ b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/geetest/GeeTestCaptchaProviderConfig.java @@ -1,5 +1,5 @@ /* - * eiam-core - Employee Identity and Access Management Program + * eiam-authentication-captcha - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.core.security.captcha.geetest; +package cn.topiam.employee.authentication.captcha.geetest; import java.io.Serial; diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/geetest/GeeTestCaptchaValidator.java b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/geetest/GeeTestCaptchaValidator.java similarity index 90% rename from eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/geetest/GeeTestCaptchaValidator.java rename to eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/geetest/GeeTestCaptchaValidator.java index 0664a3f2..93fec7d9 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/geetest/GeeTestCaptchaValidator.java +++ b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/geetest/GeeTestCaptchaValidator.java @@ -1,5 +1,5 @@ /* - * eiam-core - Employee Identity and Access Management Program + * eiam-authentication-captcha - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.core.security.captcha.geetest; +package cn.topiam.employee.authentication.captcha.geetest; import java.util.Map; @@ -33,8 +33,8 @@ import org.springframework.web.client.RestTemplate; import com.alibaba.fastjson2.JSONObject; +import cn.topiam.employee.authentication.captcha.CaptchaValidator; import cn.topiam.employee.common.util.RequestUtils; -import cn.topiam.employee.core.security.captcha.CaptchaValidator; /** * 极速验证 @@ -57,17 +57,17 @@ public class GeeTestCaptchaValidator implements CaptchaValidator { */ @Override public boolean validate(HttpServletRequest request, HttpServletResponse response) { - Map getParams = RequestUtils.getParams(request); + Map getParams = RequestUtils.getParams(request); // 1.初始化极验参数信息 String captchaId = config.getCaptchaId(); String captchaKey = config.getCaptchaKey(); String domain = "https://gcaptcha4.geetest.com"; // 2.获取用户验证后前端传过来的验证流水号等参数 - String lotNumber = getParams.get("lot_number"); - String captchaOutput = getParams.get("captcha_output"); - String passToken = getParams.get("pass_token"); - String genTime = getParams.get("gen_time"); + String lotNumber = (String) getParams.get("lot_number"); + String captchaOutput = (String) getParams.get("captcha_output"); + String passToken = (String) getParams.get("pass_token"); + String genTime = (String) getParams.get("gen_time"); // 3.生成签名 // 生成签名使用标准的hmac算法,使用用户当前完成验证的流水号lot_number作为原始消息message,使用客户验证私钥作为key diff --git a/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/geetest/package-info.java b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/geetest/package-info.java new file mode 100644 index 00000000..31ea649e --- /dev/null +++ b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/geetest/package-info.java @@ -0,0 +1,18 @@ +/* + * eiam-authentication-captcha - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.authentication.captcha.geetest; \ No newline at end of file diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/package-info.java b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/package-info.java similarity index 85% rename from eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/package-info.java rename to eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/package-info.java index 1b66a0ad..5824acb8 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/captcha/package-info.java +++ b/eiam-authentication/eiam-authentication-captcha/src/main/java/cn/topiam/employee/authentication/captcha/package-info.java @@ -1,5 +1,5 @@ /* - * eiam-core - Employee Identity and Access Management Program + * eiam-authentication-captcha - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,4 +15,4 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.core.security.captcha; \ No newline at end of file +package cn.topiam.employee.authentication.sms; \ No newline at end of file diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/enums/IdentityProviderCategory.java b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/IdentityProviderCategory.java similarity index 83% rename from eiam-common/src/main/java/cn/topiam/employee/common/enums/IdentityProviderCategory.java rename to eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/IdentityProviderCategory.java index 915487ab..e97a2ba9 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/enums/IdentityProviderCategory.java +++ b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/IdentityProviderCategory.java @@ -1,5 +1,5 @@ /* - * eiam-common - Employee Identity and Access Management Program + * eiam-authentication-core - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,12 +15,14 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.common.enums; +package cn.topiam.employee.authentication.common; import java.util.List; import com.google.common.collect.Lists; +import cn.topiam.employee.common.enums.AuthenticationType; +import cn.topiam.employee.common.enums.BaseEnum; import cn.topiam.employee.support.web.converter.EnumConvert; /** @@ -35,20 +37,17 @@ public enum IdentityProviderCategory implements BaseEnum { */ social("social", "社交", Lists.newArrayList( IdentityProviderType.QQ, - IdentityProviderType.WECHAT_SCAN_CODE, - IdentityProviderType.WEIBO, - IdentityProviderType.GITHUB, - IdentityProviderType.GOOGLE, - IdentityProviderType.ALIPAY)), + IdentityProviderType.WECHAT_QR)), /** * 企业 */ enterprise("enterprise", "企业", Lists .newArrayList( - IdentityProviderType.WECHATWORK_SCAN_CODE, - IdentityProviderType.DINGTALK_SCAN_CODE, + IdentityProviderType.WECHAT_WORK_QR, + IdentityProviderType.DINGTALK_QR, IdentityProviderType.DINGTALK_OAUTH, - IdentityProviderType.LDAP)); + IdentityProviderType.LDAP, + IdentityProviderType.FEISHU_OAUTH)); private final String code; @@ -62,10 +61,12 @@ public enum IdentityProviderCategory implements BaseEnum { this.providers = providers; } + @Override public String getCode() { return code; } + @Override public String getDesc() { return desc; } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/enums/converter/IdentityProviderCategoryConverter.java b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/IdentityProviderCategoryConverter.java similarity index 92% rename from eiam-common/src/main/java/cn/topiam/employee/common/enums/converter/IdentityProviderCategoryConverter.java rename to eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/IdentityProviderCategoryConverter.java index 813b1acc..dcfee952 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/enums/converter/IdentityProviderCategoryConverter.java +++ b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/IdentityProviderCategoryConverter.java @@ -1,5 +1,5 @@ /* - * eiam-common - Employee Identity and Access Management Program + * eiam-authentication-core - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,15 +15,13 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.common.enums.converter; +package cn.topiam.employee.authentication.common; import java.util.Objects; import javax.persistence.AttributeConverter; import javax.persistence.Converter; -import cn.topiam.employee.common.enums.IdentityProviderCategory; - /** * @author TopIAM * Created by support@topiam.cn on 2020/12/11 19:42 diff --git a/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/IdentityProviderService.java b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/IdentityProviderService.java index 7c5ecda1..0efd2843 100644 --- a/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/IdentityProviderService.java +++ b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/IdentityProviderService.java @@ -23,7 +23,6 @@ import java.util.Map; import org.springframework.transaction.annotation.Transactional; import cn.topiam.employee.authentication.common.config.IdentityProviderConfig; -import cn.topiam.employee.common.enums.IdentityProviderType; /** * IdentityProviderService diff --git a/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/IdentityProviderType.java b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/IdentityProviderType.java new file mode 100644 index 00000000..ba7f6c60 --- /dev/null +++ b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/IdentityProviderType.java @@ -0,0 +1,109 @@ +/* + * eiam-authentication-core - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.authentication.common; + +import org.springframework.util.Assert; +import static cn.topiam.employee.common.constants.AuthorizeConstants.AUTHORIZATION_REQUEST_URI; +import static cn.topiam.employee.common.constants.AuthorizeConstants.LOGIN_PATH; + +/** + * @author SanLi + * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2022/12/31 15:18 + */ +public record IdentityProviderType(String value,String name,String desc){ +/** + * 飞书 + */ +public static final IdentityProviderType FEISHU_OAUTH=new IdentityProviderType("feishu_oauth","飞书认证","通过飞书进行身份验证"); +/** + * 钉钉 + */ +public static final IdentityProviderType DINGTALK_OAUTH=new IdentityProviderType("dingtalk_oauth","钉钉Oauth认证","通过钉钉进行身份认证"); +/** + * 钉钉扫码 + */ +public static final IdentityProviderType DINGTALK_QR=new IdentityProviderType("dingtalk_qr","钉钉扫码认证","通过钉钉扫码进行身份认证"); +/** + * 微信开放平台 + */ +public static final IdentityProviderType WECHAT_QR=new IdentityProviderType("wechat_qr","微信扫码登录","通过微信扫码进行身份认证"); +/** + * 企业微信 + */ +public static final IdentityProviderType WECHAT_WORK_QR=new IdentityProviderType("wechatwork_qr","企业微信扫码认证","通过企业微信同步的用户可使用企业微信扫码登录进行身份认证"); + +/** + * QQ认证 + */ +public static final IdentityProviderType QQ=new IdentityProviderType("qq_oauth","QQ认证","通过QQ进行身份认证"); + +/** + * IDAP + */ +public static final IdentityProviderType LDAP=new IdentityProviderType("ldap","LDAP认证","通过 LDAP 进行身份验证"); + +/** + * 用户名密码 + */ +public static final IdentityProviderType USERNAME_PASSWORD=new IdentityProviderType("username_password","用户名密码认证","通过用户名密码进行身份认证"); + +/** + * 短信验证码 + */ +public static final IdentityProviderType SMS=new IdentityProviderType("sms","短信验证码认证","通过短信验证码进行身份认证"); + +/** + * Constructs an {@code IdentityProviderType} using the provided value. + * + * @param value the value of the authorization grant type + */ +public IdentityProviderType{Assert.hasText(value,"value cannot be empty");} + +/** + * Returns the value of the authorization grant type. + * + * @return the value of the authorization grant type + */ +@Override public String value(){return this.value;} + +@Override public boolean equals(Object obj){if(this==obj){return true;}if(obj==null||this.getClass()!=obj.getClass()){return false;}IdentityProviderType that=(IdentityProviderType)obj;return this.value().equals(that.value());} + +@Override public int hashCode(){return this.value().hashCode();} + + @Override + public String name() { + return name; + } + + @Override + public String desc() { + return desc; + } + + public String getLoginPathPrefix() { + return LOGIN_PATH + "/" + value(); + } + + public String getAuthorizationPathPrefix() { + return AUTHORIZATION_REQUEST_URI + "/" + value(); + } + + public static int size() { + return 9; + } +} diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/handler/package-info.java b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/constant/AuthenticationConstants.java similarity index 68% rename from eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/handler/package-info.java rename to eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/constant/AuthenticationConstants.java index cc7f6a92..091e42f7 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/handler/package-info.java +++ b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/constant/AuthenticationConstants.java @@ -1,5 +1,5 @@ /* - * eiam-protocol-oidc - Employee Identity and Access Management Program + * eiam-authentication-core - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,10 +15,18 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +package cn.topiam.employee.authentication.common.constant; + /** - * 处理器 + * 认证常量 * * @author TopIAM - * Created by support@topiam.cn on 2020/10/29 23:12 + * Created by support@topiam.cn on 2021/12/20 23:19 */ -package cn.topiam.employee.protocol.oidc.handler; +public final class AuthenticationConstants { + + /** + * 提供商ID + */ + public static final String PROVIDER_CODE = "providerId"; +} \ No newline at end of file diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/sms/SmsOtpProviderValidator.java b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/exception/IdentityProviderNotExistException.java similarity index 58% rename from eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/sms/SmsOtpProviderValidator.java rename to eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/exception/IdentityProviderNotExistException.java index 5f5ff2c9..7bc3565a 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/sms/SmsOtpProviderValidator.java +++ b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/exception/IdentityProviderNotExistException.java @@ -1,5 +1,5 @@ /* - * eiam-portal - Employee Identity and Access Management Program + * eiam-authentication-core - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,24 +15,20 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.portal.mfa.sms; +package cn.topiam.employee.authentication.common.exception; -import cn.topiam.employee.core.security.mfa.MfaProviderValidator; +import cn.topiam.employee.support.exception.TopIamException; +import static org.springframework.http.HttpStatus.BAD_REQUEST; /** - * OTP 提供商验证 + * 身份提供商不存在 * * @author TopIAM - * Created by support@topiam.cn on 2022/7/31 20:50 + * Created by support@topiam.cn on 2022/12/20 22:50 */ -public class SmsOtpProviderValidator implements MfaProviderValidator { - /** - * 验证 - * - * @param code {@link String} - */ - @Override - public boolean validate(String code) { - return true; +public class IdentityProviderNotExistException extends TopIamException { + + public IdentityProviderNotExistException() { + super("idp_not_exist", "身份提供商不存在", BAD_REQUEST); } } diff --git a/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/filter/AbstractIdpAuthenticationProcessingFilter.java b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/filter/AbstractIdpAuthenticationProcessingFilter.java index 9dae4cf8..8d687075 100644 --- a/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/filter/AbstractIdpAuthenticationProcessingFilter.java +++ b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/filter/AbstractIdpAuthenticationProcessingFilter.java @@ -35,10 +35,11 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import com.alibaba.fastjson2.JSONObject; +import cn.topiam.employee.authentication.common.IdentityProviderType; +import cn.topiam.employee.authentication.common.exception.IdentityProviderNotExistException; import cn.topiam.employee.authentication.common.modal.IdpUser; import cn.topiam.employee.authentication.common.service.UserIdpService; import cn.topiam.employee.common.entity.authentication.IdentityProviderEntity; -import cn.topiam.employee.common.enums.IdentityProviderType; import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; import cn.topiam.employee.core.security.authentication.IdpAuthentication; import cn.topiam.employee.core.security.userdetails.UserDetails; @@ -65,14 +66,18 @@ public abstract class AbstractIdpAuthenticationProcessingFilter extends * @param request {@link HttpServletRequest} * @param response {@link HttpServletResponse} * @param provider {@link IdentityProviderType} - * @param providerId {@link String} + * @param providerCode {@link String} * @param info {@link JSONObject} * @return {@link Authentication} */ public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response, - IdentityProviderType provider, String providerId, + IdentityProviderType provider, String providerCode, IdpUser info) throws IOException { + IdentityProviderEntity identityProvider = identityProviderRepository + .findByCodeAndEnabledIsTrue(providerCode) + .orElseThrow(IdentityProviderNotExistException::new); + String providerId = String.valueOf(identityProvider.getId()); info.setProviderId(providerId); info.setProviderType(provider); //调用接口查询是否已绑定 @@ -81,7 +86,7 @@ public abstract class AbstractIdpAuthenticationProcessingFilter extends //是否自动绑定 if (!userIdpService.isAutoBindUserIdp(providerId)) { setUserBindSessionContent(request, info); - return new IdpAuthentication(provider.getCode(), providerId); + return new IdpAuthentication(provider.value(), providerId); } //调用接口进行绑定操作 info.setProviderId(providerId); @@ -136,16 +141,16 @@ public abstract class AbstractIdpAuthenticationProcessingFilter extends String providerId, HttpServletRequest request) { //认证 UserDetails userDetails = userIdpService.getUserDetails(openId, providerId); - IdpAuthentication token = new IdpAuthentication(userDetails, provider.getCode(), providerId, + IdpAuthentication token = new IdpAuthentication(userDetails, provider.value(), providerId, true, userDetails.getAuthorities()); // Allow subclasses to set the "details" property token.setDetails(this.authenticationDetailsSource.buildDetails(request)); return token; } - public IdentityProviderEntity getIdentityProviderEntity(String providerId) { + public IdentityProviderEntity getIdentityProviderEntity(String code) { Optional optional = getIdentityProviderRepository() - .findByIdAndEnabledIsTrue(Long.valueOf(providerId)); + .findByCodeAndEnabledIsTrue(code); if (optional.isEmpty()) { //无效身份提供商 OAuth2Error oauth2Error = new OAuth2Error(INVALID_IDP); diff --git a/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/modal/IdpUser.java b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/modal/IdpUser.java index 99f0a993..ec26d132 100644 --- a/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/modal/IdpUser.java +++ b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/modal/IdpUser.java @@ -19,7 +19,7 @@ package cn.topiam.employee.authentication.common.modal; import java.util.Map; -import cn.topiam.employee.common.enums.IdentityProviderType; +import cn.topiam.employee.authentication.common.IdentityProviderType; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/util/AuthenticationUtils.java b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/util/AuthenticationUtils.java new file mode 100644 index 00000000..709f6ebc --- /dev/null +++ b/eiam-authentication/eiam-authentication-core/src/main/java/cn/topiam/employee/authentication/common/util/AuthenticationUtils.java @@ -0,0 +1,60 @@ +/* + * eiam-authentication-core - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.authentication.common.util; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; + +import cn.topiam.employee.core.security.authentication.IdpAuthentication; +import cn.topiam.employee.core.security.authentication.SmsAuthentication; +import cn.topiam.employee.core.security.mfa.MfaAuthentication; +import static cn.topiam.employee.authentication.common.IdentityProviderType.SMS; +import static cn.topiam.employee.authentication.common.IdentityProviderType.USERNAME_PASSWORD; + +/** + * + * @author SanLi + * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2022/12/31 14:29 + */ +public class AuthenticationUtils { + /** + * 获取认证类型 + * + * @param authentication {@link Authentication} + * @return {@link String} + */ + public static String geAuthType(Authentication authentication) { + //用户名密码 + if (authentication instanceof UsernamePasswordAuthenticationToken) { + return USERNAME_PASSWORD.value(); + } + //身份提供商 + if (authentication instanceof IdpAuthentication) { + return ((IdpAuthentication) authentication).getProviderType(); + } + //短信登录 + if (authentication instanceof SmsAuthentication) { + return SMS.value(); + } + //MFA + if (authentication instanceof MfaAuthentication) { + return geAuthType(((MfaAuthentication) authentication).getFirst()); + } + throw new IllegalArgumentException("未知认证对象"); + } +} diff --git a/eiam-authentication/eiam-authentication-dingtalk/src/main/java/cn/topiam/employee/authentication/dingtalk/filter/DingtalkOAuth2AuthorizationRequestRedirectFilter.java b/eiam-authentication/eiam-authentication-dingtalk/src/main/java/cn/topiam/employee/authentication/dingtalk/filter/DingtalkOAuth2AuthorizationRequestRedirectFilter.java index 055f527f..a8a3ccf4 100644 --- a/eiam-authentication/eiam-authentication-dingtalk/src/main/java/cn/topiam/employee/authentication/dingtalk/filter/DingtalkOAuth2AuthorizationRequestRedirectFilter.java +++ b/eiam-authentication/eiam-authentication-dingtalk/src/main/java/cn/topiam/employee/authentication/dingtalk/filter/DingtalkOAuth2AuthorizationRequestRedirectFilter.java @@ -51,9 +51,10 @@ import cn.topiam.employee.common.entity.authentication.IdentityProviderEntity; import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.RESPONSE_TYPE; +import static cn.topiam.employee.authentication.common.IdentityProviderType.DINGTALK_OAUTH; +import static cn.topiam.employee.authentication.common.constant.AuthenticationConstants.PROVIDER_CODE; import static cn.topiam.employee.authentication.dingtalk.constant.DingTalkAuthenticationConstants.URL_AUTHORIZE; import static cn.topiam.employee.authentication.dingtalk.filter.DingtalkOauthAuthenticationFilter.getLoginUrl; -import static cn.topiam.employee.common.enums.IdentityProviderType.DINGTALK_OAUTH; /** * 微信扫码登录请求重定向过滤器 @@ -67,16 +68,11 @@ public class DingtalkOAuth2AuthorizationRequestRedirectFilter extends OncePerReq private final Logger logger = LoggerFactory .getLogger(DingtalkOAuth2AuthorizationRequestRedirectFilter.class); - /** - * 提供商ID - */ - public static final String PROVIDER_ID = "providerId"; - /** * AntPathRequestMatcher */ public static final AntPathRequestMatcher DINGTALK_OAUTH2_REQUEST_MATCHER = new AntPathRequestMatcher( - DINGTALK_OAUTH.getAuthorizationPathPrefix() + "/" + "{" + PROVIDER_ID + "}", + DINGTALK_OAUTH.getAuthorizationPathPrefix() + "/" + "{" + PROVIDER_CODE + "}", HttpMethod.GET.name()); /** @@ -108,9 +104,9 @@ public class DingtalkOAuth2AuthorizationRequestRedirectFilter extends OncePerReq return; } Map variables = matcher.getVariables(); - String providerId = variables.get(PROVIDER_ID); + String providerCode = variables.get(PROVIDER_CODE); Optional optional = identityProviderRepository - .findByIdAndEnabledIsTrue(Long.valueOf(providerId)); + .findByCodeAndEnabledIsTrue(providerCode); if (optional.isEmpty()) { throw new NullPointerException("未查询到身份提供商信息"); } @@ -121,7 +117,8 @@ public class DingtalkOAuth2AuthorizationRequestRedirectFilter extends OncePerReq //构建授权请求 OAuth2AuthorizationRequest.Builder builder = OAuth2AuthorizationRequest.authorizationCode() .clientId(config.getAppKey()).authorizationUri(URL_AUTHORIZE) - .redirectUri(getLoginUrl(providerId)).state(DEFAULT_STATE_GENERATOR.generateKey()); + .redirectUri(getLoginUrl(optional.get().getCode())) + .state(DEFAULT_STATE_GENERATOR.generateKey()); builder.parameters(parameters -> { parameters.put(RESPONSE_TYPE, OAuth2ParameterNames.CODE); parameters.put("prompt", "consent"); diff --git a/eiam-authentication/eiam-authentication-dingtalk/src/main/java/cn/topiam/employee/authentication/dingtalk/filter/DingtalkOauthAuthenticationFilter.java b/eiam-authentication/eiam-authentication-dingtalk/src/main/java/cn/topiam/employee/authentication/dingtalk/filter/DingtalkOauthAuthenticationFilter.java index ca09ed18..2ccb014e 100644 --- a/eiam-authentication/eiam-authentication-dingtalk/src/main/java/cn/topiam/employee/authentication/dingtalk/filter/DingtalkOauthAuthenticationFilter.java +++ b/eiam-authentication/eiam-authentication-dingtalk/src/main/java/cn/topiam/employee/authentication/dingtalk/filter/DingtalkOauthAuthenticationFilter.java @@ -59,8 +59,9 @@ import cn.topiam.employee.core.context.ServerContextHelp; import cn.topiam.employee.support.exception.TopIamException; import cn.topiam.employee.support.trace.TraceUtils; import cn.topiam.employee.support.util.HttpUrlUtils; -import static cn.topiam.employee.authentication.dingtalk.filter.DingtalkScanCodeAuthorizationRequestGetFilter.PROVIDER_ID; -import static cn.topiam.employee.common.enums.IdentityProviderType.DINGTALK_OAUTH; +import static cn.topiam.employee.authentication.common.IdentityProviderType.DINGTALK_OAUTH; +import static cn.topiam.employee.authentication.common.IdentityProviderType.DINGTALK_QR; +import static cn.topiam.employee.authentication.common.constant.AuthenticationConstants.PROVIDER_CODE; /** * 钉钉认证过滤器 @@ -72,13 +73,14 @@ import static cn.topiam.employee.common.enums.IdentityProviderType.DINGTALK_OAUT */ @SuppressWarnings("DuplicatedCode") public class DingtalkOauthAuthenticationFilter extends AbstractIdpAuthenticationProcessingFilter { - public final static String DEFAULT_FILTER_PROCESSES_URI = DINGTALK_OAUTH + public final static String DEFAULT_FILTER_PROCESSES_URI = DINGTALK_QR .getLoginPathPrefix() + "/*"; /** * AntPathRequestMatcher */ public static final AntPathRequestMatcher REQUEST_MATCHER = new AntPathRequestMatcher( - DINGTALK_OAUTH.getLoginPathPrefix() + "/" + "{" + PROVIDER_ID + "}", HttpMethod.GET.name()); + DINGTALK_OAUTH.getLoginPathPrefix() + "/" + "{" + PROVIDER_CODE + "}", + HttpMethod.GET.name()); /** * Creates a new instance @@ -108,7 +110,7 @@ public class DingtalkOauthAuthenticationFilter extends AbstractIdpAuthentication TraceUtils.put(UUID.randomUUID().toString()); RequestMatcher.MatchResult matcher = REQUEST_MATCHER.matcher(request); Map variables = matcher.getVariables(); - String providerId = variables.get(PROVIDER_ID); + String providerId = variables.get(PROVIDER_CODE); //code 钉钉新版登录为 authCode String code = request.getParameter(AUTH_CODE); if (StringUtils.isEmpty(code)) { @@ -154,7 +156,7 @@ public class DingtalkOauthAuthenticationFilter extends AbstractIdpAuthentication } //执行逻辑 IdpUser idpUser = IdpUser.builder().openId(user.getBody().getOpenId()).build(); - return attemptAuthentication(request, response, DINGTALK_OAUTH, providerId, idpUser); + return attemptAuthentication(request, response, DINGTALK_QR, providerId, idpUser); } /** @@ -199,8 +201,8 @@ public class DingtalkOauthAuthenticationFilter extends AbstractIdpAuthentication private Cache cache; public static String getLoginUrl(String providerId) { - String url = ServerContextHelp.getPortalPublicBaseUrl() - + DINGTALK_OAUTH.getLoginPathPrefix() + "/" + providerId; + String url = ServerContextHelp.getPortalPublicBaseUrl() + DINGTALK_QR.getLoginPathPrefix() + + "/" + providerId; return HttpUrlUtils.format(url); } diff --git a/eiam-authentication/eiam-authentication-dingtalk/src/main/java/cn/topiam/employee/authentication/dingtalk/filter/DingtalkScanCodeAuthenticationFilter.java b/eiam-authentication/eiam-authentication-dingtalk/src/main/java/cn/topiam/employee/authentication/dingtalk/filter/DingtalkScanCodeAuthenticationFilter.java index 13d39e1b..a949aaa0 100644 --- a/eiam-authentication/eiam-authentication-dingtalk/src/main/java/cn/topiam/employee/authentication/dingtalk/filter/DingtalkScanCodeAuthenticationFilter.java +++ b/eiam-authentication/eiam-authentication-dingtalk/src/main/java/cn/topiam/employee/authentication/dingtalk/filter/DingtalkScanCodeAuthenticationFilter.java @@ -59,15 +59,14 @@ import cn.topiam.employee.authentication.common.modal.IdpUser; import cn.topiam.employee.authentication.common.service.UserIdpService; import cn.topiam.employee.authentication.dingtalk.DingTalkIdpScanCodeConfig; import cn.topiam.employee.common.entity.authentication.IdentityProviderEntity; -import cn.topiam.employee.common.enums.IdentityProviderType; import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; import cn.topiam.employee.core.context.ServerContextHelp; import cn.topiam.employee.support.exception.TopIamException; import cn.topiam.employee.support.trace.TraceUtils; import cn.topiam.employee.support.util.HttpUrlUtils; +import static cn.topiam.employee.authentication.common.IdentityProviderType.DINGTALK_QR; +import static cn.topiam.employee.authentication.common.constant.AuthenticationConstants.PROVIDER_CODE; import static cn.topiam.employee.authentication.dingtalk.constant.DingTalkAuthenticationConstants.*; -import static cn.topiam.employee.authentication.dingtalk.filter.DingtalkScanCodeAuthorizationRequestGetFilter.PROVIDER_ID; -import static cn.topiam.employee.common.enums.IdentityProviderType.DINGTALK_SCAN_CODE; /** * 钉钉认证过滤器 @@ -80,14 +79,13 @@ import static cn.topiam.employee.common.enums.IdentityProviderType.DINGTALK_SCAN @SuppressWarnings("DuplicatedCode") public class DingtalkScanCodeAuthenticationFilter extends AbstractIdpAuthenticationProcessingFilter { - public final static String DEFAULT_FILTER_PROCESSES_URI = DINGTALK_SCAN_CODE + public final static String DEFAULT_FILTER_PROCESSES_URI = DINGTALK_QR .getLoginPathPrefix() + "/*"; /** * AntPathRequestMatcher */ public static final AntPathRequestMatcher REQUEST_MATCHER = new AntPathRequestMatcher( - DINGTALK_SCAN_CODE.getLoginPathPrefix() + "/" + "{" + PROVIDER_ID + "}", - HttpMethod.GET.name()); + DINGTALK_QR.getLoginPathPrefix() + "/" + "{" + PROVIDER_CODE + "}", HttpMethod.GET.name()); /** * Creates a new instance @@ -117,7 +115,7 @@ public class DingtalkScanCodeAuthenticationFilter extends TraceUtils.put(UUID.randomUUID().toString()); RequestMatcher.MatchResult matcher = REQUEST_MATCHER.matcher(request); Map variables = matcher.getVariables(); - String providerId = variables.get(PROVIDER_ID); + String providerId = variables.get(PROVIDER_CODE); //code String code = request.getParameter(OAuth2ParameterNames.CODE); if (StringUtils.isEmpty(code)) { @@ -195,8 +193,7 @@ public class DingtalkScanCodeAuthenticationFilter extends //4、执行逻辑 OapiV2UserGetResponse.UserGetResponse result = rspGetResponse.getResult(); IdpUser idpUser = IdpUser.builder().openId(result.getUserid()).build(); - return attemptAuthentication(request, response, IdentityProviderType.DINGTALK_SCAN_CODE, - providerId, idpUser); + return attemptAuthentication(request, response, DINGTALK_QR, providerId, idpUser); } /** @@ -234,8 +231,8 @@ public class DingtalkScanCodeAuthenticationFilter extends private Cache cache; public static String getLoginUrl(String providerId) { - String url = ServerContextHelp.getPortalPublicBaseUrl() - + DINGTALK_SCAN_CODE.getLoginPathPrefix() + "/" + providerId; + String url = ServerContextHelp.getPortalPublicBaseUrl() + DINGTALK_QR.getLoginPathPrefix() + + "/" + providerId; return HttpUrlUtils.format(url); } diff --git a/eiam-authentication/eiam-authentication-dingtalk/src/main/java/cn/topiam/employee/authentication/dingtalk/filter/DingtalkScanCodeAuthorizationRequestGetFilter.java b/eiam-authentication/eiam-authentication-dingtalk/src/main/java/cn/topiam/employee/authentication/dingtalk/filter/DingtalkScanCodeAuthorizationRequestGetFilter.java index 799b6638..581440d3 100644 --- a/eiam-authentication/eiam-authentication-dingtalk/src/main/java/cn/topiam/employee/authentication/dingtalk/filter/DingtalkScanCodeAuthorizationRequestGetFilter.java +++ b/eiam-authentication/eiam-authentication-dingtalk/src/main/java/cn/topiam/employee/authentication/dingtalk/filter/DingtalkScanCodeAuthorizationRequestGetFilter.java @@ -55,10 +55,11 @@ import cn.topiam.employee.support.util.HttpResponseUtils; import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.CODE; import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.RESPONSE_TYPE; +import static cn.topiam.employee.authentication.common.IdentityProviderType.DINGTALK_QR; +import static cn.topiam.employee.authentication.common.constant.AuthenticationConstants.PROVIDER_CODE; import static cn.topiam.employee.authentication.dingtalk.constant.DingTalkAuthenticationConstants.APP_ID; import static cn.topiam.employee.authentication.dingtalk.constant.DingTalkAuthenticationConstants.SCAN_CODE_URL_AUTHORIZE; import static cn.topiam.employee.authentication.dingtalk.filter.DingtalkScanCodeAuthenticationFilter.getLoginUrl; -import static cn.topiam.employee.common.enums.IdentityProviderType.DINGTALK_SCAN_CODE; /** * 微信扫码登录请求重定向过滤器 @@ -72,16 +73,11 @@ public class DingtalkScanCodeAuthorizationRequestGetFilter extends OncePerReques private final Logger logger = LoggerFactory .getLogger(DingtalkScanCodeAuthorizationRequestGetFilter.class); - /** - * 提供商ID - */ - public static final String PROVIDER_ID = "providerId"; - /** * AntPathRequestMatcher */ public static final AntPathRequestMatcher DINGTALK_SCAN_CODE_REQUEST_MATCHER = new AntPathRequestMatcher( - DINGTALK_SCAN_CODE.getAuthorizationPathPrefix() + "/" + "{" + PROVIDER_ID + "}", + DINGTALK_QR.getAuthorizationPathPrefix() + "/" + "{" + PROVIDER_CODE + "}", HttpMethod.GET.name()); /** @@ -109,9 +105,9 @@ public class DingtalkScanCodeAuthorizationRequestGetFilter extends OncePerReques } Map variables = matcher.getVariables(); //校验身份提供商 - String providerId = variables.get(PROVIDER_ID); + String providerCode = variables.get(PROVIDER_CODE); Optional optional = identityProviderRepository - .findByIdAndEnabledIsTrue(Long.valueOf(providerId)); + .findByCodeAndEnabledIsTrue(providerCode); if (optional.isEmpty()) { logger.error("身份提供商不存在"); throw new NullPointerException("身份提供商不存在"); @@ -131,7 +127,7 @@ public class DingtalkScanCodeAuthorizationRequestGetFilter extends OncePerReques .clientId(config.getAppKey()) .scopes(Sets.newHashSet("snsapi_login")) .authorizationUri(SCAN_CODE_URL_AUTHORIZE) - .redirectUri(getLoginUrl(providerId)) + .redirectUri(getLoginUrl(optional.get().getCode())) .state(DEFAULT_STATE_GENERATOR.generateKey()) .attributes(attributes); builder.parameters(parameters -> { diff --git a/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/FeiShuIdpScanCodeConfig.java b/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/FeiShuIdpScanCodeConfig.java new file mode 100644 index 00000000..d7a3ff51 --- /dev/null +++ b/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/FeiShuIdpScanCodeConfig.java @@ -0,0 +1,52 @@ +/* + * eiam-authentication-feishu - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.authentication.feishu; + +import java.io.Serial; + +import javax.validation.constraints.NotBlank; + +import cn.topiam.employee.authentication.common.config.IdentityProviderConfig; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 飞书扫码 认证配置 + * + * @author TopIAM + * Created by support@topiam.cn on 2022/12/19 22:58 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class FeiShuIdpScanCodeConfig extends IdentityProviderConfig { + @Serial + private static final long serialVersionUID = -6850223527422243076L; + + /** + * APP ID + */ + @NotBlank(message = "APP ID 不能为空") + private String appId; + + /** + * APP Secret + */ + @NotBlank(message = "APP Secret 不能为空") + private String appSecret; +} diff --git a/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/configurer/FeiShuScanCodeAuthenticationConfigurer.java b/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/configurer/FeiShuScanCodeAuthenticationConfigurer.java new file mode 100644 index 00000000..fd9ecf4c --- /dev/null +++ b/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/configurer/FeiShuScanCodeAuthenticationConfigurer.java @@ -0,0 +1,91 @@ +/* + * eiam-authentication-feishu - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.authentication.feishu.configurer; + +import org.springframework.security.config.annotation.web.HttpSecurityBuilder; +import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer; +import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter; +import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.OrRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.util.Assert; + +import cn.topiam.employee.authentication.common.service.UserIdpService; +import cn.topiam.employee.authentication.feishu.filter.FeiShuAuthorizationRequestGetFilter; +import cn.topiam.employee.authentication.feishu.filter.FeiShuLoginAuthenticationFilter; +import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; + +/** + * 认证配置 + * + * @author TopIAM + * Created by support@topiam.cn on 2021/12/19 23:58 + */ +public final class FeiShuScanCodeAuthenticationConfigurer> extends + AbstractAuthenticationFilterConfigurer, FeiShuLoginAuthenticationFilter> { + + private final IdentityProviderRepository identityProviderRepository; + private final UserIdpService userIdpService; + + public FeiShuScanCodeAuthenticationConfigurer(IdentityProviderRepository identityProviderRepository, + UserIdpService userIdpService) { + Assert.notNull(identityProviderRepository, "identityProviderRepository must not be null"); + Assert.notNull(userIdpService, "userIdpService must not be null"); + this.identityProviderRepository = identityProviderRepository; + this.userIdpService = userIdpService; + } + + /** + * Create the {@link RequestMatcher} given a loginProcessingUrl + * + * @param loginProcessingUrl creates the {@link RequestMatcher} based upon the + * loginProcessingUrl + * @return the {@link RequestMatcher} to use based upon the loginProcessingUrl + */ + @Override + protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) { + return new AntPathRequestMatcher(loginProcessingUrl); + } + + @Override + public void init(H http) throws Exception { + //微信扫码登录认证 + FeiShuLoginAuthenticationFilter loginAuthenticationFilter = new FeiShuLoginAuthenticationFilter( + identityProviderRepository, userIdpService); + this.setAuthenticationFilter(loginAuthenticationFilter); + //处理URL + super.loginProcessingUrl(FeiShuLoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI); + super.init(http); + } + + @Override + public void configure(H http) throws Exception { + //微信扫码请求重定向 + FeiShuAuthorizationRequestGetFilter requestRedirectFilter = new FeiShuAuthorizationRequestGetFilter( + identityProviderRepository); + http.addFilterBefore(requestRedirectFilter, OAuth2AuthorizationRequestRedirectFilter.class); + http.addFilterBefore(this.getAuthenticationFilter(), OAuth2LoginAuthenticationFilter.class); + super.configure(http); + } + + public RequestMatcher getRequestMatcher() { + return new OrRequestMatcher(FeiShuAuthorizationRequestGetFilter.getRequestMatcher(), + FeiShuLoginAuthenticationFilter.getRequestMatcher()); + } +} diff --git a/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/constant/FeiShuAuthenticationConstants.java b/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/constant/FeiShuAuthenticationConstants.java new file mode 100644 index 00000000..532424a3 --- /dev/null +++ b/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/constant/FeiShuAuthenticationConstants.java @@ -0,0 +1,39 @@ +/* + * eiam-authentication-feishu - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.authentication.feishu.constant; + +/** + * 飞书认证常量 + * + * @author TopIAM + * Created by support@topiam.cn on 2021/12/19 23:19 + */ +public final class FeiShuAuthenticationConstants { + + public static final String AUTHORIZATION_REQUEST = "https://passport.feishu.cn/suite/passport/oauth/authorize"; + public static final String ACCESS_TOKEN = "https://passport.feishu.cn/suite/passport/oauth/token"; + public static final String USER_INFO = "https://passport.feishu.cn/suite/passport/oauth/userinfo"; + + public static final String CLIENT_ID = "client_id"; + public static final String CLIENT_SECRET = "client_secret"; + public static final String OPEN_ID = "open_id"; + + public static final String CODE = "code"; + public static final String HREF = "href"; + +} \ No newline at end of file diff --git a/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/filter/FeiShuAuthenticationFilter.java b/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/filter/FeiShuAuthenticationFilter.java deleted file mode 100644 index 5fe2fcf3..00000000 --- a/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/filter/FeiShuAuthenticationFilter.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * eiam-authentication-feishu - Employee Identity and Access Management Program - * Copyright © 2020-2023 TopIAM (support@topiam.cn) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package cn.topiam.employee.authentication.feishu.filter; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; -import org.springframework.security.web.util.matcher.RequestMatcher; - -/** - * 飞书认证过滤器 - * https://open.feishu.cn/document/common-capabilities/sso/web-application-sso/qr-sdk-documentation - * - * @author TopIAM - * Created by support@topiam.cn on 2021/12/8 21:11 - */ -public class FeiShuAuthenticationFilter extends AbstractAuthenticationProcessingFilter { - - /** - * Creates a new instance - * - * @param requiresAuthenticationRequestMatcher the {@link RequestMatcher} used to - * determine if authentication is required. Cannot be null. - */ - protected FeiShuAuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher) { - super(requiresAuthenticationRequestMatcher); - } - - /** - * qq认证 - * - * @param request {@link HttpServletRequest} - * @param response {@link HttpServletRequest} - * @return {@link HttpServletRequest} - * @throws AuthenticationException AuthenticationException - * @throws IOException IOException - * @throws ServletException ServletException - */ - @Override - public Authentication attemptAuthentication(HttpServletRequest request, - HttpServletResponse response) throws AuthenticationException, - IOException, - ServletException { - //@formatter:off - - //@formatter:on - return null; - } -} diff --git a/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/filter/FeiShuAuthorizationRequestGetFilter.java b/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/filter/FeiShuAuthorizationRequestGetFilter.java new file mode 100644 index 00000000..52869b93 --- /dev/null +++ b/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/filter/FeiShuAuthorizationRequestGetFilter.java @@ -0,0 +1,163 @@ +/* + * eiam-authentication-feishu - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.authentication.feishu.filter; + +import java.io.IOException; +import java.util.*; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpMethod; +import org.springframework.lang.NonNull; +import org.springframework.security.crypto.keygen.Base64StringKeyGenerator; +import org.springframework.security.crypto.keygen.StringKeyGenerator; +import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository; +import org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.web.DefaultRedirectStrategy; +import org.springframework.security.web.RedirectStrategy; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.util.Assert; +import org.springframework.web.filter.OncePerRequestFilter; + +import com.alibaba.fastjson2.JSONObject; +import com.google.common.collect.Maps; + +import cn.topiam.employee.authentication.feishu.FeiShuIdpScanCodeConfig; +import cn.topiam.employee.common.entity.authentication.IdentityProviderEntity; +import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; +import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.RESPONSE_TYPE; + +import static cn.topiam.employee.authentication.common.IdentityProviderType.FEISHU_OAUTH; +import static cn.topiam.employee.authentication.common.constant.AuthenticationConstants.PROVIDER_CODE; +import static cn.topiam.employee.authentication.feishu.constant.FeiShuAuthenticationConstants.*; +import static cn.topiam.employee.authentication.feishu.filter.FeiShuLoginAuthenticationFilter.getLoginUrl; + +/** + * 飞书认证过滤器 + * + * https://open.feishu.cn/document/common-capabilities/sso/web-application-sso/qr-sdk-documentation + * + * @author TopIAM + * Created by support@topiam.cn on 2021/12/8 21:11 + */ +public class FeiShuAuthorizationRequestGetFilter extends OncePerRequestFilter { + + private final Logger logger = LoggerFactory + .getLogger(FeiShuAuthorizationRequestGetFilter.class); + + /** + * AntPathRequestMatcher + */ + public static final AntPathRequestMatcher FEI_SHU_SCAN_CODE_REQUEST_MATCHER = new AntPathRequestMatcher( + FEISHU_OAUTH.getAuthorizationPathPrefix() + "/" + "{" + PROVIDER_CODE + "}", + HttpMethod.GET.name()); + + /** + * 认证请求存储库 + */ + private final AuthorizationRequestRepository authorizationRequestRepository = new HttpSessionOAuth2AuthorizationRequestRepository(); + + private static final StringKeyGenerator DEFAULT_STATE_GENERATOR = new Base64StringKeyGenerator( + Base64.getUrlEncoder()); + private final IdentityProviderRepository identityProviderRepository; + + public FeiShuAuthorizationRequestGetFilter(IdentityProviderRepository identityProviderRepository) { + this.identityProviderRepository = identityProviderRepository; + } + + @Override + protected void doFilterInternal(@NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain) throws IOException, + ServletException { + RequestMatcher.MatchResult matcher = FEI_SHU_SCAN_CODE_REQUEST_MATCHER.matcher(request); + if (!matcher.isMatch()) { + filterChain.doFilter(request, response); + return; + } + Map variables = matcher.getVariables(); + String providerCode = variables.get(PROVIDER_CODE); + Optional optional = identityProviderRepository + .findByCodeAndEnabledIsTrue(providerCode); + if (optional.isEmpty()) { + throw new NullPointerException("未查询到身份提供商信息"); + } + IdentityProviderEntity entity = optional.get(); + FeiShuIdpScanCodeConfig config = JSONObject.parseObject(entity.getConfig(), + FeiShuIdpScanCodeConfig.class); + Assert.notNull(config, "飞书扫码登录配置不能为空"); + //构建授权请求 + //@formatter:off + HashMap<@Nullable String, @Nullable Object> attributes = Maps.newHashMap(); + attributes.put(RESPONSE_TYPE, CODE); + OAuth2AuthorizationRequest.Builder builder = OAuth2AuthorizationRequest.authorizationCode() + .clientId(config.getAppId()) + .authorizationUri(AUTHORIZATION_REQUEST) + .redirectUri(getLoginUrl(optional.get().getCode())) + .state(DEFAULT_STATE_GENERATOR.generateKey()) + .attributes(attributes); + //@formatter:on + builder.parameters(parameters -> { + HashMap linkedParameters = new LinkedHashMap<>(); + parameters.forEach((key, value) -> { + if (OAuth2ParameterNames.CLIENT_ID.equals(key)) { + linkedParameters.put(CLIENT_ID, value); + } + if (OAuth2ParameterNames.STATE.equals(key)) { + linkedParameters.put(OAuth2ParameterNames.STATE, value); + } + if (OAuth2ParameterNames.REDIRECT_URI.equals(key)) { + linkedParameters.put(OAuth2ParameterNames.REDIRECT_URI, value); + } + if (RESPONSE_TYPE.equals(key)) { + linkedParameters.put(RESPONSE_TYPE, value); + } + }); + parameters.clear(); + parameters.putAll(linkedParameters); + }); + this.sendRedirectForAuthorization(request, response, builder.build()); + } + + private void sendRedirectForAuthorization(HttpServletRequest request, + HttpServletResponse response, + OAuth2AuthorizationRequest authorizationRequest) throws IOException { + this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, + response); + this.authorizationRedirectStrategy.sendRedirect(request, response, + authorizationRequest.getAuthorizationRequestUri()); + } + + /** + * 重定向策略 + */ + private final RedirectStrategy authorizationRedirectStrategy = new DefaultRedirectStrategy(); + + public static RequestMatcher getRequestMatcher() { + return FEI_SHU_SCAN_CODE_REQUEST_MATCHER; + } +} diff --git a/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/filter/FeiShuLoginAuthenticationFilter.java b/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/filter/FeiShuLoginAuthenticationFilter.java new file mode 100644 index 00000000..de02dcfe --- /dev/null +++ b/eiam-authentication/eiam-authentication-feishu/src/main/java/cn/topiam/employee/authentication/feishu/filter/FeiShuLoginAuthenticationFilter.java @@ -0,0 +1,152 @@ +/* + * eiam-authentication-feishu - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.authentication.feishu.filter; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.apache.http.message.BasicHeader; +import org.springframework.http.HttpMethod; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.OAuth2Error; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.nimbusds.oauth2.sdk.GrantType; + +import cn.topiam.employee.authentication.common.filter.AbstractIdpAuthenticationProcessingFilter; +import cn.topiam.employee.authentication.common.modal.IdpUser; +import cn.topiam.employee.authentication.common.service.UserIdpService; +import cn.topiam.employee.authentication.feishu.FeiShuIdpScanCodeConfig; +import cn.topiam.employee.common.entity.authentication.IdentityProviderEntity; +import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; +import cn.topiam.employee.core.context.ServerContextHelp; +import cn.topiam.employee.support.util.HttpClientUtils; +import static cn.topiam.employee.authentication.common.IdentityProviderType.FEISHU_OAUTH; +import static cn.topiam.employee.authentication.common.constant.AuthenticationConstants.PROVIDER_CODE; +import static cn.topiam.employee.authentication.feishu.constant.FeiShuAuthenticationConstants.*; + +/** + * 飞书扫码登录过滤器 + * + * @author TopIAM + * Created by support@topiam.cn on 2021/12/8 21:11 + */ +public class FeiShuLoginAuthenticationFilter extends AbstractIdpAuthenticationProcessingFilter { + + public final static String DEFAULT_FILTER_PROCESSES_URI = FEISHU_OAUTH + .getLoginPathPrefix() + "/*"; + public static final AntPathRequestMatcher REQUEST_MATCHER = new AntPathRequestMatcher( + FEISHU_OAUTH.getLoginPathPrefix() + "/" + "{" + PROVIDER_CODE + "}", HttpMethod.GET.name()); + + /** + * Creates a new instance + * + * @param identityProviderRepository the {@link IdentityProviderRepository} + * @param authenticationUserDetails {@link UserIdpService} + */ + public FeiShuLoginAuthenticationFilter(IdentityProviderRepository identityProviderRepository, + UserIdpService authenticationUserDetails) { + super(DEFAULT_FILTER_PROCESSES_URI, authenticationUserDetails, identityProviderRepository); + } + + /** + * 飞书认证 + * + * @param request {@link HttpServletRequest} + * @param response {@link HttpServletRequest} + * @return {@link HttpServletRequest} + * @throws AuthenticationException {@link AuthenticationException} AuthenticationException + */ + @Override + public Authentication attemptAuthentication(HttpServletRequest request, + HttpServletResponse response) throws AuthenticationException, + IOException { + OAuth2AuthorizationRequest authorizationRequest = getOAuth2AuthorizationRequest(request, + response); + RequestMatcher.MatchResult matcher = REQUEST_MATCHER.matcher(request); + Map variables = matcher.getVariables(); + String providerCode = variables.get(PROVIDER_CODE); + //code + String code = request.getParameter(OAuth2ParameterNames.CODE); + if (StringUtils.isEmpty(code)) { + OAuth2Error oauth2Error = new OAuth2Error(INVALID_CODE_PARAMETER_ERROR_CODE); + throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); + } + // state + String state = request.getParameter(OAuth2ParameterNames.STATE); + if (StringUtils.isEmpty(state)) { + OAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE); + throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); + } + if (!authorizationRequest.getState().equals(state)) { + OAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE); + throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); + } + //获取身份提供商 + IdentityProviderEntity provider = getIdentityProviderEntity(providerCode); + FeiShuIdpScanCodeConfig config = JSONObject.parseObject(provider.getConfig(), + FeiShuIdpScanCodeConfig.class); + if (Objects.isNull(config)) { + logger.error("未查询到飞书扫码登录配置"); + //无效身份提供商 + OAuth2Error oauth2Error = new OAuth2Error( + AbstractIdpAuthenticationProcessingFilter.INVALID_IDP_CONFIG); + throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); + } + //获取access token + HashMap param = new HashMap<>(16); + param.put(CLIENT_ID, config.getAppId()); + param.put(CLIENT_SECRET, config.getAppSecret()); + param.put(OAuth2ParameterNames.CODE, code); + param.put(OAuth2ParameterNames.REDIRECT_URI, getLoginUrl(provider.getCode())); + param.put(OAuth2ParameterNames.GRANT_TYPE, GrantType.AUTHORIZATION_CODE.getValue()); + JSONObject result = JSON.parseObject(HttpClientUtils.post(ACCESS_TOKEN, param)); + // 获取user信息 + param = new HashMap<>(16); + BasicHeader authorization = new BasicHeader( + "Authorization", result.getString(OAuth2ParameterNames.TOKEN_TYPE) + " " + + result.getString(OAuth2ParameterNames.ACCESS_TOKEN)); + result = JSON.parseObject(HttpClientUtils.get(USER_INFO, param, authorization)); + // 返回 + IdpUser idpUser = IdpUser.builder().openId(result.getString(OPEN_ID)).build(); + return attemptAuthentication(request, response, FEISHU_OAUTH, providerCode, idpUser); + } + + public static String getLoginUrl(String providerId) { + String url = ServerContextHelp.getPortalPublicBaseUrl() + FEISHU_OAUTH.getLoginPathPrefix() + + "/" + providerId; + return url.replaceAll("(?. */ -package cn.topiam.employee.portal.mfa; +package cn.topiam.employee.authentication.mfa; import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.util.Assert; -import cn.topiam.employee.portal.handler.PortalAuthenticationHandler; +import cn.topiam.employee.core.security.otp.OtpContextHelp; /** * Mfa Authentication Configurer @@ -34,7 +37,19 @@ import cn.topiam.employee.portal.handler.PortalAuthenticationHandler; public final class MfaAuthenticationConfigurer> extends AbstractAuthenticationFilterConfigurer, MfaAuthenticationFilter> { - public MfaAuthenticationConfigurer() { + private final OtpContextHelp otpContextHelp; + private final MfaAuthenticationHandler mfaAuthenticationHandler; + + public MfaAuthenticationConfigurer(OtpContextHelp otpContextHelp, + AuthenticationSuccessHandler successHandler, + AuthenticationFailureHandler authenticationFailureHandler) { + Assert.notNull(otpContextHelp, "otpContextHelp must not be null"); + Assert.notNull(successHandler, "successHandler must not be null"); + Assert.notNull(authenticationFailureHandler, + "authenticationFailureHandler must not be null"); + this.otpContextHelp = otpContextHelp; + mfaAuthenticationHandler = new MfaAuthenticationHandler(successHandler, + authenticationFailureHandler); } /** @@ -52,8 +67,8 @@ public final class MfaAuthenticationConfigurer> @Override public void init(H http) throws Exception { //设置登录成功失败处理器 - super.successHandler(new PortalAuthenticationHandler()); - super.failureHandler(new PortalAuthenticationHandler()); + super.successHandler(mfaAuthenticationHandler); + super.failureHandler(mfaAuthenticationHandler); //MFA认证 MfaAuthenticationFilter loginAuthenticationFilter = new MfaAuthenticationFilter(); this.setAuthenticationFilter(loginAuthenticationFilter); @@ -64,12 +79,20 @@ public final class MfaAuthenticationConfigurer> @Override public void configure(H http) throws Exception { - http.addFilterAfter(this.getAuthenticationFilter(), + //Mfa认证方式 + http.addFilterBefore(new MfaAuthenticationMfaFactorsFilter(), UsernamePasswordAuthenticationFilter.class); + //Mfa认证方式 + http.addFilterAfter(new MfaAuthenticationSendOtpFilter(otpContextHelp), + MfaAuthenticationMfaFactorsFilter.class); + //Mfa认证 + http.addFilterAfter(this.getAuthenticationFilter(), + MfaAuthenticationMfaFactorsFilter.class); super.configure(http); } - public RequestMatcher getRequestMatcher() { + public static RequestMatcher getRequestMatcher() { return MfaAuthenticationFilter.getRequestMatcher(); } + } diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/MfaAuthenticationFilter.java b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/MfaAuthenticationFilter.java similarity index 82% rename from eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/MfaAuthenticationFilter.java rename to eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/MfaAuthenticationFilter.java index 8ef9adcd..9d1a4d67 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/MfaAuthenticationFilter.java +++ b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/MfaAuthenticationFilter.java @@ -1,5 +1,5 @@ /* - * eiam-portal - Employee Identity and Access Management Program + * eiam-authentication-mfa - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,12 +15,10 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.portal.mfa; +package cn.topiam.employee.authentication.mfa; -import java.io.IOException; import java.util.Objects; -import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -35,16 +33,15 @@ import org.springframework.security.web.authentication.AbstractAuthenticationPro import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; +import cn.topiam.employee.authentication.mfa.email.EmailOtpProviderValidator; +import cn.topiam.employee.authentication.mfa.sms.SmsOtpProviderValidator; +import cn.topiam.employee.authentication.mfa.totp.TotpProviderValidator; import cn.topiam.employee.common.entity.account.UserEntity; -import cn.topiam.employee.common.enums.MessageNoticeChannel; import cn.topiam.employee.common.enums.MfaFactor; import cn.topiam.employee.core.security.mfa.MfaAuthentication; import cn.topiam.employee.core.security.mfa.exception.MfaRequiredException; -import cn.topiam.employee.core.security.otp.OtpContextHelp; import cn.topiam.employee.core.security.util.UserUtils; -import cn.topiam.employee.portal.mfa.totp.TotpProviderValidator; -import cn.topiam.employee.support.context.ApplicationContextHelp; -import static cn.topiam.employee.common.constants.AuthorizeConstants.MFA_VALIDATE; +import static cn.topiam.employee.authentication.mfa.constant.MfaAuthenticationConstants.MFA_VALIDATE; import static cn.topiam.employee.common.enums.MfaFactor.SMS_OTP; /** @@ -69,7 +66,7 @@ public class MfaAuthenticationFilter extends AbstractAuthenticationProcessingFil super(MFA_LOGIN_MATCHER); } - public static RequestMatcher getRequestMatcher() { + protected static RequestMatcher getRequestMatcher() { return MFA_LOGIN_MATCHER; } @@ -95,14 +92,11 @@ public class MfaAuthenticationFilter extends AbstractAuthenticationProcessingFil */ @Override public Authentication attemptAuthentication(HttpServletRequest request, - HttpServletResponse response) throws AuthenticationException, - IOException, - ServletException { + HttpServletResponse response) throws AuthenticationException { UserEntity user = UserUtils.getUser(); - OtpContextHelp bean = ApplicationContextHelp.getBean(OtpContextHelp.class); MfaAuthentication authentication = (MfaAuthentication) SecurityContextHolder.getContext() .getAuthentication(); - Boolean result = false; + boolean result = false; //获取类型 MfaFactor type = MfaFactor.getType(request.getParameter(SPRING_SECURITY_FORM_TYPE_KEY)); if (Objects.isNull(type)) { @@ -114,7 +108,7 @@ public class MfaAuthenticationFilter extends AbstractAuthenticationProcessingFil if (StringUtils.isBlank(otp)) { throw new MfaRequiredException("OTP 参数不存在"); } - result = bean.checkOtp(type.getCode(), MessageNoticeChannel.SMS, "", otp); + result = smsOtpProviderValidator.validate(otp); } //Mail OPT if (MfaFactor.EMAIL_OTP.equals(type)) { @@ -122,12 +116,12 @@ public class MfaAuthenticationFilter extends AbstractAuthenticationProcessingFil if (StringUtils.isBlank(otp)) { throw new MfaRequiredException("OTP 参数不存在"); } - result = bean.checkOtp(type.getCode(), MessageNoticeChannel.MAIL, "", otp); + result = emailOtpProviderValidator.validate(otp); } //TOTP if (MfaFactor.APP_TOTP.equals(type)) { long totp = Long.parseLong(request.getParameter(SPRING_SECURITY_FORM_TOTP_KEY)); - result = new TotpProviderValidator().validate(String.valueOf(totp)); + result = totpProviderValidator.validate(String.valueOf(totp)); } if (!result) { logger.error("用户ID: [{}] 用户名: [{}] {} 认证失败", type.getDesc(), user.getId(), @@ -140,4 +134,8 @@ public class MfaAuthenticationFilter extends AbstractAuthenticationProcessingFil authentication.setValidated(true); return authentication; } + + protected final EmailOtpProviderValidator emailOtpProviderValidator = new EmailOtpProviderValidator(); + protected final SmsOtpProviderValidator smsOtpProviderValidator = new SmsOtpProviderValidator(); + protected final TotpProviderValidator totpProviderValidator = new TotpProviderValidator(); } diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/handler/PortalAuthenticationHandler.java b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/MfaAuthenticationHandler.java similarity index 74% rename from eiam-portal/src/main/java/cn/topiam/employee/portal/handler/PortalAuthenticationHandler.java rename to eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/MfaAuthenticationHandler.java index 89814fd7..33d3c4aa 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/handler/PortalAuthenticationHandler.java +++ b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/MfaAuthenticationHandler.java @@ -1,5 +1,5 @@ /* - * eiam-portal - Employee Identity and Access Management Program + * eiam-authentication-mfa - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.portal.handler; +package cn.topiam.employee.authentication.mfa; import java.io.IOException; @@ -31,11 +31,10 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.WebAttributes; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.util.Assert; import cn.topiam.employee.common.constants.AuthorizeConstants; import cn.topiam.employee.core.context.ServerContextHelp; -import cn.topiam.employee.core.security.authentication.IdpAuthentication; -import cn.topiam.employee.core.security.authentication.SmsAuthentication; import cn.topiam.employee.core.security.mfa.MfaAuthentication; import cn.topiam.employee.support.result.ApiRestResult; import cn.topiam.employee.support.util.HttpResponseUtils; @@ -53,14 +52,20 @@ import static cn.topiam.employee.support.exception.enums.ExceptionStatus.EX00010 * Created by support@topiam.cn on 2022/7/28 23:36 */ @SuppressWarnings("DuplicatedCode") -public class PortalAuthenticationHandler implements AuthenticationSuccessHandler, - AuthenticationFailureHandler { +public class MfaAuthenticationHandler implements AuthenticationSuccessHandler, + AuthenticationFailureHandler { + private static final String REQUIRE_MFA = "require_mfa"; - private final AuthenticationSuccessHandler successHandler = new PortalAuthenticationSuccessHandler(); - private final AuthenticationFailureHandler failureHandler = new PortalAuthenticationFailureHandler(); + private final AuthenticationSuccessHandler successHandler; + private final AuthenticationFailureHandler failureHandler; - private static final String REQUIRE_MFA = "require_mfa"; - private static final String REQUIRE_USER_BIND = "require_user_bind"; + public MfaAuthenticationHandler(AuthenticationSuccessHandler successHandler, + AuthenticationFailureHandler failureHandler) { + Assert.notNull(successHandler, "userIdpService must not be null"); + Assert.notNull(failureHandler, "userIdpService must not be null"); + this.successHandler = successHandler; + this.failureHandler = failureHandler; + } /** * Called when an authentication attempt fails. @@ -90,35 +95,6 @@ public class PortalAuthenticationHandler implements AuthenticationSuccessHandler Authentication authentication) throws IOException, ServletException { boolean isTextHtml = acceptIncludeTextHtml(request); - //TODO SMS 不需要双因素 - if (authentication instanceof SmsAuthentication) { - successHandler.onAuthenticationSuccess(request, response, authentication); - return; - } - //TODO IDP 未关联 - if (authentication instanceof IdpAuthentication - && !((IdpAuthentication) authentication).getAssociated()) { - //Clear Authentication Attributes - clearAuthenticationAttributes(request); - if (response.isCommitted()) { - return; - } - if (!isTextHtml) { - HttpResponseUtils.flushResponseJson(response, HttpStatus.BAD_REQUEST.value(), - ApiRestResult.builder().status(REQUIRE_USER_BIND).message(REQUIRE_USER_BIND) - .build()); - return; - } - //跳转登录,前端会有接口获取状态,并进行展示绑定页面 - response.sendRedirect(HttpUrlUtils - .format(ServerContextHelp.getPortalPublicBaseUrl() + AuthorizeConstants.FE_LOGIN)); - return; - } - //TODO IDP 不需要双因素 - if (authentication instanceof IdpAuthentication) { - successHandler.onAuthenticationSuccess(request, response, authentication); - return; - } //TODO MFA启用、但是对象非MFA,说明需要MFA认证 if (isMfaEnabled() && !(authentication instanceof MfaAuthentication)) { SecurityContextHolder.getContext() diff --git a/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/MfaAuthenticationMfaFactorsFilter.java b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/MfaAuthenticationMfaFactorsFilter.java new file mode 100644 index 00000000..eb479f4f --- /dev/null +++ b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/MfaAuthenticationMfaFactorsFilter.java @@ -0,0 +1,132 @@ +/* + * eiam-authentication-mfa - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.authentication.mfa; + +import java.io.IOException; +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.web.filter.OncePerRequestFilter; + +import cn.topiam.employee.common.entity.account.UserEntity; +import cn.topiam.employee.common.enums.MfaFactor; +import cn.topiam.employee.core.security.util.UserUtils; +import cn.topiam.employee.support.result.ApiRestResult; +import cn.topiam.employee.support.util.DesensitizationUtil; +import cn.topiam.employee.support.util.HttpResponseUtils; + +import lombok.Builder; +import lombok.Data; +import static cn.topiam.employee.authentication.mfa.constant.MfaAuthenticationConstants.LOGIN_MFA_FACTORS; +import static cn.topiam.employee.core.context.SettingContextHelp.getMfaFactors; + +/** + * MfaAuthenticationMfaFactorsFilter + * + * @author SanLi + * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2023/1/2 13:28 + */ +public class MfaAuthenticationMfaFactorsFilter extends OncePerRequestFilter { + + public final static String DEFAULT_FILTER_PROCESSES_URI = LOGIN_MFA_FACTORS; + + public static final RequestMatcher LOGIN_MFA_FACTORS_MATCHER = new AntPathRequestMatcher( + DEFAULT_FILTER_PROCESSES_URI, HttpMethod.GET.name()); + + @Override + @SuppressWarnings("AlibabaAvoidComplexCondition") + protected void doFilterInternal(@NotNull HttpServletRequest request, + @NotNull HttpServletResponse response, + @NotNull FilterChain filterChain) throws ServletException, + IOException { + if (!getRequestMatcher().matches(request)) { + filterChain.doFilter(request, response); + return; + } + UserEntity user = UserUtils.getUser(); + List list = new ArrayList<>(); + List factors = getMfaFactors(); + for (MfaFactor provider : factors) { + MfaFactorResult result = MfaFactorResult.builder().build(); + result.setFactor(provider); + result.setUsable(false); + //sms + if (provider.equals(MfaFactor.SMS_OTP) && StringUtils.isNotBlank(user.getPhone())) { + result.setTarget(DesensitizationUtil.phoneEncrypt(user.getPhone())); + result.setUsable(true); + } + //otp + if (provider.equals(MfaFactor.EMAIL_OTP) && StringUtils.isNotBlank(user.getEmail())) { + result.setTarget(DesensitizationUtil.emailEncrypt(user.getEmail())); + result.setUsable(true); + } + //totp + if (provider.equals(MfaFactor.APP_TOTP) + && (!Objects.isNull(user.getTotpBind()) && user.getTotpBind())) { + result.setUsable(true); + } + list.add(result); + } + HttpResponseUtils.flushResponseJson(response, HttpStatus.OK.value(), + ApiRestResult.ok(list)); + } + + public static RequestMatcher getRequestMatcher() { + return LOGIN_MFA_FACTORS_MATCHER; + } + + /** + * Mfa 登录方式 + * + * @author TopIAM + * Created by support@topiam.cn on 2022/8/13 21:29 + */ + @Builder + @Data + public static class MfaFactorResult implements Serializable { + + @Serial + private static final long serialVersionUID = 7255002979319970337L; + /** + * provider + */ + private MfaFactor factor; + /** + * 可用 + */ + private Boolean usable; + /** + * 目标 + */ + private String target; + } +} diff --git a/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/MfaAuthenticationSendOtpFilter.java b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/MfaAuthenticationSendOtpFilter.java new file mode 100644 index 00000000..536bed47 --- /dev/null +++ b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/MfaAuthenticationSendOtpFilter.java @@ -0,0 +1,159 @@ +/* + * eiam-authentication-mfa - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.authentication.mfa; + +import java.io.IOException; +import java.io.Serializable; +import java.util.Map; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.validation.ConstraintViolationException; + +import org.jetbrains.annotations.NotNull; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.web.filter.OncePerRequestFilter; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import cn.topiam.employee.common.entity.account.UserEntity; +import cn.topiam.employee.common.enums.MailType; +import cn.topiam.employee.common.enums.MessageNoticeChannel; +import cn.topiam.employee.common.enums.SmsType; +import cn.topiam.employee.common.exception.LoginOtpActionNotSupportException; +import cn.topiam.employee.common.util.RequestUtils; +import cn.topiam.employee.core.security.mfa.MfaAuthentication; +import cn.topiam.employee.core.security.otp.OtpContextHelp; +import cn.topiam.employee.core.security.userdetails.UserDetails; +import cn.topiam.employee.core.security.util.SecurityUtils; +import cn.topiam.employee.core.security.util.UserUtils; +import cn.topiam.employee.support.result.ApiRestResult; +import cn.topiam.employee.support.util.HttpResponseUtils; +import cn.topiam.employee.support.validation.ValidationHelp; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.Parameter; +import static cn.topiam.employee.authentication.mfa.constant.MfaAuthenticationConstants.OTP_SEND_OTP; + +/** + * 发送短信OPT + * + * @author SanLi + * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2023/1/1 22:01 + */ +public class MfaAuthenticationSendOtpFilter extends OncePerRequestFilter { + public final static String DEFAULT_FILTER_PROCESSES_URI = OTP_SEND_OTP; + + public static final RequestMatcher SMS_SEND_OPT_MATCHER = new AntPathRequestMatcher( + DEFAULT_FILTER_PROCESSES_URI, HttpMethod.POST.name()); + + @Override + protected void doFilterInternal(@NotNull HttpServletRequest request, + @NotNull HttpServletResponse response, + @NotNull FilterChain filterChain) throws ServletException, + IOException { + if (!getRequestMatcher().matches(request)) { + filterChain.doFilter(request, response); + return; + } + SecurityContext securityContext = SecurityUtils.getSecurityContext(); + Authentication authentication = securityContext.getAuthentication(); + //非MFA对象 + if (!(authentication instanceof MfaAuthentication)) { + HttpResponseUtils.flushResponseJson(response, HttpStatus.UNAUTHORIZED.value(), + ApiRestResult.ok()); + return; + } + Map params = RequestUtils.getParams(request); + String value = OBJECT_MAPPER.writeValueAsString(params); + SendOtpRequest sendOtpRequest = OBJECT_MAPPER.readValue(value, SendOtpRequest.class); + ValidationHelp.ValidationResult validationResult = ValidationHelp + .validateEntity(sendOtpRequest); + if (validationResult.isHasErrors()) { + throw new ConstraintViolationException(validationResult.getConstraintViolations()); + } + //MFA,从会话上下文中获取手机号及邮箱信息 + UserDetails principal = (UserDetails) ((MfaAuthentication) authentication).getFirst() + .getPrincipal(); + UserEntity user = UserUtils.getUser(principal.getId()); + String email = user.getEmail(); + if (MessageNoticeChannel.MAIL.equals(sendOtpRequest.getChannel())) { + send(email, MessageNoticeChannel.MAIL); + HttpResponseUtils.flushResponseJson(response, HttpStatus.OK.value(), + ApiRestResult.ok()); + return; + } + String phone = user.getPhone(); + if (MessageNoticeChannel.SMS.equals(sendOtpRequest.getChannel())) { + send(phone, MessageNoticeChannel.SMS); + HttpResponseUtils.flushResponseJson(response, HttpStatus.OK.value(), + ApiRestResult.ok()); + return; + } + throw new LoginOtpActionNotSupportException(); + } + + /** + * 发送 + * + * @param target {@link String} + * @param channel {@link MessageNoticeChannel} + */ + private void send(String target, MessageNoticeChannel channel) { + String type; + if (channel == MessageNoticeChannel.MAIL) { + type = MailType.AGAIN_VERIFY.getCode(); + } else { + type = SmsType.AGAIN_VERIFY.getCode(); + } + otpContextHelp.sendOtp(target, type, channel); + } + + /** + * 发送 OTP 请求 + */ + @Data + public static class SendOtpRequest implements Serializable { + /** + * 渠道 + */ + @Parameter(description = "channel") + @javax.validation.constraints.NotNull(message = "消息渠道不能为空") + private MessageNoticeChannel channel; + } + + public static RequestMatcher getRequestMatcher() { + return SMS_SEND_OPT_MATCHER; + } + + private final OtpContextHelp otpContextHelp; + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + public MfaAuthenticationSendOtpFilter(OtpContextHelp otpContextHelp) { + this.otpContextHelp = otpContextHelp; + } +} diff --git a/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/constant/MfaAuthenticationConstants.java b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/constant/MfaAuthenticationConstants.java new file mode 100644 index 00000000..8c79d61a --- /dev/null +++ b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/constant/MfaAuthenticationConstants.java @@ -0,0 +1,48 @@ +/* + * eiam-authentication-mfa - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.authentication.mfa.constant; + +import static cn.topiam.employee.common.constants.AuthorizeConstants.LOGIN_PATH; + +/** + * Mfa 认证常量 + * + * @author TopIAM + * Created by support@topiam.cn on 2021/12/19 23:19 + */ +public final class MfaAuthenticationConstants { + /** + * mfa + */ + public static final String LOGIN_MFA = LOGIN_PATH + "/mfa"; + /** + * mfa 登录提供者 + */ + public static final String LOGIN_MFA_FACTORS = LOGIN_MFA + "/factors"; + + /** + * maf 验证 + */ + public static final String MFA_VALIDATE = LOGIN_MFA + "/validate"; + + /** + * 发送 OTP + */ + public static final String OTP_SEND_OTP = LOGIN_MFA + "/send"; + +} \ No newline at end of file diff --git a/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/configurer/package-info.java b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/constant/package-info.java similarity index 85% rename from eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/configurer/package-info.java rename to eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/constant/package-info.java index 3c293f6c..d98f7da2 100644 --- a/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/configurer/package-info.java +++ b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/constant/package-info.java @@ -1,5 +1,5 @@ /* - * eiam-authentication-sms - Employee Identity and Access Management Program + * eiam-authentication-mfa - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,4 +15,4 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.authentication.sms.configurer; \ No newline at end of file +package cn.topiam.employee.authentication.mfa.constant; \ No newline at end of file diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/email/EmailOtpProviderValidator.java b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/email/EmailOtpProviderValidator.java similarity index 60% rename from eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/email/EmailOtpProviderValidator.java rename to eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/email/EmailOtpProviderValidator.java index 9f0779f0..6308f87a 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/email/EmailOtpProviderValidator.java +++ b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/email/EmailOtpProviderValidator.java @@ -1,5 +1,5 @@ /* - * eiam-portal - Employee Identity and Access Management Program + * eiam-authentication-mfa - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,9 +15,15 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.portal.mfa.email; +package cn.topiam.employee.authentication.mfa.email; +import cn.topiam.employee.common.entity.account.UserEntity; +import cn.topiam.employee.common.enums.MailType; +import cn.topiam.employee.common.enums.MessageNoticeChannel; import cn.topiam.employee.core.security.mfa.MfaProviderValidator; +import cn.topiam.employee.core.security.otp.OtpContextHelp; +import cn.topiam.employee.core.security.util.UserUtils; +import cn.topiam.employee.support.context.ApplicationContextHelp; /** * OTP 提供商验证 @@ -33,6 +39,9 @@ public class EmailOtpProviderValidator implements MfaProviderValidator { */ @Override public boolean validate(String code) { - return true; + UserEntity user = UserUtils.getUser(); + OtpContextHelp bean = ApplicationContextHelp.getBean(OtpContextHelp.class); + return bean.checkOtp(MailType.AGAIN_VERIFY.getCode(), MessageNoticeChannel.MAIL, + user.getEmail(), code); } } diff --git a/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/sms/SmsOtpProviderValidator.java b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/sms/SmsOtpProviderValidator.java new file mode 100644 index 00000000..bdba5dfc --- /dev/null +++ b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/sms/SmsOtpProviderValidator.java @@ -0,0 +1,47 @@ +/* + * eiam-authentication-mfa - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.authentication.mfa.sms; + +import cn.topiam.employee.common.entity.account.UserEntity; +import cn.topiam.employee.common.enums.MessageNoticeChannel; +import cn.topiam.employee.common.enums.SmsType; +import cn.topiam.employee.core.security.mfa.MfaProviderValidator; +import cn.topiam.employee.core.security.otp.OtpContextHelp; +import cn.topiam.employee.core.security.util.UserUtils; +import cn.topiam.employee.support.context.ApplicationContextHelp; + +/** + * 短信 OTP 提供商验证 + * + * @author TopIAM + * Created by support@topiam.cn on 2022/7/31 20:50 + */ +public class SmsOtpProviderValidator implements MfaProviderValidator { + /** + * 验证 + * + * @param code {@link String} + */ + @Override + public boolean validate(String code) { + UserEntity user = UserUtils.getUser(); + OtpContextHelp bean = ApplicationContextHelp.getBean(OtpContextHelp.class); + return bean.checkOtp(SmsType.AGAIN_VERIFY.getCode(), MessageNoticeChannel.SMS, + user.getPhone(), code); + } +} diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/totp/TotpProviderValidator.java b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/totp/TotpProviderValidator.java similarity index 82% rename from eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/totp/TotpProviderValidator.java rename to eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/totp/TotpProviderValidator.java index f96799ff..e7f92bef 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/totp/TotpProviderValidator.java +++ b/eiam-authentication/eiam-authentication-mfa/src/main/java/cn/topiam/employee/authentication/mfa/totp/TotpProviderValidator.java @@ -1,5 +1,5 @@ /* - * eiam-portal - Employee Identity and Access Management Program + * eiam-authentication-mfa - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.portal.mfa.totp; +package cn.topiam.employee.authentication.mfa.totp; import cn.topiam.employee.common.entity.account.UserEntity; import cn.topiam.employee.core.security.mfa.MfaProviderValidator; @@ -37,7 +37,9 @@ public class TotpProviderValidator implements MfaProviderValidator { @Override public boolean validate(String code) { UserEntity user = UserUtils.getUser(); - return new TotpAuthenticator().checkCode(user.getSharedSecret(), Long.parseLong(code), + return totpAuthenticator.checkCode(user.getSharedSecret(), Long.parseLong(code), System.currentTimeMillis()); } + + private final TotpAuthenticator totpAuthenticator = new TotpAuthenticator(); } diff --git a/eiam-authentication/eiam-authentication-qq/src/main/java/cn/topiam/employee/authentication/qq/filter/QqOAuth2AuthorizationRequestRedirectFilter.java b/eiam-authentication/eiam-authentication-qq/src/main/java/cn/topiam/employee/authentication/qq/filter/QqOAuth2AuthorizationRequestRedirectFilter.java index 6a6a130b..41b6749e 100644 --- a/eiam-authentication/eiam-authentication-qq/src/main/java/cn/topiam/employee/authentication/qq/filter/QqOAuth2AuthorizationRequestRedirectFilter.java +++ b/eiam-authentication/eiam-authentication-qq/src/main/java/cn/topiam/employee/authentication/qq/filter/QqOAuth2AuthorizationRequestRedirectFilter.java @@ -49,8 +49,9 @@ import com.alibaba.fastjson2.JSONObject; import cn.topiam.employee.authentication.qq.QqIdpOauthConfig; import cn.topiam.employee.common.entity.authentication.IdentityProviderEntity; import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; +import static cn.topiam.employee.authentication.common.IdentityProviderType.QQ; +import static cn.topiam.employee.authentication.common.constant.AuthenticationConstants.PROVIDER_CODE; import static cn.topiam.employee.authentication.qq.filter.QqOAuth2LoginAuthenticationFilter.getLoginUrl; -import static cn.topiam.employee.common.enums.IdentityProviderType.QQ; import static cn.topiam.employee.portal.idp.qq.constant.QqAuthenticationConstants.URL_AUTHORIZE; /** @@ -64,16 +65,12 @@ public class QqOAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFi private final Logger logger = LoggerFactory .getLogger(QqOAuth2AuthorizationRequestRedirectFilter.class); - /** - * 提供商ID - */ - public static final String PROVIDER_ID = "providerId"; /** * AntPathRequestMatcher */ public static final AntPathRequestMatcher QQ_REQUEST_MATCHER = new AntPathRequestMatcher( - QQ.getAuthorizationPathPrefix() + "/" + "{" + PROVIDER_ID + "}", HttpMethod.GET.name()); + QQ.getAuthorizationPathPrefix() + "/" + "{" + PROVIDER_CODE + "}", HttpMethod.GET.name()); /** * 重定向策略 @@ -104,9 +101,9 @@ public class QqOAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFi return; } Map variables = matcher.getVariables(); - String providerId = variables.get(PROVIDER_ID); + String providerCode = variables.get(PROVIDER_CODE); Optional optional = identityProviderRepository - .findByIdAndEnabledIsTrue(Long.valueOf(providerId)); + .findByCodeAndEnabledIsTrue(providerCode); if (optional.isEmpty()) { throw new NullPointerException("未查询到身份提供商信息"); } @@ -117,7 +114,8 @@ public class QqOAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFi //构建授权请求 OAuth2AuthorizationRequest.Builder builder = OAuth2AuthorizationRequest.authorizationCode() .clientId(config.getAppId()).authorizationUri(URL_AUTHORIZE) - .redirectUri(getLoginUrl(providerId)).state(DEFAULT_STATE_GENERATOR.generateKey()); + .redirectUri(getLoginUrl(optional.get().getCode())) + .state(DEFAULT_STATE_GENERATOR.generateKey()); builder.parameters(parameters -> { parameters.put(OAuth2ParameterNames.RESPONSE_TYPE, OAuth2ParameterNames.CODE); }); diff --git a/eiam-authentication/eiam-authentication-qq/src/main/java/cn/topiam/employee/authentication/qq/filter/QqOAuth2LoginAuthenticationFilter.java b/eiam-authentication/eiam-authentication-qq/src/main/java/cn/topiam/employee/authentication/qq/filter/QqOAuth2LoginAuthenticationFilter.java index 0b55c440..c197ed4d 100644 --- a/eiam-authentication/eiam-authentication-qq/src/main/java/cn/topiam/employee/authentication/qq/filter/QqOAuth2LoginAuthenticationFilter.java +++ b/eiam-authentication/eiam-authentication-qq/src/main/java/cn/topiam/employee/authentication/qq/filter/QqOAuth2LoginAuthenticationFilter.java @@ -53,8 +53,8 @@ import cn.topiam.employee.support.trace.TraceUtils; import cn.topiam.employee.support.util.HttpClientUtils; import static com.nimbusds.oauth2.sdk.GrantType.AUTHORIZATION_CODE; -import static cn.topiam.employee.authentication.qq.filter.QqOAuth2AuthorizationRequestRedirectFilter.PROVIDER_ID; -import static cn.topiam.employee.common.enums.IdentityProviderType.QQ; +import static cn.topiam.employee.authentication.common.IdentityProviderType.QQ; +import static cn.topiam.employee.authentication.common.constant.AuthenticationConstants.PROVIDER_CODE; import static cn.topiam.employee.portal.idp.qq.constant.QqAuthenticationConstants.URL_GET_ACCESS_TOKEN; import static cn.topiam.employee.portal.idp.qq.constant.QqAuthenticationConstants.URL_GET_OPEN_ID; @@ -70,7 +70,7 @@ public class QqOAuth2LoginAuthenticationFilter extends AbstractIdpAuthentication public final static String DEFAULT_FILTER_PROCESSES_URI = QQ.getLoginPathPrefix() + "/*"; public static final AntPathRequestMatcher REQUEST_MATCHER = new AntPathRequestMatcher( - QQ.getLoginPathPrefix() + "/" + "{" + PROVIDER_ID + "}", HttpMethod.GET.name()); + QQ.getLoginPathPrefix() + "/" + "{" + PROVIDER_CODE + "}", HttpMethod.GET.name()); /** * Creates a new instance @@ -100,7 +100,7 @@ public class QqOAuth2LoginAuthenticationFilter extends AbstractIdpAuthentication TraceUtils.put(UUID.randomUUID().toString()); RequestMatcher.MatchResult matcher = REQUEST_MATCHER.matcher(request); Map variables = matcher.getVariables(); - String providerId = variables.get(PROVIDER_ID); + String providerId = variables.get(PROVIDER_CODE); //code String code = request.getParameter(OAuth2ParameterNames.CODE); if (StringUtils.isEmpty(code)) { @@ -133,7 +133,7 @@ public class QqOAuth2LoginAuthenticationFilter extends AbstractIdpAuthentication param.put(OAuth2ParameterNames.CLIENT_ID, config.getAppId().trim()); param.put(OAuth2ParameterNames.CLIENT_SECRET, config.getAppKey().trim()); param.put(OAuth2ParameterNames.CODE, code.trim()); - param.put(OAuth2ParameterNames.REDIRECT_URI, getLoginUrl(providerId)); + param.put(OAuth2ParameterNames.REDIRECT_URI, getLoginUrl(provider.getCode())); param.put("fmt", "json"); //注意:QQ不能使用编码后的get请求,否则会报 {"error_description":"redirect uri is illegal","error":100010} JSONObject result = JSON.parseObject(HttpClientUtils.doGet(URL_GET_ACCESS_TOKEN, param)); diff --git a/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/SendSmsCaptchaFilter.java b/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/SendSmsCaptchaFilter.java new file mode 100644 index 00000000..a708c3be --- /dev/null +++ b/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/SendSmsCaptchaFilter.java @@ -0,0 +1,94 @@ +/* + * eiam-authentication-sms - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.authentication.sms; + +import java.io.IOException; +import java.util.Objects; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.web.filter.OncePerRequestFilter; + +import cn.topiam.employee.authentication.sms.exception.PhoneNotExistException; +import cn.topiam.employee.common.entity.account.UserEntity; +import cn.topiam.employee.common.repository.account.UserRepository; +import cn.topiam.employee.core.security.otp.OtpContextHelp; +import cn.topiam.employee.support.result.ApiRestResult; +import cn.topiam.employee.support.util.HttpResponseUtils; +import static cn.topiam.employee.authentication.sms.constant.SmsAuthenticationConstants.PHONE_KEY; +import static cn.topiam.employee.authentication.sms.constant.SmsAuthenticationConstants.SMS_SEND_OTP; +import static cn.topiam.employee.common.enums.MessageNoticeChannel.SMS; +import static cn.topiam.employee.common.enums.SmsType.LOGIN; + +/** + * 发送短信OPT + * + * @author SanLi + * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2023/1/1 22:01 + */ +public class SendSmsCaptchaFilter extends OncePerRequestFilter { + public final static String DEFAULT_FILTER_PROCESSES_URI = SMS_SEND_OTP; + + public static final RequestMatcher SMS_SEND_OPT_MATCHER = new AntPathRequestMatcher( + DEFAULT_FILTER_PROCESSES_URI, HttpMethod.POST.name()); + + @Override + protected void doFilterInternal(@NotNull HttpServletRequest request, + @NotNull HttpServletResponse response, + @NotNull FilterChain filterChain) throws ServletException, + IOException { + if (!getRequestMatcher().matches(request)) { + filterChain.doFilter(request, response); + return; + } + String phone = request.getParameter(PHONE_KEY); + if (StringUtils.isBlank(phone)) { + throw new PhoneNotExistException(); + } + //判断是否存在用户 + UserEntity user = userRepository.findByPhone(phone); + if (Objects.isNull(user)) { + HttpResponseUtils.flushResponseJson(response, HttpStatus.OK.value(), + ApiRestResult.ok()); + return; + } + //发送OPT + otpContextHelp.sendOtp(phone, LOGIN.getCode(), SMS); + } + + public static RequestMatcher getRequestMatcher() { + return SMS_SEND_OPT_MATCHER; + } + + private final UserRepository userRepository; + private final OtpContextHelp otpContextHelp; + + public SendSmsCaptchaFilter(UserRepository userRepository, OtpContextHelp otpContextHelp) { + this.userRepository = userRepository; + this.otpContextHelp = otpContextHelp; + } +} diff --git a/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/configurer/SmsAuthenticationConfigurer.java b/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/SmsAuthenticationConfigurer.java similarity index 72% rename from eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/configurer/SmsAuthenticationConfigurer.java rename to eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/SmsAuthenticationConfigurer.java index efeecc89..2d87d306 100644 --- a/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/configurer/SmsAuthenticationConfigurer.java +++ b/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/SmsAuthenticationConfigurer.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.authentication.sms.configurer; +package cn.topiam.employee.authentication.sms; import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer; @@ -25,7 +25,8 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; -import cn.topiam.employee.authentication.sms.filter.SmsAuthenticationFilter; +import cn.topiam.employee.common.repository.account.UserRepository; +import cn.topiam.employee.core.security.otp.OtpContextHelp; /** * 认证配置 @@ -35,11 +36,20 @@ import cn.topiam.employee.authentication.sms.filter.SmsAuthenticationFilter; */ public final class SmsAuthenticationConfigurer> extends AbstractAuthenticationFilterConfigurer, SmsAuthenticationFilter> { + private final UserRepository userRepository; private final UserDetailsService userDetailsService; - public SmsAuthenticationConfigurer(UserDetailsService userDetailsService) { + private final OtpContextHelp otpContextHelp; + + public SmsAuthenticationConfigurer(UserRepository userRepository, + UserDetailsService userDetailsService, + OtpContextHelp otpContextHelp) { + Assert.notNull(userDetailsService, "userRepository must not be null"); Assert.notNull(userDetailsService, "userDetailsService must not be null"); + Assert.notNull(otpContextHelp, "otpContextHelp must not be null"); this.userDetailsService = userDetailsService; + this.userRepository = userRepository; + this.otpContextHelp = otpContextHelp; } /** @@ -58,7 +68,7 @@ public final class SmsAuthenticationConfigurer> public void init(H http) throws Exception { //SMS SmsAuthenticationFilter loginAuthenticationFilter = new SmsAuthenticationFilter( - userDetailsService); + userDetailsService, otpContextHelp); this.setAuthenticationFilter(loginAuthenticationFilter); //处理URL super.loginProcessingUrl(SmsAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI); @@ -67,12 +77,10 @@ public final class SmsAuthenticationConfigurer> @Override public void configure(H http) throws Exception { - http.addFilterAfter(this.getAuthenticationFilter(), - UsernamePasswordAuthenticationFilter.class); + SendSmsCaptchaFilter sendSmsCaptchaFilter = new SendSmsCaptchaFilter(userRepository, + otpContextHelp); + http.addFilterAfter(sendSmsCaptchaFilter, UsernamePasswordAuthenticationFilter.class); + http.addFilterAfter(this.getAuthenticationFilter(), SendSmsCaptchaFilter.class); super.configure(http); } - - public RequestMatcher getRequestMatcher() { - return SmsAuthenticationFilter.getRequestMatcher(); - } } diff --git a/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/filter/SmsAuthenticationFilter.java b/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/SmsAuthenticationFilter.java similarity index 72% rename from eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/filter/SmsAuthenticationFilter.java rename to eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/SmsAuthenticationFilter.java index 6bf48b78..074508c4 100644 --- a/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/filter/SmsAuthenticationFilter.java +++ b/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/SmsAuthenticationFilter.java @@ -15,12 +15,14 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.authentication.sms.filter; +package cn.topiam.employee.authentication.sms; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.security.authentication.AuthenticationServiceException; @@ -28,15 +30,23 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; +import cn.topiam.employee.authentication.sms.exception.CaptchaNotExistException; +import cn.topiam.employee.authentication.sms.exception.PhoneNotExistException; +import cn.topiam.employee.common.enums.MessageNoticeChannel; import cn.topiam.employee.core.security.authentication.SmsAuthentication; +import cn.topiam.employee.core.security.otp.OtpContextHelp; import cn.topiam.employee.support.result.ApiRestResult; import cn.topiam.employee.support.util.HttpResponseUtils; +import static cn.topiam.employee.authentication.sms.constant.SmsAuthenticationConstants.CODE_KEY; +import static cn.topiam.employee.authentication.sms.constant.SmsAuthenticationConstants.PHONE_KEY; import static cn.topiam.employee.common.constants.AuthorizeConstants.SMS_LOGIN; +import static cn.topiam.employee.common.enums.SmsType.LOGIN; import static cn.topiam.employee.support.exception.enums.ExceptionStatus.EX000102; /** @@ -47,13 +57,15 @@ import static cn.topiam.employee.support.exception.enums.ExceptionStatus.EX00010 */ public class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter { - public static final String PHONE_KEY = "phone"; + private final Logger logger = LoggerFactory + .getLogger(SmsAuthenticationFilter.class); /** * 请求方法 */ public static final String METHOD = "POST"; private String phoneParameter = PHONE_KEY; + private String codeParameter = CODE_KEY; /** * 是否值处理POST请求 */ @@ -64,11 +76,6 @@ public class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFil public static final RequestMatcher SMS_LOGIN_MATCHER = new AntPathRequestMatcher( DEFAULT_FILTER_PROCESSES_URI, HttpMethod.POST.name()); - public SmsAuthenticationFilter(UserDetailsService userDetailsService) { - super(SMS_LOGIN_MATCHER); - this.userDetailsService = userDetailsService; - } - @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { @@ -79,7 +86,21 @@ public class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFil } // 获取手机号 String phone = StringUtils.defaultString(obtainUsername(request), "").trim(); + if (StringUtils.isBlank(phone)) { + throw new PhoneNotExistException(); + } + String code = StringUtils.defaultString(obtainCode(request), "").trim(); + if (StringUtils.isBlank(code)) { + throw new CaptchaNotExistException(); + } UserDetails userDetails = userDetailsService.loadUserByUsername(phone); + //判断短信验证码 + Boolean checkOtp = otpContextHelp.checkOtp(LOGIN.getCode(), MessageNoticeChannel.SMS, + phone, code); + if (!checkOtp) { + logger.error("用户手机号: [{}], 验证码: [{}] 认证失败", phone, code); + throw new UsernameNotFoundException("用户名或密码错误"); + } SmsAuthentication authentication = new SmsAuthentication(userDetails, phone, userDetails.getAuthorities()); // Allow subclasses to set the "details" property @@ -106,6 +127,10 @@ public class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFil return request.getParameter(phoneParameter); } + protected String obtainCode(HttpServletRequest request) { + return request.getParameter(codeParameter); + } + /** * Provided so that subclasses may configure what is put into the * authentication request's details property. @@ -135,9 +160,18 @@ public class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFil return phoneParameter; } - public void setPhoneParameter(String phoneParameter) { + public final String getCodeParameter() { + return codeParameter; + } + + public void setPhoneParameter(String codeParameter) { Assert.hasText(phoneParameter, "Mobile parameter must not be empty or null"); - this.phoneParameter = phoneParameter; + this.codeParameter = codeParameter; + } + + public void setCodeParameter(String codeParameter) { + Assert.hasText(codeParameter, "Code parameter must not be empty or null"); + this.codeParameter = codeParameter; } public static RequestMatcher getRequestMatcher() { @@ -145,4 +179,13 @@ public class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFil } private final UserDetailsService userDetailsService; + + private final OtpContextHelp otpContextHelp; + + public SmsAuthenticationFilter(UserDetailsService userDetailsService, + OtpContextHelp otpContextHelp) { + super(SMS_LOGIN_MATCHER); + this.userDetailsService = userDetailsService; + this.otpContextHelp = otpContextHelp; + } } diff --git a/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/constant/ProtocolConstants.java b/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/constant/SmsAuthenticationConstants.java similarity index 59% rename from eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/constant/ProtocolConstants.java rename to eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/constant/SmsAuthenticationConstants.java index 2122276f..5606889b 100644 --- a/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/constant/ProtocolConstants.java +++ b/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/constant/SmsAuthenticationConstants.java @@ -1,5 +1,5 @@ /* - * eiam-protocol-form - Employee Identity and Access Management Program + * eiam-authentication-sms - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,23 +15,29 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.protocol.form.constant; +package cn.topiam.employee.authentication.sms.constant; -import static cn.topiam.employee.common.constants.AuthorizeConstants.AUTHORIZE_PATH; -import static cn.topiam.employee.common.constants.ProtocolConstants.APP_CODE_VARIABLE; +import static cn.topiam.employee.common.constants.AuthorizeConstants.LOGIN_PATH; /** - * 协议常量 + * Sms认证常量 * * @author TopIAM - * Created by support@topiam.cn on 2021/12/8 21:29 + * Created by support@topiam.cn on 2021/12/19 23:19 */ -public class ProtocolConstants { +public final class SmsAuthenticationConstants { /** - * FORM IDP SSO 发起 + * sms login 路径 */ - public static final String IDP_FORM_SSO_INITIATOR = AUTHORIZE_PATH + "/form/" - + APP_CODE_VARIABLE + "/initiator"; + public static final String SMS_LOGIN = LOGIN_PATH + "/sms"; -} + /** + * 发送短信OTP + */ + public static final String SMS_SEND_OTP = SMS_LOGIN + "/send"; + + public static final String PHONE_KEY = "phone"; + public static final String CODE_KEY = "code"; + +} \ No newline at end of file diff --git a/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/filter/package-info.java b/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/constant/package-info.java similarity index 93% rename from eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/filter/package-info.java rename to eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/constant/package-info.java index 09f5ba56..c250c94b 100644 --- a/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/filter/package-info.java +++ b/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/constant/package-info.java @@ -15,4 +15,4 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.authentication.sms.filter; \ No newline at end of file +package cn.topiam.employee.authentication.sms.constant; \ No newline at end of file diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/SmsProviderValidator.java b/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/exception/CaptchaNotExistException.java similarity index 61% rename from eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/SmsProviderValidator.java rename to eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/exception/CaptchaNotExistException.java index 3e9843c0..58a63c68 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/SmsProviderValidator.java +++ b/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/exception/CaptchaNotExistException.java @@ -1,5 +1,5 @@ /* - * eiam-portal - Employee Identity and Access Management Program + * eiam-authentication-sms - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,24 +15,17 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.portal.mfa; +package cn.topiam.employee.authentication.sms.exception; -import cn.topiam.employee.core.security.mfa.MfaProviderValidator; +import cn.topiam.employee.support.exception.TopIamException; /** - * Sms提供商验证 * - * @author TopIAM - * Created by support@topiam.cn on 2022/7/31 20:50 + * @author SanLi + * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2023/1/2 13:00 */ -public class SmsProviderValidator implements MfaProviderValidator { - /** - * 验证 - * - * @param code {@link String} - */ - @Override - public boolean validate(String code) { - return true; +public class CaptchaNotExistException extends TopIamException { + public CaptchaNotExistException() { + super("captcha_not_exist", "验证码不存在", DEFAULT_STATUS); } } diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/EmailProviderValidator.java b/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/exception/PhoneNotExistException.java similarity index 60% rename from eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/EmailProviderValidator.java rename to eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/exception/PhoneNotExistException.java index 181daba1..e80bb762 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/EmailProviderValidator.java +++ b/eiam-authentication/eiam-authentication-sms/src/main/java/cn/topiam/employee/authentication/sms/exception/PhoneNotExistException.java @@ -1,5 +1,5 @@ /* - * eiam-portal - Employee Identity and Access Management Program + * eiam-authentication-sms - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,24 +15,19 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.portal.mfa; +package cn.topiam.employee.authentication.sms.exception; -import cn.topiam.employee.core.security.mfa.MfaProviderValidator; +import cn.topiam.employee.support.exception.TopIamException; /** - * Email提供商验证 + * 手机号不存在异常 * - * @author TopIAM - * Created by support@topiam.cn on 2022/7/31 20:50 + * @author SanLi + * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2023/1/2 12:59 */ -public class EmailProviderValidator implements MfaProviderValidator { - /** - * 验证 - * - * @param code {@link String} - */ - @Override - public boolean validate(String code) { - return true; +public class PhoneNotExistException extends TopIamException { + public PhoneNotExistException() { + super("phone_not_exist", "手机号不存在", DEFAULT_STATUS); } + } diff --git a/eiam-authentication/eiam-authentication-wechat/src/main/java/cn/topiam/employee/authentication/wechat/filter/WeChatScanCodeAuthorizationRequestRedirectFilter.java b/eiam-authentication/eiam-authentication-wechat/src/main/java/cn/topiam/employee/authentication/wechat/filter/WeChatScanCodeAuthorizationRequestRedirectFilter.java index b7643b3e..c63ea3bd 100644 --- a/eiam-authentication/eiam-authentication-wechat/src/main/java/cn/topiam/employee/authentication/wechat/filter/WeChatScanCodeAuthorizationRequestRedirectFilter.java +++ b/eiam-authentication/eiam-authentication-wechat/src/main/java/cn/topiam/employee/authentication/wechat/filter/WeChatScanCodeAuthorizationRequestRedirectFilter.java @@ -51,8 +51,9 @@ import com.google.common.collect.Sets; import cn.topiam.employee.authentication.wechat.WeChatIdpScanCodeConfig; import cn.topiam.employee.common.entity.authentication.IdentityProviderEntity; import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; +import static cn.topiam.employee.authentication.common.IdentityProviderType.WECHAT_QR; +import static cn.topiam.employee.authentication.common.constant.AuthenticationConstants.PROVIDER_CODE; import static cn.topiam.employee.authentication.wechat.constant.WeChatAuthenticationConstants.*; -import static cn.topiam.employee.common.enums.IdentityProviderType.WECHAT_SCAN_CODE; /** * 微信扫码登录请求重定向过滤器 @@ -66,16 +67,11 @@ public class WeChatScanCodeAuthorizationRequestRedirectFilter extends OncePerReq private final Logger logger = LoggerFactory .getLogger(WeChatScanCodeAuthorizationRequestRedirectFilter.class); - /** - * 提供商ID - */ - public static final String PROVIDER_ID = "providerId"; - /** * AntPathRequestMatcher */ public static final AntPathRequestMatcher WE_CHAT_SCAN_CODE_REQUEST_MATCHER = new AntPathRequestMatcher( - WECHAT_SCAN_CODE.getAuthorizationPathPrefix() + "/" + "{" + PROVIDER_ID + "}", + WECHAT_QR.getAuthorizationPathPrefix() + "/" + "{" + PROVIDER_CODE + "}", HttpMethod.GET.name()); /** @@ -107,9 +103,9 @@ public class WeChatScanCodeAuthorizationRequestRedirectFilter extends OncePerReq return; } Map variables = matcher.getVariables(); - String providerId = variables.get(PROVIDER_ID); + String providerCode = variables.get(PROVIDER_CODE); Optional optional = identityProviderRepository - .findByIdAndEnabledIsTrue(Long.valueOf(providerId)); + .findByCodeAndEnabledIsTrue(providerCode); if (optional.isEmpty()) { throw new NullPointerException("未查询到身份提供商信息"); } @@ -124,7 +120,7 @@ public class WeChatScanCodeAuthorizationRequestRedirectFilter extends OncePerReq .clientId(config.getAppId()) .scopes(Sets.newHashSet(SNSAPI_LOGIN)) .authorizationUri(AUTHORIZATION_REQUEST) - .redirectUri(WeChatScanCodeLoginAuthenticationFilter.getLoginUrl(providerId)) + .redirectUri(WeChatScanCodeLoginAuthenticationFilter.getLoginUrl(optional.get().getCode())) .state(DEFAULT_STATE_GENERATOR.generateKey()) .attributes(attributes); //@formatter:on @@ -160,13 +156,13 @@ public class WeChatScanCodeAuthorizationRequestRedirectFilter extends OncePerReq authorizationRequest.getAuthorizationRequestUri()); } - private final static String STYLE = "" + private static final String STYLE = "" + ".impowerBox .qrcode {width: 280px;border: none;margin-top:10px;}\n" + ".impowerBox .title {display: none;}\n" + ".impowerBox .info {display: none;}\n" + ".status_icon {display: none}\n" + ".impowerBox .status {text-align: center;} "; - private final static String STYLE_BASE64 = "data:text/css;base64," + Base64.getEncoder() + private static final String STYLE_BASE64 = "data:text/css;base64," + Base64.getEncoder() .encodeToString(STYLE.getBytes(StandardCharsets.UTF_8)); public static RequestMatcher getRequestMatcher() { diff --git a/eiam-authentication/eiam-authentication-wechat/src/main/java/cn/topiam/employee/authentication/wechat/filter/WeChatScanCodeLoginAuthenticationFilter.java b/eiam-authentication/eiam-authentication-wechat/src/main/java/cn/topiam/employee/authentication/wechat/filter/WeChatScanCodeLoginAuthenticationFilter.java index 9118375e..fb1f6ea5 100644 --- a/eiam-authentication/eiam-authentication-wechat/src/main/java/cn/topiam/employee/authentication/wechat/filter/WeChatScanCodeLoginAuthenticationFilter.java +++ b/eiam-authentication/eiam-authentication-wechat/src/main/java/cn/topiam/employee/authentication/wechat/filter/WeChatScanCodeLoginAuthenticationFilter.java @@ -46,16 +46,15 @@ import cn.topiam.employee.authentication.common.service.UserIdpService; import cn.topiam.employee.authentication.wechat.WeChatIdpScanCodeConfig; import cn.topiam.employee.authentication.wechat.constant.WeChatAuthenticationConstants; import cn.topiam.employee.common.entity.authentication.IdentityProviderEntity; -import cn.topiam.employee.common.enums.IdentityProviderType; import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; import cn.topiam.employee.core.context.ServerContextHelp; import cn.topiam.employee.support.exception.TopIamException; import cn.topiam.employee.support.util.HttpClientUtils; import static org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE; +import static cn.topiam.employee.authentication.common.IdentityProviderType.WECHAT_QR; +import static cn.topiam.employee.authentication.common.constant.AuthenticationConstants.PROVIDER_CODE; import static cn.topiam.employee.authentication.wechat.constant.WeChatAuthenticationConstants.*; -import static cn.topiam.employee.authentication.wechat.filter.WeChatScanCodeAuthorizationRequestRedirectFilter.PROVIDER_ID; -import static cn.topiam.employee.common.enums.IdentityProviderType.WECHAT_SCAN_CODE; /** * 微信扫码登录过滤器 @@ -66,11 +65,10 @@ import static cn.topiam.employee.common.enums.IdentityProviderType.WECHAT_SCAN_C public class WeChatScanCodeLoginAuthenticationFilter extends AbstractIdpAuthenticationProcessingFilter { - public final static String DEFAULT_FILTER_PROCESSES_URI = WECHAT_SCAN_CODE + public final static String DEFAULT_FILTER_PROCESSES_URI = WECHAT_QR .getLoginPathPrefix() + "/*"; public static final AntPathRequestMatcher REQUEST_MATCHER = new AntPathRequestMatcher( - WECHAT_SCAN_CODE.getLoginPathPrefix() + "/" + "{" + PROVIDER_ID + "}", - HttpMethod.GET.name()); + WECHAT_QR.getLoginPathPrefix() + "/" + "{" + PROVIDER_CODE + "}", HttpMethod.GET.name()); /** * Creates a new instance @@ -99,7 +97,7 @@ public class WeChatScanCodeLoginAuthenticationFilter extends response); RequestMatcher.MatchResult matcher = REQUEST_MATCHER.matcher(request); Map variables = matcher.getVariables(); - String providerId = variables.get(PROVIDER_ID); + String providerId = variables.get(PROVIDER_CODE); //code String code = request.getParameter(OAuth2ParameterNames.CODE); if (StringUtils.isEmpty(code)) { @@ -152,13 +150,12 @@ public class WeChatScanCodeLoginAuthenticationFilter extends } // 返回 IdpUser idpUser = IdpUser.builder().openId(param.get(OidcScopes.OPENID)).build(); - return attemptAuthentication(request, response, IdentityProviderType.WECHAT_SCAN_CODE, - providerId, idpUser); + return attemptAuthentication(request, response, WECHAT_QR, providerId, idpUser); } public static String getLoginUrl(String providerId) { - String url = ServerContextHelp.getPortalPublicBaseUrl() - + WECHAT_SCAN_CODE.getLoginPathPrefix() + "/" + providerId; + String url = ServerContextHelp.getPortalPublicBaseUrl() + WECHAT_QR.getLoginPathPrefix() + + "/" + providerId; return url.replaceAll("(? variables = matcher.getVariables(); - String providerId = variables.get(PROVIDER_ID); + String providerCode = variables.get(PROVIDER_CODE); Optional optional = identityProviderRepository - .findByIdAndEnabledIsTrue(Long.valueOf(providerId)); + .findByCodeAndEnabledIsTrue(providerCode); if (optional.isEmpty()) { throw new NullPointerException("未查询到身份提供商信息"); } @@ -115,7 +111,8 @@ public class WeChatWorkScanCodeAuthorizationRequestRedirectFilter extends OncePe OAuth2AuthorizationRequest.Builder builder = OAuth2AuthorizationRequest.authorizationCode() .clientId(config.getCorpId()) .authorizationUri(WeChatWorkAuthenticationConstants.URL_AUTHORIZE) - .redirectUri(WeChatWorkScanCodeLoginAuthenticationFilter.getLoginUrl(providerId)) + .redirectUri( + WeChatWorkScanCodeLoginAuthenticationFilter.getLoginUrl(optional.get().getCode())) .state(DEFAULT_STATE_GENERATOR.generateKey()); builder.parameters(parameters -> { HashMap linkedParameters = new LinkedHashMap<>(); @@ -149,13 +146,13 @@ public class WeChatWorkScanCodeAuthorizationRequestRedirectFilter extends OncePe authorizationRequest.getAuthorizationRequestUri()); } - private final static String STYLE = "" + private static final String STYLE = "" + ".impowerBox .qrcode {width: 280px;border: none;margin-top:10px;}\n" + ".impowerBox .title {display: none;}\n" + ".impowerBox .info {display: none;}\n" + ".status_icon {display: none}\n" + ".impowerBox .status {text-align: center;} "; - private final static String STYLE_BASE64 = "data:text/css;base64," + Base64.getEncoder() + private static final String STYLE_BASE64 = "data:text/css;base64," + Base64.getEncoder() .encodeToString(STYLE.getBytes(StandardCharsets.UTF_8)); public static RequestMatcher getRequestMatcher() { diff --git a/eiam-authentication/eiam-authentication-wechatwork/src/main/java/cn/topiam/employee/authentication/wechatwork/filter/WeChatWorkScanCodeLoginAuthenticationFilter.java b/eiam-authentication/eiam-authentication-wechatwork/src/main/java/cn/topiam/employee/authentication/wechatwork/filter/WeChatWorkScanCodeLoginAuthenticationFilter.java index 8c732d63..0898cd5f 100644 --- a/eiam-authentication/eiam-authentication-wechatwork/src/main/java/cn/topiam/employee/authentication/wechatwork/filter/WeChatWorkScanCodeLoginAuthenticationFilter.java +++ b/eiam-authentication/eiam-authentication-wechatwork/src/main/java/cn/topiam/employee/authentication/wechatwork/filter/WeChatWorkScanCodeLoginAuthenticationFilter.java @@ -53,8 +53,8 @@ import cn.topiam.employee.common.repository.authentication.IdentityProviderRepos import cn.topiam.employee.core.context.ServerContextHelp; import cn.topiam.employee.support.trace.TraceUtils; import cn.topiam.employee.support.util.HttpClientUtils; -import static cn.topiam.employee.authentication.wechatwork.filter.WeChatWorkScanCodeAuthorizationRequestRedirectFilter.PROVIDER_ID; -import static cn.topiam.employee.common.enums.IdentityProviderType.WECHATWORK_SCAN_CODE; +import static cn.topiam.employee.authentication.common.IdentityProviderType.WECHAT_WORK_QR; +import static cn.topiam.employee.authentication.common.constant.AuthenticationConstants.PROVIDER_CODE; /** * 企业微信扫码登录 @@ -67,10 +67,10 @@ public class WeChatWorkScanCodeLoginAuthenticationFilter extends AbstractIdpAuthenticationProcessingFilter { final String ERROR_CODE = "errcode"; final String SUCCESS = "0"; - public final static String DEFAULT_FILTER_PROCESSES_URI = WECHATWORK_SCAN_CODE + public final static String DEFAULT_FILTER_PROCESSES_URI = WECHAT_WORK_QR .getLoginPathPrefix() + "/*"; public static final AntPathRequestMatcher REQUEST_MATCHER = new AntPathRequestMatcher( - WECHATWORK_SCAN_CODE.getLoginPathPrefix() + "/" + "{" + PROVIDER_ID + "}", + WECHAT_WORK_QR.getLoginPathPrefix() + "/" + "{" + PROVIDER_CODE + "}", HttpMethod.GET.name()); /** @@ -101,7 +101,7 @@ public class WeChatWorkScanCodeLoginAuthenticationFilter extends TraceUtils.put(UUID.randomUUID().toString()); RequestMatcher.MatchResult matcher = REQUEST_MATCHER.matcher(request); Map variables = matcher.getVariables(); - String providerId = variables.get(PROVIDER_ID); + String providerId = variables.get(PROVIDER_CODE); //code String code = request.getParameter(OAuth2ParameterNames.CODE); if (StringUtils.isEmpty(code)) { @@ -145,7 +145,7 @@ public class WeChatWorkScanCodeLoginAuthenticationFilter extends String userId = StringUtils.defaultString(result.getString("UserId"), result.getString("OpenId")); IdpUser idpUser = IdpUser.builder().openId(userId).build(); - return attemptAuthentication(request, response, WECHATWORK_SCAN_CODE, providerId, idpUser); + return attemptAuthentication(request, response, WECHAT_WORK_QR, providerId, idpUser); } /** @@ -186,7 +186,7 @@ public class WeChatWorkScanCodeLoginAuthenticationFilter extends public static String getLoginUrl(String providerId) { String url = ServerContextHelp.getPortalPublicBaseUrl() - + WECHATWORK_SCAN_CODE.getLoginPathPrefix() + "/" + providerId; + + WECHAT_WORK_QR.getLoginPathPrefix() + "/" + providerId; return url.replaceAll("(?eiam-authentication-wechatwork eiam-authentication-sms eiam-authentication-mfa + eiam-authentication-captcha eiam-authentication-all diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/constants/AuthorizeConstants.java b/eiam-common/src/main/java/cn/topiam/employee/common/constants/AuthorizeConstants.java index b297cbc3..b0f3e887 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/constants/AuthorizeConstants.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/constants/AuthorizeConstants.java @@ -37,21 +37,12 @@ public final class AuthorizeConstants { * form 表单登录 */ public static final String FORM_LOGIN = LOGIN_PATH; + /** * sms login 路径 */ public static final String SMS_LOGIN = LOGIN_PATH + "/sms"; - /** - * maf 验证 - */ - public static final String MFA_VALIDATE = LOGIN_PATH + "/mfa/validate"; - - /** - * mfa 登录提供者 - */ - public static final String LOGIN_MFA_FACTORS = LOGIN_PATH + "/mfa/factors"; - /** * 登录配置 */ diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/constants/ConfigBeanNameConstants.java b/eiam-common/src/main/java/cn/topiam/employee/common/constants/ConfigBeanNameConstants.java index 3163b649..bb8af34f 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/constants/ConfigBeanNameConstants.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/constants/ConfigBeanNameConstants.java @@ -32,10 +32,12 @@ public final class ConfigBeanNameConstants { * 安全过滤器链 */ public static final String DEFAULT_SECURITY_FILTER_CHAIN = "defaultSecurityFilterChain"; - public static final String SOCIAL_SECURITY_FILTER_CHAIN = "socialSecurityFilterChain"; + public static final String IDP_SECURITY_FILTER_CHAIN = "idpSecurityFilterChain"; public static final String SAML2_PROTOCOL_SECURITY_FILTER_CHAIN = "saml2ProtocolSecurityFilterChain"; public static final String OIDC_PROTOCOL_SECURITY_FILTER_CHAIN = "oidcProtocolSecurityFilterChain"; + public static final String FORM_PROTOCOL_SECURITY_FILTER_CHAIN = "formProtocolSecurityFilterChain"; public static final String CAS_PROTOCOL_SECURITY_FILTER_CHAIN = "casProtocolSecurityFilterChain"; + public static final String TSA_PROTOCOL_SECURITY_FILTER_CHAIN = "tsaProtocolSecurityFilterChain"; /** * 默认密码策略管理器 diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/constants/ProtocolConstants.java b/eiam-common/src/main/java/cn/topiam/employee/common/constants/ProtocolConstants.java index 0c263bc4..18f05639 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/constants/ProtocolConstants.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/constants/ProtocolConstants.java @@ -64,6 +64,16 @@ public final class ProtocolConstants { */ public static final String APP_CERT_CACHE_NAME = APP_CACHE_NAME_PREFIX + "cert"; + /** + * FORM 配置缓存名称 + */ + public static final String FORM_CONFIG_CACHE_NAME = APP_CACHE_NAME_PREFIX + "form"; + + /** + * TSA 配置缓存名称 + */ + public static final String TSA_CONFIG_CACHE_NAME = APP_CACHE_NAME_PREFIX + "tsa"; + /** * OIDC Endpoint config */ @@ -75,19 +85,19 @@ public final class ProtocolConstants { */ public final static String OIDC_AUTHORIZE_BASE_PATH = AUTHORIZE_PATH + "/" + APP_CODE_VARIABLE; - public final static String OIDC_AUTHORIZE_PATH = OIDC_AUTHORIZE_BASE_PATH + "/oidc"; + public final static String OIDC_AUTHORIZE_PATH = OIDC_AUTHORIZE_BASE_PATH +"/oidc"; - public final static String OAUTH2_AUTHORIZE_PATH = OIDC_AUTHORIZE_BASE_PATH + "/oauth2"; + public final static String OAUTH2_AUTHORIZE_PATH = OIDC_AUTHORIZE_BASE_PATH +"/oauth2"; /** * OpenID Provider metadata. */ - public static final String WELL_KNOWN_OPENID_CONFIGURATION = OIDC_AUTHORIZE_PATH + OPENID_PROVIDER_WELL_KNOWN_PATH; + public static final String WELL_KNOWN_OPENID_CONFIGURATION = OIDC_AUTHORIZE_PATH +OPENID_PROVIDER_WELL_KNOWN_PATH; /** * Jwk Set Endpoint */ - public static final String JWK_SET_ENDPOINT = OIDC_AUTHORIZE_PATH + "/jwks"; + public static final String JWK_SET_ENDPOINT = OIDC_AUTHORIZE_PATH + "/jwks"; /** * OIDC Client Registration Endpoint @@ -97,27 +107,32 @@ public final class ProtocolConstants { /** * Authorization Endpoint */ - public static final String AUTHORIZATION_ENDPOINT = OAUTH2_AUTHORIZE_PATH + "/auth"; + public static final String AUTHORIZATION_ENDPOINT = OAUTH2_AUTHORIZE_PATH + "/auth"; + + /** + * Authorization Consent Endpoint + */ + public static final String AUTHORIZATION_CONSENT_ENDPOINT = AUTHORIZATION_ENDPOINT+"/consent"; /** * Token Endpoint */ - public static final String TOKEN_ENDPOINT = OAUTH2_AUTHORIZE_PATH + "/token"; + public static final String TOKEN_ENDPOINT = OAUTH2_AUTHORIZE_PATH + "/token"; /** * Jwk Revocation Endpoint */ - public static final String TOKEN_REVOCATION_ENDPOINT = OAUTH2_AUTHORIZE_PATH + "/revoke"; + public static final String TOKEN_REVOCATION_ENDPOINT = OAUTH2_AUTHORIZE_PATH + "/revoke"; /** * Token Introspection Endpoint */ - public static final String TOKEN_INTROSPECTION_ENDPOINT = OAUTH2_AUTHORIZE_PATH + "/introspect"; + public static final String TOKEN_INTROSPECTION_ENDPOINT = OAUTH2_AUTHORIZE_PATH + "/introspect"; /** * OIDC User Info Endpoint */ - public static final String OIDC_USER_INFO_ENDPOINT = OAUTH2_AUTHORIZE_PATH + "/userinfo"; + public static final String OIDC_USER_INFO_ENDPOINT = OAUTH2_AUTHORIZE_PATH + "/userinfo"; //@formatter:on } @@ -149,6 +164,30 @@ public final class ProtocolConstants { public static final String SAML_SSO_PATH = SAML2_AUTHORIZE_BASE_PATH + "/sso"; } + /** + * Form Endpoint config + */ + @Data + public static class FormEndpointConstants { + + /** + * FORM 认证路径 + */ + public final static String FORM_AUTHORIZE_BASE_PATH = AUTHORIZE_PATH + "/form/" + + APP_CODE_VARIABLE; + + /** + * FORM_SSO + */ + public static final String FORM_SSO_PATH = FORM_AUTHORIZE_BASE_PATH + "/sso"; + + /** + * FORM IDP SSO 发起 + */ + public static final String IDP_FORM_SSO_INITIATOR = FORM_AUTHORIZE_BASE_PATH + + "/initiator"; + } + @Data public static class CasEndpointConstants { /** @@ -160,6 +199,12 @@ public final class ProtocolConstants { * cas 登陆地址 */ public final static String CAS_LOGIN_PATH = CAS_AUTHORIZE_BASE_PATH + "/login"; + + /** + * cas 登出地址 + */ + public final static String CAS_LOGOUT_PATH = CAS_AUTHORIZE_BASE_PATH + "/logout"; + /** * cas ticket校验地址 */ @@ -172,4 +217,21 @@ public final class ProtocolConstants { + "/p3/serviceValidate"; } + /** + * TSA Endpoint config + */ + @Data + public static class TsaEndpointConstants { + + /** + * TSA 认证路径 + */ + public final static String TSA_AUTHORIZE_BASE_PATH = AUTHORIZE_PATH + "/tsa/" + + APP_CODE_VARIABLE; + + /** + * TSA_SSO + */ + public static final String TSA_SSO_PATH = TSA_AUTHORIZE_BASE_PATH + "/sso"; + } } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/constants/SettingConstants.java b/eiam-common/src/main/java/cn/topiam/employee/common/constants/SettingConstants.java index a37d26e6..eb9f75be 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/constants/SettingConstants.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/constants/SettingConstants.java @@ -48,4 +48,8 @@ public final class SettingConstants { */ public static final String ADMIN_CACHE_NAME = "admin"; + /** + * 应用AES秘钥 + */ + public static final String AES_SECRET = "security.aes_secret"; } diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/package-info.java b/eiam-common/src/main/java/cn/topiam/employee/common/context/package-info.java similarity index 87% rename from eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/package-info.java rename to eiam-common/src/main/java/cn/topiam/employee/common/context/package-info.java index 0522db3c..936d37cd 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/package-info.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/context/package-info.java @@ -1,5 +1,5 @@ /* - * eiam-portal - Employee Identity and Access Management Program + * eiam-common - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,4 +15,4 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.portal.mfa; \ No newline at end of file +package cn.topiam.employee.common.context; \ No newline at end of file diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/crypto/Encrypt.java b/eiam-common/src/main/java/cn/topiam/employee/common/crypto/Encrypt.java new file mode 100644 index 00000000..a7445dca --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/crypto/Encrypt.java @@ -0,0 +1,40 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.crypto; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.fasterxml.jackson.annotation.JacksonAnnotation; + +/** + * Encrypt + * + * @author TopIAM + * Created by support@topiam.cn on 2022/12/22 21:53 + */ +@JacksonAnnotation +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER }) +public @interface Encrypt { + Type serializer() default Type.ENCRYPT; + + Type deserializer() default Type.DECRYPT; +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptContextHelp.java b/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptContextHelp.java new file mode 100644 index 00000000..2238c360 --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptContextHelp.java @@ -0,0 +1,68 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.crypto; + +import java.util.Objects; + +import org.springframework.util.Assert; + +import cn.topiam.employee.common.entity.setting.SettingEntity; +import cn.topiam.employee.common.repository.setting.SettingRepository; +import cn.topiam.employee.support.context.ApplicationContextHelp; +import cn.topiam.employee.support.util.AesUtils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import static cn.topiam.employee.common.constants.SettingConstants.AES_SECRET; + +/** + * EncryptContextHelp + * + * @author TopIAM + * Created by support@topiam.cn on 2022/12/22 21:53 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class EncryptContextHelp { + private static final AesUtils AES_UTILS = new AesUtils(getAesSecret()); + + public static String encrypt(String content) { + return AES_UTILS.encrypt(content); + } + + public static String decrypt(String content) { + if (Objects.isNull(content)) { + return null; + } + return AES_UTILS.decrypt(content); + } + + /** + * 获取AES秘钥 + * + * @return {@link String} + */ + public static String getAesSecret() { + SettingEntity setting = getSettingRepository().findByName(AES_SECRET); + Assert.notNull(setting, "aes secret must not be null"); + return setting.getValue(); + } + + private static SettingRepository getSettingRepository() { + return ApplicationContextHelp.getBean(SettingRepository.class); + } +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptedDeserializerModifier.java b/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptedDeserializerModifier.java new file mode 100644 index 00000000..8120795f --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptedDeserializerModifier.java @@ -0,0 +1,61 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.crypto; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder; +import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier; + +/** + * @author TopIAM + * Created by support@topiam.cn on 2022/12/22 21:53 + */ +public class EncryptedDeserializerModifier extends BeanDeserializerModifier { + + private final Type type; + + public EncryptedDeserializerModifier() { + this.type = null; + } + + public EncryptedDeserializerModifier(Type type) { + this.type = type; + } + + @Override + public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, + BeanDescription beanDesc, + BeanDeserializerBuilder builder) { + var properties = builder.getProperties(); + while (properties.hasNext()) { + var property = properties.next(); + Encrypt annotation = property.getAnnotation(Encrypt.class); + if (annotation != null) { + Type deserializer = type; + if (type == null) { + deserializer = annotation.deserializer(); + } + builder.addOrReplaceProperty( + property.withValueDeserializer(new EncryptedJsonDeserializer(deserializer)), + true); + } + } + return builder; + } +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptedJsonDeserializer.java b/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptedJsonDeserializer.java new file mode 100644 index 00000000..085213f8 --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptedJsonDeserializer.java @@ -0,0 +1,54 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.crypto; + +import java.io.IOException; + +import org.apache.commons.lang3.StringUtils; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +/** + * @author TopIAM + * Created by support@topiam.cn on 2022/12/22 21:53 + */ +public class EncryptedJsonDeserializer extends JsonDeserializer { + + private final Type deserializerType; + + public EncryptedJsonDeserializer(Type deserializer) { + this.deserializerType = deserializer; + } + + @Override + public Object deserialize(final JsonParser parser, + final DeserializationContext context) throws IOException { + String value = parser.getValueAsString(); + if (StringUtils.isBlank(value)) { + return null; + } + if (Type.ENCRYPT == deserializerType) { + return EncryptContextHelp.encrypt(value); + } else if (Type.DECRYPT == deserializerType) { + return EncryptContextHelp.decrypt(value); + } + return value; + } +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptedJsonSerializer.java b/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptedJsonSerializer.java new file mode 100644 index 00000000..87789b38 --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptedJsonSerializer.java @@ -0,0 +1,108 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.crypto; + +import java.io.IOException; +import java.io.StringWriter; + +import org.apache.commons.lang3.StringUtils; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; + +/** + * @author TopIAM + * Created by support@topiam.cn on 2022/12/22 21:53 + */ +public class EncryptedJsonSerializer extends JsonSerializer { + + /** + * 默认序列化工具对象 + */ + private final JsonSerializer serializer; + private final Type serializerType; + + public EncryptedJsonSerializer() { + this.serializer = null; + this.serializerType = null; + } + + public EncryptedJsonSerializer(JsonSerializer serializer, Type type) { + this.serializer = serializer; + this.serializerType = type; + } + + @Override + public void serialize(Object obj, JsonGenerator jsonGenerator, + SerializerProvider serializerProvider) throws IOException { + StringWriter stringWriter = new StringWriter(); + ObjectCodec objectCodec = jsonGenerator.getCodec(); + JsonGenerator nestedGenerator = null; + + //空对象或空字符串不处理。 + if (obj == null || StringUtils.isEmpty(String.valueOf(obj))) { + if (serializer == null) { + serializerProvider.defaultSerializeValue(obj, jsonGenerator); + } else { + serializer.serialize(obj, jsonGenerator, serializerProvider); + } + return; + } + /* + 生成一个新的JsonGenerator,用于将obj写入。 + */ + if (objectCodec instanceof ObjectMapper) { + nestedGenerator = objectCodec.getFactory().createGenerator(stringWriter); + } + + if (nestedGenerator == null) { + throw new NullPointerException("nestedGenerator == null"); + } + + /* + 将数据写入到新生成的JsonGenerator中 + */ + if (serializer == null) { + serializerProvider.defaultSerializeValue(obj, nestedGenerator); + } else { + serializer.serialize(obj, nestedGenerator, serializerProvider); + } + + nestedGenerator.close(); + /* + JsonGenerator会生成一个带双引号的字符串, 将数据加密后写入。 + */ + String value = stringWriter.getBuffer().toString(); + try { + String newValue = value.substring(1, value.length() - 1); + if (StringUtils.isNotEmpty(newValue)) { + if (Type.ENCRYPT == serializerType) { + newValue = EncryptContextHelp.encrypt(newValue); + } else if (Type.DECRYPT == serializerType) { + newValue = EncryptContextHelp.decrypt(value); + } + } + jsonGenerator.writeString(newValue); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptedSerializerModifier.java b/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptedSerializerModifier.java new file mode 100644 index 00000000..f3ae0104 --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptedSerializerModifier.java @@ -0,0 +1,71 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.crypto; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; + +/** + * @author TopIAM + * Created by support@topiam.cn on 2022/12/22 21:53 + */ +public class EncryptedSerializerModifier extends BeanSerializerModifier { + + private final Type type; + + public EncryptedSerializerModifier() { + this.type = null; + } + + public EncryptedSerializerModifier(Type type) { + this.type = type; + } + + @Override + public List changeProperties(SerializationConfig config, + BeanDescription beanDesc, + List beanProperties) { + /* + 遍历beanProperties处理Encrypt.class注解 + */ + List newWriter = new ArrayList<>(); + for (BeanPropertyWriter writer : beanProperties) { + Encrypt annotation = writer.getAnnotation(Encrypt.class); + if (null == annotation) { + newWriter.add(writer); + } else { + Type deserializer = type; + if (type == null) { + deserializer = annotation.deserializer(); + } + JsonSerializer serializer = new EncryptedJsonSerializer( + writer.getSerializer(), deserializer); + writer.assignSerializer(serializer); + newWriter.add(writer); + } + } + + return newWriter; + } +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptionModule.java b/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptionModule.java new file mode 100644 index 00000000..ec66395a --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/crypto/EncryptionModule.java @@ -0,0 +1,75 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.crypto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.module.SimpleModule; + +/** + * @author TopIAM + * Created by support@topiam.cn on 2022/12/22 21:53 + */ +public class EncryptionModule extends SimpleModule { + + private final Type serializer; + private final Type deserializer; + + public EncryptionModule() { + this.serializer = null; + this.deserializer = null; + } + + public EncryptionModule(Type serializer, Type deserializer) { + this.serializer = serializer; + this.deserializer = deserializer; + } + + @Override + public void setupModule(SetupContext setupContext) { + setupContext.addBeanSerializerModifier(new EncryptedSerializerModifier(serializer)); + setupContext.addBeanDeserializerModifier(new EncryptedDeserializerModifier(deserializer)); + } + + public static ObjectMapper serializerEncrypt() { + return createMapper(Type.ENCRYPT, Type.NONE); + } + + public static ObjectMapper deserializerEncrypt() { + return createMapper(Type.NONE, Type.ENCRYPT); + } + + public static ObjectMapper serializerDecrypt() { + return createMapper(Type.DECRYPT, Type.NONE); + } + + public static ObjectMapper deserializerDecrypt() { + return createMapper(Type.NONE, Type.DECRYPT); + } + + public static ObjectMapper createMapper(Type serializer, Type deserializer) { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + objectMapper.registerModule(new EncryptionModule(serializer, deserializer)); + return objectMapper; + } +} \ No newline at end of file diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/util/CasUtils.java b/eiam-common/src/main/java/cn/topiam/employee/common/crypto/Type.java similarity index 66% rename from eiam-common/src/main/java/cn/topiam/employee/common/util/CasUtils.java rename to eiam-common/src/main/java/cn/topiam/employee/common/crypto/Type.java index 58fa4fab..ae8de653 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/util/CasUtils.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/crypto/Type.java @@ -15,16 +15,24 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.common.util; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +package cn.topiam.employee.common.crypto; /** * @author TopIAM - * Created by support@topiam.cn on 2022/12/30 01:06 + * Created by support@topiam.cn on 2022/12/22 21:53 */ -public class CasUtils { - private final static Logger logger = LoggerFactory.getLogger(CasUtils.class); +public enum Type { + /** + * Encrypt + */ + ENCRYPT, + /** + * Decrypt + */ + DECRYPT, + /** + * None + */ + NONE } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/OrganizationEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/OrganizationEntity.java index 59e4c521..d6b13219 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/OrganizationEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/OrganizationEntity.java @@ -25,14 +25,19 @@ import javax.persistence.Entity; import javax.persistence.Table; import org.hibernate.Hibernate; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; import cn.topiam.employee.common.enums.DataOrigin; import cn.topiam.employee.common.enums.OrganizationType; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** *

@@ -46,8 +51,11 @@ import lombok.ToString; @Setter @ToString @Entity -@Table(name = "`organization`") -public class OrganizationEntity extends BaseEntity { +@Table(name = "organization") +@SQLDelete(sql = "update organization set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update organization set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class OrganizationEntity extends LogicDeleteEntity { @Serial private static final long serialVersionUID = 8143944323232082295L; diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/OrganizationMemberEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/OrganizationMemberEntity.java index c6dd357f..d3ee275a 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/OrganizationMemberEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/OrganizationMemberEntity.java @@ -24,13 +24,18 @@ import javax.persistence.Entity; import javax.persistence.Table; import org.hibernate.Hibernate; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * 组织机构成员 @@ -43,8 +48,11 @@ import lombok.experimental.Accessors; @ToString @Accessors(chain = true) @Entity -@Table(name = "`organization_member`") -public class OrganizationMemberEntity extends BaseEntity { +@Table(name = "organization_member") +@SQLDelete(sql = "update organization_member set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update organization_member set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class OrganizationMemberEntity extends LogicDeleteEntity { /** * 组织机构ID */ diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserDetailEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserDetailEntity.java index f872d729..f1c37995 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserDetailEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserDetailEntity.java @@ -25,14 +25,19 @@ import javax.persistence.Entity; import javax.persistence.Table; import org.hibernate.Hibernate; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; import cn.topiam.employee.common.enums.UserIdType; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** *

@@ -48,7 +53,10 @@ import lombok.experimental.Accessors; @Accessors(chain = true) @Entity @Table(name = "user_detail") -public class UserDetailEntity extends BaseEntity { +@SQLDelete(sql = "update user_detail set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update user_detail set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class UserDetailEntity extends LogicDeleteEntity { @Serial private static final long serialVersionUID = -3599183663669763315L; diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserEntity.java index e09da282..5a546393 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserEntity.java @@ -28,17 +28,22 @@ import javax.persistence.Table; import javax.persistence.Transient; import org.hibernate.Hibernate; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; import com.fasterxml.jackson.annotation.JsonIgnore; import cn.topiam.employee.common.enums.DataOrigin; import cn.topiam.employee.common.enums.UserStatus; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** *

@@ -53,8 +58,11 @@ import lombok.experimental.Accessors; @ToString @Accessors(chain = true) @Entity -@Table(name = "`user`") -public class UserEntity extends BaseEntity { +@Table(name = "user") +@SQLDelete(sql = "update user set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update user set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class UserEntity extends LogicDeleteEntity { @Serial private static final long serialVersionUID = -2619231849746900857L; diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserGroupEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserGroupEntity.java index b22055f6..86033a8a 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserGroupEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserGroupEntity.java @@ -23,12 +23,18 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** *

@@ -43,8 +49,11 @@ import lombok.experimental.Accessors; @ToString @Accessors(chain = true) @Entity -@Table(name = "`user_group`") -public class UserGroupEntity extends BaseEntity { +@Table(name = "user_group") +@SQLDelete(sql = "update user_group set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update user_group set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class UserGroupEntity extends LogicDeleteEntity { @Serial private static final long serialVersionUID = -2619231849746900857L; diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserGroupMemberEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserGroupMemberEntity.java index e64282cd..4233275e 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserGroupMemberEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserGroupMemberEntity.java @@ -21,12 +21,18 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * 用户组成员 @@ -39,8 +45,11 @@ import lombok.experimental.Accessors; @ToString @Accessors(chain = true) @Entity -@Table(name = "`user_group_member`") -public class UserGroupMemberEntity extends BaseEntity { +@Table(name = "user_group_member") +@SQLDelete(sql = "update user_group_member set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update user_group_member set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class UserGroupMemberEntity extends LogicDeleteEntity { /** * 组ID */ diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserHistoryPasswordEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserHistoryPasswordEntity.java index 9ba74092..9381fa73 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserHistoryPasswordEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserHistoryPasswordEntity.java @@ -24,14 +24,20 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + import com.fasterxml.jackson.annotation.JsonIgnore; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** *

@@ -46,8 +52,11 @@ import lombok.experimental.Accessors; @ToString @Accessors(chain = true) @Entity -@Table(name = "`user_history_password`") -public class UserHistoryPasswordEntity extends BaseEntity { +@Table(name = "user_history_password") +@SQLDelete(sql = "update user_history_password set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update user_history_password set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class UserHistoryPasswordEntity extends LogicDeleteEntity { @Serial private static final long serialVersionUID = -2619231849746900857L; diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserIdpBindEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserIdpBindEntity.java index 1171373c..d06bf831 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserIdpBindEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/account/UserIdpBindEntity.java @@ -24,13 +24,18 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; -import cn.topiam.employee.common.enums.IdentityProviderType; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * 用户认证方式绑定表 @@ -40,47 +45,50 @@ import lombok.experimental.Accessors; */ @Entity @Table(name = "user_idp_bind") +@SQLDelete(sql = "update user_idp_bind set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update user_idp_bind set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) @Accessors(chain = true) @Getter @Setter @ToString -public class UserIdpBindEntity extends BaseEntity { +public class UserIdpBindEntity extends LogicDeleteEntity { @Serial - private static final long serialVersionUID = -14364708756807242L; + private static final long serialVersionUID = -14364708756807242L; /** * 用户ID */ @Column(name = "user_id") - private Long userId; + private Long userId; /** * OpenId */ @Column(name = "open_id") - private String openId; + private String openId; /** * 身份提供商 ID */ @Column(name = "idp_id") - private String idpId; + private String idpId; /** * 身份提供商 类型 */ @Column(name = "idp_type") - private IdentityProviderType idpType; + private String idpType; /** * 绑定时间 */ @Column(name = "bind_time") - private LocalDateTime bindTime; + private LocalDateTime bindTime; /** * 附加信息 */ @Column(name = "addition_info") - private String additionInfo; + private String additionInfo; } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppAccessPolicyEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppAccessPolicyEntity.java index 823e2494..4def668b 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppAccessPolicyEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppAccessPolicyEntity.java @@ -21,13 +21,19 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + import cn.topiam.employee.common.enums.PolicySubjectType; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * 应用授权策略 @@ -41,7 +47,10 @@ import lombok.experimental.Accessors; @Entity @Accessors(chain = true) @Table(name = "app_access_policy") -public class AppAccessPolicyEntity extends BaseEntity { +@SQLDelete(sql = "update app_access_policy set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update app_access_policy set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class AppAccessPolicyEntity extends LogicDeleteEntity { /** * 应用ID */ diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppAccountEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppAccountEntity.java index 41252ea6..84ca85f5 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppAccountEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppAccountEntity.java @@ -21,12 +21,18 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * 应用账户 @@ -40,7 +46,10 @@ import lombok.experimental.Accessors; @Entity @Accessors(chain = true) @Table(name = "app_account") -public class AppAccountEntity extends BaseEntity { +@SQLDelete(sql = "update app_account set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update app_account set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class AppAccountEntity extends LogicDeleteEntity { /** * 应用ID */ @@ -58,4 +67,10 @@ public class AppAccountEntity extends BaseEntity { */ @Column(name = "account_") private String account; + + /** + * 账户密码 + */ + @Column(name = "password_") + private String password; } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppCasConfigEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppCasConfigEntity.java index 0f2d67f0..d41788fe 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppCasConfigEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppCasConfigEntity.java @@ -25,6 +25,7 @@ import org.hibernate.annotations.TypeDef; import com.vladmihalcea.hibernate.type.json.JsonStringType; +import cn.topiam.employee.common.enums.app.CasUserIdentityType; import cn.topiam.employee.support.repository.domain.BaseEntity; import lombok.Getter; @@ -50,12 +51,24 @@ public class AppCasConfigEntity extends BaseEntity { * APP ID */ @Column(name = "app_id") - private Long appId; + private Long appId; /** - * SP 接受回调地址 + * 用户身份类型 */ - @Column(name = "sp_callback_url") - private String spCallbackUrl; + @Column(name = "user_identity_type") + private CasUserIdentityType userIdentityType; + + /** + * 客户端服务URL + */ + @Column(name = "client_service_url") + private String clientServiceUrl; + + /** + * serviceTicket 过期时间(秒) + */ + @Column(name = "service_ticket_expire_time") + private Integer serviceTicketExpireTime; } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppCertEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppCertEntity.java index b4b69aae..81111318 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppCertEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppCertEntity.java @@ -24,13 +24,19 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + import cn.topiam.employee.common.enums.app.AppCertUsingType; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * @author TopIAM @@ -42,7 +48,10 @@ import lombok.experimental.Accessors; @Entity @Accessors(chain = true) @Table(name = "app_cert") -public class AppCertEntity extends BaseEntity { +@SQLDelete(sql = "update app_cert set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update app_cert set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class AppCertEntity extends LogicDeleteEntity { /** * 应用ID */ diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppEntity.java index 1367c07b..c784481e 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppEntity.java @@ -21,16 +21,22 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + import cn.topiam.employee.common.enums.app.AppProtocol; import cn.topiam.employee.common.enums.app.AppType; import cn.topiam.employee.common.enums.app.AuthorizationType; import cn.topiam.employee.common.enums.app.InitLoginType; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * 应用 @@ -44,7 +50,10 @@ import lombok.experimental.Accessors; @Entity @Accessors(chain = true) @Table(name = "app") -public class AppEntity extends BaseEntity { +@SQLDelete(sql = "update app set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update app set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class AppEntity extends LogicDeleteEntity { /** * 应用名称 diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppFormConfigEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppFormConfigEntity.java new file mode 100644 index 00000000..2fa6c528 --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppFormConfigEntity.java @@ -0,0 +1,107 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.entity.app; + +import java.io.Serializable; +import java.util.List; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +import org.hibernate.annotations.*; + +import com.vladmihalcea.hibernate.type.json.JsonStringType; + +import cn.topiam.employee.common.enums.app.FormSubmitType; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; + +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +import io.swagger.v3.oas.annotations.media.Schema; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; + +/** + * APP Form 配置 + * + * @author TopIAM + * Created by support@topiam.cn on 2022/12/13 22:31 + */ +@Getter +@Setter +@ToString +@Entity +@Accessors(chain = true) +@Table(name = "app_form_config") +@SQLDelete(sql = "update app_form_config set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update app_form_config set " + SOFT_DELETE_SET + " where id_ = ?") +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Where(clause = SOFT_DELETE_WHERE) +public class AppFormConfigEntity extends LogicDeleteEntity { + + /** + * APP ID + */ + @Column(name = "app_id") + private Long appId; + + /** + * 登录URL + */ + @Column(name = "login_url") + private String loginUrl; + + /** + * 登录名属性名称 + */ + @Column(name = "username_field") + private String usernameField; + + /** + * 登录密码属性名称 + */ + @Column(name = "password_field") + private String passwordField; + + /** + * 登录提交方式 + */ + @Column(name = "submit_type") + private FormSubmitType submitType; + + /** + * 登录其他信息 + */ + @Column(name = "other_field") + @Type(type = "json") + private List otherField; + + @Data + @Schema(description = "表单其他信息") + public static class OtherField implements Serializable { + + private String fieldName; + + private String fieldValue; + } +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppOidcConfigEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppOidcConfigEntity.java index efedbf1b..b1d60aab 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppOidcConfigEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppOidcConfigEntity.java @@ -23,17 +23,18 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; -import org.hibernate.annotations.Type; -import org.hibernate.annotations.TypeDef; +import org.hibernate.annotations.*; import com.vladmihalcea.hibernate.type.json.JsonStringType; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * APP OIDC 配置 @@ -47,8 +48,11 @@ import lombok.experimental.Accessors; @Entity @Accessors(chain = true) @Table(name = "app_oidc_config") +@SQLDelete(sql = "update app_oidc_config set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update app_oidc_config set " + SOFT_DELETE_SET + " where id_ = ?") @TypeDef(name = "json", typeClass = JsonStringType.class) -public class AppOidcConfigEntity extends BaseEntity { +@Where(clause = SOFT_DELETE_WHERE) +public class AppOidcConfigEntity extends LogicDeleteEntity { /** * APP ID diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppPermissionActionEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppPermissionActionEntity.java index 097bfeb1..7c6cf858 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppPermissionActionEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppPermissionActionEntity.java @@ -21,13 +21,19 @@ import java.io.Serial; import javax.persistence.*; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + import cn.topiam.employee.common.enums.PermissionActionType; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * 应用权限 @@ -40,8 +46,11 @@ import lombok.experimental.Accessors; @ToString @Entity @Accessors(chain = true) -@Table(name = "`app_permission_action`") -public class AppPermissionActionEntity extends BaseEntity { +@Table(name = "app_permission_action") +@SQLDelete(sql = "update app_permission_action set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update app_permission_action set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class AppPermissionActionEntity extends LogicDeleteEntity { @Serial private static final long serialVersionUID = -3954680915360748087L; diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppPermissionPolicyEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppPermissionPolicyEntity.java index f8b51c68..50aa2c82 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppPermissionPolicyEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppPermissionPolicyEntity.java @@ -21,15 +21,21 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + import cn.topiam.employee.common.enums.PolicyEffect; import cn.topiam.employee.common.enums.PolicyObjectType; import cn.topiam.employee.common.enums.PolicySubjectType; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * 应用策略 @@ -42,8 +48,11 @@ import lombok.experimental.Accessors; @ToString @Entity @Accessors(chain = true) -@Table(name = "`app_permission_policy`") -public class AppPermissionPolicyEntity extends BaseEntity { +@Table(name = "app_permission_policy") +@SQLDelete(sql = "update app_permission_policy set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update app_permission_policy set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class AppPermissionPolicyEntity extends LogicDeleteEntity { /** * 应用id diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppPermissionResourceEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppPermissionResourceEntity.java index 4ebea00b..f9f4e994 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppPermissionResourceEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppPermissionResourceEntity.java @@ -22,7 +22,11 @@ import java.util.List; import javax.persistence.*; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; @@ -30,6 +34,9 @@ import lombok.ToString; import lombok.experimental.Accessors; import static javax.persistence.FetchType.LAZY; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; + /** *

* 应用资源关联 @@ -43,8 +50,11 @@ import static javax.persistence.FetchType.LAZY; @ToString @Entity @Accessors(chain = true) -@Table(name = "`app_permission_resource`") -public class AppPermissionResourceEntity extends BaseEntity { +@Table(name = "app_permission_resource") +@SQLDelete(sql = "update app_permission_resource set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update app_permission_resource set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class AppPermissionResourceEntity extends LogicDeleteEntity { @Serial private static final long serialVersionUID = 7342074686605139968L; diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppPermissionRoleEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppPermissionRoleEntity.java index cf01d0d3..3b36e343 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppPermissionRoleEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppPermissionRoleEntity.java @@ -23,12 +23,18 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** *

@@ -43,8 +49,11 @@ import lombok.experimental.Accessors; @ToString @Entity @Accessors(chain = true) -@Table(name = "`app_permission_role`") -public class AppPermissionRoleEntity extends BaseEntity { +@Table(name = "app_permission_role") +@SQLDelete(sql = "update app_permission_role set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update app_permission_role set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class AppPermissionRoleEntity extends LogicDeleteEntity { @Serial private static final long serialVersionUID = -7761332532995424593L; diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppSaml2ConfigEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppSaml2ConfigEntity.java index 935ede51..c22920e9 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppSaml2ConfigEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppSaml2ConfigEntity.java @@ -26,20 +26,21 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; -import org.hibernate.annotations.Type; -import org.hibernate.annotations.TypeDef; +import org.hibernate.annotations.*; import com.fasterxml.jackson.annotation.JsonAlias; import com.vladmihalcea.hibernate.type.json.JsonStringType; import cn.topiam.employee.common.enums.app.*; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Data; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * APP SAML 配置 @@ -53,8 +54,11 @@ import lombok.experimental.Accessors; @Entity @Accessors(chain = true) @Table(name = "app_saml2_config") +@SQLDelete(sql = "update app_saml2_config set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update app_saml2_config set " + SOFT_DELETE_SET + " where id_ = ?") @TypeDef(name = "json", typeClass = JsonStringType.class) -public class AppSaml2ConfigEntity extends BaseEntity { +@Where(clause = SOFT_DELETE_WHERE) +public class AppSaml2ConfigEntity extends LogicDeleteEntity { /** * APP ID */ diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppTsaConfigEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppTsaConfigEntity.java new file mode 100644 index 00000000..06993dc4 --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/AppTsaConfigEntity.java @@ -0,0 +1,128 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.entity.app; + +import java.io.Serializable; +import java.util.List; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +import org.hibernate.annotations.*; + +import com.vladmihalcea.hibernate.type.json.JsonStringType; + +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; + +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +import io.swagger.v3.oas.annotations.media.Schema; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; + +/** + * APP Form 配置 + * + * @author TopIAM + * Created by support@topiam.cn on 2022/12/13 22:31 + */ +@Getter +@Setter +@ToString +@Entity +@Accessors(chain = true) +@Table(name = "app_tsa_config") +@SQLDelete(sql = "update app_tsa_config set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update app_tsa_config set " + SOFT_DELETE_SET + " where id_ = ?") +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Where(clause = SOFT_DELETE_WHERE) +public class AppTsaConfigEntity extends LogicDeleteEntity { + + /** + * APP ID + */ + @Column(name = "app_id") + private Long appId; + + /** + * 登录页面 + */ + @Column(name = "login_page") + private String loginPage; + + /** + * 自动登录步骤 + */ + @Column(name = "auto_login_steps") + @Type(type = "json") + private List autoLoginSteps; + + /** + * 创建账号步骤 + */ + @Column(name = "create_account_steps") + @Type(type = "json") + private List createAccountSteps; + + @Data + @Schema(description = "自动登录步骤") + public static class AutoLoginStep implements Serializable { + + private String action; + + private String target; + + private String value; + } + + @Data + @Schema(description = "创建账号步骤") + public static class CreateAccountStep implements Serializable { + + private String title; + + private String titleI18n; + + private FormItemProp formItemProps; + } + + @Data + @Schema(description = "表单内容") + public static class FormItemProp implements Serializable { + + private List name; + + private List rules; + } + + @Data + @Schema(description = "表单验证规则") + public static class Rule implements Serializable { + + private Boolean required; + + private String message; + + private String messageI18n; + } +} diff --git a/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/model/AppFormConfigGetResult.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/po/AppFormConfigPO.java similarity index 60% rename from eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/model/AppFormConfigGetResult.java rename to eiam-common/src/main/java/cn/topiam/employee/common/entity/app/po/AppFormConfigPO.java index 89635696..c15d5cbd 100644 --- a/eiam-application/eiam-application-form/src/main/java/cn/topiam/employee/application/form/model/AppFormConfigGetResult.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/po/AppFormConfigPO.java @@ -1,5 +1,5 @@ /* - * eiam-application-form - Employee Identity and Access Management Program + * eiam-common - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,42 +15,61 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.application.form.model; - -import java.io.Serializable; +package cn.topiam.employee.common.entity.app.po; +import cn.topiam.employee.common.entity.app.AppFormConfigEntity; import cn.topiam.employee.common.enums.app.AuthorizationType; import cn.topiam.employee.common.enums.app.InitLoginType; import lombok.Data; - -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Schema; +import lombok.EqualsAndHashCode; /** - * Form 配置返回 * * @author TopIAM - * Created by support@topiam.cn on 2022/5/31 22:46 + * Created by support@topiam.cn on 2022/12/13 23:45 */ @Data -@Schema(description = "Form 配置返回结果") -public class AppFormConfigGetResult implements Serializable { +@EqualsAndHashCode(callSuper = true) +public class AppFormConfigPO extends AppFormConfigEntity { + + /** + * 应用编码 + */ + private String appCode; + + /** + * 模版 + */ + private String appTemplate; + + /** + * 客户端ID + */ + private String clientId; + + /** + * 客户端秘钥 + */ + private String clientSecret; + /** * SSO 发起方 */ - @Parameter(description = "SSO 发起方") private InitLoginType initLoginType; /** * SSO 登录链接 */ - @Parameter(description = "SSO 登录链接") private String initLoginUrl; /** * 授权范围 */ - @Parameter(description = "SSO 授权范围") private AuthorizationType authorizationType; + + /** + * 应用是否启用 + */ + private Boolean enabled; } diff --git a/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/model/AppCasStandardConfigGetResult.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/po/AppTsaConfigPO.java similarity index 53% rename from eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/model/AppCasStandardConfigGetResult.java rename to eiam-common/src/main/java/cn/topiam/employee/common/entity/app/po/AppTsaConfigPO.java index f4815280..65edad5e 100644 --- a/eiam-application/eiam-application-cas/src/main/java/cn/topiam/employee/application/cas/model/AppCasStandardConfigGetResult.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/app/po/AppTsaConfigPO.java @@ -1,5 +1,5 @@ /* - * eiam-application-cas - Employee Identity and Access Management Program + * eiam-common - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,50 +15,60 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.application.cas.model; +package cn.topiam.employee.common.entity.app.po; +import cn.topiam.employee.common.entity.app.AppTsaConfigEntity; import cn.topiam.employee.common.enums.app.AuthorizationType; -import cn.topiam.employee.common.enums.app.InitLoginType; import lombok.Data; - -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Schema; +import lombok.EqualsAndHashCode; /** + * * @author TopIAM - * Created by support@topiam.cn on 2023/1/2 22:23 + * Created by support@topiam.cn on 2022/01/14 10:45 */ @Data -@Schema(description = "CAS 配置返回结果") -public class AppCasStandardConfigGetResult { +@EqualsAndHashCode(callSuper = true) +public class AppTsaConfigPO extends AppTsaConfigEntity { /** - * 应用ID + * 应用编码 */ - @Schema(description = "授权类型") - private AuthorizationType authorizationType; + private String appCode; /** - * SSO 发起登录类型 + * 模版 */ - @Schema(description = "SSO 发起登录类型") - private InitLoginType initLoginType; + private String appTemplate; /** - * SSO 发起登录URL + * 客户端ID + */ + private String clientId; + + /** + * 客户端秘钥 + */ + private String clientSecret; + + // /** + // * SSO 发起方 + // */ + // private InitLoginType initLoginType; + + /** + * SSO 登录链接 */ - @Schema(description = "SSO 发起登录URL") private String initLoginUrl; /** - * 单点登录 SP 回调地址 + * 授权范围 */ - @Parameter(name = "单点登录 sp Callback Url") - private String spCallbackUrl; + private AuthorizationType authorizationType; /** - * Server端配置前缀 + * 应用是否启用 */ - private String serverUrlPrefix; + private Boolean enabled; } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/authentication/IdentityProviderEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/authentication/IdentityProviderEntity.java index 4bb9876e..4e77e331 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/authentication/IdentityProviderEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/authentication/IdentityProviderEntity.java @@ -23,14 +23,18 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; -import cn.topiam.employee.common.enums.IdentityProviderCategory; -import cn.topiam.employee.common.enums.IdentityProviderType; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** *

@@ -46,51 +50,54 @@ import lombok.experimental.Accessors; @Entity @Accessors(chain = true) @Table(name = "identity_provider") -public class IdentityProviderEntity extends BaseEntity { +@SQLDelete(sql = "update identity_provider set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update identity_provider set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class IdentityProviderEntity extends LogicDeleteEntity { @Serial - private static final long serialVersionUID = -7936931011805155568L; + private static final long serialVersionUID = -7936931011805155568L; /** * 名称 */ @Column(name = "name_") - private String name; + private String name; /** * 唯一CODE 不可修改 */ @Column(name = "code_") - private String code; + private String code; /** * 平台 */ @Column(name = "type_") - private IdentityProviderType type; + private String type; /** * 分类 */ @Column(name = "category_") - private IdentityProviderCategory category; + private String category; /** * 配置JSON串 */ @Column(name = "config_") - private String config; + private String config; /** * 是否启用 */ @Column(name = "is_enabled") - private Boolean enabled; + private Boolean enabled; /** * 是否展示 */ @Column(name = "is_displayed") - private Boolean displayed; + private Boolean displayed; } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/IdentitySourceEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/IdentitySourceEntity.java index 18056331..d6b4f15d 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/IdentitySourceEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/IdentitySourceEntity.java @@ -23,18 +23,23 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; import org.hibernate.annotations.Type; +import org.hibernate.annotations.Where; import cn.topiam.employee.common.entity.identitysource.config.JobConfig; import cn.topiam.employee.common.entity.identitysource.config.StrategyConfig; -import cn.topiam.employee.common.enums.identityprovider.IdentitySourceProvider; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.common.enums.identitysource.IdentitySourceProvider; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; import lombok.extern.slf4j.Slf4j; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** *

@@ -51,7 +56,10 @@ import lombok.extern.slf4j.Slf4j; @Accessors(chain = true) @Slf4j @Table(name = "identity_source") -public class IdentitySourceEntity extends BaseEntity { +@SQLDelete(sql = "update identity_source set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update identity_source set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class IdentitySourceEntity extends LogicDeleteEntity { @Serial private static final long serialVersionUID = -7936931011805155568L; diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/IdentitySourceEventRecordEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/IdentitySourceEventRecordEntity.java index 31559b40..a08fa618 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/IdentitySourceEventRecordEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/IdentitySourceEventRecordEntity.java @@ -23,16 +23,22 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + import cn.topiam.employee.common.enums.SyncStatus; import cn.topiam.employee.common.enums.identitysource.IdentitySourceActionType; import cn.topiam.employee.common.enums.identitysource.IdentitySourceObjectType; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * 身份源事件记录 @@ -47,7 +53,10 @@ import lombok.experimental.Accessors; @Accessors(chain = true) @NoArgsConstructor @Table(name = "identity_source_event_record") -public class IdentitySourceEventRecordEntity extends BaseEntity { +@SQLDelete(sql = "update identity_source_event_record set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update identity_source_event_record set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class IdentitySourceEventRecordEntity extends LogicDeleteEntity { /** * 身份源ID diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/IdentitySourceSyncHistoryEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/IdentitySourceSyncHistoryEntity.java index 0b458833..9c22cc9d 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/IdentitySourceSyncHistoryEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/IdentitySourceSyncHistoryEntity.java @@ -23,16 +23,22 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + import cn.topiam.employee.common.enums.SyncStatus; import cn.topiam.employee.common.enums.TriggerType; import cn.topiam.employee.common.enums.identitysource.IdentitySourceObjectType; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * 身份源同步记录表 @@ -47,7 +53,10 @@ import lombok.experimental.Accessors; @Accessors(chain = true) @NoArgsConstructor @Table(name = "identity_source_sync_history") -public class IdentitySourceSyncHistoryEntity extends BaseEntity { +@SQLDelete(sql = "update identity_source_sync_history set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update identity_source_sync_history set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class IdentitySourceSyncHistoryEntity extends LogicDeleteEntity { /** * 批号 diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/IdentitySourceSyncRecordEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/IdentitySourceSyncRecordEntity.java index 89f94eb6..b47e3614 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/IdentitySourceSyncRecordEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/IdentitySourceSyncRecordEntity.java @@ -21,16 +21,22 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + import cn.topiam.employee.common.enums.SyncStatus; import cn.topiam.employee.common.enums.identitysource.IdentitySourceActionType; import cn.topiam.employee.common.enums.identitysource.IdentitySourceObjectType; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * 身份源同步详情 @@ -45,7 +51,10 @@ import lombok.experimental.Accessors; @Accessors(chain = true) @NoArgsConstructor @Table(name = "identity_source_sync_record") -public class IdentitySourceSyncRecordEntity extends BaseEntity { +@SQLDelete(sql = "update identity_source_sync_record set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update identity_source_sync_record set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class IdentitySourceSyncRecordEntity extends LogicDeleteEntity { /** * 同步历史ID diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/config/JobConfig.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/config/JobConfig.java index d5256f71..0e546e29 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/config/JobConfig.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/identitysource/config/JobConfig.java @@ -170,14 +170,14 @@ public class JobConfig { } } //模式为定时 解析时分秒 - if (mode.equals(JobConfig.Mode.timed)) { + if (mode.equals(Mode.timed)) { LocalTime time = LocalTime.parse(value, DateTimeFormatter.ofPattern("H[H]:mm:ss")); hour = on(time.getHour()); minute = on(time.getMinute()); second = on(time.getSecond()); } //模式为周期(0- 某个小时)执行 - if (mode.equals(JobConfig.Mode.period)) { + if (mode.equals(Mode.period)) { hour = new Every(on(0), new IntegerFieldValue(Integer.parseInt(value))); minute = on(0); second = on(0); diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/MailSendRecordEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/message/MailSendRecordEntity.java similarity index 74% rename from eiam-common/src/main/java/cn/topiam/employee/common/entity/MailSendRecordEntity.java rename to eiam-common/src/main/java/cn/topiam/employee/common/entity/message/MailSendRecordEntity.java index c5eec298..cfa788fe 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/MailSendRecordEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/message/MailSendRecordEntity.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.common.entity; +package cn.topiam.employee.common.entity.message; import java.time.LocalDateTime; @@ -23,14 +23,20 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + import cn.topiam.employee.common.enums.MailType; import cn.topiam.employee.common.message.enums.MailProvider; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * 邮件发送记录 @@ -44,7 +50,10 @@ import lombok.experimental.Accessors; @Setter @ToString @Table(name = "mail_send_record") -public class MailSendRecordEntity extends BaseEntity { +@SQLDelete(sql = "update mail_send_record set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update mail_send_record set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class MailSendRecordEntity extends LogicDeleteEntity { /** * subject */ diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/SmsSendRecordEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/message/SmsSendRecordEntity.java similarity index 75% rename from eiam-common/src/main/java/cn/topiam/employee/common/entity/SmsSendRecordEntity.java rename to eiam-common/src/main/java/cn/topiam/employee/common/entity/message/SmsSendRecordEntity.java index 915df827..a6c741b8 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/SmsSendRecordEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/message/SmsSendRecordEntity.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.common.entity; +package cn.topiam.employee.common.entity.message; import java.time.LocalDateTime; @@ -23,15 +23,21 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + import cn.topiam.employee.common.enums.MessageCategory; import cn.topiam.employee.common.enums.SmsType; import cn.topiam.employee.common.message.enums.SmsProvider; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** * 短信记录发送表 @@ -45,7 +51,10 @@ import lombok.experimental.Accessors; @Setter @ToString @Table(name = "sms_send_record") -public class SmsSendRecordEntity extends BaseEntity { +@SQLDelete(sql = "update sms_send_record set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update sms_send_record set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class SmsSendRecordEntity extends LogicDeleteEntity { /** * phone_ */ diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/setting/AdministratorEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/setting/AdministratorEntity.java index a3c436ce..f41c5849 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/setting/AdministratorEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/setting/AdministratorEntity.java @@ -24,13 +24,19 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + import cn.topiam.employee.common.enums.UserStatus; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** *

@@ -45,8 +51,11 @@ import lombok.experimental.Accessors; @ToString @Accessors(chain = true) @Entity -@Table(name = "`administrator`") -public class AdministratorEntity extends BaseEntity { +@Table(name = "administrator") +@SQLDelete(sql = "update administrator set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update administrator set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class AdministratorEntity extends LogicDeleteEntity { @Serial private static final long serialVersionUID = -2619231849746900857L; diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/setting/MailTemplateEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/setting/MailTemplateEntity.java index e664e9ce..f9f60324 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/setting/MailTemplateEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/setting/MailTemplateEntity.java @@ -23,13 +23,19 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + import cn.topiam.employee.common.enums.MailType; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** *

@@ -45,7 +51,10 @@ import lombok.experimental.Accessors; @Entity @Accessors(chain = true) @Table(name = "mail_template") -public class MailTemplateEntity extends BaseEntity { +@SQLDelete(sql = "update mail_template set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update mail_template set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class MailTemplateEntity extends LogicDeleteEntity { @Serial private static final long serialVersionUID = 5983857137670090984L; diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/entity/setting/SettingEntity.java b/eiam-common/src/main/java/cn/topiam/employee/common/entity/setting/SettingEntity.java index 3ceb0101..9a1b2cf9 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/entity/setting/SettingEntity.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/entity/setting/SettingEntity.java @@ -21,12 +21,18 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; -import cn.topiam.employee.support.repository.domain.BaseEntity; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.Where; + +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** *

@@ -41,8 +47,11 @@ import lombok.experimental.Accessors; @ToString @Entity @Accessors(chain = true) -@Table(name = "`setting`") -public class SettingEntity extends BaseEntity { +@Table(name = "setting") +@SQLDelete(sql = "update setting set " + SOFT_DELETE_SET + " where id_ = ?") +@SQLDeleteAll(sql = "update setting set " + SOFT_DELETE_SET + " where id_ = ?") +@Where(clause = SOFT_DELETE_WHERE) +public class SettingEntity extends LogicDeleteEntity { /** * name diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/enums/IdentityProviderType.java b/eiam-common/src/main/java/cn/topiam/employee/common/enums/IdentityProviderType.java deleted file mode 100644 index ef43433c..00000000 --- a/eiam-common/src/main/java/cn/topiam/employee/common/enums/IdentityProviderType.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * eiam-common - Employee Identity and Access Management Program - * Copyright © 2020-2023 TopIAM (support@topiam.cn) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package cn.topiam.employee.common.enums; - -import com.fasterxml.jackson.annotation.JsonValue; - -import cn.topiam.employee.support.web.converter.EnumConvert; -import static cn.topiam.employee.common.constants.AuthorizeConstants.AUTHORIZATION_REQUEST_URI; -import static cn.topiam.employee.common.constants.AuthorizeConstants.LOGIN_PATH; - -/** - * 认证提供商 - * - * @author TopIAM - * Created by support@topiam.cn on 2020/8/13 22:18 - */ -public enum IdentityProviderType implements BaseEnum { - /** - * 微信扫码登录 - */ - WECHAT_SCAN_CODE("wechat_scan_code", "微信扫码登录", - "通过微信扫码进行身份认证"), - /** - * 钉钉扫码登录 - */ - DINGTALK_SCAN_CODE("dingtalk_scan_code", - "钉钉扫码认证", - - "通过钉钉扫码进行身份认证"), - /** - * 钉钉Oauth2 - */ - DINGTALK_OAUTH("dingtalk_oauth", "钉钉Oauth认证", - "通过钉钉进行身份认证"), - /** - * 企业微信 - */ - WECHATWORK_SCAN_CODE("wechatwork_scan_code", - "企业微信扫码认证", - - "通过企业微信同步的用户可使用企业微信扫码登录进行身份认证"), - /** - * QQ - */ - QQ("qq_oauth", "QQ认证", "通过QQ进行身份认证"), - /** - * 微博 - */ - WEIBO("weibo_oauth", "微博认证", "通过微博进行身份认证"), - /** - * Github - */ - GITHUB("github_oauth", "Github", - "通过 GitHub 进行身份认证"), - /** - * Google - */ - GOOGLE("google_oauth", "Google", - "通过 Google 进行身份认证"), - /** - * 支付宝扫码认证 - */ - ALIPAY("alipay_oauth", "支付宝认证", - "通过支付宝进行身份认证"), - - /** - * LDAP - */ - LDAP("ldap", "LDAP 认证源", "通过 LDAP 认证源进行身份验证"); - - @JsonValue - private final String code; - private final String name; - private final String desc; - - IdentityProviderType(String code, String name, String desc) { - this.code = code; - this.name = name; - this.desc = desc; - } - - public String getCode() { - return code; - } - - public String getName() { - return name; - } - - public String getDesc() { - return desc; - } - - public String getAuthorizationPathPrefix() { - return AUTHORIZATION_REQUEST_URI + "/" + getCode(); - } - - public String getLoginPathPrefix() { - return LOGIN_PATH + "/" + getCode(); - } - - /** - * 获取认证平台 - * - * @param code {@link String} - * @return {@link IdentityProviderType} - */ - @EnumConvert - public static IdentityProviderType getType(String code) { - IdentityProviderType[] values = values(); - for (IdentityProviderType status : values) { - if (String.valueOf(status.getCode()).equals(code)) { - return status; - } - } - return null; - } -} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/enums/ListEnumDeserializer.java b/eiam-common/src/main/java/cn/topiam/employee/common/enums/ListEnumDeserializer.java index 13703bcf..b989c177 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/enums/ListEnumDeserializer.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/enums/ListEnumDeserializer.java @@ -22,7 +22,10 @@ import java.lang.reflect.Field; import java.util.*; import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.node.ArrayNode; diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/enums/SmsType.java b/eiam-common/src/main/java/cn/topiam/employee/common/enums/SmsType.java index ce3e2b8a..0ffdfac5 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/enums/SmsType.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/enums/SmsType.java @@ -63,7 +63,7 @@ public enum SmsType implements BaseEnum { RESET_PASSWORD_SUCCESS("reset_password_success", "重置密码成功", MessageCategory.NOTICE), /** - * 登录验证 未使用 + * 登录验证 */ LOGIN("login", "登录验证", MessageCategory.CODE), diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/AppProtocol.java b/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/AppProtocol.java index ff0f8a0e..6af6e6fb 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/AppProtocol.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/AppProtocol.java @@ -52,7 +52,12 @@ public enum AppProtocol implements BaseEnum { /** * FORM表单 */ - FORM("form", "表单代填"); + FORM("form", "表单代填"), + + /** + * TSA + */ + TSA("tsa", "TSA"); @JsonValue private final String code; diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/AppType.java b/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/AppType.java index a7d53320..59ea6fe9 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/AppType.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/AppType.java @@ -42,7 +42,11 @@ public enum AppType implements BaseEnum { /** * 自研 */ - SELF_DEVELOPED("self_developed", "自研应用"); + SELF_DEVELOPED("self_developed", "自研应用"), + /** + * TSA + */ + TSA("tsa", "TSA"),; /** * code diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/CasUserIdentityType.java b/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/CasUserIdentityType.java new file mode 100644 index 00000000..06d94c9e --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/CasUserIdentityType.java @@ -0,0 +1,78 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.enums.app; + +import com.fasterxml.jackson.annotation.JsonValue; + +import cn.topiam.employee.support.web.converter.EnumConvert; + +/** + * Cas 用户标识类型 + * + * @author TopIAM + * Created by support@topiam.cn on 2022/5/22 23:49 + */ +public enum CasUserIdentityType { + /** + * 用户名 + */ + USER_USERNAME("user.username"), + /** + * 姓名 + */ + USER_FULL_NAME("user.fullName"), + /** + * 昵称 + */ + USER_NICK_NAME("user.nickName"), + /** + * 邮箱 + */ + USER_EMAIL("user.email"), + /** + * 应用账户 + */ + APP_USERNAME("app_user.username"); + + @JsonValue + private final String code; + + CasUserIdentityType(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + + @EnumConvert + public static CasUserIdentityType getType(String code) { + CasUserIdentityType[] values = values(); + for (CasUserIdentityType status : values) { + if (String.valueOf(status.getCode()).equals(code)) { + return status; + } + } + return null; + } + + @Override + public String toString() { + return code; + } +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/FormSubmitType.java b/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/FormSubmitType.java new file mode 100644 index 00000000..00964ace --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/FormSubmitType.java @@ -0,0 +1,84 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.enums.app; + +import com.fasterxml.jackson.annotation.JsonValue; + +import cn.topiam.employee.common.enums.BaseEnum; +import cn.topiam.employee.support.web.converter.EnumConvert; + +/** + * + * @author SanLi + * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2022/12/21 17:20 + */ +public enum FormSubmitType implements BaseEnum { + /** + * POST + */ + POST("post", "POST"), + /** + * GET + */ + GET("get", "GET"); + + /** + * code + */ + @JsonValue + private final String code; + /** + * desc + */ + private final String desc; + + FormSubmitType(String code, String desc) { + this.code = code; + this.desc = desc; + } + + public String getCode() { + return code; + } + + public String getDesc() { + return desc; + } + + /** + * 获取类型 + * + * @param code {@link String} + * @return {@link InitLoginType} + */ + @EnumConvert + public static FormSubmitType getType(String code) { + FormSubmitType[] values = values(); + for (FormSubmitType status : values) { + if (String.valueOf(status.getCode()).equals(code)) { + return status; + } + } + return null; + } + + @Override + public String toString() { + return this.code; + } +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/InitLoginType.java b/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/InitLoginType.java index 7a437e0d..2093768d 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/InitLoginType.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/InitLoginType.java @@ -53,10 +53,12 @@ public enum InitLoginType implements BaseEnum { this.desc = desc; } + @Override public String getCode() { return code; } + @Override public String getDesc() { return desc; } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/converter/CasUserIdentityTypeConverter.java b/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/converter/CasUserIdentityTypeConverter.java new file mode 100644 index 00000000..c46fdfff --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/converter/CasUserIdentityTypeConverter.java @@ -0,0 +1,46 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.enums.app.converter; + +import java.util.Objects; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +import cn.topiam.employee.common.enums.app.CasUserIdentityType; + +/** + * @author TopIAM + * Created by support@topiam.cn on 2022/5/22 23:25 + */ +@Converter(autoApply = true) +public class CasUserIdentityTypeConverter implements + AttributeConverter { + @Override + public String convertToDatabaseColumn(CasUserIdentityType attribute) { + if (Objects.isNull(attribute)) { + return null; + } + return attribute.getCode(); + } + + @Override + public CasUserIdentityType convertToEntityAttribute(String dbData) { + return CasUserIdentityType.getType(dbData); + } +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/enums/converter/IdentityProviderTypeConverter.java b/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/converter/FormSubmitTypeConverter.java similarity index 75% rename from eiam-common/src/main/java/cn/topiam/employee/common/enums/converter/IdentityProviderTypeConverter.java rename to eiam-common/src/main/java/cn/topiam/employee/common/enums/app/converter/FormSubmitTypeConverter.java index cae8b060..b983e49a 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/enums/converter/IdentityProviderTypeConverter.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/enums/app/converter/FormSubmitTypeConverter.java @@ -15,22 +15,21 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.common.enums.converter; +package cn.topiam.employee.common.enums.app.converter; import java.util.Objects; import javax.persistence.AttributeConverter; import javax.persistence.Converter; -import cn.topiam.employee.common.enums.IdentityProviderType; +import cn.topiam.employee.common.enums.app.FormSubmitType; /** * @author TopIAM - * Created by support@topiam.cn on 2020/12/11 19:42 + * Created by support@topiam.cn on 2020/12/11 23:48 */ @Converter(autoApply = true) -public class IdentityProviderTypeConverter implements - AttributeConverter { +public class FormSubmitTypeConverter implements AttributeConverter { /** * Converts the value stored in the entity attribute into the @@ -41,11 +40,11 @@ public class IdentityProviderTypeConverter implements * column */ @Override - public String convertToDatabaseColumn(IdentityProviderType attribute) { - if (!Objects.isNull(attribute)) { - return attribute.getCode(); + public String convertToDatabaseColumn(FormSubmitType attribute) { + if (Objects.isNull(attribute)) { + return null; } - return null; + return attribute.getCode(); } /** @@ -62,7 +61,7 @@ public class IdentityProviderTypeConverter implements * attribute */ @Override - public IdentityProviderType convertToEntityAttribute(String dbData) { - return IdentityProviderType.getType(dbData); + public FormSubmitType convertToEntityAttribute(String dbData) { + return FormSubmitType.getType(dbData); } } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/enums/identityprovider/IdentitySourceProvider.java b/eiam-common/src/main/java/cn/topiam/employee/common/enums/identitysource/IdentitySourceProvider.java similarity index 98% rename from eiam-common/src/main/java/cn/topiam/employee/common/enums/identityprovider/IdentitySourceProvider.java rename to eiam-common/src/main/java/cn/topiam/employee/common/enums/identitysource/IdentitySourceProvider.java index e38c3372..67e09ef4 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/enums/identityprovider/IdentitySourceProvider.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/enums/identitysource/IdentitySourceProvider.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.common.enums.identityprovider; +package cn.topiam.employee.common.enums.identitysource; import com.fasterxml.jackson.annotation.JsonValue; diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/enums/identityprovider/converter/IdentitySourceProviderConverter.java b/eiam-common/src/main/java/cn/topiam/employee/common/enums/identitysource/converter/IdentitySourceProviderConverter.java similarity index 94% rename from eiam-common/src/main/java/cn/topiam/employee/common/enums/identityprovider/converter/IdentitySourceProviderConverter.java rename to eiam-common/src/main/java/cn/topiam/employee/common/enums/identitysource/converter/IdentitySourceProviderConverter.java index 73225c17..186dbdbd 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/enums/identityprovider/converter/IdentitySourceProviderConverter.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/enums/identitysource/converter/IdentitySourceProviderConverter.java @@ -15,14 +15,14 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.common.enums.identityprovider.converter; +package cn.topiam.employee.common.enums.identitysource.converter; import java.util.Objects; import javax.persistence.AttributeConverter; import javax.persistence.Converter; -import cn.topiam.employee.common.enums.identityprovider.IdentitySourceProvider; +import cn.topiam.employee.common.enums.identitysource.IdentitySourceProvider; /** * 身份源提供商 diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/geo/District.java b/eiam-common/src/main/java/cn/topiam/employee/common/geo/District.java new file mode 100644 index 00000000..aa4c0546 --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/geo/District.java @@ -0,0 +1,739 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.geo; + +import java.util.HashMap; +import java.util.Map; + +/** + * GeoLocationResponse + * + * @author TopIAM + * Created by support@topiam.cn on 2023/02/04 14:19 + */ +public final class District { + public static final Map PROVINCE_DISTRICT = new HashMap<>() { + { + put("北京市", "110000"); + put("天津市", "120000"); + put("河北省", "130000"); + put(" 石家庄市", "130100"); + put(" 唐山市", "130200"); + put(" 秦皇岛市", "130300"); + put(" 邯郸市", "130400"); + put(" 邢台市", "130500"); + put(" 保定市", "130600"); + put(" 张家口市", "130700"); + put(" 承德市", "130800"); + put(" 沧州市", "130900"); + put(" 廊坊市", "131000"); + put(" 衡水市", "131100"); + put("山西省", "140000"); + put(" 太原市", "140100"); + put(" 大同市", "140200"); + put(" 阳泉市", "140300"); + put(" 长治市", "140400"); + put(" 晋城市", "140500"); + put(" 朔州市", "140600"); + put(" 晋中市", "140700"); + put(" 运城市", "140800"); + put(" 忻州市", "140900"); + put(" 临汾市", "141000"); + put(" 吕梁市", "141100"); + put("内蒙古自治区", "150000"); + put(" 呼和浩特市", "150100"); + put(" 包头市", "150200"); + put(" 乌海市", "150300"); + put(" 赤峰市", "150400"); + put(" 通辽市", "150500"); + put(" 鄂尔多斯市", "150600"); + put(" 呼伦贝尔市", "150700"); + put(" 巴彦淖尔市", "150800"); + put(" 乌兰察布市", "150900"); + put(" 兴安盟", "152200"); + put(" 锡林郭勒盟", "152500"); + put(" 阿拉善盟", "152900"); + put("辽宁省", "210000"); + put(" 沈阳市", "210100"); + put(" 大连市", "210200"); + put(" 鞍山市", "210300"); + put(" 抚顺市", "210400"); + put(" 本溪市", "210500"); + put(" 丹东市", "210600"); + put(" 锦州市", "210700"); + put(" 营口市", "210800"); + put(" 阜新市", "210900"); + put(" 辽阳市", "211000"); + put(" 盘锦市", "211100"); + put(" 铁岭市", "211200"); + put(" 朝阳市", "211300"); + put(" 葫芦岛市", "211400"); + put("吉林省", "220000"); + put(" 长春市", "220100"); + put(" 吉林市", "220200"); + put(" 四平市", "220300"); + put(" 辽源市", "220400"); + put(" 通化市", "220500"); + put(" 白山市", "220600"); + put(" 松原市", "220700"); + put(" 白城市", "220800"); + put(" 延边朝鲜族自治州", "222400"); + put("黑龙江省", "230000"); + put(" 哈尔滨市", "230100"); + put(" 齐齐哈尔市", "230200"); + put(" 鸡西市", "230300"); + put(" 鹤岗市", "230400"); + put(" 双鸭山市", "230500"); + put(" 大庆市", "230600"); + put(" 伊春市", "230700"); + put(" 佳木斯市", "230800"); + put(" 七台河市", "230900"); + put(" 牡丹江市", "231000"); + put(" 黑河市", "231100"); + put(" 绥化市", "231200"); + put(" 大兴安岭地区", "232700"); + put("上海市", "310000"); + put("江苏省", "320000"); + put(" 南京市", "320100"); + put(" 无锡市", "320200"); + put(" 徐州市", "320300"); + put(" 常州市", "320400"); + put(" 苏州市", "320500"); + put(" 南通市", "320600"); + put(" 连云港市", "320700"); + put(" 淮安市", "320800"); + put(" 盐城市", "320900"); + put(" 扬州市", "321000"); + put(" 镇江市", "321100"); + put(" 泰州市", "321200"); + put(" 宿迁市", "321300"); + put("浙江省", "330000"); + put(" 杭州市", "330100"); + put(" 宁波市", "330200"); + put(" 温州市", "330300"); + put(" 嘉兴市", "330400"); + put(" 湖州市", "330500"); + put(" 绍兴市", "330600"); + put(" 金华市", "330700"); + put(" 衢州市", "330800"); + put(" 舟山市", "330900"); + put(" 台州市", "331000"); + put(" 丽水市", "331100"); + put("安徽省", "340000"); + put(" 合肥市", "340100"); + put(" 芜湖市", "340200"); + put(" 蚌埠市", "340300"); + put(" 淮南市", "340400"); + put(" 马鞍山市", "340500"); + put(" 淮北市", "340600"); + put(" 铜陵市", "340700"); + put(" 安庆市", "340800"); + put(" 黄山市", "341000"); + put(" 滁州市", "341100"); + put(" 阜阳市", "341200"); + put(" 宿州市", "341300"); + put(" 六安市", "341500"); + put(" 亳州市", "341600"); + put(" 池州市", "341700"); + put(" 宣城市", "341800"); + put("福建省", "350000"); + put(" 福州市", "350100"); + put(" 厦门市", "350200"); + put(" 莆田市", "350300"); + put(" 三明市", "350400"); + put(" 泉州市", "350500"); + put(" 漳州市", "350600"); + put(" 南平市", "350700"); + put(" 龙岩市", "350800"); + put(" 宁德市", "350900"); + put("江西省", "360000"); + put(" 南昌市", "360100"); + put(" 景德镇市", "360200"); + put(" 萍乡市", "360300"); + put(" 九江市", "360400"); + put(" 新余市", "360500"); + put(" 鹰潭市", "360600"); + put(" 赣州市", "360700"); + put(" 吉安市", "360800"); + put(" 宜春市", "360900"); + put(" 抚州市", "361000"); + put(" 上饶市", "361100"); + put("山东省", "370000"); + put(" 济南市", "370100"); + put(" 青岛市", "370200"); + put(" 淄博市", "370300"); + put(" 枣庄市", "370400"); + put(" 东营市", "370500"); + put(" 烟台市", "370600"); + put(" 潍坊市", "370700"); + put(" 济宁市", "370800"); + put(" 泰安市", "370900"); + put(" 威海市", "371000"); + put(" 日照市", "371100"); + put(" 临沂市", "371300"); + put(" 德州市", "371400"); + put(" 聊城市", "371500"); + put(" 滨州市", "371600"); + put(" 菏泽市", "371700"); + put("河南省", "410000"); + put(" 郑州市", "410100"); + put(" 开封市", "410200"); + put(" 洛阳市", "410300"); + put(" 平顶山市", "410400"); + put(" 安阳市", "410500"); + put(" 鹤壁市", "410600"); + put(" 新乡市", "410700"); + put(" 焦作市", "410800"); + put(" 濮阳市", "410900"); + put(" 许昌市", "411000"); + put(" 漯河市", "411100"); + put(" 三门峡市", "411200"); + put(" 南阳市", "411300"); + put(" 商丘市", "411400"); + put(" 信阳市", "411500"); + put(" 周口市", "411600"); + put(" 驻马店市", "411700"); + put("湖北省", "420000"); + put(" 武汉市", "420100"); + put(" 黄石市", "420200"); + put(" 十堰市", "420300"); + put(" 宜昌市", "420500"); + put(" 襄阳市", "420600"); + put(" 鄂州市", "420700"); + put(" 荆门市", "420800"); + put(" 孝感市", "420900"); + put(" 荆州市", "421000"); + put(" 黄冈市", "421100"); + put(" 咸宁市", "421200"); + put(" 随州市", "421300"); + put(" 恩施土家族苗族自治州", "422800"); + put("湖南省", "430000"); + put(" 长沙市", "430100"); + put(" 株洲市", "430200"); + put(" 湘潭市", "430300"); + put(" 衡阳市", "430400"); + put(" 邵阳市", "430500"); + put(" 岳阳市", "430600"); + put(" 常德市", "430700"); + put(" 张家界市", "430800"); + put(" 益阳市", "430900"); + put(" 郴州市", "431000"); + put(" 永州市", "431100"); + put(" 怀化市", "431200"); + put(" 娄底市", "431300"); + put(" 湘西土家族苗族自治州", "433100"); + put("广东省", "440000"); + put(" 广州市", "440100"); + put(" 韶关市", "440200"); + put(" 深圳市", "440300"); + put(" 珠海市", "440400"); + put(" 汕头市", "440500"); + put(" 佛山市", "440600"); + put(" 江门市", "440700"); + put(" 湛江市", "440800"); + put(" 茂名市", "440900"); + put(" 肇庆市", "441200"); + put(" 惠州市", "441300"); + put(" 梅州市", "441400"); + put(" 汕尾市", "441500"); + put(" 河源市", "441600"); + put(" 阳江市", "441700"); + put(" 清远市", "441800"); + put(" 东莞市", "441900"); + put(" 中山市", "442000"); + put(" 潮州市", "445100"); + put(" 揭阳市", "445200"); + put(" 云浮市", "445300"); + put("广西壮族自治区", "450000"); + put(" 南宁市", "450100"); + put(" 柳州市", "450200"); + put(" 桂林市", "450300"); + put(" 梧州市", "450400"); + put(" 北海市", "450500"); + put(" 防城港市", "450600"); + put(" 钦州市", "450700"); + put(" 贵港市", "450800"); + put(" 玉林市", "450900"); + put(" 百色市", "451000"); + put(" 贺州市", "451100"); + put(" 河池市", "451200"); + put(" 来宾市", "451300"); + put(" 崇左市", "451400"); + put("海南省", "460000"); + put(" 海口市", "460100"); + put(" 三亚市", "460200"); + put(" 三沙市", "460300"); + put(" 儋州市", "460400"); + put("重庆市", "500000"); + put("四川省", "510000"); + put(" 成都市", "510100"); + put(" 自贡市", "510300"); + put(" 攀枝花市", "510400"); + put(" 泸州市", "510500"); + put(" 德阳市", "510600"); + put(" 绵阳市", "510700"); + put(" 广元市", "510800"); + put(" 遂宁市", "510900"); + put(" 内江市", "511000"); + put(" 乐山市", "511100"); + put(" 南充市", "511300"); + put(" 眉山市", "511400"); + put(" 宜宾市", "511500"); + put(" 广安市", "511600"); + put(" 达州市", "511700"); + put(" 雅安市", "511800"); + put(" 巴中市", "511900"); + put(" 资阳市", "512000"); + put(" 阿坝藏族羌族自治州", "513200"); + put(" 甘孜藏族自治州", "513300"); + put(" 凉山彝族自治州", "513400"); + put("贵州省", "520000"); + put(" 贵阳市", "520100"); + put(" 六盘水市", "520200"); + put(" 遵义市", "520300"); + put(" 安顺市", "520400"); + put(" 毕节市", "520500"); + put(" 铜仁市", "520600"); + put(" 黔西南布依族苗族自治州", "522300"); + put(" 黔东南苗族侗族自治州", "522600"); + put(" 黔南布依族苗族自治州", "522700"); + put("云南省", "530000"); + put(" 昆明市", "530100"); + put(" 曲靖市", "530300"); + put(" 玉溪市", "530400"); + put(" 保山市", "530500"); + put(" 昭通市", "530600"); + put(" 丽江市", "530700"); + put(" 普洱市", "530800"); + put(" 临沧市", "530900"); + put(" 楚雄彝族自治州", "532300"); + put(" 红河哈尼族彝族自治州", "532500"); + put(" 文山壮族苗族自治州", "532600"); + put(" 西双版纳傣族自治州", "532800"); + put(" 大理白族自治州", "532900"); + put(" 德宏傣族景颇族自治州", "533100"); + put(" 怒江傈僳族自治州", "533300"); + put(" 迪庆藏族自治州", "533400"); + put("西藏自治区", "540000"); + put(" 拉萨市", "540100"); + put(" 日喀则市", "540200"); + put(" 昌都市", "540300"); + put(" 林芝市", "540400"); + put(" 山南市", "540500"); + put(" 那曲市", "540600"); + put(" 阿里地区", "542500"); + put("陕西省", "610000"); + put(" 西安市", "610100"); + put(" 铜川市", "610200"); + put(" 宝鸡市", "610300"); + put(" 咸阳市", "610400"); + put(" 渭南市", "610500"); + put(" 延安市", "610600"); + put(" 汉中市", "610700"); + put(" 榆林市", "610800"); + put(" 安康市", "610900"); + put(" 商洛市", "611000"); + put("甘肃省", "620000"); + put(" 兰州市", "620100"); + put(" 嘉峪关市", "620200"); + put(" 金昌市", "620300"); + put(" 白银市", "620400"); + put(" 天水市", "620500"); + put(" 武威市", "620600"); + put(" 张掖市", "620700"); + put(" 平凉市", "620800"); + put(" 酒泉市", "620900"); + put(" 庆阳市", "621000"); + put(" 定西市", "621100"); + put(" 陇南市", "621200"); + put(" 临夏回族自治州", "622900"); + put(" 甘南藏族自治州", "623000"); + put("青海省", "630000"); + put(" 西宁市", "630100"); + put(" 海东市", "630200"); + put(" 海北藏族自治州", "632200"); + put(" 黄南藏族自治州", "632300"); + put(" 海南藏族自治州", "632500"); + put(" 果洛藏族自治州", "632600"); + put(" 玉树藏族自治州", "632700"); + put(" 海西蒙古族藏族自治州", "632800"); + put("宁夏回族自治区", "640000"); + put(" 银川市", "640100"); + put(" 石嘴山市", "640200"); + put(" 吴忠市", "640300"); + put(" 固原市", "640400"); + put(" 中卫市", "640500"); + put("新疆维吾尔自治区", "650000"); + put(" 乌鲁木齐市", "650100"); + put(" 克拉玛依市", "650200"); + put(" 吐鲁番市", "650400"); + put(" 哈密市", "650500"); + put(" 昌吉回族自治州", "652300"); + put(" 博尔塔拉蒙古自治州", "652700"); + put(" 巴音郭楞蒙古自治州", "652800"); + put(" 阿克苏地区", "652900"); + put(" 克孜勒苏柯尔克孜自治州", "653000"); + put(" 喀什地区", "653100"); + put(" 和田地区", "653200"); + put(" 伊犁哈萨克自治州", "654000"); + put(" 塔城地区", "654200"); + put(" 阿勒泰地区", "654300"); + put("台湾省", "710000"); + put("香港特别行政区", "810000"); + put("澳门特别行政区", "820000"); + } + }; + + public static final Map CITY_DISTRICT = new HashMap<>() { + { + put("石家庄市", "130100"); + put("唐山市", "130200"); + put("秦皇岛市", "130300"); + put("邯郸市", "130400"); + put("邢台市", "130500"); + put("保定市", "130600"); + put("张家口市", "130700"); + put("承德市", "130800"); + put("沧州市", "130900"); + put("廊坊市", "131000"); + put("衡水市", "131100"); + put("太原市", "140100"); + put("大同市", "140200"); + put("阳泉市", "140300"); + put("长治市", "140400"); + put("晋城市", "140500"); + put("朔州市", "140600"); + put("晋中市", "140700"); + put("运城市", "140800"); + put("忻州市", "140900"); + put("临汾市", "141000"); + put("吕梁市", "141100"); + put("呼和浩特市", "150100"); + put("包头市", "150200"); + put("乌海市", "150300"); + put("赤峰市", "150400"); + put("通辽市", "150500"); + put("鄂尔多斯市", "150600"); + put("呼伦贝尔市", "150700"); + put("巴彦淖尔市", "150800"); + put("乌兰察布市", "150900"); + put("兴安盟", "152200"); + put("锡林郭勒盟", "152500"); + put("阿拉善盟", "152900"); + put("沈阳市", "210100"); + put("大连市", "210200"); + put("鞍山市", "210300"); + put("抚顺市", "210400"); + put("本溪市", "210500"); + put("丹东市", "210600"); + put("锦州市", "210700"); + put("营口市", "210800"); + put("阜新市", "210900"); + put("辽阳市", "211000"); + put("盘锦市", "211100"); + put("铁岭市", "211200"); + put("朝阳市", "211300"); + put("葫芦岛市", "211400"); + put("长春市", "220100"); + put("吉林市", "220200"); + put("四平市", "220300"); + put("辽源市", "220400"); + put("通化市", "220500"); + put("白山市", "220600"); + put("松原市", "220700"); + put("白城市", "220800"); + put("延边朝鲜族自治州", "222400"); + put("哈尔滨市", "230100"); + put("齐齐哈尔市", "230200"); + put("鸡西市", "230300"); + put("鹤岗市", "230400"); + put("双鸭山市", "230500"); + put("大庆市", "230600"); + put("伊春市", "230700"); + put("佳木斯市", "230800"); + put("七台河市", "230900"); + put("牡丹江市", "231000"); + put("黑河市", "231100"); + put("绥化市", "231200"); + put("大兴安岭地区", "232700"); + put("南京市", "320100"); + put("无锡市", "320200"); + put("徐州市", "320300"); + put("常州市", "320400"); + put("苏州市", "320500"); + put("南通市", "320600"); + put("连云港市", "320700"); + put("淮安市", "320800"); + put("盐城市", "320900"); + put("扬州市", "321000"); + put("镇江市", "321100"); + put("泰州市", "321200"); + put("宿迁市", "321300"); + put("杭州市", "330100"); + put("宁波市", "330200"); + put("温州市", "330300"); + put("嘉兴市", "330400"); + put("湖州市", "330500"); + put("绍兴市", "330600"); + put("金华市", "330700"); + put("衢州市", "330800"); + put("舟山市", "330900"); + put("台州市", "331000"); + put("丽水市", "331100"); + put("合肥市", "340100"); + put("芜湖市", "340200"); + put("蚌埠市", "340300"); + put("淮南市", "340400"); + put("马鞍山市", "340500"); + put("淮北市", "340600"); + put("铜陵市", "340700"); + put("安庆市", "340800"); + put("黄山市", "341000"); + put("滁州市", "341100"); + put("阜阳市", "341200"); + put("宿州市", "341300"); + put("六安市", "341500"); + put("亳州市", "341600"); + put("池州市", "341700"); + put("宣城市", "341800"); + put("福州市", "350100"); + put("厦门市", "350200"); + put("莆田市", "350300"); + put("三明市", "350400"); + put("泉州市", "350500"); + put("漳州市", "350600"); + put("南平市", "350700"); + put("龙岩市", "350800"); + put("宁德市", "350900"); + put("南昌市", "360100"); + put("景德镇市", "360200"); + put("萍乡市", "360300"); + put("九江市", "360400"); + put("新余市", "360500"); + put("鹰潭市", "360600"); + put("赣州市", "360700"); + put("吉安市", "360800"); + put("宜春市", "360900"); + put("抚州市", "361000"); + put("上饶市", "361100"); + put("济南市", "370100"); + put("青岛市", "370200"); + put("淄博市", "370300"); + put("枣庄市", "370400"); + put("东营市", "370500"); + put("烟台市", "370600"); + put("潍坊市", "370700"); + put("济宁市", "370800"); + put("泰安市", "370900"); + put("威海市", "371000"); + put("日照市", "371100"); + put("临沂市", "371300"); + put("德州市", "371400"); + put("聊城市", "371500"); + put("滨州市", "371600"); + put("菏泽市", "371700"); + put("郑州市", "410100"); + put("开封市", "410200"); + put("洛阳市", "410300"); + put("平顶山市", "410400"); + put("安阳市", "410500"); + put("鹤壁市", "410600"); + put("新乡市", "410700"); + put("焦作市", "410800"); + put("濮阳市", "410900"); + put("许昌市", "411000"); + put("漯河市", "411100"); + put("三门峡市", "411200"); + put("南阳市", "411300"); + put("商丘市", "411400"); + put("信阳市", "411500"); + put("周口市", "411600"); + put("驻马店市", "411700"); + put("武汉市", "420100"); + put("黄石市", "420200"); + put("十堰市", "420300"); + put("宜昌市", "420500"); + put("襄阳市", "420600"); + put("鄂州市", "420700"); + put("荆门市", "420800"); + put("孝感市", "420900"); + put("荆州市", "421000"); + put("黄冈市", "421100"); + put("咸宁市", "421200"); + put("随州市", "421300"); + put("恩施土家族苗族自治州", "422800"); + put("长沙市", "430100"); + put("株洲市", "430200"); + put("湘潭市", "430300"); + put("衡阳市", "430400"); + put("邵阳市", "430500"); + put("岳阳市", "430600"); + put("常德市", "430700"); + put("张家界市", "430800"); + put("益阳市", "430900"); + put("郴州市", "431000"); + put("永州市", "431100"); + put("怀化市", "431200"); + put("娄底市", "431300"); + put("湘西土家族苗族自治州", "433100"); + put("广州市", "440100"); + put("韶关市", "440200"); + put("深圳市", "440300"); + put("珠海市", "440400"); + put("汕头市", "440500"); + put("佛山市", "440600"); + put("江门市", "440700"); + put("湛江市", "440800"); + put("茂名市", "440900"); + put("肇庆市", "441200"); + put("惠州市", "441300"); + put("梅州市", "441400"); + put("汕尾市", "441500"); + put("河源市", "441600"); + put("阳江市", "441700"); + put("清远市", "441800"); + put("东莞市", "441900"); + put("中山市", "442000"); + put("潮州市", "445100"); + put("揭阳市", "445200"); + put("云浮市", "445300"); + put("南宁市", "450100"); + put("柳州市", "450200"); + put("桂林市", "450300"); + put("梧州市", "450400"); + put("北海市", "450500"); + put("防城港市", "450600"); + put("钦州市", "450700"); + put("贵港市", "450800"); + put("玉林市", "450900"); + put("百色市", "451000"); + put("贺州市", "451100"); + put("河池市", "451200"); + put("来宾市", "451300"); + put("崇左市", "451400"); + put("海口市", "460100"); + put("三亚市", "460200"); + put("三沙市", "460300"); + put("儋州市", "460400"); + put("成都市", "510100"); + put("自贡市", "510300"); + put("攀枝花市", "510400"); + put("泸州市", "510500"); + put("德阳市", "510600"); + put("绵阳市", "510700"); + put("广元市", "510800"); + put("遂宁市", "510900"); + put("内江市", "511000"); + put("乐山市", "511100"); + put("南充市", "511300"); + put("眉山市", "511400"); + put("宜宾市", "511500"); + put("广安市", "511600"); + put("达州市", "511700"); + put("雅安市", "511800"); + put("巴中市", "511900"); + put("资阳市", "512000"); + put("阿坝藏族羌族自治州", "513200"); + put("甘孜藏族自治州", "513300"); + put("凉山彝族自治州", "513400"); + put("贵阳市", "520100"); + put("六盘水市", "520200"); + put("遵义市", "520300"); + put("安顺市", "520400"); + put("毕节市", "520500"); + put("铜仁市", "520600"); + put("黔西南布依族苗族自治州", "522300"); + put("黔东南苗族侗族自治州", "522600"); + put("黔南布依族苗族自治州", "522700"); + put("昆明市", "530100"); + put("曲靖市", "530300"); + put("玉溪市", "530400"); + put("保山市", "530500"); + put("昭通市", "530600"); + put("丽江市", "530700"); + put("普洱市", "530800"); + put("临沧市", "530900"); + put("楚雄彝族自治州", "532300"); + put("红河哈尼族彝族自治州", "532500"); + put("文山壮族苗族自治州", "532600"); + put("西双版纳傣族自治州", "532800"); + put("大理白族自治州", "532900"); + put("德宏傣族景颇族自治州", "533100"); + put("怒江傈僳族自治州", "533300"); + put("迪庆藏族自治州", "533400"); + put("拉萨市", "540100"); + put("日喀则市", "540200"); + put("昌都市", "540300"); + put("林芝市", "540400"); + put("山南市", "540500"); + put("那曲市", "540600"); + put("阿里地区", "542500"); + put("西安市", "610100"); + put("铜川市", "610200"); + put("宝鸡市", "610300"); + put("咸阳市", "610400"); + put("渭南市", "610500"); + put("延安市", "610600"); + put("汉中市", "610700"); + put("榆林市", "610800"); + put("安康市", "610900"); + put("商洛市", "611000"); + put("兰州市", "620100"); + put("嘉峪关市", "620200"); + put("金昌市", "620300"); + put("白银市", "620400"); + put("天水市", "620500"); + put("武威市", "620600"); + put("张掖市", "620700"); + put("平凉市", "620800"); + put("酒泉市", "620900"); + put("庆阳市", "621000"); + put("定西市", "621100"); + put("陇南市", "621200"); + put("临夏回族自治州", "622900"); + put("甘南藏族自治州", "623000"); + put("西宁市", "630100"); + put("海东市", "630200"); + put("海北藏族自治州", "632200"); + put("黄南藏族自治州", "632300"); + put("海南藏族自治州", "632500"); + put("果洛藏族自治州", "632600"); + put("玉树藏族自治州", "632700"); + put("海西蒙古族藏族自治州", "632800"); + put("银川市", "640100"); + put("石嘴山市", "640200"); + put("吴忠市", "640300"); + put("固原市", "640400"); + put("中卫市", "640500"); + put("乌鲁木齐市", "650100"); + put("克拉玛依市", "650200"); + put("吐鲁番市", "650400"); + put("哈密市", "650500"); + put("昌吉回族自治州", "652300"); + put("博尔塔拉蒙古自治州", "652700"); + put("巴音郭楞蒙古自治州", "652800"); + put("阿克苏地区", "652900"); + put("克孜勒苏柯尔克孜自治州", "653000"); + put("喀什地区", "653100"); + put("和田地区", "653200"); + put("伊犁哈萨克自治州", "654000"); + put("塔城地区", "654200"); + put("阿勒泰地区", "654300"); + } + }; +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/geo/maxmind/MaxmindGeoLocationServiceImpl.java b/eiam-common/src/main/java/cn/topiam/employee/common/geo/maxmind/MaxmindGeoLocationServiceImpl.java index 9102638d..53978e1a 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/geo/maxmind/MaxmindGeoLocationServiceImpl.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/geo/maxmind/MaxmindGeoLocationServiceImpl.java @@ -34,6 +34,7 @@ import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.web.client.ResourceAccessException; @@ -54,6 +55,8 @@ import lombok.extern.slf4j.Slf4j; import dev.failsafe.Failsafe; import dev.failsafe.RetryPolicy; +import static cn.topiam.employee.common.geo.District.CITY_DISTRICT; +import static cn.topiam.employee.common.geo.District.PROVINCE_DISTRICT; import static cn.topiam.employee.common.geo.maxmind.enums.GeoLocationProvider.MAXMIND; /** @@ -116,9 +119,9 @@ public class MaxmindGeoLocationServiceImpl implements GeoLocationService { .setCountryName(country.getName()) .setCountryCode(country.getGeoNameId().toString()) .setCityName(city.getName()) - .setCityCode(String.valueOf(city.getGeoNameId())) + .setCityCode(StringUtils.defaultString(CITY_DISTRICT.get(city.getName()), String.valueOf(city.getGeoNameId()))) .setProvinceName(subdivision.getName()) - .setProvinceCode(subdivision.getIsoCode()) + .setProvinceCode(StringUtils.defaultString(PROVINCE_DISTRICT.get(subdivision.getName()), subdivision.getIsoCode())) .setLongitude(location.getLongitude()) .setLatitude(location.getLatitude()) .setProvider(MAXMIND); diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/geo/maxmind/MaxmindProviderConfig.java b/eiam-common/src/main/java/cn/topiam/employee/common/geo/maxmind/MaxmindProviderConfig.java index 94e2eb0b..59f95d2c 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/geo/maxmind/MaxmindProviderConfig.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/geo/maxmind/MaxmindProviderConfig.java @@ -19,6 +19,7 @@ package cn.topiam.employee.common.geo.maxmind; import javax.validation.constraints.NotEmpty; +import cn.topiam.employee.common.crypto.Encrypt; import cn.topiam.employee.common.geo.GeoLocationProviderConfig; import lombok.Data; @@ -35,6 +36,7 @@ public class MaxmindProviderConfig extends GeoLocationProviderConfig.GeoLocation /** * 密码 */ + @Encrypt @NotEmpty(message = "密码不能为空") private String sessionKey; } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/message/mail/MailProviderConfig.java b/eiam-common/src/main/java/cn/topiam/employee/common/message/mail/MailProviderConfig.java index 6a5aacd5..20c2abf5 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/message/mail/MailProviderConfig.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/message/mail/MailProviderConfig.java @@ -20,9 +20,9 @@ package cn.topiam.employee.common.message.mail; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; +import cn.topiam.employee.common.crypto.EncryptContextHelp; import cn.topiam.employee.common.message.enums.MailProvider; import cn.topiam.employee.common.message.enums.MailSafetyType; -import cn.topiam.employee.support.util.AesUtils; import lombok.Builder; import lombok.Data; @@ -86,6 +86,6 @@ public class MailProviderConfig { private String secret; public String getDecryptSecret() { - return AesUtils.decrypt(this.secret); + return EncryptContextHelp.decrypt(this.secret); } } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/message/sms/aliyun/AliyunSmsProviderConfig.java b/eiam-common/src/main/java/cn/topiam/employee/common/message/sms/aliyun/AliyunSmsProviderConfig.java index 23bfcf3e..9ccb84f9 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/message/sms/aliyun/AliyunSmsProviderConfig.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/message/sms/aliyun/AliyunSmsProviderConfig.java @@ -19,6 +19,7 @@ package cn.topiam.employee.common.message.sms.aliyun; import javax.validation.constraints.NotEmpty; +import cn.topiam.employee.common.crypto.Encrypt; import cn.topiam.employee.common.message.sms.SmsProviderConfig; import lombok.Data; @@ -45,6 +46,7 @@ public class AliyunSmsProviderConfig extends SmsProviderConfig { /** * accessKeySecret */ + @Encrypt @NotEmpty(message = "accessKeySecret不能为空") private String accessKeySecret; diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/message/sms/qiniu/QiNiuSmsProviderConfig.java b/eiam-common/src/main/java/cn/topiam/employee/common/message/sms/qiniu/QiNiuSmsProviderConfig.java index 1f21e672..05e52295 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/message/sms/qiniu/QiNiuSmsProviderConfig.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/message/sms/qiniu/QiNiuSmsProviderConfig.java @@ -19,6 +19,7 @@ package cn.topiam.employee.common.message.sms.qiniu; import javax.validation.constraints.NotEmpty; +import cn.topiam.employee.common.crypto.Encrypt; import cn.topiam.employee.common.message.sms.SmsProviderConfig; import lombok.Data; @@ -44,6 +45,7 @@ public class QiNiuSmsProviderConfig extends SmsProviderConfig { /** * secretKey */ + @Encrypt @NotEmpty(message = "secretKey不能为空") private String secretKey; } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/message/sms/tencent/TencentSmsProviderConfig.java b/eiam-common/src/main/java/cn/topiam/employee/common/message/sms/tencent/TencentSmsProviderConfig.java index e67e8f66..208372e3 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/message/sms/tencent/TencentSmsProviderConfig.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/message/sms/tencent/TencentSmsProviderConfig.java @@ -19,6 +19,7 @@ package cn.topiam.employee.common.message.sms.tencent; import javax.validation.constraints.NotEmpty; +import cn.topiam.employee.common.crypto.Encrypt; import cn.topiam.employee.common.message.sms.SmsProviderConfig; import lombok.Data; @@ -45,6 +46,7 @@ public class TencentSmsProviderConfig extends SmsProviderConfig { /** * secretKey */ + @Encrypt @NotEmpty(message = "SecretKey不能为空") private String secretKey; diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/message/sms/tencent/TencentSmsProviderSend.java b/eiam-common/src/main/java/cn/topiam/employee/common/message/sms/tencent/TencentSmsProviderSend.java index 85036b92..105822d5 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/message/sms/tencent/TencentSmsProviderSend.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/message/sms/tencent/TencentSmsProviderSend.java @@ -89,9 +89,7 @@ public class TencentSmsProviderSend implements SmsProviderSend { /* 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,若无模板参数,则设置为空 */ Map parameters = sendSmsParam.getParameters(); List templateParamList = new ArrayList<>(); - parameters.forEach((key, value) -> { - templateParamList.add(value); - }); + parameters.forEach((key, value) -> templateParamList.add(value)); req.setTemplateParamSet(templateParamList.toArray(new String[0])); /* 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号] * 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号 */ diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/OrganizationMemberRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/OrganizationMemberRepository.java index 4e6e2f43..5bcfa77b 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/OrganizationMemberRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/OrganizationMemberRepository.java @@ -19,7 +19,6 @@ package cn.topiam.employee.common.repository.account; import java.util.List; -import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; @@ -28,6 +27,8 @@ import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import cn.topiam.employee.common.entity.account.OrganizationMemberEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; /** * 组织机构成员 @@ -36,7 +37,8 @@ import cn.topiam.employee.common.entity.account.OrganizationMemberEntity; * Created by support@topiam.cn on 2021/11/30 03:06 */ @Repository -public interface OrganizationMemberRepository extends JpaRepository, +public interface OrganizationMemberRepository extends + LogicDeleteRepository, QuerydslPredicateExecutor, OrganizationMemberCustomizedRepository { @@ -46,7 +48,10 @@ public interface OrganizationMemberRepository extends JpaRepository userIds); /** @@ -72,6 +79,8 @@ public interface OrganizationMemberRepository extends JpaRepository @@ -44,8 +44,7 @@ import cn.topiam.employee.common.enums.DataOrigin; * Created by support@topiam.cn on 2020-08-09 */ @Repository -public interface OrganizationRepository extends CrudRepository, - PagingAndSortingRepository, +public interface OrganizationRepository extends LogicDeleteRepository, JpaSpecificationExecutor, QuerydslPredicateExecutor, OrganizationRepositoryCustomized { @@ -197,4 +196,13 @@ public interface OrganizationRepository extends CrudRepository findByIdInOrderByOrderAsc(Collection parentIds); + /** + * findByIdContainsDeleted + * + * @param id must not be {@literal null}. + * @return {@link OrganizationEntity} + */ + @NotNull + @Query(value = "SELECT * FROM organization WHERE id_ = :id", nativeQuery = true) + Optional findByIdContainsDeleted(@NotNull @Param(value = "id") String id); } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserDetailRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserDetailRepository.java index d1883241..a2528190 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserDetailRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserDetailRepository.java @@ -23,12 +23,13 @@ import java.util.Optional; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import cn.topiam.employee.common.entity.account.UserDetailEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_HQL_SET; /** *

@@ -39,8 +40,7 @@ import cn.topiam.employee.common.entity.account.UserDetailEntity; * Created by support@topiam.cn on 2020-08-07 */ @Repository -public interface UserDetailRepository extends PagingAndSortingRepository, - CrudRepository, +public interface UserDetailRepository extends LogicDeleteRepository, QuerydslPredicateExecutor, UserDetailRepositoryCustomized { /** @@ -56,16 +56,21 @@ public interface UserDetailRepository extends PagingAndSortingRepository userIds); + @Transactional(rollbackFor = Exception.class) + @Query(value = "UPDATE UserDetailEntity SET " + SOFT_DELETE_HQL_SET + + " WHERE userId IN (:userIds)") + void deleteAllByUserIds(@Param("userIds") Iterable userIds); /** * 根据用户ID查询用户详情 @@ -73,7 +78,5 @@ public interface UserDetailRepository extends PagingAndSortingRepository findAllByUserIds(@Param("userIds") Iterable userIds); + List findAllByUserIdIn(List userIds); } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserGroupMemberRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserGroupMemberRepository.java index 3391b807..56b94296 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserGroupMemberRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserGroupMemberRepository.java @@ -19,7 +19,6 @@ package cn.topiam.employee.common.repository.account; import java.util.List; -import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; @@ -28,6 +27,8 @@ import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import cn.topiam.employee.common.entity.account.UserGroupMemberEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; /** * 用户组成员 @@ -36,7 +37,8 @@ import cn.topiam.employee.common.entity.account.UserGroupMemberEntity; * Created by support@topiam.cn on 2021/11/30 03:04 */ @Repository -public interface UserGroupMemberRepository extends JpaRepository, +public interface UserGroupMemberRepository extends + LogicDeleteRepository, QuerydslPredicateExecutor, UserGroupMemberRepositoryCustomized { @@ -46,7 +48,10 @@ public interface UserGroupMemberRepository extends JpaRepository userIds); /** @@ -73,6 +80,8 @@ public interface UserGroupMemberRepository extends JpaRepository @@ -33,8 +37,16 @@ import cn.topiam.employee.common.entity.account.UserGroupEntity; * Created by support@topiam.cn on 2020-07-31 */ @Repository -public interface UserGroupRepository extends CrudRepository, - PagingAndSortingRepository, +public interface UserGroupRepository extends LogicDeleteRepository, QuerydslPredicateExecutor { + /** + * findByIdContainsDeleted + * + * @param id must not be {@literal null}. + * @return {@link UserGroupEntity} + */ + @NotNull + @Query(value = "SELECT * FROM user_group WHERE id_ = :id", nativeQuery = true) + Optional findByIdContainsDeleted(@NotNull @Param(value = "id") Long id); } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserHistoryPasswordRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserHistoryPasswordRepository.java index def209b6..27e656c7 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserHistoryPasswordRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserHistoryPasswordRepository.java @@ -20,11 +20,10 @@ package cn.topiam.employee.common.repository.account; import java.util.List; import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.stereotype.Repository; import cn.topiam.employee.common.entity.account.UserHistoryPasswordEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; /** *

@@ -36,8 +35,7 @@ import cn.topiam.employee.common.entity.account.UserHistoryPasswordEntity; */ @Repository public interface UserHistoryPasswordRepository extends - CrudRepository, - PagingAndSortingRepository, + LogicDeleteRepository, QuerydslPredicateExecutor { /** * 根据用户ID查询历史密码 diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserIdpRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserIdpRepository.java index 645ddba9..a966587a 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserIdpRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserIdpRepository.java @@ -18,10 +18,10 @@ package cn.topiam.employee.common.repository.account; import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; import cn.topiam.employee.common.entity.account.UserIdpBindEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; /** * 用户身份绑定表 @@ -30,7 +30,7 @@ import cn.topiam.employee.common.entity.account.UserIdpBindEntity; * Created by support@topiam.cn on 2022/4/3 22:18 */ @Repository -public interface UserIdpRepository extends CrudRepository, +public interface UserIdpRepository extends LogicDeleteRepository, QuerydslPredicateExecutor, UserIdpRepositoryCustomized { diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserRepository.java index 1b5f9e82..f18c424b 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserRepository.java @@ -29,8 +29,6 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -38,7 +36,9 @@ import org.springframework.transaction.annotation.Transactional; import cn.topiam.employee.common.entity.account.UserEntity; import cn.topiam.employee.common.enums.DataOrigin; import cn.topiam.employee.common.enums.UserStatus; +import cn.topiam.employee.support.repository.LogicDeleteRepository; import static cn.topiam.employee.common.constants.AccountConstants.USER_CACHE_NAME; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_WHERE; /** *

@@ -50,8 +50,7 @@ import static cn.topiam.employee.common.constants.AccountConstants.USER_CACHE_NA */ @Repository @CacheConfig(cacheNames = { USER_CACHE_NAME }) -public interface UserRepository extends CrudRepository, - PagingAndSortingRepository, +public interface UserRepository extends LogicDeleteRepository, QuerydslPredicateExecutor, UserRepositoryCustomized { /** * findById @@ -62,7 +61,18 @@ public interface UserRepository extends CrudRepository, @NotNull @Override @Cacheable(key = "#p0", unless = "#result==null") - Optional findById(@NotNull Long id); + Optional findById(@NotNull @Param(value = "id") Long id); + + /** + * findByIdContainsDeleted + * + * @param id must not be {@literal null}. + * @return {@link UserEntity} + */ + @NotNull + @Cacheable(key = "#p0", unless = "#result==null") + @Query(value = "SELECT * FROM user WHERE id_ = :id", nativeQuery = true) + Optional findByIdContainsDeleted(@NotNull @Param(value = "id") Long id); /** * findById @@ -195,7 +205,8 @@ public interface UserRepository extends CrudRepository, * @param expireWarnDays {@link Integer} 即将到期日期 * @return {@link UserEntity} */ - @Query(value = "SELECT * FROM `user` WHERE DATE_ADD(DATE_FORMAT(last_update_password_time,'%Y-%m-%d'), INTERVAL :expireWarnDays DAY ) <= CURDATE() and user.status_ != 'locked'", nativeQuery = true) + @Query(value = "SELECT * FROM `user` WHERE DATE_ADD(DATE_FORMAT(last_update_password_time,'%Y-%m-%d'), INTERVAL :expireWarnDays DAY ) <= CURDATE() and user.status_ != 'locked' AND " + + SOFT_DELETE_WHERE, nativeQuery = true) List findPasswordExpireWarnUser(@Param(value = "expireWarnDays") Integer expireWarnDays); /** @@ -204,7 +215,8 @@ public interface UserRepository extends CrudRepository, * @param expireDays {@link Integer} 密码过期日期 * @return {@link UserEntity} */ - @Query(value = "SELECT * FROM `user` WHERE DATE_ADD(DATE_FORMAT(last_update_password_time,'%Y-%m-%d'), INTERVAL :expireDays DAY ) BETWEEN DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 HOUR),'%Y-%m-%d %h') AND DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 HOUR),'%Y-%m-%d %h') and user.status_ != 'password_expired_locked'", nativeQuery = true) + @Query(value = "SELECT * FROM `user` WHERE DATE_ADD(DATE_FORMAT(last_update_password_time,'%Y-%m-%d'), INTERVAL :expireDays DAY ) BETWEEN DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 HOUR),'%Y-%m-%d %h') AND DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 HOUR),'%Y-%m-%d %h') AND user.status_ != 'password_expired_locked' AND " + + SOFT_DELETE_WHERE, nativeQuery = true) List findPasswordExpireUser(@Param(value = "expireDays") Integer expireDays); /** @@ -212,7 +224,8 @@ public interface UserRepository extends CrudRepository, * * @return {@link UserEntity} */ - @Query(value = "SELECT * from `user` WHERE expire_date <= CURDATE() and status_ != 'expired_locked'", nativeQuery = true) + @Query(value = "SELECT * FROM `user` WHERE expire_date <= CURDATE() and status_ != 'expired_locked' AND " + + SOFT_DELETE_WHERE, nativeQuery = true) List findExpireUser(); /** @@ -230,13 +243,6 @@ public interface UserRepository extends CrudRepository, @Param(value = "sharedSecret") String sharedSecret, @Param(value = "totpBind") Boolean totpBind); - /** - * 根据第三方扩展ID 删除用户 - * - * @param externalIds {@link List} - */ - void deleteAllByExternalIdIn(Collection externalIds); - /** * 根据用户名查询全部 * @@ -268,4 +274,17 @@ public interface UserRepository extends CrudRepository, * @return {@link List} */ List findAllByIdNotInAndDataOrigin(Collection ids, DataOrigin dataOrigin); + + /** + * 更新认证成功信息 + * + * @param id {@link String} + * @param ip {@link String} + * @param loginTime {@link LocalDateTime} + */ + @CacheEvict(allEntries = true) + @Transactional(rollbackFor = Exception.class) + @Modifying + @Query(value = "UPDATE user SET auth_total = (IFNULL(auth_total,0) +1),last_auth_ip = ?2,last_auth_time = ?3 WHERE id_ = ?1", nativeQuery = true) + void updateAuthSucceedInfo(String id, String ip, LocalDateTime loginTime); } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserRepositoryCustomized.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserRepositoryCustomized.java index 67769df8..c3385255 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserRepositoryCustomized.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/UserRepositoryCustomized.java @@ -44,7 +44,7 @@ public interface UserRepositoryCustomized { Page getUserList(UserListQuery query, Pageable pageable); /** - * 获取用户组成员列表 + * 获取用户组不存在成员列表 * * @param query {@link UserListNotInGroupQuery} * @param pageable {@link Pageable} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/impl/UserGroupMemberRepositoryCustomizedImpl.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/impl/UserGroupMemberRepositoryCustomizedImpl.java index 7227a360..c7dd7d38 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/impl/UserGroupMemberRepositoryCustomizedImpl.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/impl/UserGroupMemberRepositoryCustomizedImpl.java @@ -83,12 +83,13 @@ public class UserGroupMemberRepositoryCustomizedImpl implements group_concat( organization_.display_path ) AS org_display_path FROM user_group_member ugm - INNER JOIN user u ON ugm.user_id = u.id_ - INNER JOIN user_group ug ON ug.id_ = ugm.group_id - LEFT JOIN organization_member ON ( u.id_ = organization_member.user_id ) - LEFT JOIN organization organization_ ON ( organization_.id_ = organization_member.org_id ) + INNER JOIN user u ON ugm.user_id = u.id_ AND u.is_deleted = '0' + INNER JOIN user_group ug ON ug.id_ = ugm.group_id AND ug.is_deleted = '0' + LEFT JOIN organization_member ON ( u.id_ = organization_member.user_id AND organization_member.is_deleted = '0') + LEFT JOIN organization organization_ ON ( organization_.id_ = organization_member.org_id AND organization_.is_deleted = '0') WHERE - ugm.group_id = '%s' + ugm.is_deleted = '0' + AND ugm.group_id = '%s' AND ug.id_ = '%s' """.formatted(query.getId(), query.getId())); //用户名 diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/impl/UserIdpRepositoryCustomizedImpl.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/impl/UserIdpRepositoryCustomizedImpl.java index 12f6d539..36f979e8 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/impl/UserIdpRepositoryCustomizedImpl.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/impl/UserIdpRepositoryCustomizedImpl.java @@ -22,6 +22,7 @@ import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.springframework.cache.annotation.CacheConfig; +import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @@ -54,7 +55,7 @@ public class UserIdpRepositoryCustomizedImpl implements UserIdpRepositoryCustomi @Override public Optional findByIdpIdAndOpenId(String idpId, String openId) { //@formatter:off - StringBuilder builder = new StringBuilder("SELECT uidp.*,`user`.username_,idp.name_ as idp_name FROM user_idp_bind uidp LEFT JOIN `user` ON uidp.user_id = `user`.id_ LEFT JOIN identity_provider idp ON uidp.idp_id = idp.id_ WHERE 1=1"); + StringBuilder builder = new StringBuilder("SELECT uidp.*,`user`.username_,idp.name_ as idp_name FROM user_idp_bind uidp LEFT JOIN `user` ON uidp.user_id = `user`.id_ AND `user`.is_deleted = '0' LEFT JOIN identity_provider idp ON uidp.idp_id = idp.id_ AND idp.is_deleted = '0' WHERE uidp.is_deleted = '0' "); //身份提供商ID if (StringUtils.isNoneBlank(idpId)) { builder.append(" AND uidp.idp_id = '").append(idpId).append("'"); @@ -65,8 +66,13 @@ public class UserIdpRepositoryCustomizedImpl implements UserIdpRepositoryCustomi } //@formatter:on String sql = builder.toString(); - UserIdpBindPo userIdpBindPo = jdbcTemplate.queryForObject(sql, new UserIdpBindPoMapper()); - return Optional.ofNullable(userIdpBindPo); + try { + UserIdpBindPo userIdpBindPo = jdbcTemplate.queryForObject(sql, + new UserIdpBindPoMapper()); + return Optional.ofNullable(userIdpBindPo); + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + } } /** @@ -79,7 +85,7 @@ public class UserIdpRepositoryCustomizedImpl implements UserIdpRepositoryCustomi @Override public Optional findByIdpIdAndUserId(String idpId, Long userId) { //@formatter:off - StringBuilder builder = new StringBuilder("SELECT uidp.*,`user`.username_,idp.name_ as idp_name FROM user_idp_bind uidp LEFT JOIN `user` ON uidp.user_id = `user`.id_ LEFT JOIN identity_provider idp ON uidp.idp_id = idp.id_ WHERE 1=1"); + StringBuilder builder = new StringBuilder("SELECT uidp.*,`user`.username_,idp.name_ as idp_name FROM user_idp_bind uidp LEFT JOIN `user` ON uidp.user_id = `user`.id_ AND `user`.is_deleted = '0' LEFT JOIN identity_provider idp ON uidp.idp_id = idp.id_ AND idp.is_deleted = '0' WHERE uidp.is_deleted = '0' "); //身份提供商ID if (StringUtils.isNoneBlank(idpId)) { builder.append(" AND uidp.idp_id = '").append(idpId).append("'"); @@ -90,8 +96,13 @@ public class UserIdpRepositoryCustomizedImpl implements UserIdpRepositoryCustomi } //@formatter:on String sql = builder.toString(); - UserIdpBindPo userIdpBindPo = jdbcTemplate.queryForObject(sql, new UserIdpBindPoMapper()); - return Optional.ofNullable(userIdpBindPo); + try { + UserIdpBindPo userIdpBindPo = jdbcTemplate.queryForObject(sql, + new UserIdpBindPoMapper()); + return Optional.ofNullable(userIdpBindPo); + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + } } /** @@ -103,7 +114,7 @@ public class UserIdpRepositoryCustomizedImpl implements UserIdpRepositoryCustomi @Override public Iterable getUserIdpBindList(Long userId) { //@formatter:off - StringBuilder builder = new StringBuilder("SELECT uidp.*,idp.name_ as idp_name FROM user_idp_bind uidp LEFT JOIN identity_provider idp ON uidp.idp_id = idp.id_ WHERE 1=1"); + StringBuilder builder = new StringBuilder("SELECT uidp.*,idp.name_ as idp_name FROM user_idp_bind uidp LEFT JOIN identity_provider idp ON uidp.idp_id = idp.id_ AND idp.is_deleted = '0' WHERE uidp.is_deleted = '0' "); //用户ID if (Objects.nonNull(userId)) { builder.append(" AND uidp.user_id = '").append(userId).append("'"); diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/impl/UserRepositoryCustomizedImpl.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/impl/UserRepositoryCustomizedImpl.java index 6d0e385b..763bbe77 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/impl/UserRepositoryCustomizedImpl.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/impl/UserRepositoryCustomizedImpl.java @@ -68,7 +68,7 @@ public class UserRepositoryCustomizedImpl implements UserRepositoryCustomized { @Override public Page getUserList(UserListQuery query, Pageable pageable) { //@formatter:off - StringBuilder builder = new StringBuilder("SELECT `user`.id_, `user`.username_,`user`.password_, `user`.email_, `user`.phone_,`user`.phone_area_code, `user`.full_name ,`user`.nick_name, `user`.avatar_ , `user`.status_, `user`.data_origin, `user`.email_verified, `user`.phone_verified, `user`.shared_secret, `user`.totp_bind , `user`.auth_total, `user`.last_auth_ip, `user`.last_auth_time, `user`.expand_, `user`.external_id , `user`.expire_date,`user`.create_by, `user`.create_time, `user`.update_by , `user`.update_time, `user`.remark_, group_concat(organization_.display_path) AS org_display_path FROM `user` INNER JOIN `organization_member` ON (`user`.id_ = organization_member.user_id) INNER JOIN `organization` organization_ ON (organization_.id_ = organization_member.org_id) WHERE 1=1"); + StringBuilder builder = new StringBuilder("SELECT `user`.id_, `user`.username_,`user`.password_, `user`.email_, `user`.phone_,`user`.phone_area_code, `user`.full_name ,`user`.nick_name, `user`.avatar_ , `user`.status_, `user`.data_origin, `user`.email_verified, `user`.phone_verified, `user`.shared_secret, `user`.totp_bind , `user`.auth_total, `user`.last_auth_ip, `user`.last_auth_time, `user`.expand_, `user`.external_id , `user`.expire_date,`user`.create_by, `user`.create_time, `user`.update_by , `user`.update_time, `user`.remark_, group_concat(organization_.display_path) AS org_display_path FROM `user` INNER JOIN `organization_member` ON (`user`.id_ = organization_member.user_id) INNER JOIN `organization` organization_ ON (organization_.id_ = organization_member.org_id) WHERE `user`.is_deleted = 0"); //组织条件 if (StringUtils.isNoneBlank(query.getOrganizationId())) { //包含子节点 @@ -118,7 +118,7 @@ public class UserRepositoryCustomizedImpl implements UserRepositoryCustomized { } /** - * 获取用户组成员列表 + * 获取用户组不存在成员列表 * * @param query {@link UserListNotInGroupQuery} * @param pageable {@link Pageable} @@ -127,48 +127,52 @@ public class UserRepositoryCustomizedImpl implements UserRepositoryCustomized { @Override public Page getUserListNotInGroupId(UserListNotInGroupQuery query, Pageable pageable) { //@formatter:off - StringBuilder builder = new StringBuilder("SELECT\n" + - " \t`user`.id_,\n" + - " \t`user`.username_,\n" + - " \t`user`.password_,\n" + - " \t`user`.email_,\n" + - " \t`user`.phone_,\n" + - " \t`user`.phone_area_code,\n" + - " \t`user`.full_name,\n" + - " \t`user`.nick_name,\n" + - " \t`user`.avatar_,\n" + - " \t`user`.status_,\n" + - " \t`user`.data_origin,\n" + - " \t`user`.email_verified,\n" + - " \t`user`.phone_verified,\n" + - " \t`user`.shared_secret,\n" + - " \t`user`.totp_bind,\n" + - " \t`user`.auth_total,\n" + - " \t`user`.last_auth_ip,\n" + - " \t`user`.last_auth_time,\n" + - " \t`user`.expand_,\n" + - " \t`user`.external_id,\n" + - " \t`user`.expire_date,\n" + - " \t`user`.create_by,\n" + - " \t`user`.create_time,\n" + - " \t`user`.update_by,\n" + - " \t`user`.update_time,\n" + - " \t`user`.remark_,\n" + - " \tgroup_concat( organization_.display_path ) AS org_display_path \n" + - " FROM\n" + - " `user` \n" + - " LEFT JOIN `organization_member` ON ( `user`.id_ = organization_member.user_id )\n" + - " LEFT JOIN `organization` organization_ ON ( organization_.id_ = organization_member.org_id ) \n" + - " WHERE\n" + - " \tuser.id_ NOT IN (\n" + - " \tSELECT\n" + - " \t\tu.id_ \n" + - " \tFROM\n" + - " \t\tuser u\n" + - " \t\tINNER JOIN user_group_member ugm ON ugm.user_id = u.id_\n" + - " \t\tINNER JOIN user_group ug ON ug.id_ = ugm.group_id \n" + - " \tWHERE\n" + - " \tug.id_ = '%s' AND ugm.group_id = '%s')".formatted(query.getId(), query.getId())); + StringBuilder builder = new StringBuilder( + """ + SELECT + `user`.id_, + `user`.username_, + `user`.password_, + `user`.email_, + `user`.phone_, + `user`.phone_area_code, + `user`.full_name, + `user`.nick_name, + `user`.avatar_, + `user`.status_, + `user`.data_origin, + `user`.email_verified, + `user`.phone_verified, + `user`.shared_secret, + `user`.totp_bind, + `user`.auth_total, + `user`.last_auth_ip, + `user`.last_auth_time, + `user`.expand_, + `user`.external_id, + `user`.expire_date, + `user`.create_by, + `user`.create_time, + `user`.update_by, + `user`.update_time, + `user`.remark_, + group_concat( organization_.display_path ) AS org_display_path + FROM `user` + LEFT JOIN `organization_member` ON ( `user`.id_ = organization_member.user_id AND organization_member.is_deleted = '0' ) + LEFT JOIN `organization` organization_ ON ( organization_.id_ = organization_member.org_id AND organization_.is_deleted = '0' ) + WHERE + user.is_deleted = 0 AND + user.id_ NOT IN ( + SELECT + u.id_ + FROM + user u + INNER JOIN user_group_member ugm ON ugm.user_id = u.id_ + INNER JOIN user_group ug ON ug.id_ = ugm.group_id + WHERE + u.is_deleted = '0' + AND ug.id_ = '%s' AND ugm.group_id = '%s') + """.formatted(query.getId(), query.getId())); if (StringUtils.isNoneBlank(query.getKeyword())) { builder.append(" AND user.username_ LIKE '%").append(query.getKeyword()).append("%'"); builder.append(" OR user.full_name LIKE '%").append(query.getKeyword()).append("%'"); diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/impl/mapper/UserIdpBindPoMapper.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/impl/mapper/UserIdpBindPoMapper.java index d370c7ad..6e844753 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/impl/mapper/UserIdpBindPoMapper.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/account/impl/mapper/UserIdpBindPoMapper.java @@ -25,7 +25,6 @@ import org.springframework.jdbc.core.RowMapper; import org.springframework.lang.NonNull; import cn.topiam.employee.common.entity.account.po.UserIdpBindPo; -import cn.topiam.employee.common.enums.IdentityProviderType; /** * @author TopIAM @@ -52,7 +51,7 @@ public class UserIdpBindPoMapper implements RowMapper { userIdpBindPo.setUserId(rs.getLong("user_id")); userIdpBindPo.setOpenId(rs.getString("open_id")); userIdpBindPo.setIdpId(rs.getString("idp_id")); - userIdpBindPo.setIdpType(IdentityProviderType.getType(rs.getString("idp_type"))); + userIdpBindPo.setIdpType(rs.getString("idp_type")); userIdpBindPo.setBindTime(rs.getTimestamp("bind_time").toLocalDateTime()); userIdpBindPo.setAdditionInfo(rs.getString("addition_info")); if (isExistColumn(rs, "username_")) { diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppAccessPolicyRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppAccessPolicyRepository.java index 6232c5cf..21a0fe8a 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppAccessPolicyRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppAccessPolicyRepository.java @@ -19,12 +19,17 @@ package cn.topiam.employee.common.repository.app; import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import cn.topiam.employee.common.entity.app.AppAccessPolicyEntity; import cn.topiam.employee.common.enums.PolicySubjectType; +import cn.topiam.employee.support.repository.LogicDeleteRepository; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; /** * 应用授权策略 Repository @@ -33,7 +38,8 @@ import cn.topiam.employee.common.enums.PolicySubjectType; * Created by support@topiam.cn on 2022/6/4 19:54 */ @Repository -public interface AppAccessPolicyRepository extends JpaRepository, +public interface AppAccessPolicyRepository extends + LogicDeleteRepository, QuerydslPredicateExecutor, AppAccessPolicyRepositoryCustomized { /** @@ -41,7 +47,11 @@ public interface AppAccessPolicyRepository extends JpaRepository, +public interface AppAccountRepository extends LogicDeleteRepository, QuerydslPredicateExecutor, AppAccountRepositoryCustomized { /** @@ -78,7 +83,11 @@ public interface AppAccountRepository extends JpaRepository, +public interface AppCertRepository extends LogicDeleteRepository, QuerydslPredicateExecutor { /** * 根据应用ID查询证书 @@ -77,7 +82,11 @@ public interface AppCertRepository extends JpaRepository, * @param appId {@link Long} */ @CacheEvict(allEntries = true) - void deleteByAppId(Long appId); + @Modifying + @Transactional(rollbackFor = Exception.class) + @Query(value = "UPDATE app_cert SET " + SOFT_DELETE_SET + + " WHERE app_id = :appId", nativeQuery = true) + void deleteByAppId(@Param("appId") Long appId); /** * find by id diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppFormConfigRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppFormConfigRepository.java new file mode 100644 index 00000000..3ab1efee --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppFormConfigRepository.java @@ -0,0 +1,85 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.repository.app; + +import java.util.Optional; + +import org.jetbrains.annotations.NotNull; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.querydsl.QuerydslPredicateExecutor; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import cn.topiam.employee.common.entity.app.AppFormConfigEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; +import static cn.topiam.employee.common.constants.ProtocolConstants.FORM_CONFIG_CACHE_NAME; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; + +/** + * @author TopIAM + */ +@Repository +@CacheConfig(cacheNames = { FORM_CONFIG_CACHE_NAME }) +public interface AppFormConfigRepository extends LogicDeleteRepository, + QuerydslPredicateExecutor, + AppFormConfigRepositoryCustomized { + /** + * 按应用 ID 删除 + * + * @param appId {@link Long} + */ + @CacheEvict(allEntries = true) + @Modifying + @Transactional(rollbackFor = Exception.class) + @Query(value = "UPDATE app_form_config SET " + SOFT_DELETE_SET + + " WHERE app_id = :appId", nativeQuery = true) + void deleteByAppId(@Param("appId") Long appId); + + /** + * delete + * + * @param id must not be {@literal null}. + */ + @CacheEvict(allEntries = true) + @Override + void deleteById(@NotNull Long id); + + /** + * save + * + * @param entity must not be {@literal null}. + * @param {@link S} + * @return {@link AppFormConfigEntity} + */ + @NotNull + @Override + @CacheEvict(allEntries = true) + S save(@NotNull S entity); + + /** + * 根据应用ID获取配置 + * + * @param appId {@link Long} + * @return {@link AppFormConfigEntity} + */ + Optional findByAppId(Long appId); +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppFormConfigRepositoryCustomized.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppFormConfigRepositoryCustomized.java new file mode 100644 index 00000000..27433d7a --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppFormConfigRepositoryCustomized.java @@ -0,0 +1,50 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.repository.app; + +import cn.topiam.employee.common.entity.app.po.AppFormConfigPO; + +/** + * @author TopIAM + * Created by support@topiam.cn on 2022/12/13 22:58 + */ +public interface AppFormConfigRepositoryCustomized { + /** + * 根据应用ID获取 + * + * @param appId {@link Long} + * @return {@link AppFormConfigPO} + */ + AppFormConfigPO getByAppId(Long appId); + + /** + * 根据应用 Client 获取 + * + * @param clientId {@link String} + * @return {@link AppFormConfigPO} + */ + AppFormConfigPO getByClientId(String clientId); + + /** + * 根据应用编码查询应用配置 + * + * @param appCode {@link String} + * @return {@link AppFormConfigPO} + */ + AppFormConfigPO findByAppCode(String appCode); +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppOidcConfigRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppOidcConfigRepository.java index 827d834c..e513fbf0 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppOidcConfigRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppOidcConfigRepository.java @@ -22,19 +22,24 @@ import java.util.Optional; import org.jetbrains.annotations.NotNull; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; -import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import cn.topiam.employee.common.entity.app.AppOidcConfigEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; import static cn.topiam.employee.common.constants.ProtocolConstants.OIDC_CONFIG_CACHE_NAME; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; /** * @author TopIAM */ @Repository @CacheConfig(cacheNames = { OIDC_CONFIG_CACHE_NAME }) -public interface AppOidcConfigRepository extends JpaRepository, +public interface AppOidcConfigRepository extends LogicDeleteRepository, QuerydslPredicateExecutor, AppOidcConfigRepositoryCustomized { /** @@ -43,7 +48,11 @@ public interface AppOidcConfigRepository extends JpaRepository, - PagingAndSortingRepository, + LogicDeleteRepository, QuerydslPredicateExecutor { /** * findAllByResource diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppPermissionPolicyRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppPermissionPolicyRepository.java index 4febb017..1fe46923 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppPermissionPolicyRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppPermissionPolicyRepository.java @@ -23,13 +23,13 @@ import java.util.Collection; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import cn.topiam.employee.common.entity.app.AppPermissionPolicyEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; /** * @author TopIAM @@ -37,29 +37,40 @@ import cn.topiam.employee.common.entity.app.AppPermissionPolicyEntity; */ @Repository public interface AppPermissionPolicyRepository extends AppPermissionPolicyRepositoryCustomized, - CrudRepository, - PagingAndSortingRepository, + LogicDeleteRepository, QuerydslPredicateExecutor { /** * 按主体 ID 删除所有 * * @param subjectIds {@link String} */ - void deleteAllBySubjectIdIn(Collection subjectIds); + @Modifying + @Transactional(rollbackFor = Exception.class) + @Query(value = "UPDATE app_permission_policy SET " + SOFT_DELETE_SET + + " WHERE subject_id IN (:subjectIds)", nativeQuery = true) + void deleteAllBySubjectIdIn(@Param("subjectIds") Collection subjectIds); /** * 按客体 ID 删除所有 * * @param objectIds {@link String} */ - void deleteAllByObjectIdIn(Collection objectIds); + @Modifying + @Transactional(rollbackFor = Exception.class) + @Query(value = "UPDATE app_permission_policy SET " + SOFT_DELETE_SET + + " WHERE object_id IN (:objectIds)", nativeQuery = true) + void deleteAllByObjectIdIn(@Param("objectIds") Collection objectIds); /** * 根据主体删除所有 * * @param objectId */ - void deleteAllByObjectId(Long objectId); + @Modifying + @Transactional(rollbackFor = Exception.class) + @Query(value = "UPDATE app_permission_policy SET " + SOFT_DELETE_SET + + " WHERE object_id = :objectId", nativeQuery = true) + void deleteAllByObjectId(@Param("objectId") Long objectId); /** * 更新启用/禁用 diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppPermissionResourceRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppPermissionResourceRepository.java index 18d7ba09..2b457f42 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppPermissionResourceRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppPermissionResourceRepository.java @@ -17,13 +17,18 @@ */ package cn.topiam.employee.common.repository.app; +import java.util.Optional; + +import org.jetbrains.annotations.NotNull; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import cn.topiam.employee.common.entity.app.AppPermissionResourceEntity; import cn.topiam.employee.common.repository.authorization.ResourceRepositoryCustomized; +import cn.topiam.employee.support.repository.LogicDeleteRepository; /** *

@@ -35,9 +40,18 @@ import cn.topiam.employee.common.repository.authorization.ResourceRepositoryCust */ @Repository public interface AppPermissionResourceRepository extends - CrudRepository, + LogicDeleteRepository, ResourceRepositoryCustomized, - PagingAndSortingRepository, QuerydslPredicateExecutor { + /** + * findByIdContainsDeleted + * + * @param id must not be {@literal null}. + * @return {@link AppPermissionResourceEntity} + */ + @NotNull + @Cacheable + @Query(value = "SELECT * FROM app_permission_resource WHERE id_ = :id", nativeQuery = true) + Optional findByIdContainsDeleted(@NotNull @Param(value = "id") Long id); } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppPermissionRoleRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppPermissionRoleRepository.java index 9a9b3c89..98266937 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppPermissionRoleRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppPermissionRoleRepository.java @@ -17,17 +17,20 @@ */ package cn.topiam.employee.common.repository.app; +import java.util.Optional; + +import org.jetbrains.annotations.NotNull; +import org.springframework.cache.annotation.Cacheable; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import cn.topiam.employee.common.entity.app.AppPermissionRoleEntity; import cn.topiam.employee.common.repository.authorization.RoleRepositoryCustomized; +import cn.topiam.employee.support.repository.LogicDeleteRepository; /** *

@@ -38,9 +41,8 @@ import cn.topiam.employee.common.repository.authorization.RoleRepositoryCustomiz * Created by support@topiam.cn on 2020-08-10 */ @Repository -public interface AppPermissionRoleRepository extends CrudRepository, - RoleRepositoryCustomized, - PagingAndSortingRepository, +public interface AppPermissionRoleRepository extends RoleRepositoryCustomized, + LogicDeleteRepository, QuerydslPredicateExecutor { /** * 更新角色状态 @@ -52,4 +54,15 @@ public interface AppPermissionRoleRepository extends CrudRepository findByIdContainsDeleted(@NotNull @Param(value = "id") Long id); } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppRepository.java index 2b8b75b2..26c8ceca 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppRepository.java @@ -23,11 +23,13 @@ import org.jetbrains.annotations.NotNull; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; -import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import cn.topiam.employee.common.entity.app.AppEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; import static cn.topiam.employee.common.constants.AppConstants.APP_CACHE_NAME; /** @@ -35,7 +37,7 @@ import static cn.topiam.employee.common.constants.AppConstants.APP_CACHE_NAME; */ @Repository @CacheConfig(cacheNames = { APP_CACHE_NAME }) -public interface AppRepository extends JpaRepository, +public interface AppRepository extends LogicDeleteRepository, QuerydslPredicateExecutor, AppRepositoryCustomized { /** * 根据应用ID查询已启用应用 @@ -76,7 +78,18 @@ public interface AppRepository extends JpaRepository, @NotNull @Override @Cacheable - Optional findById(@NotNull Long id); + Optional findById(@NotNull @Param(value = "id") Long id); + + /** + * find by id + * + * @param id must not be {@literal null}. + * @return {@link AppEntity} + */ + @NotNull + @Cacheable + @Query(value = "SELECT * FROM app WHERE id_ = :id", nativeQuery = true) + Optional findByIdContainsDeleted(@NotNull @Param(value = "id") Long id); /** * 根据clientId获取配置 diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppSaml2ConfigRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppSaml2ConfigRepository.java index 0e165738..eb7b6667 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppSaml2ConfigRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppSaml2ConfigRepository.java @@ -22,19 +22,24 @@ import java.util.Optional; import org.jetbrains.annotations.NotNull; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; -import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import cn.topiam.employee.common.entity.app.AppSaml2ConfigEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; import static cn.topiam.employee.common.constants.ProtocolConstants.SAML2_CONFIG_CACHE_NAME; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; /** * @author TopIAM */ @Repository @CacheConfig(cacheNames = { SAML2_CONFIG_CACHE_NAME }) -public interface AppSaml2ConfigRepository extends JpaRepository, +public interface AppSaml2ConfigRepository extends LogicDeleteRepository, QuerydslPredicateExecutor, AppSaml2ConfigRepositoryCustomized { /** @@ -43,7 +48,11 @@ public interface AppSaml2ConfigRepository extends JpaRepository. + */ +package cn.topiam.employee.common.repository.app; + +import java.util.Optional; + +import org.jetbrains.annotations.NotNull; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.querydsl.QuerydslPredicateExecutor; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import cn.topiam.employee.common.entity.app.AppTsaConfigEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; +import static cn.topiam.employee.common.constants.ProtocolConstants.TSA_CONFIG_CACHE_NAME; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; + +/** + * @author TopIAM + */ +@Repository +@CacheConfig(cacheNames = { TSA_CONFIG_CACHE_NAME }) +public interface AppTsaConfigRepository extends LogicDeleteRepository, + QuerydslPredicateExecutor, + AppTsaConfigRepositoryCustomized { + /** + * 按应用 ID 删除 + * + * @param appId {@link Long} + */ + @CacheEvict(allEntries = true) + @Modifying + @Transactional(rollbackFor = Exception.class) + @Query(value = "UPDATE app_tsa_config SET " + SOFT_DELETE_SET + + " WHERE app_id = :appId", nativeQuery = true) + void deleteByAppId(@Param("appId") Long appId); + + /** + * delete + * + * @param id must not be {@literal null}. + */ + @CacheEvict(allEntries = true) + @Override + void deleteById(@NotNull Long id); + + /** + * save + * + * @param entity must not be {@literal null}. + * @param {@link S} + * @return {@link AppTsaConfigEntity} + */ + @NotNull + @Override + @CacheEvict(allEntries = true) + S save(@NotNull S entity); + + /** + * 根据应用ID获取配置 + * + * @param appId {@link Long} + * @return {@link AppTsaConfigEntity} + */ + Optional findByAppId(Long appId); +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppTsaConfigRepositoryCustomized.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppTsaConfigRepositoryCustomized.java new file mode 100644 index 00000000..db8032bf --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/AppTsaConfigRepositoryCustomized.java @@ -0,0 +1,50 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.repository.app; + +import cn.topiam.employee.common.entity.app.po.AppTsaConfigPO; + +/** + * @author TopIAM + * Created by support@topiam.cn on 2022/01/14 09:58 + */ +public interface AppTsaConfigRepositoryCustomized { + /** + * 根据应用ID获取 + * + * @param appId {@link Long} + * @return {@link AppTsaConfigPO} + */ + AppTsaConfigPO getByAppId(Long appId); + + /** + * 根据应用 Client 获取 + * + * @param clientId {@link String} + * @return {@link AppTsaConfigPO} + */ + AppTsaConfigPO getByClientId(String clientId); + + /** + * 根据应用编码查询应用配置 + * + * @param appCode {@link String} + * @return {@link AppTsaConfigPO} + */ + AppTsaConfigPO findByAppCode(String appCode); +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppAccessPolicyRepositoryCustomizedImpl.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppAccessPolicyRepositoryCustomizedImpl.java index aeaaad44..1c64fe9f 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppAccessPolicyRepositoryCustomizedImpl.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppAccessPolicyRepositoryCustomizedImpl.java @@ -56,8 +56,46 @@ public class AppAccessPolicyRepositoryCustomizedImpl implements @Override public Page getAppPolicyList(AppAccessPolicyQuery query, Pageable pageable) { //@formatter:off - StringBuilder builder = new StringBuilder("SELECT a.id_,a.app_id,a.subject_id,a.subject_type,a.create_time,`subject`.name_,app.name_ as app_name,app.type_ as app_type,app.template_ as app_template,app.protocol_ as app_protocol FROM app_access_policy a LEFT JOIN app ON a.app_id = app.id_ LEFT JOIN "); - builder.append("(SELECT id_,name_ FROM user_group UNION ALL SELECT id_,name_ FROM organization UNION ALL SELECT id_,username_ as name_ FROM `user`) `subject` ON a.subject_id = `subject`.id_ WHERE 1=1"); + StringBuilder builder = new StringBuilder(""" + SELECT + a.id_, + a.app_id, + a.subject_id, + a.subject_type, + a.create_time, + subject.name_, + app.name_ AS app_name, + app.type_ AS app_type, + app.template_ AS app_template, + app.protocol_ AS app_protocol + FROM + app_access_policy a + LEFT JOIN app ON a.app_id = app.id_ AND app.is_deleted = '0' + LEFT JOIN + """); + builder.append(""" + ( SELECT + id_, + name_, + is_deleted + FROM + user_group UNION ALL + SELECT + id_, + name_, + is_deleted + FROM + organization UNION ALL + SELECT + id_, + username_ AS name_, + is_deleted + FROM + `user` + ) `subject` ON a.subject_id = `subject`.id_ AND `subject`.is_deleted = '0' + WHERE + a.is_deleted = '0' + """); if (ObjectUtils.isNotEmpty(query.getSubjectType())) { builder.append(" AND a.subject_type = '").append(query.getSubjectType().getCode()).append("'"); } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppAccountRepositoryCustomizedImpl.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppAccountRepositoryCustomizedImpl.java index 2f64341f..a1e4cb2b 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppAccountRepositoryCustomizedImpl.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppAccountRepositoryCustomizedImpl.java @@ -54,7 +54,25 @@ public class AppAccountRepositoryCustomizedImpl implements AppAccountRepositoryC @Override public Page getAppAccountList(AppAccountQuery query, Pageable pageable) { //@formatter:off - StringBuilder builder = new StringBuilder("SELECT a.id_,a.app_id,a.user_id,a.account_,a.create_time,u.username_,p.name_ as app_name,p.type_ as app_type,p.template_ as app_template,p.protocol_ as app_protocol FROM app_account a LEFT JOIN `user` u ON a.user_id = u.id_ LEFT JOIN app p ON a.app_id = p.id_ WHERE 1=1"); + StringBuilder builder = new StringBuilder(""" + SELECT + a.id_, + a.app_id, + a.user_id, + a.account_, + a.create_time, + u.username_, + p.name_ AS app_name, + p.type_ AS app_type, + p.template_ AS app_template, + p.protocol_ AS app_protocol + FROM + app_account a + LEFT JOIN `user` u ON a.user_id = u.id_ AND u.is_deleted = '0' + LEFT JOIN app p ON a.app_id = p.id_ AND p.is_deleted = '0' + WHERE + a.is_deleted = '0' + """); //用户名 if (StringUtils.isNoneBlank(query.getUsername())) { builder.append(" AND u.username_ like '%").append(query.getUsername()).append("%'"); diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppCasConfigRepositoryCustomizedImpl.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppCasConfigRepositoryCustomizedImpl.java index c3ce145b..28a910a4 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppCasConfigRepositoryCustomizedImpl.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppCasConfigRepositoryCustomizedImpl.java @@ -19,6 +19,7 @@ package cn.topiam.employee.common.repository.app.impl; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.Cacheable; +import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @@ -50,17 +51,25 @@ public class AppCasConfigRepositoryCustomizedImpl implements AppCasConfigReposit @Override @Cacheable(key = "#p0", unless = "#result==null") public AppCasConfigPO getByAppId(Long appId) { - String sql = "select acc.*,app.init_login_url,app.init_login_type,app.authorization_type,app.template_,app.code_,client_id,client_secret from app left join app_cas_config acc on app.id_ = acc.app_id where 1=1" + String sql = "select acc.*,app.init_login_url,app.init_login_type,app.authorization_type,app.template_,app.code_,client_id,client_secret from app left join app_cas_config acc on app.id_ = acc.app_id where acc.is_deleted=0" + " AND app_id = " + appId; - return jdbcTemplate.queryForObject(sql, new AppCasConfigPoMapper()); + try { + return jdbcTemplate.queryForObject(sql, new AppCasConfigPoMapper()); + } catch (EmptyResultDataAccessException e) { + return null; + } } @Override @Cacheable(key = "#p0", unless = "#result==null") public AppCasConfigPO findByAppCode(String appCode) { - String sql = "select acc.*,app.init_login_url,app.init_login_type,app.authorization_type,app.template_,app.code_,client_id,client_secret from app left join app_cas_config acc on app.id_ = acc.app_id where 1=1" + String sql = "select acc.*,app.init_login_url,app.init_login_type,app.authorization_type,app.template_,app.code_,client_id,client_secret from app left join app_cas_config acc on app.id_ = acc.app_id where acc.is_deleted=0" + " AND code_ = " + "'" + appCode + "'"; - return jdbcTemplate.queryForObject(sql, new AppCasConfigPoMapper()); + try { + return jdbcTemplate.queryForObject(sql, new AppCasConfigPoMapper()); + } catch (EmptyResultDataAccessException e) { + return null; + } } /** diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppFormConfigRepositoryCustomizedImpl.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppFormConfigRepositoryCustomizedImpl.java new file mode 100644 index 00000000..bfba5234 --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppFormConfigRepositoryCustomizedImpl.java @@ -0,0 +1,111 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.repository.app.impl; + +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import cn.topiam.employee.common.entity.app.po.AppFormConfigPO; +import cn.topiam.employee.common.repository.app.AppFormConfigRepositoryCustomized; +import cn.topiam.employee.common.repository.app.impl.mapper.AppFormConfigPoMapper; + +import lombok.AllArgsConstructor; +import static cn.topiam.employee.common.constants.ProtocolConstants.FORM_CONFIG_CACHE_NAME; + +/** + * + * @author TopIAM + * Created by support@topiam.cn on 2022/12/13 23:58 + */ +@Repository +@AllArgsConstructor +@CacheConfig(cacheNames = { FORM_CONFIG_CACHE_NAME }) +public class AppFormConfigRepositoryCustomizedImpl implements AppFormConfigRepositoryCustomized { + private static final String SELECT_SQL = """ + SELECT + afc.*, + app.init_login_url, + app.init_login_type, + app.authorization_type, + app.template_, + app.code_, + app.is_enabled, + app.client_id, + app.client_secret + FROM + app + LEFT JOIN app_form_config afc ON app.id_ = afc.app_id AND afc.is_deleted = '0' + WHERE + app.is_deleted = '0' + """; + + /** + * 根据应用ID获取 + * + * @param appId {@link Long} + * @return {@link AppFormConfigPO} + */ + @Override + @Cacheable(key = "#p0", unless = "#result==null") + public AppFormConfigPO getByAppId(Long appId) { + //@formatter:off + String sql = SELECT_SQL + " AND app_id = " + appId; + //@formatter:on + return jdbcTemplate.queryForObject(sql, new AppFormConfigPoMapper()); + } + + @Override + @Cacheable(key = "#p0", unless = "#result==null") + public AppFormConfigPO getByClientId(String clientId) { + //@formatter:off + try { + String sql = SELECT_SQL + " AND app.client_id = " + "'"+clientId+"'"; + return jdbcTemplate.queryForObject(sql, new AppFormConfigPoMapper()); + } catch (EmptyResultDataAccessException e){ + return null; + } + //@formatter:on + } + + /** + * 根据应用编码查询应用配置 + * + * @param appCode {@link String} + * @return {@link AppFormConfigPO} + */ + @Override + @Cacheable(key = "#p0", unless = "#result==null") + public AppFormConfigPO findByAppCode(String appCode) { + //@formatter:off + String sql = SELECT_SQL + " AND app.code_ = " + "'"+appCode+"'"; + //@formatter:on + try { + return jdbcTemplate.queryForObject(sql, new AppFormConfigPoMapper()); + } catch (EmptyResultDataAccessException e) { + return null; + } + } + + /** + * JdbcTemplate + */ + private final JdbcTemplate jdbcTemplate; +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppOidcConfigRepositoryCustomizedImpl.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppOidcConfigRepositoryCustomizedImpl.java index 83a32abc..dcdc6de4 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppOidcConfigRepositoryCustomizedImpl.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppOidcConfigRepositoryCustomizedImpl.java @@ -39,7 +39,23 @@ import static cn.topiam.employee.common.constants.ProtocolConstants.OIDC_CONFIG_ @AllArgsConstructor @CacheConfig(cacheNames = { OIDC_CONFIG_CACHE_NAME }) public class AppOidcConfigRepositoryCustomizedImpl implements AppOidcConfigRepositoryCustomized { - private final String SELECT_SQL = "SELECT aoc.*,app.init_login_url,app.init_login_type,app.authorization_type,app.template_,app.code_,app.is_enabled,app.client_id,app.client_secret from app left join app_oidc_config aoc on app.id_ = aoc.app_id where 1=1"; + private static final String SELECT_SQL = """ + SELECT + aoc.*, + app.init_login_url, + app.init_login_type, + app.authorization_type, + app.template_, + app.code_, + app.is_enabled, + app.client_id, + app.client_secret + FROM + app + LEFT JOIN app_oidc_config aoc ON app.id_ = aoc.app_id and aoc.is_deleted = '0' + WHERE + app.is_deleted = '0' + """; /** * 根据应用ID获取 diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppPermissionPolicyRepositoryCustomizedImpl.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppPermissionPolicyRepositoryCustomizedImpl.java index 69a43483..7e210f5d 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppPermissionPolicyRepositoryCustomizedImpl.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppPermissionPolicyRepositoryCustomizedImpl.java @@ -45,7 +45,7 @@ public class AppPermissionPolicyRepositoryCustomizedImpl implements AppPermissionPolicyRepositoryCustomized { private String leftJoin(String table, String condition) { - return " LEFT JOIN " + table + " ON " + condition; + return " LEFT JOIN " + table + " ON " + condition + " AND " + table + ".is_deleted = '0' "; } @Override @@ -53,7 +53,7 @@ public class AppPermissionPolicyRepositoryCustomizedImpl implements //查询条件 //@formatter:off // 所属应用 - StringBuilder where = new StringBuilder("WHERE policy.app_id = '").append(query.getAppId()).append("' "); + StringBuilder where = new StringBuilder("WHERE policy.is_deleted = '0' AND policy.app_id = '").append(query.getAppId()).append("' "); // 主体类型 where.append(" AND policy.subject_type = '").append(query.getSubjectType().getCode()).append("' "); // 客体类型 diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppRepositoryCustomizedImpl.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppRepositoryCustomizedImpl.java index 6cd0b4cd..4a44f43e 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppRepositoryCustomizedImpl.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppRepositoryCustomizedImpl.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; +import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; @@ -62,9 +63,13 @@ public class AppRepositoryCustomizedImpl implements AppRepositoryCustomized { @Override @Transactional(rollbackFor = Exception.class) public Integer updateAppStatus(Long id, Boolean enabled) { - StringBuilder builder = new StringBuilder("UPDATE app SET is_enabled=?where id_=?"); + StringBuffer builder = new StringBuffer("UPDATE app SET is_enabled=?where id_=?"); //@formatter:on - return jdbcTemplate.queryForObject(builder.toString(), Integer.class, enabled, id); + try { + return jdbcTemplate.queryForObject(builder.toString(), Integer.class, enabled, id); + } catch (EmptyResultDataAccessException e) { + return null; + } } /** @@ -90,7 +95,23 @@ public class AppRepositoryCustomizedImpl implements AppRepositoryCustomized { Map paramMap = new HashMap<>(16); paramMap.put("subjectIds", paramList); //@formatter:off - StringBuilder builder = new StringBuilder("SELECT distinct app.* from app LEFT JOIN app_access_policy app_acce ON app.id_ = app_acce.app_id WHERE app_acce.subject_id in (:subjectIds) "); + StringBuilder builder = new StringBuilder(""" + SELECT DISTINCT + app.* + FROM + app + LEFT JOIN app_access_policy app_acce ON app.id_ = app_acce.app_id AND app_acce.is_deleted = '0' + WHERE + app.is_enabled = 1 + AND app.is_deleted = '0' + AND app_acce.subject_id IN (:subjectIds) + """); + //用户名 + if (StringUtils.isNoneBlank(name)) { + builder.append(" AND app.name_ like '%").append(name).append("%'"); + } + //或者是全员可访问的应用 + builder.append(" OR app.authorization_type = '").append("all_access'"); //用户名 if (StringUtils.isNoneBlank(name)) { builder.append(" AND app.name_ like '%").append(name).append("%'"); diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppSaml2ConfigRepositoryCustomizedImpl.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppSaml2ConfigRepositoryCustomizedImpl.java index e723b7c8..d70a4371 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppSaml2ConfigRepositoryCustomizedImpl.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppSaml2ConfigRepositoryCustomizedImpl.java @@ -19,6 +19,7 @@ package cn.topiam.employee.common.repository.app.impl; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.Cacheable; +import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @@ -48,20 +49,58 @@ public class AppSaml2ConfigRepositoryCustomizedImpl implements AppSaml2ConfigRep @Cacheable(key = "#p0", unless = "#result==null") public AppSaml2ConfigPO getByAppId(Long appId) { //@formatter:off - String sql = "select as2c.*,app.init_login_url,app.init_login_type,app.authorization_type,app.template_,app.code_,client_id,client_secret from app left join app_saml2_config as2c on app.id_ = as2c.app_id where 1=1" + String sql = """ + SELECT + as2c.*, + app.init_login_url, + app.init_login_type, + app.authorization_type, + app.template_, + app.code_, + client_id, + client_secret + FROM + app + LEFT JOIN app_saml2_config as2c ON app.id_ = as2c.app_id AND as2c.is_deleted = '0' + WHERE + app.is_deleted = '0' + """ + " AND app_id = " + appId; //@formatter:on - return jdbcTemplate.queryForObject(sql, new AppSaml2ConfigPoMapper()); + try { + return jdbcTemplate.queryForObject(sql, new AppSaml2ConfigPoMapper()); + } catch (EmptyResultDataAccessException e) { + return null; + } } @Override @Cacheable(key = "#p0", unless = "#result==null") public AppSaml2ConfigPO findByAppCode(String appCode) { //@formatter:off - String sql = "select as2c.*,app.init_login_url,app.init_login_type,app.authorization_type,app.template_,app.code_,client_id,client_secret from app left join app_saml2_config as2c on app.id_ = as2c.app_id where 1=1" + String sql = """ + SELECT + as2c.*, + app.init_login_url, + app.init_login_type, + app.authorization_type, + app.template_, + app.code_, + client_id, + client_secret + FROM + app + LEFT JOIN app_saml2_config as2c ON app.id_ = as2c.app_id and as2c.is_deleted = '0' + WHERE + app.is_deleted = '0' + """ + " AND code_ = " + "'"+appCode+"'"; //@formatter:on - return jdbcTemplate.queryForObject(sql, new AppSaml2ConfigPoMapper()); + try { + return jdbcTemplate.queryForObject(sql, new AppSaml2ConfigPoMapper()); + } catch (EmptyResultDataAccessException e) { + return null; + } } /** diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppTsaConfigRepositoryCustomizedImpl.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppTsaConfigRepositoryCustomizedImpl.java new file mode 100644 index 00000000..9c117b1d --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/AppTsaConfigRepositoryCustomizedImpl.java @@ -0,0 +1,111 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.repository.app.impl; + +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import cn.topiam.employee.common.entity.app.po.AppTsaConfigPO; +import cn.topiam.employee.common.repository.app.AppTsaConfigRepositoryCustomized; +import cn.topiam.employee.common.repository.app.impl.mapper.AppTsaConfigPoMapper; + +import lombok.AllArgsConstructor; +import static cn.topiam.employee.common.constants.ProtocolConstants.TSA_CONFIG_CACHE_NAME; + +/** + * + * @author TopIAM + * Created by support@topiam.cn on 2022/01/14 10:58 + */ +@Repository +@AllArgsConstructor +@CacheConfig(cacheNames = { TSA_CONFIG_CACHE_NAME }) +public class AppTsaConfigRepositoryCustomizedImpl implements AppTsaConfigRepositoryCustomized { + private static final String SELECT_SQL = """ + SELECT + afc.*, + app.init_login_url, + app.init_login_type, + app.authorization_type, + app.template_, + app.code_, + app.is_enabled, + app.client_id, + app.client_secret + FROM + app + LEFT JOIN app_tsa_config atc ON app.id_ = atc.app_id AND atc.is_deleted = '0' + WHERE + app.is_deleted = '0' + """; + + /** + * 根据应用ID获取 + * + * @param appId {@link Long} + * @return {@link AppTsaConfigPO} + */ + @Override + @Cacheable(key = "#p0", unless = "#result==null") + public AppTsaConfigPO getByAppId(Long appId) { + //@formatter:off + String sql = SELECT_SQL + " AND app_id = " + appId; + //@formatter:on + return jdbcTemplate.queryForObject(sql, new AppTsaConfigPoMapper()); + } + + @Override + @Cacheable(key = "#p0", unless = "#result==null") + public AppTsaConfigPO getByClientId(String clientId) { + //@formatter:off + try { + String sql = SELECT_SQL + " AND app.client_id = " + "'"+clientId+"'"; + return jdbcTemplate.queryForObject(sql, new AppTsaConfigPoMapper()); + } catch (EmptyResultDataAccessException e){ + return null; + } + //@formatter:on + } + + /** + * 根据应用编码查询应用配置 + * + * @param appCode {@link String} + * @return {@link AppTsaConfigPO} + */ + @Override + @Cacheable(key = "#p0", unless = "#result==null") + public AppTsaConfigPO findByAppCode(String appCode) { + //@formatter:off + String sql = SELECT_SQL + " AND app.code_ = " + "'"+appCode+"'"; + //@formatter:on + try { + return jdbcTemplate.queryForObject(sql, new AppTsaConfigPoMapper()); + } catch (EmptyResultDataAccessException e) { + return null; + } + } + + /** + * JdbcTemplate + */ + private final JdbcTemplate jdbcTemplate; +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/mapper/AppCasConfigPoMapper.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/mapper/AppCasConfigPoMapper.java index f256dc3c..e399bc60 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/mapper/AppCasConfigPoMapper.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/mapper/AppCasConfigPoMapper.java @@ -17,15 +17,16 @@ */ package cn.topiam.employee.common.repository.app.impl.mapper; +import cn.topiam.employee.common.entity.app.po.AppCasConfigPO; +import cn.topiam.employee.common.enums.app.AuthorizationType; +import cn.topiam.employee.common.enums.app.CasUserIdentityType; +import cn.topiam.employee.common.enums.app.InitLoginType; +import org.springframework.jdbc.core.RowMapper; + import java.sql.ResultSet; import java.sql.SQLException; import java.time.LocalDateTime; -import org.springframework.jdbc.core.RowMapper; - -import cn.topiam.employee.common.entity.app.po.AppCasConfigPO; -import cn.topiam.employee.common.enums.app.InitLoginType; - /** * AppCasConfigPOPOMapper * @@ -36,21 +37,26 @@ public class AppCasConfigPoMapper implements RowMapper { @Override public AppCasConfigPO mapRow(ResultSet rs, int rowNum) throws SQLException { - AppCasConfigPO configPO = new AppCasConfigPO(); - configPO.setAppId(rs.getLong("id_")); - configPO.setAppId(rs.getLong("app_id")); - configPO.setAppCode(rs.getString("code_")); - configPO.setClientId(rs.getString("client_id")); - configPO.setClientSecret(rs.getString("client_secret")); - configPO.setInitLoginType(InitLoginType.getType(rs.getString("init_login_type"))); - configPO.setInitLoginUrl(rs.getString("init_login_url")); - configPO.setAppTemplate(rs.getString("template_")); - configPO.setCreateBy(rs.getString("create_by")); - configPO.setCreateTime(rs.getObject("create_time", LocalDateTime.class)); - configPO.setUpdateBy(rs.getString("update_by")); - configPO.setCreateTime(rs.getObject("update_time", LocalDateTime.class)); - configPO.setRemark(rs.getString("remark_")); - configPO.setSpCallbackUrl(rs.getString("sp_callback_url")); - return configPO; + AppCasConfigPO configPo = new AppCasConfigPO(); + configPo.setAppId(rs.getLong("id_")); + configPo.setAppId(rs.getLong("app_id")); + configPo.setAppCode(rs.getString("code_")); + configPo.setClientId(rs.getString("client_id")); + configPo.setClientSecret(rs.getString("client_secret")); + configPo.setInitLoginType(InitLoginType.getType(rs.getString("init_login_type"))); + configPo.setInitLoginUrl(rs.getString("init_login_url")); + configPo + .setAuthorizationType(AuthorizationType.getType(rs.getString("authorization_type"))); + configPo.setAppTemplate(rs.getString("template_")); + configPo.setCreateBy(rs.getString("create_by")); + configPo.setCreateTime(rs.getObject("create_time", LocalDateTime.class)); + configPo.setUpdateBy(rs.getString("update_by")); + configPo.setCreateTime(rs.getObject("update_time", LocalDateTime.class)); + configPo.setRemark(rs.getString("remark_")); + configPo.setClientServiceUrl(rs.getString("client_service_url")); + configPo.setServiceTicketExpireTime(rs.getInt("service_ticket_expire_time")); + configPo + .setUserIdentityType(CasUserIdentityType.getType(rs.getString("user_identity_type"))); + return configPo; } } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/mapper/AppFormConfigPoMapper.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/mapper/AppFormConfigPoMapper.java new file mode 100644 index 00000000..e4e66f13 --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/mapper/AppFormConfigPoMapper.java @@ -0,0 +1,91 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.repository.app.impl.mapper; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.util.Objects; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.jdbc.core.RowMapper; + +import com.alibaba.fastjson2.JSON; + +import cn.topiam.employee.common.entity.app.AppFormConfigEntity; +import cn.topiam.employee.common.entity.app.po.AppFormConfigPO; +import cn.topiam.employee.common.enums.app.AuthorizationType; +import cn.topiam.employee.common.enums.app.FormSubmitType; +import cn.topiam.employee.common.enums.app.InitLoginType; + +/** + * @author TopIAM + * Created by support@topiam.cn on 2022/12/13 22:58 + */ +@SuppressWarnings("DuplicatedCode") +public class AppFormConfigPoMapper implements RowMapper { + + /** + * Implementations must implement this method to map each row of data + * in the ResultSet. This method should not call {@code next()} on + * the ResultSet; it is only supposed to map values of the current row. + * + * @param rs the ResultSet to map (pre-initialized for the current row) + * @param rowNum the number of the current row + * @return the result object for the current row (may be {@code null}) + * @throws SQLException if an SQLException is encountered getting + * column values (that is, there's no need to catch SQLException) + */ + @Override + public AppFormConfigPO mapRow(ResultSet rs, int rowNum) throws SQLException { + //@formatter:off + AppFormConfigPO appForm = new AppFormConfigPO(); + appForm.setAppId(rs.getLong("app_id")); + //应用表相关 + appForm.setAppCode(rs.getString("code_")); + appForm.setAppTemplate(rs.getString("template_")); + appForm.setEnabled(rs.getBoolean("is_enabled")); + appForm.setClientId(rs.getString("client_id")); + appForm.setClientSecret(rs.getString("client_secret")); + appForm.setInitLoginType(InitLoginType.getType(rs.getString("init_login_type"))); + appForm.setInitLoginUrl(rs.getString("init_login_url")); + appForm.setAuthorizationType(AuthorizationType.getType(rs.getString("authorization_type"))); + //配置相关 + appForm.setLoginUrl(rs.getString("login_url")); + appForm.setUsernameField(rs.getString("username_field")); + appForm.setPasswordField(rs.getString("password_field")); + String submitType = rs.getString("submit_type"); + if (!Objects.isNull(submitType)){ + appForm.setSubmitType(FormSubmitType.getType(submitType)); + } + String otherField = rs.getString("other_field"); + if (StringUtils.isNotBlank(otherField)){ + appForm.setOtherField(JSON.parseArray(rs.getString("other_field")) + .toList(AppFormConfigEntity.OtherField.class)); + } + //创建修改相关 + appForm.setCreateBy(rs.getString("create_by")); + appForm.setCreateTime(rs.getObject("create_time", LocalDateTime.class)); + appForm.setUpdateBy(rs.getString("update_by")); + appForm.setCreateTime(rs.getObject("update_time", LocalDateTime.class)); + appForm.setRemark(rs.getString("remark_")); + return appForm; + //@formatter:on + } + +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/mapper/AppOidcConfigPoMapper.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/mapper/AppOidcConfigPoMapper.java index dcd317b3..522faba2 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/mapper/AppOidcConfigPoMapper.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/mapper/AppOidcConfigPoMapper.java @@ -51,41 +51,41 @@ public class AppOidcConfigPoMapper implements RowMapper { @Override public AppOidcConfigPO mapRow(ResultSet rs, int rowNum) throws SQLException { //@formatter:off - AppOidcConfigPO appAccount = new AppOidcConfigPO(); - appAccount.setAppId(rs.getLong("id_")); - appAccount.setAppId(rs.getLong("app_id")); + AppOidcConfigPO appOidc = new AppOidcConfigPO(); + appOidc.setAppId(rs.getLong("id_")); + appOidc.setAppId(rs.getLong("app_id")); //应用表相关 - appAccount.setAppCode(rs.getString("code_")); - appAccount.setAppTemplate(rs.getString("template_")); - appAccount.setEnabled(rs.getBoolean("is_enabled")); - appAccount.setClientId(rs.getString("client_id")); - appAccount.setClientSecret(rs.getString("client_secret")); - appAccount.setInitLoginType(InitLoginType.getType(rs.getString("init_login_type"))); - appAccount.setInitLoginUrl(rs.getString("init_login_url")); - appAccount.setAuthorizationType(AuthorizationType.getType(rs.getString("authorization_type"))); + appOidc.setAppCode(rs.getString("code_")); + appOidc.setAppTemplate(rs.getString("template_")); + appOidc.setEnabled(rs.getBoolean("is_enabled")); + appOidc.setClientId(rs.getString("client_id")); + appOidc.setClientSecret(rs.getString("client_secret")); + appOidc.setInitLoginType(InitLoginType.getType(rs.getString("init_login_type"))); + appOidc.setInitLoginUrl(rs.getString("init_login_url")); + appOidc.setAuthorizationType(AuthorizationType.getType(rs.getString("authorization_type"))); //配置相关 - appAccount.setClientAuthMethods(JSONObject.parseObject(rs.getString("client_auth_methods"), Set.class)); - appAccount.setAuthGrantTypes(JSONObject.parseObject(rs.getString("auth_grant_types"), Set.class)); - appAccount.setResponseTypes(JSONObject.parseObject(rs.getString("response_types"), Set.class)); - appAccount.setRedirectUris(JSONObject.parseObject(rs.getString("redirect_uris"), Set.class)); - appAccount.setGrantScopes(JSONObject.parseObject(rs.getString("grant_scopes"), Set.class)); - appAccount.setRequireAuthConsent(rs.getBoolean("require_auth_consent")); - appAccount.setRequireProofKey(rs.getBoolean("require_proof_key")); - appAccount.setTokenEndpointAuthSigningAlgorithm( + appOidc.setClientAuthMethods(JSONObject.parseObject(rs.getString("client_auth_methods"), Set.class)); + appOidc.setAuthGrantTypes(JSONObject.parseObject(rs.getString("auth_grant_types"), Set.class)); + appOidc.setResponseTypes(JSONObject.parseObject(rs.getString("response_types"), Set.class)); + appOidc.setRedirectUris(JSONObject.parseObject(rs.getString("redirect_uris"), Set.class)); + appOidc.setGrantScopes(JSONObject.parseObject(rs.getString("grant_scopes"), Set.class)); + appOidc.setRequireAuthConsent(rs.getBoolean("require_auth_consent")); + appOidc.setRequireProofKey(rs.getBoolean("require_proof_key")); + appOidc.setTokenEndpointAuthSigningAlgorithm( rs.getString("token_endpoint_auth_signing_algorithm")); - appAccount.setRefreshTokenTimeToLive(rs.getInt("refresh_token_time_to_live")); - appAccount.setAccessTokenFormat(rs.getString("access_token_format")); - appAccount.setAccessTokenTimeToLive(rs.getInt("access_token_time_to_live")); - appAccount.setIdTokenTimeToLive(rs.getInt("id_token_time_to_live")); - appAccount.setIdTokenSignatureAlgorithm(rs.getString("id_token_signature_algorithm")); - appAccount.setReuseRefreshToken(rs.getBoolean("reuse_refresh_token")); + appOidc.setRefreshTokenTimeToLive(rs.getInt("refresh_token_time_to_live")); + appOidc.setAccessTokenFormat(rs.getString("access_token_format")); + appOidc.setAccessTokenTimeToLive(rs.getInt("access_token_time_to_live")); + appOidc.setIdTokenTimeToLive(rs.getInt("id_token_time_to_live")); + appOidc.setIdTokenSignatureAlgorithm(rs.getString("id_token_signature_algorithm")); + appOidc.setReuseRefreshToken(rs.getBoolean("reuse_refresh_token")); //创建修改相关 - appAccount.setCreateBy(rs.getString("create_by")); - appAccount.setCreateTime(rs.getObject("create_time", LocalDateTime.class)); - appAccount.setUpdateBy(rs.getString("update_by")); - appAccount.setCreateTime(rs.getObject("update_time", LocalDateTime.class)); - appAccount.setRemark(rs.getString("remark_")); - return appAccount; + appOidc.setCreateBy(rs.getString("create_by")); + appOidc.setCreateTime(rs.getObject("create_time", LocalDateTime.class)); + appOidc.setUpdateBy(rs.getString("update_by")); + appOidc.setCreateTime(rs.getObject("update_time", LocalDateTime.class)); + appOidc.setRemark(rs.getString("remark_")); + return appOidc; //@formatter:on } } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/mapper/AppTsaConfigPoMapper.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/mapper/AppTsaConfigPoMapper.java new file mode 100644 index 00000000..6ac96681 --- /dev/null +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/app/impl/mapper/AppTsaConfigPoMapper.java @@ -0,0 +1,80 @@ +/* + * eiam-common - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.common.repository.app.impl.mapper; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDateTime; + +import org.springframework.jdbc.core.RowMapper; + +import com.alibaba.fastjson2.JSON; + +import cn.topiam.employee.common.entity.app.AppTsaConfigEntity; +import cn.topiam.employee.common.entity.app.po.AppTsaConfigPO; +import cn.topiam.employee.common.enums.app.AuthorizationType; + +/** + * @author TopIAM + * Created by support@topiam.cn on 2022/01/14 10:58 + */ +@SuppressWarnings("DuplicatedCode") +public class AppTsaConfigPoMapper implements RowMapper { + + /** + * Implementations must implement this method to map each row of data + * in the ResultSet. This method should not call {@code next()} on + * the ResultSet; it is only supposed to map values of the current row. + * + * @param rs the ResultSet to map (pre-initialized for the current row) + * @param rowNum the number of the current row + * @return the result object for the current row (may be {@code null}) + * @throws SQLException if an SQLException is encountered getting + * column values (that is, there's no need to catch SQLException) + */ + @Override + public AppTsaConfigPO mapRow(ResultSet rs, int rowNum) throws SQLException { + //@formatter:off + AppTsaConfigPO appForm = new AppTsaConfigPO(); + appForm.setAppId(rs.getLong("app_id")); + //应用表相关 + appForm.setAppCode(rs.getString("code_")); + appForm.setAppTemplate(rs.getString("template_")); + appForm.setEnabled(rs.getBoolean("is_enabled")); + appForm.setClientId(rs.getString("client_id")); + appForm.setClientSecret(rs.getString("client_secret")); +// appForm.setInitLoginType(InitLoginType.getType(rs.getString("init_login_type"))); + appForm.setInitLoginUrl(rs.getString("init_login_url")); + appForm.setAuthorizationType(AuthorizationType.getType(rs.getString("authorization_type"))); + //配置相关 + appForm.setLoginPage(rs.getString("login_page")); + appForm.setAutoLoginSteps(JSON.parseArray(rs.getString("auto_login_steps")) + .toList(AppTsaConfigEntity.AutoLoginStep.class)); + appForm.setCreateAccountSteps(JSON.parseArray(rs.getString("create_account_stepss")) + .toList(AppTsaConfigEntity.CreateAccountStep.class)); + //创建修改相关 + appForm.setCreateBy(rs.getString("create_by")); + appForm.setCreateTime(rs.getObject("create_time", LocalDateTime.class)); + appForm.setUpdateBy(rs.getString("update_by")); + appForm.setCreateTime(rs.getObject("update_time", LocalDateTime.class)); + appForm.setRemark(rs.getString("remark_")); + return appForm; + //@formatter:on + } + +} diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/authentication/IdentityProviderRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/authentication/IdentityProviderRepository.java index 11f70825..57680a16 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/authentication/IdentityProviderRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/authentication/IdentityProviderRepository.java @@ -27,14 +27,12 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import cn.topiam.employee.common.entity.authentication.IdentityProviderEntity; -import cn.topiam.employee.common.enums.IdentityProviderType; +import cn.topiam.employee.support.repository.LogicDeleteRepository; /** *

@@ -46,16 +44,16 @@ import cn.topiam.employee.common.enums.IdentityProviderType; */ @Repository @CacheConfig(cacheNames = "idp") -public interface IdentityProviderRepository extends CrudRepository, - PagingAndSortingRepository, +public interface IdentityProviderRepository extends + LogicDeleteRepository, QuerydslPredicateExecutor { /** * 根据平台类型查询认证源配置 * - * @param type {@link IdentityProviderType} + * @param type {@link String} * @return {@link IdentityProviderEntity} */ - List findByType(IdentityProviderType type); + List findByType(String type); /** * 根据平台类型查询是否显示 @@ -103,7 +101,19 @@ public interface IdentityProviderRepository extends CrudRepository findById(@NotNull Long id); + Optional findById(@NotNull @Param(value = "id") Long id); + + /** + * Retrieves an entity by its id. + * + * @param id must not be {@literal null}. + * @return the entity with the given id or {@literal Optional#empty()} if none found. + * @throws IllegalArgumentException if {@literal id} is {@literal null}. + */ + @NotNull + @Cacheable(key = "#a0") + @Query(value = "SELECT * FROM identity_provider WHERE id_ = :id", nativeQuery = true) + Optional findByIdContainsDeleted(@NotNull @Param(value = "id") Long id); /** * 更新社交认证源状态 @@ -127,4 +137,13 @@ public interface IdentityProviderRepository extends CrudRepository findByIdAndEnabledIsTrue(Long id); + + /** + * 根据code查找,并且为启用 + * + * @param code {@link Long} + * @return {@link IdentityProviderEntity} + */ + @Cacheable(key = "#a0", unless = "#result == null") + Optional findByCodeAndEnabledIsTrue(String code); } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/identitysource/IdentitySourceEventRecordRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/identitysource/IdentitySourceEventRecordRepository.java index beaf762b..f0b50403 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/identitysource/IdentitySourceEventRecordRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/identitysource/IdentitySourceEventRecordRepository.java @@ -18,10 +18,10 @@ package cn.topiam.employee.common.repository.identitysource; import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.stereotype.Repository; import cn.topiam.employee.common.entity.identitysource.IdentitySourceEventRecordEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; /** * 身份源事件记录 @@ -31,7 +31,7 @@ import cn.topiam.employee.common.entity.identitysource.IdentitySourceEventRecord */ @Repository public interface IdentitySourceEventRecordRepository extends - PagingAndSortingRepository, + LogicDeleteRepository, QuerydslPredicateExecutor, IdentitySourceEventRecordRepositoryCustomized { } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/identitysource/IdentitySourceRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/identitysource/IdentitySourceRepository.java index 5664f3d9..84b8b4bd 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/identitysource/IdentitySourceRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/identitysource/IdentitySourceRepository.java @@ -26,8 +26,6 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.Param; import org.springframework.lang.NonNull; import org.springframework.stereotype.Repository; @@ -35,6 +33,7 @@ import org.springframework.transaction.annotation.Transactional; import cn.topiam.employee.common.constants.AccountConstants; import cn.topiam.employee.common.entity.identitysource.IdentitySourceEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; /** *

@@ -48,8 +47,7 @@ import cn.topiam.employee.common.entity.identitysource.IdentitySourceEntity; */ @Repository @CacheConfig(cacheNames = { AccountConstants.IDS_CACHE_NAME }) -public interface IdentitySourceRepository extends CrudRepository, - PagingAndSortingRepository, +public interface IdentitySourceRepository extends LogicDeleteRepository, QuerydslPredicateExecutor { /** * 根据ID查询 @@ -59,7 +57,17 @@ public interface IdentitySourceRepository extends CrudRepository findById(Long id); + Optional findById(@Param(value = "id") Long id); + + /** + * 根据ID查询 + * + * @param id {@link Long} + * @return {@link IdentitySourceEntity} + */ + @Cacheable(key = "#p0", unless = "#result==null") + @Query(value = "SELECT * FROM identity_source WHERE id_ = :id", nativeQuery = true) + Optional findByIdContainsDeleted(@Param(value = "id") Long id); /** * 查询启用的身份源 diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/identitysource/IdentitySourceSyncHistoryRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/identitysource/IdentitySourceSyncHistoryRepository.java index d912df55..a3d10f7c 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/identitysource/IdentitySourceSyncHistoryRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/identitysource/IdentitySourceSyncHistoryRepository.java @@ -18,11 +18,10 @@ package cn.topiam.employee.common.repository.identitysource; import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.stereotype.Repository; import cn.topiam.employee.common.entity.identitysource.IdentitySourceSyncHistoryEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; /** * 身份源同步结果 @@ -32,7 +31,6 @@ import cn.topiam.employee.common.entity.identitysource.IdentitySourceSyncHistory */ @Repository public interface IdentitySourceSyncHistoryRepository extends - CrudRepository, - PagingAndSortingRepository, + LogicDeleteRepository, QuerydslPredicateExecutor { } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/identitysource/IdentitySourceSyncRecordRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/identitysource/IdentitySourceSyncRecordRepository.java index 9fbb6a70..50f02bb7 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/identitysource/IdentitySourceSyncRecordRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/identitysource/IdentitySourceSyncRecordRepository.java @@ -18,10 +18,10 @@ package cn.topiam.employee.common.repository.identitysource; import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.stereotype.Repository; import cn.topiam.employee.common.entity.identitysource.IdentitySourceSyncRecordEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; /** * 身份源同步详情 @@ -31,7 +31,7 @@ import cn.topiam.employee.common.entity.identitysource.IdentitySourceSyncRecordE */ @Repository public interface IdentitySourceSyncRecordRepository extends - PagingAndSortingRepository, + LogicDeleteRepository, QuerydslPredicateExecutor, IdentitySourceSyncRecordRepositoryCustomized { } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/MailSendRecordRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/message/MailSendRecordRepository.java similarity index 74% rename from eiam-common/src/main/java/cn/topiam/employee/common/repository/MailSendRecordRepository.java rename to eiam-common/src/main/java/cn/topiam/employee/common/repository/message/MailSendRecordRepository.java index 30359311..3061079a 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/MailSendRecordRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/message/MailSendRecordRepository.java @@ -15,12 +15,12 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.common.repository; +package cn.topiam.employee.common.repository.message; -import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; -import cn.topiam.employee.common.entity.MailSendRecordEntity; +import cn.topiam.employee.common.entity.message.MailSendRecordEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; /** * MailSendRecordRepository @@ -29,5 +29,6 @@ import cn.topiam.employee.common.entity.MailSendRecordEntity; * Created by support@topiam.cn on 2021/10/3 03:38 */ @Repository -public interface MailSendRecordRepository extends CrudRepository { +public interface MailSendRecordRepository extends + LogicDeleteRepository { } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/SmsSendRecordRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/message/SmsSendRecordRepository.java similarity index 75% rename from eiam-common/src/main/java/cn/topiam/employee/common/repository/SmsSendRecordRepository.java rename to eiam-common/src/main/java/cn/topiam/employee/common/repository/message/SmsSendRecordRepository.java index e9dbe8d3..3cebecfd 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/SmsSendRecordRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/message/SmsSendRecordRepository.java @@ -15,17 +15,17 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.common.repository; +package cn.topiam.employee.common.repository.message; -import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; -import cn.topiam.employee.common.entity.SmsSendRecordEntity; +import cn.topiam.employee.common.entity.message.SmsSendRecordEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; /** * @author TopIAM */ @Repository -public interface SmsSendRecordRepository extends CrudRepository { +public interface SmsSendRecordRepository extends LogicDeleteRepository { } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/setting/AdministratorRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/setting/AdministratorRepository.java index 7231fd73..bde7efe9 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/setting/AdministratorRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/setting/AdministratorRepository.java @@ -17,6 +17,7 @@ */ package cn.topiam.employee.common.repository.setting; +import java.time.LocalDateTime; import java.util.Optional; import org.jetbrains.annotations.NotNull; @@ -26,12 +27,12 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import cn.topiam.employee.common.entity.setting.AdministratorEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; import static cn.topiam.employee.common.constants.SettingConstants.ADMIN_CACHE_NAME; /** @@ -40,7 +41,7 @@ import static cn.topiam.employee.common.constants.SettingConstants.ADMIN_CACHE_N */ @Repository @CacheConfig(cacheNames = { ADMIN_CACHE_NAME }) -public interface AdministratorRepository extends CrudRepository, +public interface AdministratorRepository extends LogicDeleteRepository, QuerydslPredicateExecutor { /** @@ -51,8 +52,19 @@ public interface AdministratorRepository extends CrudRepository findById(@NotNull Long id); + @Cacheable + Optional findById(@NotNull @Param(value = "id") Long id); + + /** + * findById + * + * @param id must not be {@literal null}. + * @return {@link AdministratorEntity} + */ + @NotNull + @Cacheable + @Query(value = "SELECT * FROM administrator WHERE id_ = :id", nativeQuery = true) + Optional findByIdContainsDeleted(@NotNull @Param(value = "id") Long id); /** * findById @@ -130,4 +142,17 @@ public interface AdministratorRepository extends CrudRepository @@ -34,7 +41,7 @@ import cn.topiam.employee.common.enums.MailType; * Created by support@topiam.cn on 2020-08-13 */ @Repository -public interface MailTemplateRepository extends CrudRepository { +public interface MailTemplateRepository extends LogicDeleteRepository { /** * 根据类型查询模板 * @@ -48,6 +55,20 @@ public interface MailTemplateRepository extends CrudRepository findByIdContainsDeleted(@NotNull @Param(value = "id") Long id); } diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/repository/setting/SettingRepository.java b/eiam-common/src/main/java/cn/topiam/employee/common/repository/setting/SettingRepository.java index fbc54b78..d42f4a28 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/repository/setting/SettingRepository.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/repository/setting/SettingRepository.java @@ -20,14 +20,18 @@ package cn.topiam.employee.common.repository.setting; import java.util.List; import java.util.Objects; -import org.springframework.data.repository.CrudRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import cn.topiam.employee.common.entity.setting.SettingEntity; +import cn.topiam.employee.support.repository.LogicDeleteRepository; import cn.topiam.employee.support.util.BeanUtils; import static cn.topiam.employee.support.repository.domain.BaseEntity.LAST_MODIFIED_BY; import static cn.topiam.employee.support.repository.domain.BaseEntity.LAST_MODIFIED_TIME; +import static cn.topiam.employee.support.repository.domain.LogicDeleteEntity.SOFT_DELETE_SET; /** * 设置表 Repository 接口 @@ -36,7 +40,7 @@ import static cn.topiam.employee.support.repository.domain.BaseEntity.LAST_MODIF * Created by support@topiam.cn on 2020/12/5 22:09 */ @Repository -public interface SettingRepository extends CrudRepository { +public interface SettingRepository extends LogicDeleteRepository { /** * 根据KEY查询 * @@ -74,16 +78,22 @@ public interface SettingRepository extends CrudRepository { * * @param name {@link String} */ + @Modifying @Transactional(rollbackFor = Exception.class) - void deleteByName(String name); + @Query(value = "UPDATE stting SET " + SOFT_DELETE_SET + + " WHERE name_ = :name", nativeQuery = true) + void deleteByName(@Param("name") String name); /** * 根据名称列表删除 * * @param names {@link String} */ + @Modifying @Transactional(rollbackFor = Exception.class) - void deleteByNameIn(List names); + @Query(value = "UPDATE stting SET " + SOFT_DELETE_SET + + " WHERE name_ IN (:names)", nativeQuery = true) + void deleteByNameIn(@Param("names") List names); /** * 保存配置 diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/storage/impl/AliYunOssStorage.java b/eiam-common/src/main/java/cn/topiam/employee/common/storage/impl/AliYunOssStorage.java index b0559822..7147f753 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/storage/impl/AliYunOssStorage.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/storage/impl/AliYunOssStorage.java @@ -32,6 +32,7 @@ import com.aliyun.oss.model.CreateBucketRequest; import com.aliyun.oss.model.GeneratePresignedUrlRequest; import com.aliyun.oss.model.PutObjectRequest; +import cn.topiam.employee.common.crypto.Encrypt; import cn.topiam.employee.common.storage.AbstractStorage; import cn.topiam.employee.common.storage.StorageConfig; import cn.topiam.employee.common.storage.StorageProviderException; @@ -92,22 +93,22 @@ public class AliYunOssStorage extends AbstractStorage { } } + /** + * 所有OSS支持的请求和各种Header参数,在URL中进行签名的算法和在Header中包含签名的算法基本一样。 + * 生成URL中的签名字符串时,除了将Date参数替换为Expires参数外,仍然包含CONTENT-TYPE、CONTENT-MD5、CanonicalizedOSSHeaders等在Header中包含签名中定义的Header(请求中虽然仍有Date请求Header,但无需将Date加入签名字符串中)。 + * 在URL中包含签名时必须对URL进行urlencode。如果在URL中多次传入Signature、Expires或OSSAccessKeyId,则以第一次传入的值为准。 + * 使用URL签名时,OSS会先验证请求时间是否晚于Expires时间,然后再验证签名。 + * urlencode(base64(hmac-sha1(AccessKeySecret, + * VERB + "\n" + * + CONTENT-MD5 + "\n" + * + CONTENT-TYPE + "\n" + * + EXPIRES + "\n" + * + CanonicalizedOSSHeaders + * + CanonicalizedResource))) + */ @Override public String download(String path) throws StorageProviderException { super.download(path); - /** - * 所有OSS支持的请求和各种Header参数,在URL中进行签名的算法和在Header中包含签名的算法基本一样。 - * 生成URL中的签名字符串时,除了将Date参数替换为Expires参数外,仍然包含CONTENT-TYPE、CONTENT-MD5、CanonicalizedOSSHeaders等在Header中包含签名中定义的Header(请求中虽然仍有Date请求Header,但无需将Date加入签名字符串中)。 - * 在URL中包含签名时必须对URL进行urlencode。如果在URL中多次传入Signature、Expires或OSSAccessKeyId,则以第一次传入的值为准。 - * 使用URL签名时,OSS会先验证请求时间是否晚于Expires时间,然后再验证签名。 - * urlencode(base64(hmac-sha1(AccessKeySecret, - * VERB + "\n" - * + CONTENT-MD5 + "\n" - * + CONTENT-TYPE + "\n" - * + EXPIRES + "\n" - * + CanonicalizedOSSHeaders - * + CanonicalizedResource))) - */ GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest( aliYunConfig.getBucket(), path, HttpMethod.GET); request.setExpiration(DateUtils.addSeconds(new Date(), EXPIRY_SECONDS)); @@ -129,6 +130,7 @@ public class AliYunOssStorage extends AbstractStorage { /** * accessKeySecret */ + @Encrypt @NotEmpty(message = "AccessKeySecret不能为空") private String accessKeySecret; /** diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/storage/impl/MinIoStorage.java b/eiam-common/src/main/java/cn/topiam/employee/common/storage/impl/MinIoStorage.java index 23bd221e..b15a0b02 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/storage/impl/MinIoStorage.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/storage/impl/MinIoStorage.java @@ -25,6 +25,7 @@ import javax.validation.constraints.NotEmpty; import org.springframework.web.multipart.MultipartFile; +import cn.topiam.employee.common.crypto.Encrypt; import cn.topiam.employee.common.storage.AbstractStorage; import cn.topiam.employee.common.storage.StorageConfig; import cn.topiam.employee.common.storage.StorageProviderException; @@ -118,6 +119,7 @@ public class MinIoStorage extends AbstractStorage { /** * SecretKey */ + @Encrypt @NotEmpty(message = "SecretKey不能为空") private String secretKey; /** diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/storage/impl/QiNiuKodoStorage.java b/eiam-common/src/main/java/cn/topiam/employee/common/storage/impl/QiNiuKodoStorage.java index 3ce1d1e7..b8c07529 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/storage/impl/QiNiuKodoStorage.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/storage/impl/QiNiuKodoStorage.java @@ -31,6 +31,7 @@ import com.qiniu.storage.UploadManager; import com.qiniu.storage.model.DefaultPutRet; import com.qiniu.util.Auth; +import cn.topiam.employee.common.crypto.Encrypt; import cn.topiam.employee.common.storage.AbstractStorage; import cn.topiam.employee.common.storage.StorageConfig; import cn.topiam.employee.common.storage.StorageProviderException; @@ -144,6 +145,7 @@ public class QiNiuKodoStorage extends AbstractStorage { /** * SecretKey */ + @Encrypt @NotEmpty(message = "SecretKey不能为空") private String secretKey; /** diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/storage/impl/TencentCosStorage.java b/eiam-common/src/main/java/cn/topiam/employee/common/storage/impl/TencentCosStorage.java index 866757c9..38fd6823 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/storage/impl/TencentCosStorage.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/storage/impl/TencentCosStorage.java @@ -37,6 +37,7 @@ import com.qcloud.cos.http.HttpProtocol; import com.qcloud.cos.model.*; import com.qcloud.cos.region.Region; +import cn.topiam.employee.common.crypto.Encrypt; import cn.topiam.employee.common.storage.AbstractStorage; import cn.topiam.employee.common.storage.StorageConfig; import cn.topiam.employee.common.storage.StorageProviderException; @@ -157,6 +158,7 @@ public class TencentCosStorage extends AbstractStorage { /** * SecretKey */ + @Encrypt @NotEmpty(message = "SecretKey不能为空") private String secretKey; /** diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/util/RequestUtils.java b/eiam-common/src/main/java/cn/topiam/employee/common/util/RequestUtils.java index 4f7037ce..aadbecd8 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/util/RequestUtils.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/util/RequestUtils.java @@ -67,8 +67,8 @@ public class RequestUtils { /** **把request转换成map数据 */ - public static Map getParams(HttpServletRequest request) { - Map params = new HashMap<>(16); + public static Map getParams(HttpServletRequest request) { + Map params = new HashMap<>(16); Map requestParams = request.getParameterMap(); for (Map.Entry entry : requestParams.entrySet()) { String[] values = entry.getValue(); diff --git a/eiam-common/src/main/java/cn/topiam/employee/common/util/SamlUtils.java b/eiam-common/src/main/java/cn/topiam/employee/common/util/SamlUtils.java index b9814f92..851e6abb 100644 --- a/eiam-common/src/main/java/cn/topiam/employee/common/util/SamlUtils.java +++ b/eiam-common/src/main/java/cn/topiam/employee/common/util/SamlUtils.java @@ -61,7 +61,7 @@ import static org.opensaml.saml.common.xml.SAMLConstants.POST_METHOD; * Created by support@topiam.cn on 2022/5/18 21:54 */ public class SamlUtils { - private final static Logger logger = LoggerFactory.getLogger(SamlUtils.class); + private static final Logger logger = LoggerFactory.getLogger(SamlUtils.class); private static final AtomicBoolean INITIALIZED = new AtomicBoolean(false); /** diff --git a/eiam-common/src/main/resources/db/1.0.0-changelog.xml b/eiam-common/src/main/resources/db/1.0.0-changelog.xml new file mode 100644 index 00000000..0f115781 --- /dev/null +++ b/eiam-common/src/main/resources/db/1.0.0-changelog.xml @@ -0,0 +1,1358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/administrator-changelog.xml b/eiam-common/src/main/resources/db/changelog/administrator-changelog.xml deleted file mode 100644 index e0f76b5a..00000000 --- a/eiam-common/src/main/resources/db/changelog/administrator-changelog.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/app-changelog.xml b/eiam-common/src/main/resources/db/changelog/app-changelog.xml deleted file mode 100644 index fc7673fa..00000000 --- a/eiam-common/src/main/resources/db/changelog/app-changelog.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/app_access_policy-changelog.xml b/eiam-common/src/main/resources/db/changelog/app_access_policy-changelog.xml deleted file mode 100644 index 8dff8ca3..00000000 --- a/eiam-common/src/main/resources/db/changelog/app_access_policy-changelog.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/app_account-changelog.xml b/eiam-common/src/main/resources/db/changelog/app_account-changelog.xml deleted file mode 100644 index 9e41362c..00000000 --- a/eiam-common/src/main/resources/db/changelog/app_account-changelog.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/app_cas_config-changelog.xml b/eiam-common/src/main/resources/db/changelog/app_cas_config-changelog.xml deleted file mode 100644 index f2d588f6..00000000 --- a/eiam-common/src/main/resources/db/changelog/app_cas_config-changelog.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/app_cert-changelog.xml b/eiam-common/src/main/resources/db/changelog/app_cert-changelog.xml deleted file mode 100644 index df2d82a5..00000000 --- a/eiam-common/src/main/resources/db/changelog/app_cert-changelog.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/app_oidc_config-changelog.xml b/eiam-common/src/main/resources/db/changelog/app_oidc_config-changelog.xml deleted file mode 100644 index 4206fac5..00000000 --- a/eiam-common/src/main/resources/db/changelog/app_oidc_config-changelog.xml +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/app_permission_action-changelog.xml b/eiam-common/src/main/resources/db/changelog/app_permission_action-changelog.xml deleted file mode 100644 index 403aa493..00000000 --- a/eiam-common/src/main/resources/db/changelog/app_permission_action-changelog.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/app_permission_policy-changelog.xml b/eiam-common/src/main/resources/db/changelog/app_permission_policy-changelog.xml deleted file mode 100644 index 2e75151d..00000000 --- a/eiam-common/src/main/resources/db/changelog/app_permission_policy-changelog.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/app_permission_resource-changelog.xml b/eiam-common/src/main/resources/db/changelog/app_permission_resource-changelog.xml deleted file mode 100644 index 8417643e..00000000 --- a/eiam-common/src/main/resources/db/changelog/app_permission_resource-changelog.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/app_permission_role-changelog.xml b/eiam-common/src/main/resources/db/changelog/app_permission_role-changelog.xml deleted file mode 100644 index 91fcea66..00000000 --- a/eiam-common/src/main/resources/db/changelog/app_permission_role-changelog.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/app_saml2_config-changelog.xml b/eiam-common/src/main/resources/db/changelog/app_saml2_config-changelog.xml deleted file mode 100644 index 88b86b43..00000000 --- a/eiam-common/src/main/resources/db/changelog/app_saml2_config-changelog.xml +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/audit-changelog.xml b/eiam-common/src/main/resources/db/changelog/audit-changelog.xml deleted file mode 100644 index 61afa258..00000000 --- a/eiam-common/src/main/resources/db/changelog/audit-changelog.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/identity_provider-changelog.xml b/eiam-common/src/main/resources/db/changelog/identity_provider-changelog.xml deleted file mode 100644 index ab578a5e..00000000 --- a/eiam-common/src/main/resources/db/changelog/identity_provider-changelog.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/eiam-common/src/main/resources/db/changelog/identity_source-changelog.xml b/eiam-common/src/main/resources/db/changelog/identity_source-changelog.xml deleted file mode 100644 index 9e301ea6..00000000 --- a/eiam-common/src/main/resources/db/changelog/identity_source-changelog.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/eiam-common/src/main/resources/db/changelog/identity_source_event_record-changelog.xml b/eiam-common/src/main/resources/db/changelog/identity_source_event_record-changelog.xml deleted file mode 100644 index 3924200f..00000000 --- a/eiam-common/src/main/resources/db/changelog/identity_source_event_record-changelog.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/eiam-common/src/main/resources/db/changelog/identity_source_sync_history-changelog.xml b/eiam-common/src/main/resources/db/changelog/identity_source_sync_history-changelog.xml deleted file mode 100644 index 23d7ec4b..00000000 --- a/eiam-common/src/main/resources/db/changelog/identity_source_sync_history-changelog.xml +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/eiam-common/src/main/resources/db/changelog/identity_source_sync_record-changelog.xml b/eiam-common/src/main/resources/db/changelog/identity_source_sync_record-changelog.xml deleted file mode 100644 index 1b17ab10..00000000 --- a/eiam-common/src/main/resources/db/changelog/identity_source_sync_record-changelog.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/eiam-common/src/main/resources/db/changelog/mail_send_record-changelog.xml b/eiam-common/src/main/resources/db/changelog/mail_send_record-changelog.xml deleted file mode 100644 index 9d3c842c..00000000 --- a/eiam-common/src/main/resources/db/changelog/mail_send_record-changelog.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/mail_template-changelog.xml b/eiam-common/src/main/resources/db/changelog/mail_template-changelog.xml deleted file mode 100644 index 4c297e1a..00000000 --- a/eiam-common/src/main/resources/db/changelog/mail_template-changelog.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/organization-changelog.xml b/eiam-common/src/main/resources/db/changelog/organization-changelog.xml deleted file mode 100644 index fbfd5c72..00000000 --- a/eiam-common/src/main/resources/db/changelog/organization-changelog.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/organization_member-changelog.xml b/eiam-common/src/main/resources/db/changelog/organization_member-changelog.xml deleted file mode 100644 index 11274d77..00000000 --- a/eiam-common/src/main/resources/db/changelog/organization_member-changelog.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/setting-changlog.xml b/eiam-common/src/main/resources/db/changelog/setting-changlog.xml deleted file mode 100644 index 034f8074..00000000 --- a/eiam-common/src/main/resources/db/changelog/setting-changlog.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/sms_send_record-changelog.xml b/eiam-common/src/main/resources/db/changelog/sms_send_record-changelog.xml deleted file mode 100644 index 38d773e7..00000000 --- a/eiam-common/src/main/resources/db/changelog/sms_send_record-changelog.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/user-changelog.xml b/eiam-common/src/main/resources/db/changelog/user-changelog.xml deleted file mode 100644 index 699e4302..00000000 --- a/eiam-common/src/main/resources/db/changelog/user-changelog.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/user_detail-changelog.xml b/eiam-common/src/main/resources/db/changelog/user_detail-changelog.xml deleted file mode 100644 index ce0add17..00000000 --- a/eiam-common/src/main/resources/db/changelog/user_detail-changelog.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/user_group-changelog.xml b/eiam-common/src/main/resources/db/changelog/user_group-changelog.xml deleted file mode 100644 index a429bdf9..00000000 --- a/eiam-common/src/main/resources/db/changelog/user_group-changelog.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/user_group_member-changelog.xml b/eiam-common/src/main/resources/db/changelog/user_group_member-changelog.xml deleted file mode 100644 index 4fd15a5e..00000000 --- a/eiam-common/src/main/resources/db/changelog/user_group_member-changelog.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/changelog/user_history_password-changelog.xml b/eiam-common/src/main/resources/db/changelog/user_history_password-changelog.xml deleted file mode 100644 index 0c5257cd..00000000 --- a/eiam-common/src/main/resources/db/changelog/user_history_password-changelog.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/eiam-common/src/main/resources/db/changelog/user_idp_bind-changlog.xml b/eiam-common/src/main/resources/db/changelog/user_idp_bind-changlog.xml deleted file mode 100644 index 5a2bd904..00000000 --- a/eiam-common/src/main/resources/db/changelog/user_idp_bind-changlog.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eiam-common/src/main/resources/db/eiam-changelog-master.xml b/eiam-common/src/main/resources/db/eiam-changelog-master.xml index b0b9a3ff..6710158c 100644 --- a/eiam-common/src/main/resources/db/eiam-changelog-master.xml +++ b/eiam-common/src/main/resources/db/eiam-changelog-master.xml @@ -23,7 +23,5 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/pro/liquibase-pro-4.3.xsd"> - - + diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/configuration/ConsoleSecurityConfiguration.java b/eiam-console/src/main/java/cn/topiam/employee/console/configuration/ConsoleSecurityConfiguration.java index fce67f30..2c6686e0 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/configuration/ConsoleSecurityConfiguration.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/configuration/ConsoleSecurityConfiguration.java @@ -84,7 +84,7 @@ public class ConsoleSecurityConfiguration { http //认证请求 .authorizeHttpRequests(authorizeRequests()) - // 表单登录配置 + //表单登录配置 .formLogin(withFormLoginConfigurerDefaults()) //x509 .x509(withDefaults()) @@ -179,12 +179,12 @@ public class ConsoleSecurityConfiguration { configurer.xssProtection(xssProtection -> xssProtection.block(false)); configurer.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin); configurer.contentSecurityPolicy( - "default-src 'self'; " + - "frame-src 'self' data:; " + + "default-src 'self' data:; " + + "frame-src 'self' login.dingtalk.com open.weixin.qq.com open.work.weixin.qq.com passport.feishu.cn data:; " + "frame-ancestors 'self' https://eiam.topiam.cn data:; " + - "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; " + + "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com sf3-cn.feishucdn.com;" + "style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net 'unsafe-inline'; " + - "img-src 'self' https://img.alicdn.com https://static-legacy.dingtalk.com https://joeschmoe.io data:; " + + "img-src 'self' https://img.alicdn.com https://static-legacy.dingtalk.com https://joeschmoe.io https://api.multiavatar.com data:; " + "font-src 'self' https://fonts.gstatic.com data:; "+ "worker-src 'self' https://storage.googleapis.com blob:;"); configurer.referrerPolicy( diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/controller/account/UserController.java b/eiam-console/src/main/java/cn/topiam/employee/console/controller/account/UserController.java index 8053cdfd..a4387e1a 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/controller/account/UserController.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/controller/account/UserController.java @@ -34,7 +34,9 @@ import cn.topiam.employee.audit.annotation.Audit; import cn.topiam.employee.audit.enums.EventType; import cn.topiam.employee.common.entity.account.query.UserListNotInGroupQuery; import cn.topiam.employee.common.entity.account.query.UserListQuery; -import cn.topiam.employee.common.enums.*; +import cn.topiam.employee.common.enums.CheckValidityType; +import cn.topiam.employee.common.enums.MessageNoticeChannel; +import cn.topiam.employee.common.enums.UserStatus; import cn.topiam.employee.console.pojo.result.account.UserListResult; import cn.topiam.employee.console.pojo.result.account.UserLoginAuditListResult; import cn.topiam.employee.console.pojo.result.account.UserResult; diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/controller/account/UserIdpBindController.java b/eiam-console/src/main/java/cn/topiam/employee/console/controller/account/UserIdpBindController.java index dc81a565..076cc165 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/controller/account/UserIdpBindController.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/controller/account/UserIdpBindController.java @@ -19,7 +19,8 @@ package cn.topiam.employee.console.controller.account; import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; import lombok.AllArgsConstructor; diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/controller/analysis/AnalysisController.java b/eiam-console/src/main/java/cn/topiam/employee/console/controller/analysis/AnalysisController.java index 0271f8df..cdc17ed2 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/controller/analysis/AnalysisController.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/controller/analysis/AnalysisController.java @@ -17,7 +17,6 @@ */ package cn.topiam.employee.console.controller.analysis; -import java.util.ArrayList; import java.util.List; import org.springframework.security.access.prepost.PreAuthorize; @@ -26,12 +25,8 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import cn.topiam.employee.audit.enums.EventStatus; import cn.topiam.employee.console.pojo.query.analysis.AnalysisQuery; -import cn.topiam.employee.console.pojo.result.analysis.AppVisitRankResult; -import cn.topiam.employee.console.pojo.result.analysis.AuthnHotProviderResult; -import cn.topiam.employee.console.pojo.result.analysis.AuthnQuantityResult; -import cn.topiam.employee.console.pojo.result.analysis.OverviewResult; +import cn.topiam.employee.console.pojo.result.analysis.*; import cn.topiam.employee.console.service.analysis.AnalysisService; import cn.topiam.employee.support.result.ApiRestResult; @@ -74,28 +69,7 @@ public class AnalysisController { @Operation(summary = "认证量") @PreAuthorize(value = "authenticated and hasAuthority(T(cn.topiam.employee.core.security.authorization.Roles).ADMIN)") public ApiRestResult> authnQuantity(@Validated AnalysisQuery query) { - if (true) { - return ApiRestResult.ok(analysisService.authnQuantity(query)); - } - List list = new ArrayList<>(); - list.add(new AuthnQuantityResult("一月", 18L, EventStatus.SUCCESS.getDesc())); - list.add(new AuthnQuantityResult("二月", 28L, EventStatus.SUCCESS.getDesc())); - list.add(new AuthnQuantityResult("三月", 39L, EventStatus.SUCCESS.getDesc())); - list.add(new AuthnQuantityResult("四月", 81L, EventStatus.SUCCESS.getDesc())); - list.add(new AuthnQuantityResult("五月", 47L, EventStatus.SUCCESS.getDesc())); - list.add(new AuthnQuantityResult("六月", 20L, EventStatus.SUCCESS.getDesc())); - list.add(new AuthnQuantityResult("七月", 24L, EventStatus.SUCCESS.getDesc())); - list.add(new AuthnQuantityResult("八月", 35L, EventStatus.SUCCESS.getDesc())); - //失败 - list.add(new AuthnQuantityResult("一月", 12L, EventStatus.FAIL.getDesc())); - list.add(new AuthnQuantityResult("二月", 23L, EventStatus.FAIL.getDesc())); - list.add(new AuthnQuantityResult("三月", 34L, EventStatus.FAIL.getDesc())); - list.add(new AuthnQuantityResult("四月", 99L, EventStatus.FAIL.getDesc())); - list.add(new AuthnQuantityResult("五月", 52L, EventStatus.FAIL.getDesc())); - list.add(new AuthnQuantityResult("六月", 35L, EventStatus.FAIL.getDesc())); - list.add(new AuthnQuantityResult("七月", 37L, EventStatus.FAIL.getDesc())); - list.add(new AuthnQuantityResult("八月", 42L, EventStatus.FAIL.getDesc())); - return ApiRestResult.ok(list); + return ApiRestResult.ok(analysisService.authnQuantity(query)); } /** @@ -107,19 +81,7 @@ public class AnalysisController { @Operation(summary = "热门认证提供商") @PreAuthorize(value = "authenticated and hasAuthority(T(cn.topiam.employee.core.security.authorization.Roles).ADMIN)") public ApiRestResult> authnHotProvider(@Validated AnalysisQuery query) { - ArrayList list = new ArrayList<>() { - { - add(new AuthnHotProviderResult("微信扫码登录", 1000L)); - add(new AuthnHotProviderResult("钉钉扫码登录", 100L)); - add(new AuthnHotProviderResult("企业微信", 99L)); - add(new AuthnHotProviderResult("QQ", 88L)); - add(new AuthnHotProviderResult("Github", 77L)); - add(new AuthnHotProviderResult("支付宝扫码认证", 66L)); - add(new AuthnHotProviderResult("LDAP", 55L)); - add(new AuthnHotProviderResult("微博", 10L)); - } - }; - return ApiRestResult.ok(list); + return ApiRestResult.ok(analysisService.authnHotProvider(query)); } /** @@ -128,8 +90,8 @@ public class AnalysisController { @GetMapping("/authn/zone") @Operation(summary = "登录区域") @PreAuthorize(value = "authenticated and hasAuthority(T(cn.topiam.employee.core.security.authorization.Roles).ADMIN)") - public void authnZone(@Validated AnalysisQuery query) { - + public ApiRestResult> authnZone(@Validated AnalysisQuery query) { + return ApiRestResult.ok(analysisService.authnZone(query)); } /** @@ -142,19 +104,7 @@ public class AnalysisController { @Operation(summary = "访问应用排名") @PreAuthorize(value = "authenticated and hasAuthority(T(cn.topiam.employee.core.security.authorization.Roles).ADMIN)") public ApiRestResult> appVisitRank(@Validated AnalysisQuery query) { - if (true) { - return ApiRestResult.ok(analysisService.appVisitRank(query)); - } - List list = new ArrayList<>(); - list.add(new AppVisitRankResult("阿里云用户", 145L)); - list.add(new AppVisitRankResult("腾讯云用户", 61L)); - list.add(new AppVisitRankResult("华为云", 52L)); - list.add(new AppVisitRankResult("百度云用户", 48L)); - list.add(new AppVisitRankResult("阿里云角色", 38L)); - list.add(new AppVisitRankResult("百度云角色", 28L)); - list.add(new AppVisitRankResult("腾讯云角色", 22L)); - list.add(new AppVisitRankResult("OIDC", 10L)); - return ApiRestResult.ok(list); + return ApiRestResult.ok(analysisService.appVisitRank(query)); } private final AnalysisService analysisService; diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/controller/app/AppCertController.java b/eiam-console/src/main/java/cn/topiam/employee/console/controller/app/AppCertController.java index b214b763..1a11b6d8 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/controller/app/AppCertController.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/controller/app/AppCertController.java @@ -26,7 +26,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import cn.topiam.employee.application.saml2.model.AppSaml2StandardConfigGetResult; +import cn.topiam.employee.application.saml2.pojo.AppSaml2StandardConfigGetResult; import cn.topiam.employee.console.pojo.query.app.AppCertQuery; import cn.topiam.employee.console.pojo.result.app.AppCertListResult; import cn.topiam.employee.console.service.app.AppCertService; diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/controller/app/AppController.java b/eiam-console/src/main/java/cn/topiam/employee/console/controller/app/AppController.java index ccaf02c8..18cddc76 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/controller/app/AppController.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/controller/app/AppController.java @@ -22,7 +22,7 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import cn.topiam.employee.application.saml2.model.AppSaml2StandardConfigGetResult; +import cn.topiam.employee.application.saml2.pojo.AppSaml2StandardConfigGetResult; import cn.topiam.employee.audit.annotation.Audit; import cn.topiam.employee.audit.enums.EventType; import cn.topiam.employee.console.pojo.query.app.AppQuery; diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/controller/session/SessionManageEndpoint.java b/eiam-console/src/main/java/cn/topiam/employee/console/controller/session/SessionManageEndpoint.java index 2b3db85e..50864cc2 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/controller/session/SessionManageEndpoint.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/controller/session/SessionManageEndpoint.java @@ -111,7 +111,7 @@ public class SessionManageEndpoint { if (principal instanceof SessionDetails) { //过滤掉当前用户的会话 if (!((SessionDetails) principal).getSessionId() - .equals(req.getSession().getId())) { + .equals(req.getSession().getId()) || true) { //@formatter:off OnlineUserConverter userConverter = ApplicationContextHelp.getBean(OnlineUserConverter.class); OnlineSession user = userConverter.sessionDetailsToOnlineSession(((SessionDetails) principal)); @@ -193,6 +193,11 @@ class OnlineSession implements Serializable { @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) private UserAgent userAgent; + /** + * 认证类型 + */ + private String authType; + /** * 登录时间 */ @@ -236,6 +241,8 @@ interface OnlineUserConverter { onlineSession.setGeoLocation(sessionDetails.getGeoLocation()); //用户代理 onlineSession.setUserAgent(sessionDetails.getUserAgent()); + //认证类型 + onlineSession.setAuthType(sessionDetails.getAuthType()); //登录时间 onlineSession.setLoginTime(sessionDetails.getLoginTime()); //最后请求时间 diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/controller/setting/MailProviderController.java b/eiam-console/src/main/java/cn/topiam/employee/console/controller/setting/MailProviderController.java index f3edbeb3..f04b5429 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/controller/setting/MailProviderController.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/controller/setting/MailProviderController.java @@ -28,7 +28,6 @@ import cn.topiam.employee.audit.annotation.Audit; import cn.topiam.employee.audit.enums.EventType; import cn.topiam.employee.common.enums.MailType; import cn.topiam.employee.console.pojo.result.setting.EmailProviderConfigResult; -import cn.topiam.employee.console.pojo.save.authentication.InitializeAdminSaveParam; import cn.topiam.employee.console.pojo.save.setting.MailProviderSaveParam; import cn.topiam.employee.console.service.setting.MessageSettingService; import cn.topiam.employee.core.context.ServerContextHelp; @@ -60,7 +59,7 @@ public class MailProviderController { /** * 保存邮件服务商配置 * - * @param param {@link InitializeAdminSaveParam} + * @param param {@link MailProviderSaveParam} * @return {@link ApiRestResult} */ @Lock diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/converter/account/UserConverter.java b/eiam-console/src/main/java/cn/topiam/employee/console/converter/account/UserConverter.java index e8aeb141..ebdd887a 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/converter/account/UserConverter.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/converter/account/UserConverter.java @@ -122,9 +122,15 @@ public interface UserConverter { UserEntity userEntity = new UserEntity(); userEntity.setRemark(param.getRemark()); userEntity.setUsername(param.getUsername()); - userEntity.setEmail(param.getEmail()); + //邮箱 + if (StringUtils.hasText(param.getEmail())) { + userEntity.setEmail(param.getEmail()); + userEntity.setEmailVerified(Boolean.TRUE); + } + //手机号 if (StringUtils.hasText(param.getPhone())) { userEntity.setPhone(getPhoneNumber(param.getPhone())); + userEntity.setPhoneVerified(Boolean.TRUE); userEntity.setPhoneAreaCode(getPhoneAreaCode(param.getPhone())); } userEntity.setFullName(param.getFullName()); @@ -133,7 +139,6 @@ public interface UserConverter { userEntity.setStatus(cn.topiam.employee.common.enums.UserStatus.ENABLE); userEntity.setAvatar("https://joeschmoe.io/api/v1/random"); userEntity.setDataOrigin(cn.topiam.employee.common.enums.DataOrigin.INPUT); - userEntity.setEmailVerified(Boolean.FALSE); userEntity.setExpireDate( java.util.Objects.isNull(param.getExpireDate()) ? java.time.LocalDate.of(2116, 12, 31) : param.getExpireDate()); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/converter/account/UserGroupConverter.java b/eiam-console/src/main/java/cn/topiam/employee/console/converter/account/UserGroupConverter.java index 9fad7a78..035d8edb 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/converter/account/UserGroupConverter.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/converter/account/UserGroupConverter.java @@ -152,7 +152,8 @@ public interface UserGroupConverter { */ default Predicate queryUserGroupListParamConvertToPredicate(UserGroupListQuery query) { QUserGroupEntity userGroup = QUserGroupEntity.userGroupEntity; - Predicate predicate = userGroup.isNotNull(); + Predicate predicate = ExpressionUtils.and(userGroup.isNotNull(), + userGroup.isDeleted.eq(Boolean.FALSE)); //查询条件 //@formatter:off predicate = StringUtils.isBlank(query.getName()) ? predicate : ExpressionUtils.and(predicate, userGroup.name.like("%" + query.getName() + "%")); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppCertConverter.java b/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppCertConverter.java index d875d383..958c9504 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppCertConverter.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppCertConverter.java @@ -48,7 +48,8 @@ public interface AppCertConverter { */ default Predicate queryAppCertListParamConvertToPredicate(AppCertQuery query) { QAppCertEntity cert = QAppCertEntity.appCertEntity; - Predicate predicate = cert.isNotNull(); + Predicate predicate = ExpressionUtils.and(cert.isNotNull(), + cert.isDeleted.eq(Boolean.FALSE)); //查询条件 //@formatter:off predicate = StringUtils.isBlank(query.getAppId()) ? predicate : ExpressionUtils.and(predicate, cert.appId.eq(Long.valueOf(query.getAppId()))); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppConverter.java b/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppConverter.java index c3cda624..13ed15a8 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppConverter.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppConverter.java @@ -58,7 +58,8 @@ public interface AppConverter { */ default Predicate queryAppListParamConvertToPredicate(AppQuery query) { QAppEntity application = QAppEntity.appEntity; - Predicate predicate = application.isNotNull(); + Predicate predicate = ExpressionUtils.and(application.isNotNull(), + application.isDeleted.eq(Boolean.FALSE)); //查询条件 //@formatter:off predicate = StringUtils.isBlank(query.getName()) ? predicate : ExpressionUtils.and(predicate, application.name.like("%" + query.getName() + "%")); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppPermissionActionConverter.java b/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppPermissionActionConverter.java index 2656e599..bdfb9cd4 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppPermissionActionConverter.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppPermissionActionConverter.java @@ -51,7 +51,8 @@ public interface AppPermissionActionConverter { */ default Predicate appPermissionActionListQueryConvertToPredicate(AppPermissionActionListQuery query) { QAppPermissionResourceEntity resource = QAppPermissionResourceEntity.appPermissionResourceEntity; - Predicate predicate = resource.isNotNull(); + Predicate predicate = ExpressionUtils.and(resource.isNotNull(), + resource.isDeleted.eq(Boolean.FALSE)); //查询条件 //@formatter:off // 资源名称 diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppPermissionResourceConverter.java b/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppPermissionResourceConverter.java index dffe4c7e..9d09c161 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppPermissionResourceConverter.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppPermissionResourceConverter.java @@ -55,7 +55,8 @@ public interface AppPermissionResourceConverter { */ default Predicate resourcePaginationParamConvertToPredicate(AppResourceListQuery query) { QAppPermissionResourceEntity resource = QAppPermissionResourceEntity.appPermissionResourceEntity; - Predicate predicate = resource.isNotNull(); + Predicate predicate = ExpressionUtils.and(resource.isNotNull(), + resource.isDeleted.eq(Boolean.FALSE)); //查询条件 //@formatter:off // 资源名称 diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppPermissionRoleConverter.java b/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppPermissionRoleConverter.java index a6c7371c..2cc39295 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppPermissionRoleConverter.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/converter/app/AppPermissionRoleConverter.java @@ -124,7 +124,8 @@ public interface AppPermissionRoleConverter { */ default Predicate rolePaginationParamConvertToPredicate(AppPermissionRoleListQuery query) { QAppPermissionRoleEntity role = QAppPermissionRoleEntity.appPermissionRoleEntity; - Predicate predicate = role.isNotNull(); + Predicate predicate = ExpressionUtils.and(role.isNotNull(), + role.isDeleted.eq(Boolean.FALSE)); //查询条件 //@formatter:off // 角色名称 diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/converter/authentication/IdentityProviderConverter.java b/eiam-console/src/main/java/cn/topiam/employee/console/converter/authentication/IdentityProviderConverter.java index c289a9e5..e3e62d9c 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/converter/authentication/IdentityProviderConverter.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/converter/authentication/IdentityProviderConverter.java @@ -23,6 +23,7 @@ import java.util.Objects; import javax.validation.ConstraintViolationException; +import org.apache.commons.lang3.RandomStringUtils; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.springframework.data.querydsl.QPageRequest; @@ -34,15 +35,17 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.querydsl.core.types.ExpressionUtils; import com.querydsl.core.types.Predicate; +import cn.topiam.employee.authentication.common.IdentityProviderCategory; +import cn.topiam.employee.authentication.common.IdentityProviderType; import cn.topiam.employee.authentication.common.config.IdentityProviderConfig; import cn.topiam.employee.authentication.dingtalk.DingTalkIdpOauthConfig; import cn.topiam.employee.authentication.dingtalk.DingTalkIdpScanCodeConfig; +import cn.topiam.employee.authentication.feishu.FeiShuIdpScanCodeConfig; import cn.topiam.employee.authentication.qq.QqIdpOauthConfig; import cn.topiam.employee.authentication.wechat.WeChatIdpScanCodeConfig; import cn.topiam.employee.authentication.wechatwork.WeChatWorkIdpScanCodeConfig; import cn.topiam.employee.common.entity.authentication.IdentityProviderEntity; import cn.topiam.employee.common.entity.authentication.QIdentityProviderEntity; -import cn.topiam.employee.common.enums.IdentityProviderType; import cn.topiam.employee.console.pojo.query.authentication.IdentityProviderListQuery; import cn.topiam.employee.console.pojo.result.authentication.IdentityProviderListResult; import cn.topiam.employee.console.pojo.result.authentication.IdentityProviderResult; @@ -54,6 +57,7 @@ import cn.topiam.employee.support.repository.page.domain.Page; import cn.topiam.employee.support.repository.page.domain.PageModel; import cn.topiam.employee.support.repository.page.domain.QueryDslRequest; import cn.topiam.employee.support.validation.ValidationHelp; +import static cn.topiam.employee.authentication.common.IdentityProviderType.*; /** * 身份提供商转换器 @@ -93,7 +97,7 @@ public interface IdentityProviderConverter { * @param entity {@link IdentityProviderEntity} * @return {@link IdentityProviderListResult} */ - @Mapping(target = "desc", source = "type.desc") + @Mapping(target = "desc", expression = "java(IdentityProviderConverter.getIdentityProviderType(entity.getType()).desc())") IdentityProviderListResult entityConverterToIdentityProviderResult(IdentityProviderEntity entity); /** @@ -106,7 +110,9 @@ public interface IdentityProviderConverter { if (param == null) { return null; } - if (!param.getCategory().getProviders().contains(param.getType())) { + IdentityProviderCategory category = IdentityProviderCategory.getType(param.getCategory()); + if (!category.getProviders().stream().map(IdentityProviderType::value).toList() + .contains(param.getType())) { throw new TopIamException("认证源类型与认证源提供商不匹配"); } try { @@ -117,19 +123,17 @@ public interface IdentityProviderConverter { objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); //封装数据 - IdentityProviderEntity identityProviderEntity = new IdentityProviderEntity(); - identityProviderEntity.setName(param.getName()); - identityProviderEntity.setCode( - org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric(32).toLowerCase()); - identityProviderEntity.setType(param.getType()); - identityProviderEntity.setCategory(param.getCategory()); - identityProviderEntity.setDisplayed(param.getDisplayed()); - identityProviderEntity.setEnabled(Boolean.TRUE); - identityProviderEntity.setRemark(param.getRemark()); + IdentityProviderEntity entity = new IdentityProviderEntity(); + entity.setName(param.getName()); + entity.setCode(RandomStringUtils.randomAlphanumeric(32).toLowerCase()); + entity.setType(param.getType()); + entity.setCategory(param.getCategory()); + entity.setDisplayed(param.getDisplayed()); + entity.setEnabled(Boolean.TRUE); + entity.setRemark(param.getRemark()); //配置 - identityProviderEntity - .setConfig(objectMapper.writeValueAsString(identityProviderConfig)); - return identityProviderEntity; + entity.setConfig(objectMapper.writeValueAsString(identityProviderConfig)); + return entity; } catch (JsonProcessingException e) { throw new RuntimeException(e); } @@ -155,7 +159,8 @@ public interface IdentityProviderConverter { result.setRemark(entity.getRemark()); //回调地址 result.setRedirectUri(ServerContextHelp.getPortalPublicBaseUrl() - + entity.getType().getLoginPathPrefix() + "/" + entity.getCode()); + + getIdentityProviderType(entity.getType()).getLoginPathPrefix() + "/" + + entity.getCode()); try { ObjectMapper objectMapper = new ObjectMapper(); // 指定序列化输入的类型 @@ -181,7 +186,8 @@ public interface IdentityProviderConverter { PageModel pageModel) { QueryDslRequest request = new QueryDslRequest(); QIdentityProviderEntity queryEntity = QIdentityProviderEntity.identityProviderEntity; - Predicate predicate = queryEntity.isNotNull(); + Predicate predicate = ExpressionUtils.and(queryEntity.isNotNull(), + queryEntity.isDeleted.eq(Boolean.FALSE)); //查询条件 //@formatter:off predicate = Objects.isNull(query.getCategory()) ? predicate : ExpressionUtils.and(predicate, queryEntity.category.eq(query.getCategory())); @@ -232,25 +238,29 @@ public interface IdentityProviderConverter { * @param config {@link JSONObject} * @return {@link IdentityProviderConfig} */ - default IdentityProviderConfig getIdentityProviderConfig(IdentityProviderType type, - JSONObject config) { + default IdentityProviderConfig getIdentityProviderConfig(String type, JSONObject config) { //开始处理不同提供商的配置 IdentityProviderConfig identityProviderConfig; - switch (type) { - //微信扫码 - case WECHAT_SCAN_CODE -> - identityProviderConfig = config.to(WeChatIdpScanCodeConfig.class); + //微信扫码 + if (type.equals(WECHAT_QR.value())) { + identityProviderConfig = config.to(WeChatIdpScanCodeConfig.class); //钉钉扫码 - case DINGTALK_SCAN_CODE -> - identityProviderConfig = config.to(DingTalkIdpScanCodeConfig.class); + } else if (type.equals(DINGTALK_QR.value())) { + identityProviderConfig = config.to(DingTalkIdpScanCodeConfig.class); //钉钉Oauth - case DINGTALK_OAUTH -> identityProviderConfig = config.to(DingTalkIdpOauthConfig.class); + } else if (type.equals(DINGTALK_OAUTH.value())) { + identityProviderConfig = config.to(DingTalkIdpOauthConfig.class); //企业微信扫码 - case WECHATWORK_SCAN_CODE -> - identityProviderConfig = config.to(WeChatWorkIdpScanCodeConfig.class); + } else if (type.equals(WECHAT_WORK_QR.value())) { + identityProviderConfig = config.to(WeChatWorkIdpScanCodeConfig.class); //QQ认证 - case QQ -> identityProviderConfig = config.to(QqIdpOauthConfig.class); - default -> throw new TopIamException("不支持此身份提供商"); + } else if (type.equals(QQ.value())) { + identityProviderConfig = config.to(QqIdpOauthConfig.class); + //飞书认证 + } else if (type.equals(FEISHU_OAUTH.value())) { + identityProviderConfig = config.to(FeiShuIdpScanCodeConfig.class); + } else { + throw new TopIamException("不支持此身份提供商"); } if (!Objects.nonNull(identityProviderConfig)) { throw new NullPointerException("提供商配置不能为空"); @@ -262,4 +272,41 @@ public interface IdentityProviderConverter { } return identityProviderConfig; } + + /** + * getIdentityProviderType + * + * @param type {@link String} + * @return {@link IdentityProviderType} + */ + static IdentityProviderType getIdentityProviderType(String type) { + if (type.equals(FEISHU_OAUTH.value())) { + return FEISHU_OAUTH; + } + if (type.equals(DINGTALK_OAUTH.value())) { + return DINGTALK_OAUTH; + } + if (type.equals(DINGTALK_QR.value())) { + return DINGTALK_QR; + } + if (type.equals(WECHAT_QR.value())) { + return WECHAT_QR; + } + if (type.equals(WECHAT_WORK_QR.value())) { + return WECHAT_WORK_QR; + } + if (type.equals(QQ.value())) { + return QQ; + } + if (type.equals(LDAP.value())) { + return LDAP; + } + if (type.equals(USERNAME_PASSWORD.value())) { + return USERNAME_PASSWORD; + } + if (type.equals(SMS.value())) { + return SMS; + } + throw new IllegalArgumentException("未知身份提供商类型"); + } } diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/converter/identitysource/IdentitySourceConverter.java b/eiam-console/src/main/java/cn/topiam/employee/console/converter/identitysource/IdentitySourceConverter.java index c1804020..56a7f73c 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/converter/identitysource/IdentitySourceConverter.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/converter/identitysource/IdentitySourceConverter.java @@ -39,7 +39,7 @@ import com.querydsl.core.types.Predicate; import cn.topiam.employee.common.constants.CommonConstants; import cn.topiam.employee.common.entity.identitysource.IdentitySourceEntity; import cn.topiam.employee.common.entity.identitysource.QIdentitySourceEntity; -import cn.topiam.employee.common.enums.identityprovider.IdentitySourceProvider; +import cn.topiam.employee.common.enums.identitysource.IdentitySourceProvider; import cn.topiam.employee.console.pojo.query.identity.IdentitySourceListQuery; import cn.topiam.employee.console.pojo.result.identitysource.IdentitySourceConfigGetResult; import cn.topiam.employee.console.pojo.result.identitysource.IdentitySourceGetResult; @@ -236,7 +236,8 @@ public interface IdentitySourceConverter { PageModel pageModel) { QueryDslRequest request = new QueryDslRequest(); QIdentitySourceEntity queryEntity = QIdentitySourceEntity.identitySourceEntity; - Predicate predicate = queryEntity.isNotNull(); + Predicate predicate = ExpressionUtils.and(queryEntity.isNotNull(), + queryEntity.isDeleted.eq(Boolean.FALSE)); //查询条件 //@formatter:off predicate = StringUtils.isBlank(query.getName()) ? predicate : ExpressionUtils.and(predicate, queryEntity.name.like("%" + query.getName() + "%")); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/converter/identitysource/IdentitySourceEventRecordConverter.java b/eiam-console/src/main/java/cn/topiam/employee/console/converter/identitysource/IdentitySourceEventRecordConverter.java index 17aa9d05..1275ceb6 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/converter/identitysource/IdentitySourceEventRecordConverter.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/converter/identitysource/IdentitySourceEventRecordConverter.java @@ -54,7 +54,8 @@ public interface IdentitySourceEventRecordConverter { */ default Predicate queryIdentitySourceEventRecordListQueryConvertToPredicate(IdentitySourceEventRecordListQuery query) { QIdentitySourceEventRecordEntity queryEntity = QIdentitySourceEventRecordEntity.identitySourceEventRecordEntity; - Predicate predicate = queryEntity.isNotNull(); + Predicate predicate = ExpressionUtils.and(queryEntity.isNotNull(), + queryEntity.isDeleted.eq(Boolean.FALSE)); //查询条件 //@formatter:off predicate = StringUtils.isBlank(query.getIdentitySourceId()) ? predicate : ExpressionUtils.and(predicate, queryEntity.identitySourceId.eq(Long.valueOf(query.getIdentitySourceId()))); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/converter/identitysource/IdentitySourceSyncConverter.java b/eiam-console/src/main/java/cn/topiam/employee/console/converter/identitysource/IdentitySourceSyncConverter.java index c4b2fddb..4be08506 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/converter/identitysource/IdentitySourceSyncConverter.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/converter/identitysource/IdentitySourceSyncConverter.java @@ -58,7 +58,8 @@ public interface IdentitySourceSyncConverter { */ default Predicate queryIdentitySourceSyncHistoryListQueryConvertToPredicate(IdentitySourceSyncHistoryListQuery query) { QIdentitySourceSyncHistoryEntity queryEntity = QIdentitySourceSyncHistoryEntity.identitySourceSyncHistoryEntity; - Predicate predicate = queryEntity.isNotNull(); + Predicate predicate = ExpressionUtils.and(queryEntity.isNotNull(), + queryEntity.isDeleted.eq(Boolean.FALSE)); //查询条件 //@formatter:off predicate = StringUtils.isBlank(query.getIdentitySourceId()) ? predicate : ExpressionUtils.and(predicate, queryEntity.identitySourceId.eq(Long.valueOf(query.getIdentitySourceId()))); @@ -156,7 +157,8 @@ public interface IdentitySourceSyncConverter { */ default Predicate queryIdentitySourceSyncRecordListQueryConvertToPredicate(IdentitySourceSyncRecordListQuery query) { QIdentitySourceSyncRecordEntity entity = QIdentitySourceSyncRecordEntity.identitySourceSyncRecordEntity; - Predicate predicate = entity.isNotNull(); + Predicate predicate = ExpressionUtils.and(entity.isNotNull(), + entity.isDeleted.eq(Boolean.FALSE)); //查询条件 //@formatter:off predicate = StringUtils.isBlank(query.getSyncHistoryId()) ? predicate : ExpressionUtils.and(predicate, entity.syncHistoryId.eq(Long.valueOf(query.getSyncHistoryId()))); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/AdministratorConverter.java b/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/AdministratorConverter.java index 4ff19cb4..40187a65 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/AdministratorConverter.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/AdministratorConverter.java @@ -139,7 +139,8 @@ public interface AdministratorConverter { */ default Predicate queryAdministratorListParamConvertToPredicate(AdministratorListQuery query) { QAdministratorEntity user = QAdministratorEntity.administratorEntity; - Predicate predicate = user.isNotNull(); + Predicate predicate = ExpressionUtils.and(user.isNotNull(), + user.isDeleted.eq(Boolean.FALSE)); //查询条件 //@formatter:off predicate = StringUtils.isBlank(query.getUsername()) ? predicate : ExpressionUtils.and(predicate, user.username.eq(query.getUsername())); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/GeoLocationSettingConverter.java b/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/GeoLocationSettingConverter.java index eacc5052..ed5fb140 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/GeoLocationSettingConverter.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/GeoLocationSettingConverter.java @@ -27,6 +27,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import cn.topiam.employee.common.crypto.EncryptionModule; import cn.topiam.employee.common.entity.setting.SettingEntity; import cn.topiam.employee.common.geo.GeoLocationProviderConfig; import cn.topiam.employee.common.geo.maxmind.MaxmindProviderConfig; @@ -53,7 +54,7 @@ public interface GeoLocationSettingConverter { * @return {@link SettingEntity} */ default SettingEntity geoLocationProviderConfigToEntity(GeoIpProviderSaveParam param) { - ObjectMapper objectMapper = new ObjectMapper(); + ObjectMapper objectMapper = EncryptionModule.serializerEncrypt(); // 指定序列化输入的类型 objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); @@ -95,7 +96,7 @@ public interface GeoLocationSettingConverter { } try { String value = entity.getValue(); - ObjectMapper objectMapper = new ObjectMapper(); + ObjectMapper objectMapper = EncryptionModule.deserializerDecrypt(); // 指定序列化输入的类型 objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/MessageSettingConverter.java b/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/MessageSettingConverter.java index f0461835..55c5e904 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/MessageSettingConverter.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/MessageSettingConverter.java @@ -29,6 +29,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import cn.topiam.employee.common.crypto.EncryptContextHelp; +import cn.topiam.employee.common.crypto.EncryptionModule; import cn.topiam.employee.common.entity.setting.SettingEntity; import cn.topiam.employee.common.entity.setting.config.SmsConfig; import cn.topiam.employee.common.enums.MessageNoticeChannel; @@ -44,7 +46,6 @@ import cn.topiam.employee.console.pojo.result.setting.EmailProviderConfigResult; import cn.topiam.employee.console.pojo.save.setting.MailProviderSaveParam; import cn.topiam.employee.console.pojo.save.setting.SmsProviderSaveParam; import cn.topiam.employee.console.pojo.setting.SmsProviderConfigResult; -import cn.topiam.employee.support.util.AesUtils; import cn.topiam.employee.support.validation.ValidationHelp; import static cn.topiam.employee.core.context.SettingContextHelp.getSmsProviderConfig; import static cn.topiam.employee.core.setting.constant.MessageSettingConstants.MESSAGE_PROVIDER_EMAIL; @@ -73,7 +74,7 @@ public interface MessageSettingConverter { MailProviderConfig.MailProviderConfigBuilder builder = MailProviderConfig.builder() .username(param.getUsername()) - .secret(AesUtils.encrypt(param.getSecret())); + .secret(EncryptContextHelp.encrypt(param.getSecret())); //根据提供商封装参数 if (MailProvider.CUSTOMIZE.equals(param.getProvider())) { desc = desc + MailProvider.CUSTOMIZE.getName(); @@ -134,13 +135,12 @@ public interface MessageSettingConverter { ValidationHelp.ValidationResult validationResult = null; String desc = MessageNoticeChannel.SMS.getDesc(); SmsProviderConfig providerConfig = new SmsProviderConfig(); - ObjectMapper objectMapper = new ObjectMapper(); + ObjectMapper objectMapper = EncryptionModule.deserializerEncrypt(); try { // 七牛云 if (SmsProvider.QINIU.equals(param.getProvider())) { QiNiuSmsProviderConfig smsConfig = objectMapper.readValue(param.getConfig().toJSONString(), QiNiuSmsProviderConfig.class); validationResult = ValidationHelp.validateEntity(smsConfig); - smsConfig.setSecretKey(AesUtils.encrypt(smsConfig.getSecretKey())); providerConfig = smsConfig; desc = desc + SmsProvider.QINIU.getDesc(); } @@ -148,7 +148,6 @@ public interface MessageSettingConverter { else if (SmsProvider.ALIYUN.equals(param.getProvider())) { AliyunSmsProviderConfig smsConfig = objectMapper.readValue(param.getConfig().toJSONString(), AliyunSmsProviderConfig.class); validationResult = ValidationHelp.validateEntity(smsConfig); - smsConfig.setAccessKeySecret(AesUtils.encrypt(smsConfig.getAccessKeySecret())); providerConfig = smsConfig; desc = desc + SmsProvider.ALIYUN.getDesc(); } @@ -156,7 +155,6 @@ public interface MessageSettingConverter { else if (SmsProvider.TENCENT.equals(param.getProvider())) { TencentSmsProviderConfig smsConfig = objectMapper.readValue(param.getConfig().toJSONString(), TencentSmsProviderConfig.class); validationResult = ValidationHelp.validateEntity(smsConfig); - smsConfig.setSecretKey(AesUtils.encrypt(smsConfig.getSecretKey())); providerConfig = smsConfig; desc = desc + SmsProvider.TENCENT.getDesc(); } @@ -205,7 +203,7 @@ public interface MessageSettingConverter { .port(setting.getPort()) .safetyType(setting.getSafetyType()) .username(setting.getUsername()) - .secret(AesUtils.decrypt(setting.getSecret())) + .secret(EncryptContextHelp.decrypt(setting.getSecret())) .smtpUrl(setting.getSmtpUrl()) .enabled(true) .build(); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/SecuritySettingConverter.java b/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/SecuritySettingConverter.java index 0835beab..7a80a980 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/SecuritySettingConverter.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/SecuritySettingConverter.java @@ -32,6 +32,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import cn.topiam.employee.authentication.captcha.geetest.GeeTestCaptchaProviderConfig; import cn.topiam.employee.common.entity.setting.SettingEntity; import cn.topiam.employee.common.enums.CaptchaProviderType; import cn.topiam.employee.common.enums.MfaFactor; @@ -43,7 +44,6 @@ import cn.topiam.employee.console.pojo.save.setting.SecurityBasicSaveParam; import cn.topiam.employee.console.pojo.save.setting.SecurityCaptchaSaveParam; import cn.topiam.employee.console.pojo.save.setting.SecurityMfaSaveParam; import cn.topiam.employee.core.security.captcha.CaptchaProviderConfig; -import cn.topiam.employee.core.security.captcha.geetest.GeeTestCaptchaProviderConfig; import cn.topiam.employee.support.validation.ValidationHelp; import static cn.topiam.employee.core.setting.constant.MfaSettingConstants.*; import static cn.topiam.employee.core.setting.constant.SecuritySettingConstants.*; diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/StorageSettingConverter.java b/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/StorageSettingConverter.java index 75257233..2613062e 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/StorageSettingConverter.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/converter/setting/StorageSettingConverter.java @@ -27,6 +27,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import cn.topiam.employee.common.crypto.EncryptionModule; import cn.topiam.employee.common.entity.setting.SettingEntity; import cn.topiam.employee.common.storage.StorageConfig; import cn.topiam.employee.common.storage.enums.StorageProvider; @@ -59,7 +60,7 @@ public interface StorageSettingConverter { ValidationHelp.ValidationResult validationResult = null; StorageConfig.StorageConfigBuilder builder = StorageConfig.builder(); builder.provider(provider); - ObjectMapper objectMapper = new ObjectMapper(); + ObjectMapper objectMapper = EncryptionModule.deserializerEncrypt(); try { //阿里云 if (provider.equals(StorageProvider.ALIYUN_OSS)) { @@ -121,7 +122,7 @@ public interface StorageSettingConverter { if (Objects.isNull(entity)) { return StorageProviderConfigResult.builder().enabled(false).build(); } - ObjectMapper objectMapper = new ObjectMapper(); + ObjectMapper objectMapper = EncryptionModule.deserializerDecrypt(); // 指定序列化输入的类型 objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); @@ -133,7 +134,7 @@ public interface StorageSettingConverter { return StorageProviderConfigResult.builder() .provider(storageConfig.getProvider()) .enabled(true) - .config(storageConfig).build(); + .config(storageConfig.getConfig()).build(); //@formatter:on } catch (JsonProcessingException e) { throw new RuntimeException(e); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/listener/ConsoleAesSecretInitializeListener.java b/eiam-console/src/main/java/cn/topiam/employee/console/listener/ConsoleAesSecretInitializeListener.java new file mode 100644 index 00000000..77af6e72 --- /dev/null +++ b/eiam-console/src/main/java/cn/topiam/employee/console/listener/ConsoleAesSecretInitializeListener.java @@ -0,0 +1,108 @@ +/* + * eiam-console - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.console.listener; + +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.JdkIdGenerator; + +import cn.topiam.employee.common.entity.setting.SettingEntity; +import cn.topiam.employee.common.repository.setting.SettingRepository; +import cn.topiam.employee.support.trace.TraceUtils; +import cn.topiam.employee.support.util.AesUtils; +import static cn.topiam.employee.common.constants.SettingConstants.AES_SECRET; +import static cn.topiam.employee.support.constant.EiamConstants.COLON; +import static cn.topiam.employee.support.lock.LockAspect.getTopiamLockKeyPrefix; + +/** + * ConsoleAesInitializeListener + * + * @author TopIAM + * Created by support@topiam.cn on 2022/12/22 21:44 + */ +@Component +public class ConsoleAesSecretInitializeListener implements + ApplicationListener { + + @Override + @Transactional(rollbackFor = Exception.class) + public void onApplicationEvent(@NonNull ApplicationPreparedEvent applicationPreparedEvent) { + //@formatter:off + String traceId = jdkIdGenerator.generateId().toString(); + TraceUtils.put(traceId); + RLock lock = redissonClient.getLock(getTopiamLockKeyPrefix() + COLON + "aes"); + boolean tryLock = false; + try { + tryLock = lock.tryLock(1, TimeUnit.SECONDS); + if (tryLock){ + SettingEntity optional = settingRepository.findByName(AES_SECRET); + if (Objects.isNull(optional)) { + // 保存AES秘钥 + saveInitAesSecret(AesUtils.generateKey()); + } + } + + } catch (Exception exception) { + int exitCode = SpringApplication.exit(applicationPreparedEvent.getApplicationContext(), + () -> 0); + System.exit(exitCode); + } finally { + if (tryLock && lock.isLocked()) { + lock.unlock(); + } + TraceUtils.remove(); + } + //@formatter:on + } + + /** + * 保存管理员 + * + * @param secret {@link String} + */ + private void saveInitAesSecret(String secret) { + SettingEntity setting = new SettingEntity(); + setting.setName(AES_SECRET); + setting.setValue(secret); + setting.setDesc("Project aes secret"); + setting.setRemark("This aes secret is automatically created during system initialization."); + settingRepository.save(setting); + } + + private final JdkIdGenerator jdkIdGenerator = new JdkIdGenerator(); + + private final SettingRepository settingRepository; + + private final RedissonClient redissonClient; + + public ConsoleAesSecretInitializeListener(SettingRepository settingRepository, + RedissonClient redissonClient) { + this.settingRepository = settingRepository; + this.redissonClient = redissonClient; + } + +} diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/other/IdentitySourceConfigValidatorParam.java b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/other/IdentitySourceConfigValidatorParam.java index 0f3e59d4..961e3ffc 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/other/IdentitySourceConfigValidatorParam.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/other/IdentitySourceConfigValidatorParam.java @@ -26,7 +26,7 @@ import org.springdoc.api.annotations.ParameterObject; import com.alibaba.fastjson2.JSONObject; -import cn.topiam.employee.common.enums.identityprovider.IdentitySourceProvider; +import cn.topiam.employee.common.enums.identitysource.IdentitySourceProvider; import lombok.Data; diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/query/analysis/AnalysisQuery.java b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/query/analysis/AnalysisQuery.java index 91eaddd3..bec2a93c 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/query/analysis/AnalysisQuery.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/query/analysis/AnalysisQuery.java @@ -67,11 +67,17 @@ public class AnalysisQuery implements Serializable { @Getter public enum Interval { - + /** + * HOUR + */ HOUR(DateHistogramInterval.HOUR, "HH时"), - + /** + * DAY + */ DAY(DateHistogramInterval.DAY, "dd日"), - + /** + * MONTH + */ MONTH(DateHistogramInterval.MONTH, "MM月"); private final DateHistogramInterval type; diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/query/authentication/IdentityProviderListQuery.java b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/query/authentication/IdentityProviderListQuery.java index 2006df03..01989573 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/query/authentication/IdentityProviderListQuery.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/query/authentication/IdentityProviderListQuery.java @@ -24,8 +24,6 @@ import javax.validation.constraints.NotNull; import org.springdoc.api.annotations.ParameterObject; -import cn.topiam.employee.common.enums.IdentityProviderCategory; - import lombok.Data; import io.swagger.v3.oas.annotations.Parameter; @@ -41,19 +39,19 @@ import io.swagger.v3.oas.annotations.media.Schema; public class IdentityProviderListQuery implements Serializable { @Serial - private static final long serialVersionUID = 1191998425971892318L; + private static final long serialVersionUID = 1191998425971892318L; /** * 认证源ID */ @Parameter(description = "认证源名称") - private String name; + private String name; /** * 认证源类型 */ @Parameter(description = "认证源分类") @NotNull(message = "认证源分类不能为空") - private IdentityProviderCategory category; + private String category; } diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/analysis/AuthnZoneResult.java b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/analysis/AuthnZoneResult.java new file mode 100644 index 00000000..13cbe992 --- /dev/null +++ b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/analysis/AuthnZoneResult.java @@ -0,0 +1,49 @@ +/* + * eiam-console - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.console.pojo.result.analysis; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * 登录区域结果 + * + * @author TopIAM + * Created by support@topiam.cn on 2023/01/24 23:16 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "登录区域结果") +public class AuthnZoneResult { + + /** + * 省份code + */ + @Schema(description = "省份code") + private String name; + + /** + * 登录次数 + */ + @Schema(description = "登录次数") + private Long count; +} diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/app/UserIdpBindListResult.java b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/app/UserIdpBindListResult.java index 9524e6a5..b94a562c 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/app/UserIdpBindListResult.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/app/UserIdpBindListResult.java @@ -19,8 +19,6 @@ package cn.topiam.employee.console.pojo.result.app; import java.time.LocalDateTime; -import cn.topiam.employee.common.enums.IdentityProviderType; - import lombok.Data; import io.swagger.v3.oas.annotations.media.Schema; @@ -39,29 +37,29 @@ public class UserIdpBindListResult { * id */ @Schema(description = "id") - private String id; + private String id; /** * open id */ @Schema(description = "open id") - private Long openId; + private String openId; /** * 提供商名称 */ @Schema(description = "提供商名称") - private String idpName; + private String idpName; /** * 提供商类型 */ @Schema(description = "提供商类型") - private IdentityProviderType idpType; + private String idpType; /** * 绑定时间 */ @Schema(description = "绑定时间") - private LocalDateTime bindTime; + private LocalDateTime bindTime; } diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/authentication/IdentityProviderCreateResult.java b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/authentication/IdentityProviderCreateResult.java index a5dd4d0b..e7056bd3 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/authentication/IdentityProviderCreateResult.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/authentication/IdentityProviderCreateResult.java @@ -19,8 +19,6 @@ package cn.topiam.employee.console.pojo.result.authentication; import java.io.Serializable; -import cn.topiam.employee.common.enums.IdentityProviderType; - import lombok.Builder; import lombok.Data; @@ -42,11 +40,11 @@ public class IdentityProviderCreateResult implements Serializable { * ID */ @Parameter(description = "ID") - private String id; + private String id; /** * 提供商类型 */ @Parameter(description = "提供商类型") - private IdentityProviderType type; + private String type; } diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/authentication/IdentityProviderListResult.java b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/authentication/IdentityProviderListResult.java index 04658241..63f780a7 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/authentication/IdentityProviderListResult.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/authentication/IdentityProviderListResult.java @@ -19,8 +19,6 @@ package cn.topiam.employee.console.pojo.result.authentication; import java.io.Serializable; -import cn.topiam.employee.common.enums.IdentityProviderType; - import lombok.Data; import io.swagger.v3.oas.annotations.Parameter; @@ -40,35 +38,35 @@ public class IdentityProviderListResult implements Serializable { * ID */ @Parameter(description = "ID") - private String id; + private String id; /** * name */ @Parameter(description = "名称") - private String name; + private String name; /** * 提供商 */ @Parameter(description = "提供商") - private IdentityProviderType type; + private String type; /** * 是否启用 */ @Parameter(description = "是否启用") - private Boolean enabled; + private Boolean enabled; /** * 描述 */ @Parameter(description = "描述") - private String desc; + private String desc; /** * 备注 */ @Parameter(description = "备注") - private String remark; + private String remark; } diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/authentication/IdentityProviderResult.java b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/authentication/IdentityProviderResult.java index 02e3916f..84670064 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/authentication/IdentityProviderResult.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/authentication/IdentityProviderResult.java @@ -21,7 +21,6 @@ import java.io.Serial; import java.io.Serializable; import cn.topiam.employee.authentication.common.config.IdentityProviderConfig; -import cn.topiam.employee.common.enums.IdentityProviderType; import lombok.Data; @@ -56,7 +55,7 @@ public class IdentityProviderResult implements Serializable { * 提供商类型 */ @Parameter(description = "提供商类型") - private IdentityProviderType type; + private String type; /** * 配置 diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/identitysource/IdentitySourceGetResult.java b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/identitysource/IdentitySourceGetResult.java index 871c9790..754af50d 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/identitysource/IdentitySourceGetResult.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/identitysource/IdentitySourceGetResult.java @@ -20,7 +20,7 @@ package cn.topiam.employee.console.pojo.result.identitysource; import java.io.Serial; import java.io.Serializable; -import cn.topiam.employee.common.enums.identityprovider.IdentitySourceProvider; +import cn.topiam.employee.common.enums.identitysource.IdentitySourceProvider; import lombok.Data; diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/setting/StorageProviderConfigResult.java b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/setting/StorageProviderConfigResult.java index d5e74bb2..8b2cf1da 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/setting/StorageProviderConfigResult.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/result/setting/StorageProviderConfigResult.java @@ -41,21 +41,21 @@ import io.swagger.v3.oas.annotations.media.Schema; public class StorageProviderConfigResult implements Serializable { @Serial - private static final long serialVersionUID = -2667374916357438335L; + private static final long serialVersionUID = -2667374916357438335L; /** * 服务商 */ @Parameter(description = "服务商") - private StorageProvider provider; + private StorageProvider provider; /** * 启用 */ @Parameter(description = "是否启用") - private Boolean enabled; + private Boolean enabled; /** * 配置信息 */ @Parameter(description = "配置信息") - private StorageConfig config; + private StorageConfig.Config config; } diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/save/app/AppAccountCreateParam.java b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/save/app/AppAccountCreateParam.java index ee69d078..65977cd0 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/save/app/AppAccountCreateParam.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/save/app/AppAccountCreateParam.java @@ -54,4 +54,10 @@ public class AppAccountCreateParam { @Schema(description = "账户名称") @NotBlank(message = "账户名称不能为空") private String account; + + /** + * 账户密码 + */ + @Schema(description = "账户密码") + private String password; } diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/save/authentication/IdentityProviderCreateParam.java b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/save/authentication/IdentityProviderCreateParam.java index eebb6876..f3fb5563 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/save/authentication/IdentityProviderCreateParam.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/save/authentication/IdentityProviderCreateParam.java @@ -25,9 +25,6 @@ import javax.validation.constraints.NotNull; import com.alibaba.fastjson2.JSONObject; -import cn.topiam.employee.common.enums.IdentityProviderCategory; -import cn.topiam.employee.common.enums.IdentityProviderType; - import lombok.Data; import io.swagger.v3.oas.annotations.media.Schema; @@ -42,46 +39,46 @@ import io.swagger.v3.oas.annotations.media.Schema; @Schema(description = "认证源保存入参") public class IdentityProviderCreateParam implements Serializable { @Serial - private static final long serialVersionUID = -1440230086940289961L; + private static final long serialVersionUID = -1440230086940289961L; /** * 认证源名称 */ @NotBlank(message = "认证源名称不能为空") @Schema(description = "认证源名称") - private String name; + private String name; /** * 提供商 */ @NotNull(message = "提供商不能为空") @Schema(description = "提供商") - private IdentityProviderType type; + private String type; /** * 身份源类型 */ @NotNull(message = "身份源类型不能为空") @Schema(description = "身份源类型") - private IdentityProviderCategory category; + private String category; /** * 配置 */ @NotNull(message = "配置不能为空") @Schema(description = "配置JSON") - private JSONObject config; + private JSONObject config; /** * 是否展示 */ @Schema(description = "是否展示") - private Boolean displayed; + private Boolean displayed; /** * 备注 */ @Schema(description = "备注") - private String remark; + private String remark; } diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/save/identitysource/IdentitySourceCreateParam.java b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/save/identitysource/IdentitySourceCreateParam.java index b6e611c9..2e6650f5 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/save/identitysource/IdentitySourceCreateParam.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/save/identitysource/IdentitySourceCreateParam.java @@ -23,7 +23,7 @@ import java.io.Serializable; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; -import cn.topiam.employee.common.enums.identityprovider.IdentitySourceProvider; +import cn.topiam.employee.common.enums.identitysource.IdentitySourceProvider; import lombok.Data; diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/update/authentication/IdpUpdateParam.java b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/update/authentication/IdpUpdateParam.java index 8ee8e6dc..e1557902 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/pojo/update/authentication/IdpUpdateParam.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/pojo/update/authentication/IdpUpdateParam.java @@ -25,8 +25,6 @@ import javax.validation.constraints.NotNull; import com.alibaba.fastjson2.JSONObject; -import cn.topiam.employee.common.enums.IdentityProviderType; - import lombok.Data; import io.swagger.v3.oas.annotations.media.Schema; @@ -41,44 +39,44 @@ import io.swagger.v3.oas.annotations.media.Schema; @Schema(description = "认证源修改参数") public class IdpUpdateParam implements Serializable { @Serial - private static final long serialVersionUID = -1440230086940289961L; + private static final long serialVersionUID = -1440230086940289961L; /** * ID */ @NotBlank(message = "ID不能为空") @Schema(description = "ID") - private String id; + private String id; /** * 名称 */ @NotBlank(message = "名称不能为空") @Schema(description = "名称") - private String name; + private String name; /** * 平台 */ @NotNull(message = "平台不能为空") @Schema(description = "平台") - private IdentityProviderType type; + private String type; /** * 配置 */ @NotNull(message = "配置JSON不能为空") @Schema(description = "配置JSON") - private JSONObject config; + private JSONObject config; /** * 备注 */ @Schema(description = "备注") - private String remark; + private String remark; /** * 是否显示 */ @Schema(description = "是否显示") - private Boolean displayed; + private Boolean displayed; } diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/security/listener/ConsoleAuthenticationSuccessEventListener.java b/eiam-console/src/main/java/cn/topiam/employee/console/security/listener/ConsoleAuthenticationSuccessEventListener.java index 73f6fcc2..1852141e 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/security/listener/ConsoleAuthenticationSuccessEventListener.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/security/listener/ConsoleAuthenticationSuccessEventListener.java @@ -19,13 +19,11 @@ package cn.topiam.employee.console.security.listener; import java.time.LocalDateTime; import java.util.List; -import java.util.Objects; import org.springframework.context.ApplicationListener; import org.springframework.lang.NonNull; import org.springframework.security.authentication.event.AuthenticationSuccessEvent; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.security.core.Authentication; import com.google.common.collect.Lists; @@ -33,13 +31,13 @@ import cn.topiam.employee.audit.entity.Target; import cn.topiam.employee.audit.enums.EventStatus; import cn.topiam.employee.audit.enums.TargetType; import cn.topiam.employee.audit.event.AuditEventPublish; +import cn.topiam.employee.authentication.common.util.AuthenticationUtils; import cn.topiam.employee.common.geo.GeoLocationService; +import cn.topiam.employee.console.service.setting.AdministratorService; import cn.topiam.employee.core.security.userdetails.UserDetails; import cn.topiam.employee.support.context.ApplicationContextHelp; import cn.topiam.employee.support.context.ServletContextHelp; import cn.topiam.employee.support.util.IpUtils; -import cn.topiam.employee.support.web.useragent.UserAgent; -import cn.topiam.employee.support.web.useragent.UserAgentUtils; import lombok.AllArgsConstructor; import static cn.topiam.employee.audit.enums.EventType.LOGIN_CONSOLE; @@ -61,24 +59,24 @@ public class ConsoleAuthenticationSuccessEventListener implements */ @Override public void onApplicationEvent(@NonNull AuthenticationSuccessEvent event) { - ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder - .getRequestAttributes(); - AuditEventPublish auditEventPublish = ApplicationContextHelp - .getBean(AuditEventPublish.class); - Object principal = event.getAuthentication().getPrincipal(); //@formatter:off + AuditEventPublish auditEventPublish = ApplicationContextHelp.getBean(AuditEventPublish.class); + AdministratorService administratorService = ApplicationContextHelp.getBean(AdministratorService.class); + Authentication authentication = event.getAuthentication(); + Object principal = authentication.getPrincipal(); //@formatter:on if (principal instanceof UserDetails) { + //认证类型 + ((UserDetails) principal).setAuthType(AuthenticationUtils.geAuthType(authentication)); //登录事件 ((UserDetails) principal).setLoginTime(LocalDateTime.now()); //区域 ((UserDetails) principal).setGeoLocation(geoLocationService .getGeoLocation(IpUtils.getIpAddr(ServletContextHelp.getRequest()))); - //浏览器 - UserAgent agent = UserAgentUtils - .getUserAgent(Objects.requireNonNull(attributes).getRequest()); - ((UserDetails) principal).setUserAgent(agent); - + //认证次数+1 + administratorService.updateAuthSucceedInfo(((UserDetails) principal).getId(), + ((UserDetails) principal).getGeoLocation().getIp(), + ((UserDetails) principal).getLoginTime()); // 审计事件 //@formatter:off List targets= Lists.newArrayList(Target.builder().type(TargetType.CONSOLE).build()); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/security/listener/ConsoleLogoutSuccessEventListener.java b/eiam-console/src/main/java/cn/topiam/employee/console/security/listener/ConsoleLogoutSuccessEventListener.java index f9cd1e4f..b55f372e 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/security/listener/ConsoleLogoutSuccessEventListener.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/security/listener/ConsoleLogoutSuccessEventListener.java @@ -30,7 +30,7 @@ import cn.topiam.employee.audit.enums.EventStatus; import cn.topiam.employee.audit.enums.TargetType; import cn.topiam.employee.audit.event.AuditEventPublish; import cn.topiam.employee.support.context.ApplicationContextHelp; -import static cn.topiam.employee.audit.enums.EventType.*; +import static cn.topiam.employee.audit.enums.EventType.LOGOUT_CONSOLE; /** * 退出成功 diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/account/OrganizationService.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/account/OrganizationService.java index b9d8a2ba..7036a205 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/account/OrganizationService.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/account/OrganizationService.java @@ -146,4 +146,12 @@ public interface OrganizationService { * @return {@link Boolean} */ Boolean batchDeleteOrg(String[] ids); + + /** + * 查询组织成员数量 + * + * @param orgId {@link String} + * @return {@link Long} + */ + Long getOrgMemberCount(String orgId); } diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/account/UserGroupService.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/account/UserGroupService.java index e6277c3f..13a2750c 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/account/UserGroupService.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/account/UserGroupService.java @@ -113,4 +113,12 @@ public interface UserGroupService { * @return {@link Boolean} */ Boolean batchRemoveMember(String id, List userIds); + + /** + * 查询用户组成员数量 + * + * @param groupId {@link String} + * @return {@link Long} + */ + Long getUserGroupMemberCount(String groupId); } diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/account/UserService.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/account/UserService.java index d4db4880..7926b3af 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/account/UserService.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/account/UserService.java @@ -146,14 +146,6 @@ public interface UserService { */ Boolean userParamCheck(CheckValidityType type, String value, Long id); - /** - * 查询组织成员数量 - * - * @param orgId {@link String} - * @return {@link Long} - */ - Long getOrgMemberCount(String orgId); - /** * 批量删除 * diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/account/impl/OrganizationServiceImpl.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/account/impl/OrganizationServiceImpl.java index f885c0b6..152ef2fe 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/account/impl/OrganizationServiceImpl.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/account/impl/OrganizationServiceImpl.java @@ -25,11 +25,20 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import com.google.common.collect.Lists; +import com.querydsl.core.types.ExpressionUtils; +import com.querydsl.core.types.Predicate; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.core.types.dsl.Expressions; +import com.querydsl.jpa.impl.JPAQuery; +import com.querydsl.jpa.impl.JPAQueryFactory; import cn.topiam.employee.audit.context.AuditContext; import cn.topiam.employee.audit.entity.Target; import cn.topiam.employee.audit.enums.TargetType; import cn.topiam.employee.common.entity.account.OrganizationEntity; +import cn.topiam.employee.common.entity.account.QOrganizationEntity; +import cn.topiam.employee.common.entity.account.QOrganizationMemberEntity; +import cn.topiam.employee.common.entity.account.QUserEntity; import cn.topiam.employee.common.enums.DataOrigin; import cn.topiam.employee.common.repository.account.OrganizationRepository; import cn.topiam.employee.console.converter.account.OrganizationConverter; @@ -40,8 +49,6 @@ import cn.topiam.employee.console.pojo.result.account.OrganizationTreeResult; import cn.topiam.employee.console.pojo.save.account.OrganizationCreateParam; import cn.topiam.employee.console.pojo.update.account.OrganizationUpdateParam; import cn.topiam.employee.console.service.account.OrganizationService; -import cn.topiam.employee.console.service.account.UserService; -import cn.topiam.employee.support.context.ApplicationContextHelp; import cn.topiam.employee.support.util.BeanUtils; import lombok.RequiredArgsConstructor; @@ -207,8 +214,7 @@ public class OrganizationServiceImpl implements OrganizationService { List list = organizationRepository.findByParentId(id); if (CollectionUtils.isEmpty(list)) { //查询当前机构和当前机构下子机构下是否存在用户,不存在删除,存在抛出异常 - Long count = ApplicationContextHelp.getBean(UserService.class) - .getOrgMemberCount(id); + Long count = getOrgMemberCount(id); if (count > 0) { throw new RuntimeException("删除机构失败,当前机构下存在用户"); } @@ -251,8 +257,6 @@ public class OrganizationServiceImpl implements OrganizationService { */ @Override public Boolean moveOrganization(String id, String parentId) { - AuditContext.setTarget(Target.builder().type(TargetType.ORGANIZATION).id(id).build(), - Target.builder().type(TargetType.ORGANIZATION).id(parentId).build()); Optional organization = organizationRepository.findById(id); if (organization.isPresent()) { OrganizationEntity entity = organization.get(); @@ -282,6 +286,13 @@ public class OrganizationServiceImpl implements OrganizationService { organizationRepository.save(oldParentOrganization.get()); } } + AuditContext.setTarget( + Target.builder().type(TargetType.ORGANIZATION) + .typeName(TargetType.ORGANIZATION.getDesc()).id(id) + .name(organization.get().getName()).build(), + Target.builder().type(TargetType.ORGANIZATION) + .typeName(TargetType.ORGANIZATION.getDesc()).id(parentId) + .name(parent.get().getName()).build()); //存在子组织,递归更改子组织 path 和 displayPath recursiveUpdateChildNodePathAndDisplayPath(entity.getId()); return true; @@ -403,10 +414,43 @@ public class OrganizationServiceImpl implements OrganizationService { return true; } + /** + * 查询组织成员数量 + * + * @param orgId {@link String} + * @return {@link Long} + */ + @Override + public Long getOrgMemberCount(String orgId) { + //条件 + QUserEntity user = QUserEntity.userEntity; + QOrganizationEntity qOrganization = QOrganizationEntity.organizationEntity; + Predicate predicate = ExpressionUtils.and(user.isNotNull(), + user.isDeleted.eq(Boolean.FALSE)); + //FIND_IN_SET函数 + BooleanExpression template = Expressions.booleanTemplate( + "FIND_IN_SET({0}, replace({1}, '/', ','))> 0", orgId, qOrganization.path); + predicate = ExpressionUtils.and(predicate, qOrganization.id.eq(orgId).or(template)); + //构造查询 + JPAQuery jpaQuery = jpaQueryFactory.selectFrom(user).select(user.count()) + .innerJoin(QOrganizationMemberEntity.organizationMemberEntity) + .on(user.id.eq(QOrganizationMemberEntity.organizationMemberEntity.userId)) + .innerJoin(qOrganization) + .on(qOrganization.id.eq(QOrganizationMemberEntity.organizationMemberEntity.orgId)) + .where(predicate); + return jpaQuery.fetch().get(0); + } + + /** + * JPAQueryFactory + */ + private final JPAQueryFactory jpaQueryFactory; + /** * 组织架构数据映射器 */ private final OrganizationConverter orgDataConverter; + /** * OrganizationRepository */ diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/account/impl/UserGroupServiceImpl.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/account/impl/UserGroupServiceImpl.java index 67fee32c..a22fed17 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/account/impl/UserGroupServiceImpl.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/account/impl/UserGroupServiceImpl.java @@ -22,19 +22,20 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; -import org.apache.commons.lang3.StringUtils; import org.springframework.data.domain.PageRequest; import org.springframework.data.querydsl.QPageRequest; import org.springframework.stereotype.Service; import com.google.common.collect.Lists; +import com.querydsl.core.types.ExpressionUtils; import com.querydsl.core.types.Predicate; +import com.querydsl.jpa.impl.JPAQuery; +import com.querydsl.jpa.impl.JPAQueryFactory; import cn.topiam.employee.audit.context.AuditContext; import cn.topiam.employee.audit.entity.Target; import cn.topiam.employee.audit.enums.TargetType; -import cn.topiam.employee.common.entity.account.UserGroupEntity; -import cn.topiam.employee.common.entity.account.UserGroupMemberEntity; +import cn.topiam.employee.common.entity.account.*; import cn.topiam.employee.common.entity.account.po.UserPO; import cn.topiam.employee.common.entity.account.query.UserGroupMemberListQuery; import cn.topiam.employee.common.repository.account.UserGroupMemberRepository; @@ -132,6 +133,11 @@ public class UserGroupServiceImpl implements UserGroupService { log.warn(AuditContext.getContent()); throw new TopIamException(AuditContext.getContent()); } + //用户组存在用户 + Long count = getUserGroupMemberCount(id); + if (count > 0) { + throw new RuntimeException("删除用户组失败,当前用户组下存在用户"); + } userGroupRepository.deleteById(Long.valueOf(id)); AuditContext.setTarget(Target.builder().id(id).type(TargetType.USER_GROUP).build()); return true; @@ -176,6 +182,13 @@ public class UserGroupServiceImpl implements UserGroupService { */ @Override public Boolean addMember(String groupId, String[] userIds) { + Optional optional = userGroupRepository.findById(Long.valueOf(groupId)); + //用户组不存在 + if (optional.isEmpty()) { + AuditContext.setContent("操作失败,用户组不存在"); + log.warn(AuditContext.getContent()); + throw new TopIamException(AuditContext.getContent()); + } List list = new ArrayList<>(); Lists.newArrayList(userIds).forEach(id -> { UserGroupMemberEntity member = new UserGroupMemberEntity(); @@ -183,12 +196,14 @@ public class UserGroupServiceImpl implements UserGroupService { member.setUserId(Long.valueOf(id)); list.add(member); }); - List targets = new ArrayList<>(Arrays.stream(userIds) - .map(i -> Target.builder().id(i).type(TargetType.USER).build()).toList()); - targets.add(Target.builder().id(groupId).type(TargetType.USER_GROUP).build()); - AuditContext.setTarget(targets); //添加 userGroupMemberRepository.saveAll(list); + + List targets = new ArrayList<>(Arrays.stream(userIds) + .map(i -> Target.builder().id(i).type(TargetType.USER).build()).toList()); + + targets.add(Target.builder().id(groupId).type(TargetType.USER_GROUP).build()); + AuditContext.setTarget(targets); return true; } @@ -212,14 +227,46 @@ public class UserGroupServiceImpl implements UserGroupService { */ @Override public Boolean batchRemoveMember(String id, List userIds) { + Optional optional = userGroupRepository.findById(Long.valueOf(id)); + //用户组不存在 + if (optional.isEmpty()) { + AuditContext.setContent("操作失败,用户组不存在"); + log.warn(AuditContext.getContent()); + throw new TopIamException(AuditContext.getContent()); + } userIds.forEach(userId -> userGroupMemberRepository .deleteByGroupIdAndUserId(Long.valueOf(id), Long.valueOf(userId))); - AuditContext.setTarget( - Target.builder().id(StringUtils.join(userIds)).type(TargetType.USER).build(), - Target.builder().id(id).type(TargetType.USER_GROUP).build()); + + List targets = new ArrayList<>(userIds.stream() + .map(i -> Target.builder().id(i).type(TargetType.USER).build()).toList()); + + targets.add(Target.builder().id(id).type(TargetType.USER_GROUP).build()); + AuditContext.setTarget(targets); return true; } + @Override + public Long getUserGroupMemberCount(String groupId) { + //条件 + QUserEntity user = QUserEntity.userEntity; + QUserGroupEntity qUserGroup = QUserGroupEntity.userGroupEntity; + Predicate predicate = ExpressionUtils.and(user.isNotNull(), + user.isDeleted.eq(Boolean.FALSE)); + predicate = ExpressionUtils.and(predicate, qUserGroup.id.eq(Long.valueOf(groupId))); + //构造查询 + JPAQuery jpaQuery = jpaQueryFactory.selectFrom(user).select(user.count()) + .innerJoin(QUserGroupMemberEntity.userGroupMemberEntity) + .on(user.id.eq(QUserGroupMemberEntity.userGroupMemberEntity.userId)) + .innerJoin(qUserGroup) + .on(qUserGroup.id.eq(QUserGroupMemberEntity.userGroupMemberEntity.groupId)) + .where(predicate); + return jpaQuery.fetch().get(0); + } + + /** + * JPAQueryFactory + */ + private final JPAQueryFactory jpaQueryFactory; /** * 用户组数据映射 */ diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/account/impl/UserServiceImpl.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/account/impl/UserServiceImpl.java index d80e0a3b..8d44803b 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/account/impl/UserServiceImpl.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/account/impl/UserServiceImpl.java @@ -38,12 +38,7 @@ import org.springframework.util.ObjectUtils; import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.Phonenumber; -import com.querydsl.core.types.ExpressionUtils; -import com.querydsl.core.types.Predicate; import com.querydsl.core.types.dsl.BooleanExpression; -import com.querydsl.core.types.dsl.Expressions; -import com.querydsl.jpa.impl.JPAQuery; -import com.querydsl.jpa.impl.JPAQueryFactory; import cn.topiam.employee.audit.context.AuditContext; import cn.topiam.employee.audit.entity.AuditElasticSearchEntity; @@ -77,7 +72,8 @@ import cn.topiam.employee.support.validation.annotation.ValidationPhone; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import static cn.topiam.employee.audit.enums.TargetType.*; +import static cn.topiam.employee.audit.enums.TargetType.USER; +import static cn.topiam.employee.audit.enums.TargetType.USER_DETAIL; import static cn.topiam.employee.common.constants.AuditConstants.getAuditIndexPrefix; import static cn.topiam.employee.core.message.sms.SmsMsgEventPublish.PASSWORD; import static cn.topiam.employee.core.message.sms.SmsMsgEventPublish.USERNAME; @@ -398,13 +394,14 @@ public class UserServiceImpl implements UserService { * @return {@link Boolean} */ @Override + @Transactional(rollbackFor = Exception.class) public Boolean batchDeleteUser(String[] ids) { //删除用户 userRepository .deleteAllById(Arrays.stream(ids).map(s -> Long.parseLong(s.trim())).toList()); //删除用户详情 userDetailsRepository - .deleteAllByUserId(Arrays.stream(ids).map(s -> Long.parseLong(s.trim())).toList()); + .deleteAllByUserIds(Arrays.stream(ids).map(s -> Long.parseLong(s.trim())).toList()); //删除组织用户关系 organizationMemberRepository .deleteAllByUserId(Arrays.stream(ids).map(s -> Long.parseLong(s.trim())).toList()); @@ -471,33 +468,6 @@ public class UserServiceImpl implements UserService { return result; } - /** - * 查询组织成员数量 - * - * @param orgId {@link String} - * @return {@link Long} - */ - @Override - public Long getOrgMemberCount(String orgId) { - //条件 - QUserEntity user = QUserEntity.userEntity; - QOrganizationEntity qOrganization = QOrganizationEntity.organizationEntity; - Predicate predicate = user.isNotNull(); - //FIND_IN_SET函数 - BooleanExpression template = Expressions.booleanTemplate( - "FIND_IN_SET({0}, replace({1}, '/', ','))> 0", orgId, qOrganization.path); - predicate = StringUtils.isBlank(orgId) ? predicate - : ExpressionUtils.and(predicate, qOrganization.id.eq(orgId).or(template)); - //构造查询 - JPAQuery jpaQuery = jpaQueryFactory.selectFrom(user).select(user.count()) - .innerJoin(QOrganizationMemberEntity.organizationMemberEntity) - .on(user.id.eq(QOrganizationMemberEntity.organizationMemberEntity.userId)) - .innerJoin(qOrganization) - .on(qOrganization.id.eq(QOrganizationMemberEntity.organizationMemberEntity.orgId)) - .where(predicate); - return jpaQuery.fetch().get(0); - } - @Override @Transactional(rollbackFor = Exception.class) public void deleteBatchUser(List removeIds) { @@ -572,11 +542,6 @@ public class UserServiceImpl implements UserService { */ private final UserDetailRepository userDetailsRepository; - /** - * JPAQueryFactory - */ - private final JPAQueryFactory jpaQueryFactory; - /** * 修改密码历史Repository */ diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/analysis/AnalysisService.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/analysis/AnalysisService.java index 119da66a..443cc3b6 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/analysis/AnalysisService.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/analysis/AnalysisService.java @@ -20,9 +20,7 @@ package cn.topiam.employee.console.service.analysis; import java.util.List; import cn.topiam.employee.console.pojo.query.analysis.AnalysisQuery; -import cn.topiam.employee.console.pojo.result.analysis.AppVisitRankResult; -import cn.topiam.employee.console.pojo.result.analysis.AuthnQuantityResult; -import cn.topiam.employee.console.pojo.result.analysis.OverviewResult; +import cn.topiam.employee.console.pojo.result.analysis.*; /** * 统计 service @@ -40,7 +38,7 @@ public interface AnalysisService { OverviewResult overview(); /** - * 认证统计 + * 认证量统计 * * @param params {@link AnalysisQuery} * @return {@link List} @@ -48,10 +46,26 @@ public interface AnalysisService { List authnQuantity(AnalysisQuery params); /** - * 认证统计 + * 应用热点统计 * * @param params {@link AnalysisQuery} - * @return {@link List< AppVisitRankResult >} + * @return {@link List} */ List appVisitRank(AnalysisQuery params); + + /** + * 热门认证方式统计 + * + * @param params {@link AnalysisQuery} + * @return {@link List} + */ + List authnHotProvider(AnalysisQuery params); + + /** + * 登录区域统计 + * + * @param params {@link AnalysisQuery} + * @return {@link List} + */ + List authnZone(AnalysisQuery params); } diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/analysis/impl/AnalysisServiceImpl.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/analysis/impl/AnalysisServiceImpl.java index e6c5a897..981b9969 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/analysis/impl/AnalysisServiceImpl.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/analysis/impl/AnalysisServiceImpl.java @@ -33,7 +33,10 @@ import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.Aggregations; import org.elasticsearch.search.aggregations.BucketOrder; -import org.elasticsearch.search.aggregations.bucket.histogram.*; +import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; +import org.elasticsearch.search.aggregations.bucket.histogram.LongBounds; +import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram; import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; @@ -51,22 +54,24 @@ import org.springframework.util.StringUtils; import cn.topiam.employee.audit.entity.AuditElasticSearchEntity; import cn.topiam.employee.audit.enums.EventStatus; import cn.topiam.employee.audit.enums.EventType; +import cn.topiam.employee.authentication.common.IdentityProviderType; import cn.topiam.employee.common.entity.app.AppEntity; import cn.topiam.employee.common.repository.account.UserRepository; import cn.topiam.employee.common.repository.app.AppRepository; import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; import cn.topiam.employee.console.pojo.query.analysis.AnalysisQuery; -import cn.topiam.employee.console.pojo.result.analysis.AppVisitRankResult; -import cn.topiam.employee.console.pojo.result.analysis.AuthnQuantityResult; -import cn.topiam.employee.console.pojo.result.analysis.OverviewResult; +import cn.topiam.employee.console.pojo.result.analysis.*; import cn.topiam.employee.console.service.analysis.AnalysisService; import cn.topiam.employee.core.configuration.EiamSupportProperties; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import static cn.topiam.employee.audit.entity.Actor.ACTOR_AUTH_TYPE; import static cn.topiam.employee.audit.entity.Event.*; +import static cn.topiam.employee.audit.entity.GeoLocation.GEO_LOCATION_PROVINCE_CODE; import static cn.topiam.employee.audit.entity.Target.TARGET_ID_KEYWORD; import static cn.topiam.employee.common.constants.AuditConstants.getAuditIndexPrefix; +import static cn.topiam.employee.console.converter.authentication.IdentityProviderConverter.getIdentityProviderType; import static cn.topiam.employee.support.constant.EiamConstants.DEFAULT_DATE_TIME_FORMATTER_PATTERN; /** @@ -78,21 +83,21 @@ import static cn.topiam.employee.support.constant.EiamConstants.DEFAULT_DATE_TIM @RequiredArgsConstructor public class AnalysisServiceImpl implements AnalysisService { - // void testLog(Query query) { - // try { - // Method searchRequest = ReflectionUtils.findMethod(Class.forName("org.springframework.data.elasticsearch.core.RequestFactory"), "searchRequest", Query.class, Class.class, IndexCoordinates.class); - // searchRequest.setAccessible(true); - // Object o = ReflectionUtils.invokeMethod(searchRequest, elasticsearchRestTemplate.getRequestFactory(), query, AuditElasticSearchEntity.class, IndexCoordinates.of(AUDIT_INDEX_PREFIX + "*")); + // void testLog(Query query) { + // try { + // Method searchRequest = ReflectionUtils.findMethod(Class.forName("org.springframework.data.elasticsearch.core.RequestFactory"), "searchRequest", Query.class, Class.class, IndexCoordinates.class); + // searchRequest.setAccessible(true); + // Object o = ReflectionUtils.invokeMethod(searchRequest, elasticsearchRestTemplate.getRequestFactory(), query, AuditElasticSearchEntity.class, IndexCoordinates.of(getAuditIndexPrefix(eiamSupportProperties.getDemo().isOpen()) + "*")); // - // Field source = ReflectionUtils.findField(Class.forName("org.elasticsearch.action.search.SearchRequest"), "source"); - // source.setAccessible(true); - // Object s = ReflectionUtils.getField(source, o); - // log.error("dsl:{}", s); - // } - // catch (Exception e) { - // e.printStackTrace(); - // } + // Field source = ReflectionUtils.findField(Class.forName("org.elasticsearch.action.search.SearchRequest"), "source"); + // source.setAccessible(true); + // Object s = ReflectionUtils.getField(source, o); + // log.error("dsl:{}", s); // } + // catch (Exception e) { + // e.printStackTrace(); + // } + // } /** * 概述 * @@ -117,7 +122,7 @@ public class AnalysisServiceImpl implements AnalysisService { } /** - * 认证统计 + * 认证量统计 * * @param params {@link AnalysisQuery} * @return {@link List} @@ -146,7 +151,6 @@ public class AnalysisServiceImpl implements AnalysisService { BoolQueryBuilder queryBuilder = getBoolQueryBuilder(builder, EventType.LOGIN_PORTAL); NativeSearchQuery authCountBuild = new NativeSearchQueryBuilder().withQuery(queryBuilder) .withAggregations(groupBuilder).build(); - // testLog(authCountBuild); SearchHits authCountResult = elasticsearchRestTemplate .search(authCountBuild, AuditElasticSearchEntity.class, IndexCoordinates .of(getAuditIndexPrefix(eiamSupportProperties.getDemo().isOpen()) + "*")); @@ -174,20 +178,13 @@ public class AnalysisServiceImpl implements AnalysisService { */ @Override public List appVisitRank(AnalysisQuery params) { - String min = params.getStartTime() - .format(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMATTER_PATTERN)); - String max = params.getEndTime() - .format(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMATTER_PATTERN)); - // 查询条件 - RangeQueryBuilder builder = QueryBuilders.rangeQuery(EVENT_TIME).timeZone(ZONE_ID) - .format(DEFAULT_DATE_TIME_FORMATTER_PATTERN).gt(min).lt(max); + RangeQueryBuilder builder = getRangeQueryBuilder(params); BoolQueryBuilder queryBuilder = getBoolQueryBuilder(builder, EventType.APP_SSO); // 应用访问频次前10条 TermsAggregationBuilder groupAppVisit = AggregationBuilders.terms("count") .field(TARGET_ID_KEYWORD).order(BucketOrder.count(false)).size(10); NativeSearchQuery appVisitBuild = new NativeSearchQueryBuilder().withQuery(queryBuilder) .withAggregations(groupAppVisit).build(); - // testLog(appVisitBuild); SearchHits appVisitResult = elasticsearchRestTemplate .search(appVisitBuild, AuditElasticSearchEntity.class, IndexCoordinates .of(getAuditIndexPrefix(eiamSupportProperties.getDemo().isOpen()) + "*")); @@ -196,13 +193,88 @@ public class AnalysisServiceImpl implements AnalysisService { List applicationVisitList = new ArrayList<>(); for (Terms.Bucket bucket : appVisitStringTerms.getBuckets()) { String key = String.valueOf(bucket.getKey()); - //单点登录 + // 单点登录 String name = getAppName(key); applicationVisitList.add(new AppVisitRankResult(name, bucket.getDocCount())); } return applicationVisitList; } + /** + * 时间查询条件 + * + * @param params {@link AnalysisQuery} + * @return {@link RangeQueryBuilder} + */ + private RangeQueryBuilder getRangeQueryBuilder(AnalysisQuery params) { + String min = params.getStartTime() + .format(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMATTER_PATTERN)); + String max = params.getEndTime() + .format(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMATTER_PATTERN)); + // 查询条件 + return QueryBuilders.rangeQuery(EVENT_TIME).timeZone(ZONE_ID) + .format(DEFAULT_DATE_TIME_FORMATTER_PATTERN).gt(min).lt(max); + } + + /** + * 热门认证方式 + * @param params {@link AnalysisQuery} + * @return {@link List} + */ + @Override + public List authnHotProvider(AnalysisQuery params) { + RangeQueryBuilder builder = getRangeQueryBuilder(params); + BoolQueryBuilder queryBuilder = getBoolQueryBuilder(builder, EventType.LOGIN_PORTAL); + queryBuilder.must(QueryBuilders.existsQuery(ACTOR_AUTH_TYPE)); + // 授权类型频次 + TermsAggregationBuilder groupAuthType = AggregationBuilders.terms("count") + .field(ACTOR_AUTH_TYPE).size(IdentityProviderType.size()); + NativeSearchQuery appVisitBuild = new NativeSearchQueryBuilder().withQuery(queryBuilder) + .withAggregations(groupAuthType).build(); + SearchHits authTypeResult = elasticsearchRestTemplate + .search(appVisitBuild, AuditElasticSearchEntity.class, IndexCoordinates + .of(getAuditIndexPrefix(eiamSupportProperties.getDemo().isOpen()) + "*")); + ParsedStringTerms authTypeStringTerms = (ParsedStringTerms) getAggregation(authTypeResult, + "count"); + List authTypeList = new ArrayList<>(); + for (Terms.Bucket bucket : authTypeStringTerms.getBuckets()) { + String key = String.valueOf(bucket.getKey()); + // 授权类型 + String name = getIdentityProviderType(key).name(); + authTypeList.add(new AuthnHotProviderResult(name, bucket.getDocCount())); + } + return authTypeList; + } + + /** + * 登录区域统计 + * + * @param params {@link AnalysisQuery} + * @return {@link List} + */ + @Override + public List authnZone(AnalysisQuery params) { + RangeQueryBuilder builder = getRangeQueryBuilder(params); + BoolQueryBuilder queryBuilder = getBoolQueryBuilder(builder, EventType.LOGIN_PORTAL); + queryBuilder.must(QueryBuilders.existsQuery(GEO_LOCATION_PROVINCE_CODE)); + // 登录城市分组统计 + TermsAggregationBuilder groupAuthZone = AggregationBuilders.terms("count") + .field(GEO_LOCATION_PROVINCE_CODE).size(36).minDocCount(0); + NativeSearchQuery appVisitBuild = new NativeSearchQueryBuilder().withQuery(queryBuilder) + .withAggregations(groupAuthZone).build(); + SearchHits authZoneResult = elasticsearchRestTemplate + .search(appVisitBuild, AuditElasticSearchEntity.class, IndexCoordinates + .of(getAuditIndexPrefix(eiamSupportProperties.getDemo().isOpen()) + "*")); + ParsedStringTerms authZoneStringTerms = (ParsedStringTerms) getAggregation(authZoneResult, + "count"); + List authnZoneResults = new ArrayList<>(); + for (Terms.Bucket bucket : authZoneStringTerms.getBuckets()) { + String key = String.valueOf(bucket.getKey()); + authnZoneResults.add(new AuthnZoneResult(key, bucket.getDocCount())); + } + return authnZoneResults; + } + /** * 获取应用名称 * @@ -217,6 +289,13 @@ public class AnalysisServiceImpl implements AnalysisService { return app.getName(); } + /** + * ES聚合查询 + * + * @param searchHits {@link SearchHits} + * @param groupName {@link String} + * @return {@link Aggregation} + */ private Aggregation getAggregation(SearchHits searchHits, String groupName) { ElasticsearchAggregations elasticsearchAggregations = (ElasticsearchAggregations) searchHits @@ -226,9 +305,16 @@ public class AnalysisServiceImpl implements AnalysisService { return aggregations.asMap().get(groupName); } + /** + * 拼装查询条件 + * + * @param builder {@link RangeQueryBuilder} + * @param eventType {@link EventType} + * @return {@link BoolQueryBuilder} + */ @NotNull private BoolQueryBuilder getBoolQueryBuilder(RangeQueryBuilder builder, EventType eventType) { - // 查询今日认证量条件 + // 查询条件 BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery(); // 事件类型 queryBuilder.must(QueryBuilders.termsQuery(EVENT_TYPE, eventType.getCode())); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppAccessPolicyServiceImpl.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppAccessPolicyServiceImpl.java index d9d594d7..428a5e2f 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppAccessPolicyServiceImpl.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppAccessPolicyServiceImpl.java @@ -124,7 +124,7 @@ public class AppAccessPolicyServiceImpl implements AppAccessPolicyService { public Boolean deleteAppAccessPolicy(String id) { Optional optional = appAccessPolicyRepository .findById(Long.valueOf(id)); - //管理员不存在 + //策略不存在 if (optional.isEmpty()) { AuditContext.setContent("删除失败,应用授权策略不存在"); log.warn(AuditContext.getContent()); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppAccountServiceImpl.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppAccountServiceImpl.java index 512e34d9..f046166f 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppAccountServiceImpl.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppAccountServiceImpl.java @@ -17,15 +17,20 @@ */ package cn.topiam.employee.console.service.app.impl; +import java.nio.charset.StandardCharsets; import java.util.Optional; import org.springframework.data.querydsl.QPageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Base64Utils; + +import com.alibaba.excel.util.StringUtils; import cn.topiam.employee.audit.context.AuditContext; import cn.topiam.employee.audit.entity.Target; import cn.topiam.employee.audit.enums.TargetType; +import cn.topiam.employee.common.crypto.EncryptContextHelp; import cn.topiam.employee.common.entity.app.AppAccountEntity; import cn.topiam.employee.common.entity.app.po.AppAccountPO; import cn.topiam.employee.common.entity.app.query.AppAccountQuery; @@ -86,9 +91,16 @@ public class AppAccountServiceImpl implements AppAccountService { throw new AppAccountExistException(); } AppAccountEntity entity = appAccountConverter.appAccountCreateParamConvertToEntity(param); + //密码不为空 + if (!StringUtils.isBlank(param.getPassword())) { + String password = new String(Base64Utils.decodeFromString(param.getPassword()), + StandardCharsets.UTF_8); + entity.setPassword(EncryptContextHelp.encrypt(password)); + } appAccountRepository.save(entity); AuditContext.setTarget( - Target.builder().id(entity.getAccount()).type(TargetType.USER).build(), + Target.builder().id(entity.getUserId().toString()).type(TargetType.USER).build(), + Target.builder().id(entity.getAccount()).type(TargetType.APPLICATION_ACCOUNT).build(), Target.builder().id(entity.getAppId().toString()).type(TargetType.APPLICATION).build()); return true; } diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppPermissionResourceServiceImpl.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppPermissionResourceServiceImpl.java index 506d0ca4..86dcae57 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppPermissionResourceServiceImpl.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppPermissionResourceServiceImpl.java @@ -231,8 +231,7 @@ public class AppPermissionResourceServiceImpl implements AppPermissionResourceSe if (StringUtils.equals(entity.getName(), value)) { return true; } - BooleanExpression eq = role.name.eq(value); - eq.and(role.appId.eq(appId)); + BooleanExpression eq = role.name.eq(value).and(role.appId.eq(appId)); result = !appResourceRepository.exists(eq); } //资源编码 @@ -240,8 +239,7 @@ public class AppPermissionResourceServiceImpl implements AppPermissionResourceSe if (StringUtils.equals(entity.getCode(), value)) { return true; } - BooleanExpression eq = role.code.eq(value); - eq.and(role.appId.eq(appId)); + BooleanExpression eq = role.code.eq(value).and(role.appId.eq(appId)); result = !appResourceRepository.exists(eq); } return result; diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppSaml2ServiceImpl.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppSaml2ServiceImpl.java index 112606a3..3a3a2bef 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppSaml2ServiceImpl.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppSaml2ServiceImpl.java @@ -40,15 +40,15 @@ import org.springframework.stereotype.Service; import cn.topiam.employee.application.ApplicationService; import cn.topiam.employee.application.ApplicationServiceLoader; -import cn.topiam.employee.application.Saml2ApplicationService; import cn.topiam.employee.application.exception.AppNotExistException; import cn.topiam.employee.application.exception.ParseSaml2MetadataException; +import cn.topiam.employee.application.saml2.Saml2ApplicationService; +import cn.topiam.employee.application.saml2.model.Saml2ProtocolConfig; import cn.topiam.employee.common.entity.app.AppEntity; import cn.topiam.employee.common.repository.app.AppRepository; import cn.topiam.employee.common.repository.app.AppSaml2ConfigRepository; import cn.topiam.employee.console.pojo.result.app.ParseSaml2MetadataResult; import cn.topiam.employee.console.service.app.AppSaml2Service; -import cn.topiam.employee.core.protocol.Saml2ProtocolConfig; import cn.topiam.employee.protocol.saml2.idp.util.Saml2Utils; import cn.topiam.employee.support.context.ServletContextHelp; import cn.topiam.employee.support.util.CertUtils; diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppServiceImpl.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppServiceImpl.java index 72aa01d0..4f377d96 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppServiceImpl.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/app/impl/AppServiceImpl.java @@ -175,6 +175,12 @@ public class AppServiceImpl implements AppService { */ @Override public Boolean enableApp(String id) { + Optional optional = appRepository.findById(Long.valueOf(id)); + if (optional.isEmpty()) { + AuditContext.setContent("操作失败,应用不存在"); + log.warn(AuditContext.getContent()); + throw new TopIamException(AuditContext.getContent()); + } Integer count = appRepository.updateAppStatus(Long.valueOf(id), Boolean.TRUE); AuditContext.setTarget(Target.builder().id(id).type(TargetType.APPLICATION).build()); return count > 0; @@ -188,6 +194,12 @@ public class AppServiceImpl implements AppService { */ @Override public Boolean disableApp(String id) { + Optional optional = appRepository.findById(Long.valueOf(id)); + if (optional.isEmpty()) { + AuditContext.setContent("操作失败,应用不存在"); + log.warn(AuditContext.getContent()); + throw new TopIamException(AuditContext.getContent()); + } Integer count = appRepository.updateAppStatus(Long.valueOf(id), Boolean.FALSE); AuditContext.setTarget(Target.builder().id(id).type(TargetType.APPLICATION).build()); return count > 0; diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/authentication/IdentityProviderService.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/authentication/IdentityProviderService.java index a7a221dc..ebdc5d1a 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/authentication/IdentityProviderService.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/authentication/IdentityProviderService.java @@ -20,7 +20,6 @@ package cn.topiam.employee.console.service.authentication; import java.util.List; import cn.topiam.employee.common.entity.authentication.IdentityProviderEntity; -import cn.topiam.employee.common.enums.IdentityProviderType; import cn.topiam.employee.console.pojo.query.authentication.IdentityProviderListQuery; import cn.topiam.employee.console.pojo.result.authentication.IdentityProviderCreateResult; import cn.topiam.employee.console.pojo.result.authentication.IdentityProviderListResult; @@ -50,10 +49,10 @@ public interface IdentityProviderService { /** * 通过平台类型获取 * - * @param provider {@link IdentityProviderType} + * @param provider {@link String} * @return {@link IdentityProviderEntity} */ - List getByIdentityProvider(IdentityProviderType provider); + List getByIdentityProvider(String provider); /** * 认证源列表 diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/authentication/impl/IdentityProviderServiceImpl.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/authentication/impl/IdentityProviderServiceImpl.java index 230e462a..7ebba99f 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/authentication/impl/IdentityProviderServiceImpl.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/authentication/impl/IdentityProviderServiceImpl.java @@ -27,7 +27,6 @@ import cn.topiam.employee.audit.context.AuditContext; import cn.topiam.employee.audit.entity.Target; import cn.topiam.employee.audit.enums.TargetType; import cn.topiam.employee.common.entity.authentication.IdentityProviderEntity; -import cn.topiam.employee.common.enums.IdentityProviderType; import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; import cn.topiam.employee.console.converter.authentication.IdentityProviderConverter; import cn.topiam.employee.console.pojo.query.authentication.IdentityProviderListQuery; @@ -77,11 +76,11 @@ public class IdentityProviderServiceImpl implements IdentityProviderService { /** * 通过平台类型获取 * - * @param provider {@link IdentityProviderType} + * @param provider {@link String} * @return {@link IdentityProviderEntity} */ @Override - public List getByIdentityProvider(IdentityProviderType provider) { + public List getByIdentityProvider(String provider) { return identityProviderRepository.findByType(provider); } @@ -158,7 +157,8 @@ public class IdentityProviderServiceImpl implements IdentityProviderService { identityProviderRepository.save(entity); ApplicationContextHelp.refresh(DEFAULT_SECURITY_FILTER_CHAIN); AuditContext.setTarget(Target.builder().id(entity.getId().toString()) - .type(TargetType.IDENTITY_PROVIDER).build()); + .name(entity.getName()).type(TargetType.IDENTITY_PROVIDER) + .typeName(TargetType.IDENTITY_PROVIDER.getDesc()).build()); return true; } throw new NullPointerException("系统不存在该身份源"); @@ -195,6 +195,14 @@ public class IdentityProviderServiceImpl implements IdentityProviderService { */ @Override public Boolean updateIdentityProviderStatus(String id, Boolean enabled) { + Optional optional = identityProviderRepository + .findById(Long.valueOf(id)); + //管理员不存在 + if (optional.isEmpty()) { + AuditContext.setContent("删除失败,认证源不存在"); + log.warn(AuditContext.getContent()); + throw new TopIamException(AuditContext.getContent()); + } boolean result = identityProviderRepository.updateIdentityProviderStatus(Long.valueOf(id), enabled) > 0; ApplicationContextHelp.refresh(DEFAULT_SECURITY_FILTER_CHAIN); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/identitysource/impl/IdentitySourceServiceImpl.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/identitysource/impl/IdentitySourceServiceImpl.java index fcf930e4..5d75537f 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/identitysource/impl/IdentitySourceServiceImpl.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/identitysource/impl/IdentitySourceServiceImpl.java @@ -228,6 +228,13 @@ public class IdentitySourceServiceImpl implements IdentitySourceService { */ @Override public void updateStrategyConfig(Long id, String strategyConfig) { + Optional optional = identitySourceRepository.findById(id); + //用户不存在 + if (optional.isEmpty()) { + AuditContext.setContent("操作失败,身份源不存在"); + log.warn(AuditContext.getContent()); + throw new TopIamException(AuditContext.getContent()); + } identitySourceRepository.updateStrategyConfig(id, strategyConfig); AuditContext .setTarget(Target.builder().id(id.toString()).type(TargetType.IDENTITY_SOURCE).build()); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/identitysource/impl/IdentitySourceSyncServiceImpl.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/identitysource/impl/IdentitySourceSyncServiceImpl.java index 728acc2d..58e61bac 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/identitysource/impl/IdentitySourceSyncServiceImpl.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/identitysource/impl/IdentitySourceSyncServiceImpl.java @@ -113,8 +113,8 @@ public class IdentitySourceSyncServiceImpl implements IdentitySourceSyncService */ @Override public void executeIdentitySourceSync(String id) { - AuditContext.setTarget(Target.builder().id(id).type(IDENTITY_SOURCE).build()); IdentitySourceEntity entity = identitySourceService.getIdentitySource(id); + AuditContext.setTarget(Target.builder().id(id).type(IDENTITY_SOURCE).build()); if (!ObjectUtils.isEmpty(entity)) { if (Objects.isNull(entity.getBasicConfig())) { throw new NullPointerException("请完善参数配置"); diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/setting/AdministratorService.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/setting/AdministratorService.java index f1fc0191..3684d5c2 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/setting/AdministratorService.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/setting/AdministratorService.java @@ -17,6 +17,8 @@ */ package cn.topiam.employee.console.service.setting; +import java.time.LocalDateTime; + import cn.topiam.employee.common.enums.CheckValidityType; import cn.topiam.employee.common.enums.UserStatus; import cn.topiam.employee.console.pojo.query.setting.AdministratorListQuery; @@ -103,4 +105,13 @@ public interface AdministratorService { * @return {@link Boolean} */ Boolean administratorParamCheck(CheckValidityType type, String value, Long id); + + /** + * 更新认证成功信息 + * + * @param id {@link String} + * @param ip {@link String} + * @param loginTime {@link LocalDateTime} + */ + Boolean updateAuthSucceedInfo(String id, String ip, LocalDateTime loginTime); } diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/setting/impl/AdministratorServiceImpl.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/setting/impl/AdministratorServiceImpl.java index abcf8738..0c8dcfc1 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/setting/impl/AdministratorServiceImpl.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/setting/impl/AdministratorServiceImpl.java @@ -18,6 +18,7 @@ package cn.topiam.employee.console.service.setting.impl; import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; import java.util.Base64; import java.util.List; import java.util.Objects; @@ -118,8 +119,9 @@ public class AdministratorServiceImpl implements AdministratorService { AdministratorEntity entity = administratorConverter .administratorCreateParamConvertToEntity(param); administratorRepository.save(entity); - AuditContext.setTarget( - Target.builder().id(entity.getId().toString()).type(TargetType.ADMINISTRATOR).build()); + AuditContext.setTarget(Target.builder().id(entity.getId().toString()) + .name(entity.getUsername()).type(TargetType.ADMINISTRATOR) + .typeName(TargetType.ADMINISTRATOR.getDesc()).build()); return true; } @@ -138,8 +140,9 @@ public class AdministratorServiceImpl implements AdministratorService { AuditContext.setContent(source.getUsername()); BeanUtils.merge(source, target, LAST_MODIFIED_TIME, LAST_MODIFIED_BY); administratorRepository.save(target); - AuditContext.setTarget( - Target.builder().id(target.getId().toString()).type(TargetType.ADMINISTRATOR).build()); + AuditContext.setTarget(Target.builder().id(target.getId().toString()) + .name(target.getUsername()).type(TargetType.ADMINISTRATOR) + .typeName(TargetType.ADMINISTRATOR.getDesc()).build()); return true; } @@ -203,6 +206,13 @@ public class AdministratorServiceImpl implements AdministratorService { */ @Override public Boolean resetAdministratorPassword(String id, String password) { + Optional optional = administratorRepository.findById(Long.valueOf(id)); + //管理员不存在 + if (optional.isEmpty()) { + AuditContext.setContent("删除失败,管理员不存在"); + log.warn(AuditContext.getContent()); + throw new TopIamException("操作失败"); + } password = new String( Base64.getUrlDecoder().decode(password.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); @@ -269,6 +279,19 @@ public class AdministratorServiceImpl implements AdministratorService { return result; } + + /** + * 更新认证成功信息 + * + * @param id {@link String} + * @param ip {@link String} + * @param loginTime {@link LocalDateTime} + */ + public Boolean updateAuthSucceedInfo(String id, String ip, LocalDateTime loginTime) { + administratorRepository.updateAuthSucceedInfo(id,ip,loginTime); + return true; + } + /** * 查询管理员详情 * @@ -278,7 +301,7 @@ public class AdministratorServiceImpl implements AdministratorService { @Override public AdministratorResult getAdministrator(String id) { AdministratorEntity entity = administratorRepository.findById(Long.valueOf(id)) - .orElse(null); + .orElse(null); return administratorConverter.entityConvertToAdministratorDetailsResult(entity); } @@ -291,7 +314,7 @@ public class AdministratorServiceImpl implements AdministratorService { /** * AdministratorConverter */ - private final AdministratorConverter administratorConverter; + private final AdministratorConverter administratorConverter; /** * AdministratorRepository @@ -301,7 +324,7 @@ public class AdministratorServiceImpl implements AdministratorService { /** * PasswordEncoder */ - private final PasswordEncoder passwordEncoder; + private final PasswordEncoder passwordEncoder; /** * SessionRegistry diff --git a/eiam-console/src/main/java/cn/topiam/employee/console/service/setting/impl/GeoLocationSettingServiceImpl.java b/eiam-console/src/main/java/cn/topiam/employee/console/service/setting/impl/GeoLocationSettingServiceImpl.java index 1eee44bc..2e53e607 100644 --- a/eiam-console/src/main/java/cn/topiam/employee/console/service/setting/impl/GeoLocationSettingServiceImpl.java +++ b/eiam-console/src/main/java/cn/topiam/employee/console/service/setting/impl/GeoLocationSettingServiceImpl.java @@ -57,7 +57,6 @@ public class GeoLocationSettingServiceImpl extends SettingServiceImpl SettingEntity settingEntity = geoLocationSettingsConverter .geoLocationProviderConfigToEntity(param); Boolean success = saveSetting(settingEntity); - downloadDbFile(); ApplicationContextHelp.refresh(GEO_LOCATION); return success; } diff --git a/eiam-console/src/main/resources/application.yml b/eiam-console/src/main/resources/application.yml index 9cb91992..bc067fea 100644 --- a/eiam-console/src/main/resources/application.yml +++ b/eiam-console/src/main/resources/application.yml @@ -159,4 +159,4 @@ topiam: console-public-base-url: ${CONSOLE_PUBLIC_BASE_URL:https://localhost:1898} portal-public-base-url: ${PORTAL_PUBLIC_BASE_URL:https://localhost:1989} openapi-public-base-url: ${OPENAPI_PUBLIC_BASE_URL:https://localhost:1988} - synchronizer-public-base-url: ${SYNCHRONIZER_PUBLIC_BASE_URL:https://localhost:1986} \ No newline at end of file + synchronizer-public-base-url: ${SYNCHRONIZER_PUBLIC_BASE_URL:https://localhost:1986} diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/configuration/CustomRedisSessionRepository.java b/eiam-core/src/main/java/cn/topiam/employee/core/configuration/CustomRedisSessionRepository.java new file mode 100644 index 00000000..4c2a9d26 --- /dev/null +++ b/eiam-core/src/main/java/cn/topiam/employee/core/configuration/CustomRedisSessionRepository.java @@ -0,0 +1,78 @@ +/* + * eiam-core - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.core.configuration; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.springframework.data.redis.core.RedisOperations; +import org.springframework.session.FindByIndexNameSessionRepository; +import org.springframework.session.Session; +import org.springframework.session.data.redis.RedisIndexedSessionRepository; + +public class CustomRedisSessionRepository extends RedisIndexedSessionRepository { + + /** + * The default namespace for each key and channel in Redis used by Spring Session. + */ + public static final String DEFAULT_NAMESPACE = "spring:session"; + + /** + * The namespace for every key used by Spring Session in Redis. + */ + private String namespace = DEFAULT_NAMESPACE + ":"; + + private final RedisOperations sessionRedisOperations; + + public CustomRedisSessionRepository(RedisOperations sessionRedisOperations) { + super(sessionRedisOperations); + this.sessionRedisOperations = sessionRedisOperations; + } + + @Override + public Map findByIndexNameAndIndexValue(String indexName, String indexValue) { + if (!PRINCIPAL_NAME_INDEX_NAME.equals(indexName)) { + return Collections.emptyMap(); + } + String principalKey = getPrincipalKey(indexValue); + + Set sessionIds = this.sessionRedisOperations.boundSetOps(principalKey).members(); + Map sessions = new HashMap<>(sessionIds.size()); + for (Object id : sessionIds) { + // TODO + Session session = findById((String) id); + if (session != null) { + sessions.put(session.getId(), session); + } + } + return sessions; + } + + String getPrincipalKey(String principalName) { + return this.namespace + "index:" + + FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME + ":" + principalName; + } + + @Override + public void setRedisKeyNamespace(String namespace) { + super.setRedisKeyNamespace(namespace); + this.namespace = namespace.trim() + ":"; + } +} diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/configuration/EiamCacheConfiguration.java b/eiam-core/src/main/java/cn/topiam/employee/core/configuration/EiamCacheConfiguration.java index 63d0fa57..5681b008 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/configuration/EiamCacheConfiguration.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/configuration/EiamCacheConfiguration.java @@ -17,6 +17,7 @@ */ package cn.topiam.employee.core.configuration; +import java.nio.charset.StandardCharsets; import java.util.LinkedHashSet; import java.util.List; @@ -27,8 +28,13 @@ import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.lang.Nullable; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonTypeInfo; @@ -113,6 +119,64 @@ public class EiamCacheConfiguration { return config; } + /** + * 自定义 RedisTemplate + * + * @param redisConnectionFactory {@link RedisConnectionFactory} + * @return {@link RedisTemplate} + */ + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + ObjectMapper objectMapper = jacksonObjectMapper.copy(); + // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + // 指定序列化输入的类型 + objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), + ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); + GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer( + objectMapper); + // 设置value的序列化规则和 key的序列化规则 + redisTemplate.setKeySerializer( + new KeyStringRedisSerializer(cacheProperties.getRedis().getKeyPrefix())); + redisTemplate.setHashKeySerializer( + new KeyStringRedisSerializer(cacheProperties.getRedis().getKeyPrefix())); + //jackson2JsonRedisSerializer就是JSON序列号规则, + redisTemplate.setValueSerializer(serializer); + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } + + /** + * 配置 StringRedisTemplate + * + * @param redisConnectionFactory {@link RedisConnectionFactory} + * @return {@link StringRedisTemplate} + */ + @Bean + public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) { + StringRedisTemplate template = new StringRedisTemplate(); + template.setConnectionFactory(redisConnectionFactory); + ObjectMapper objectMapper = jacksonObjectMapper.copy(); + // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + // 指定序列化输入的类型 + objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), + ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); + GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer( + objectMapper); + //key配置 + template.setKeySerializer( + new KeyStringRedisSerializer(cacheProperties.getRedis().getKeyPrefix())); + template.setHashKeySerializer( + new KeyStringRedisSerializer(cacheProperties.getRedis().getKeyPrefix())); + //value配置 + template.setValueSerializer(serializer); + template.setHashValueSerializer(serializer); + return template; + } + private final CacheProperties cacheProperties; private final ObjectMapper jacksonObjectMapper; @@ -122,4 +186,23 @@ public class EiamCacheConfiguration { this.cacheProperties = cacheProperties; this.jacksonObjectMapper = jacksonObjectMapper; } + + private class KeyStringRedisSerializer implements RedisSerializer { + private final String keyPrefix; + + private KeyStringRedisSerializer(String keyPrefix) { + this.keyPrefix = keyPrefix + COLON; + } + + @Override + public String deserialize(@Nullable byte[] bytes) { + return (bytes == null ? null + : new String(bytes, StandardCharsets.UTF_8).replaceFirst(keyPrefix, "")); + } + + @Override + public byte[] serialize(@Nullable String string) { + return (string == null ? null : (keyPrefix + string).getBytes(StandardCharsets.UTF_8)); + } + } } diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/configuration/EiamGeoLocationConfiguration.java b/eiam-core/src/main/java/cn/topiam/employee/core/configuration/EiamGeoLocationConfiguration.java index 3f290288..828eddad 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/configuration/EiamGeoLocationConfiguration.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/configuration/EiamGeoLocationConfiguration.java @@ -31,6 +31,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.ObjectMapper; import cn.topiam.employee.common.constants.SettingConstants; +import cn.topiam.employee.common.crypto.EncryptionModule; import cn.topiam.employee.common.entity.setting.SettingEntity; import cn.topiam.employee.common.geo.GeoLocationProviderConfig; import cn.topiam.employee.common.geo.GeoLocationService; @@ -57,7 +58,7 @@ public class EiamGeoLocationConfiguration { public GeoLocationService geoLocation(SettingRepository settingRepository, RestTemplate restTemplate) { try { - ObjectMapper objectMapper = new ObjectMapper(); + ObjectMapper objectMapper = EncryptionModule.deserializerDecrypt(); // 指定序列化输入的类型 objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/configuration/EiamSchedulingConfiguration.java b/eiam-core/src/main/java/cn/topiam/employee/core/configuration/EiamSchedulingConfiguration.java index 0f9ca7d7..c639ddb4 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/configuration/EiamSchedulingConfiguration.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/configuration/EiamSchedulingConfiguration.java @@ -17,10 +17,6 @@ */ package cn.topiam.employee.core.configuration; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.TaskScheduler; @@ -34,7 +30,6 @@ import cn.topiam.employee.support.task.TaskSchedulerRegistrarHelp; */ @Configuration @EnableScheduling -@AutoConfigureAfter(value = TaskSchedulingAutoConfiguration.class) public class EiamSchedulingConfiguration { /** @@ -44,8 +39,6 @@ public class EiamSchedulingConfiguration { * @return {@link TaskSchedulerRegistrarHelp} */ @Bean - @ConditionalOnBean(TaskScheduler.class) - @ConditionalOnMissingBean public TaskSchedulerRegistrarHelp taskSchedulerRegistrarHelp(TaskScheduler taskScheduler) { return new TaskSchedulerRegistrarHelp(taskScheduler); } diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/configuration/RedisSessionConfiguration.java b/eiam-core/src/main/java/cn/topiam/employee/core/configuration/RedisSessionConfiguration.java new file mode 100644 index 00000000..6d8d099f --- /dev/null +++ b/eiam-core/src/main/java/cn/topiam/employee/core/configuration/RedisSessionConfiguration.java @@ -0,0 +1,248 @@ +/* + * eiam-core - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.core.configuration; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; + +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.listener.ChannelTopic; +import org.springframework.data.redis.listener.PatternTopic; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.session.*; +import org.springframework.session.config.SessionRepositoryCustomizer; +import org.springframework.session.data.redis.RedisIndexedSessionRepository; +import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction; +import org.springframework.session.data.redis.config.ConfigureRedisAction; +import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory; +import org.springframework.util.ClassUtils; + +@Configuration +public class RedisSessionConfiguration implements BeanClassLoaderAware { + + private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS; + + private String redisNamespace = RedisIndexedSessionRepository.DEFAULT_NAMESPACE; + + private FlushMode flushMode = FlushMode.ON_SAVE; + + private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE; + + private RedisConnectionFactory redisConnectionFactory; + + private IndexResolver indexResolver; + + private RedisSerializer defaultRedisSerializer; + + private ApplicationEventPublisher applicationEventPublisher; + + private List> sessionRepositoryCustomizers; + + private ConfigureRedisAction configureRedisAction = new ConfigureNotifyKeyspaceEventsAction(); + + private ClassLoader classLoader; + + private Executor redisTaskExecutor; + + private Executor redisSubscriptionExecutor; + + @Autowired(required = false) + @Qualifier("springSessionRedisSubscriptionExecutor") + public void setRedisSubscriptionExecutor(Executor redisSubscriptionExecutor) { + this.redisSubscriptionExecutor = redisSubscriptionExecutor; + } + + /** + * Sets the action to perform for configuring Redis. + * @param configureRedisAction the configureRedis to set. The default is + * {@link ConfigureNotifyKeyspaceEventsAction}. + */ + @Autowired(required = false) + public void setConfigureRedisAction(ConfigureRedisAction configureRedisAction) { + this.configureRedisAction = configureRedisAction; + } + + @Autowired(required = false) + @Qualifier("springSessionDefaultRedisSerializer") + public void setDefaultRedisSerializer(RedisSerializer defaultRedisSerializer) { + this.defaultRedisSerializer = defaultRedisSerializer; + } + + @Autowired + public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + this.applicationEventPublisher = applicationEventPublisher; + } + + @Autowired + public void setRedisConnectionFactory(@SpringSessionRedisConnectionFactory ObjectProvider springSessionRedisConnectionFactory, + ObjectProvider redisConnectionFactory) { + RedisConnectionFactory redisConnectionFactoryToUse = springSessionRedisConnectionFactory + .getIfAvailable(); + if (redisConnectionFactoryToUse == null) { + redisConnectionFactoryToUse = redisConnectionFactory.getObject(); + } + this.redisConnectionFactory = redisConnectionFactoryToUse; + } + + @Autowired(required = false) + public void setIndexResolver(IndexResolver indexResolver) { + this.indexResolver = indexResolver; + } + + @Autowired(required = false) + public void setSessionRepositoryCustomizer(ObjectProvider> sessionRepositoryCustomizers) { + this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream() + .collect(Collectors.toList()); + } + + @Autowired(required = false) + @Qualifier("springSessionRedisTaskExecutor") + public void setRedisTaskExecutor(Executor redisTaskExecutor) { + this.redisTaskExecutor = redisTaskExecutor; + } + + @Bean + public RedisIndexedSessionRepository sessionRepository() { + RedisTemplate redisTemplate = createRedisTemplate(); + CustomRedisSessionRepository sessionRepository = new CustomRedisSessionRepository( + redisTemplate); + sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher); + if (this.indexResolver != null) { + sessionRepository.setIndexResolver(this.indexResolver); + } + if (this.defaultRedisSerializer != null) { + sessionRepository.setDefaultSerializer(this.defaultRedisSerializer); + } + sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds); + // TODO + // if (StringUtils.hasText(this.redisNamespace)) { + sessionRepository.setRedisKeyNamespace("topiam:session"); + // } + sessionRepository.setFlushMode(this.flushMode); + sessionRepository.setSaveMode(this.saveMode); + int database = resolveDatabase(); + sessionRepository.setDatabase(database); + this.sessionRepositoryCustomizers + .forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer + .customize(sessionRepository)); + return sessionRepository; + } + + @Bean + public RedisMessageListenerContainer springSessionRedisMessageListenerContainer(RedisIndexedSessionRepository sessionRepository) { + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); + container.setConnectionFactory(this.redisConnectionFactory); + if (this.redisTaskExecutor != null) { + container.setTaskExecutor(this.redisTaskExecutor); + } + if (this.redisSubscriptionExecutor != null) { + container.setSubscriptionExecutor(this.redisSubscriptionExecutor); + } + container.addMessageListener(sessionRepository, + Arrays.asList(new ChannelTopic(sessionRepository.getSessionDeletedChannel()), + new ChannelTopic(sessionRepository.getSessionExpiredChannel()))); + container.addMessageListener(sessionRepository, Collections.singletonList( + new PatternTopic(sessionRepository.getSessionCreatedChannelPrefix() + "*"))); + return container; + } + + @Bean + public InitializingBean enableRedisKeyspaceNotificationsInitializer() { + return new EnableRedisKeyspaceNotificationsInitializer(this.redisConnectionFactory, + this.configureRedisAction); + } + + static class EnableRedisKeyspaceNotificationsInitializer implements InitializingBean { + + private final RedisConnectionFactory connectionFactory; + + private ConfigureRedisAction configure; + + EnableRedisKeyspaceNotificationsInitializer(RedisConnectionFactory connectionFactory, + ConfigureRedisAction configure) { + this.connectionFactory = connectionFactory; + this.configure = configure; + } + + @Override + public void afterPropertiesSet() { + if (this.configure == ConfigureRedisAction.NO_OP) { + return; + } + RedisConnection connection = this.connectionFactory.getConnection(); + try { + this.configure.configure(connection); + } finally { + try { + connection.close(); + } catch (Exception ex) { + LogFactory.getLog(getClass()).error("Error closing RedisConnection", ex); + } + } + } + + } + + private RedisTemplate createRedisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + if (this.defaultRedisSerializer != null) { + redisTemplate.setDefaultSerializer(this.defaultRedisSerializer); + } + redisTemplate.setConnectionFactory(this.redisConnectionFactory); + redisTemplate.setBeanClassLoader(this.classLoader); + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } + + private int resolveDatabase() { + if (ClassUtils.isPresent("io.lettuce.core.RedisClient", null) + && this.redisConnectionFactory instanceof LettuceConnectionFactory) { + return ((LettuceConnectionFactory) this.redisConnectionFactory).getDatabase(); + } + if (ClassUtils.isPresent("redis.clients.jedis.Jedis", null) + && this.redisConnectionFactory instanceof JedisConnectionFactory) { + return ((JedisConnectionFactory) this.redisConnectionFactory).getDatabase(); + } + return RedisIndexedSessionRepository.DEFAULT_DATABASE; + } + + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } +} diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/context/SettingContextHelp.java b/eiam-core/src/main/java/cn/topiam/employee/core/context/SettingContextHelp.java index 2185c877..41c4d865 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/context/SettingContextHelp.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/context/SettingContextHelp.java @@ -27,6 +27,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import cn.topiam.employee.common.constants.SettingConstants; +import cn.topiam.employee.common.crypto.EncryptContextHelp; import cn.topiam.employee.common.entity.setting.SettingEntity; import cn.topiam.employee.common.entity.setting.config.SmsConfig; import cn.topiam.employee.common.enums.MfaFactor; @@ -40,7 +41,6 @@ import cn.topiam.employee.core.security.captcha.CaptchaProviderConfig; import cn.topiam.employee.core.setting.constant.SecuritySettingConstants; import cn.topiam.employee.support.context.ApplicationContextHelp; import cn.topiam.employee.support.exception.TopIamException; -import cn.topiam.employee.support.util.AesUtils; import static cn.topiam.employee.core.setting.constant.MessageSettingConstants.MESSAGE_SMS_PROVIDER; import static cn.topiam.employee.core.setting.constant.MfaSettingConstants.*; import static cn.topiam.employee.core.setting.constant.SecuritySettingConstants.*; @@ -127,20 +127,21 @@ public class SettingContextHelp { if (SmsProvider.ALIYUN.equals(provider)) { AliyunSmsProviderConfig smsConfig = (AliyunSmsProviderConfig) config .getConfig(); - smsConfig.setAccessKeySecret(AesUtils.decrypt(smsConfig.getAccessKeySecret())); + smsConfig.setAccessKeySecret( + EncryptContextHelp.decrypt(smsConfig.getAccessKeySecret())); return config; } //腾讯 else if (SmsProvider.TENCENT.equals(provider)) { TencentSmsProviderConfig smsConfig = (TencentSmsProviderConfig) config .getConfig(); - smsConfig.setSecretKey(AesUtils.decrypt(smsConfig.getSecretKey())); + smsConfig.setSecretKey(EncryptContextHelp.decrypt(smsConfig.getSecretKey())); return config; } //七牛 else if (SmsProvider.QINIU.equals(provider)) { QiNiuSmsProviderConfig smsConfig = (QiNiuSmsProviderConfig) config.getConfig(); - smsConfig.setSecretKey(AesUtils.decrypt(smsConfig.getSecretKey())); + smsConfig.setSecretKey(EncryptContextHelp.decrypt(smsConfig.getSecretKey())); return config; } throw new TopIamException("暂未支持此短信 [" + provider + "] 提供商配置获取"); diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/endpoint/security/CurrentSessionStatusEndpoint.java b/eiam-core/src/main/java/cn/topiam/employee/core/endpoint/security/CurrentSessionStatusEndpoint.java index fe87457f..43033b0c 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/endpoint/security/CurrentSessionStatusEndpoint.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/endpoint/security/CurrentSessionStatusEndpoint.java @@ -77,7 +77,7 @@ public class CurrentSessionStatusEndpoint extends HttpServlet { builder.status(Status.require_bind_idp); } //其他信息 - ApiRestResult build = ApiRestResult. builder() + ApiRestResult build = ApiRestResult. builder() .result(builder.build()).build(); build.setSuccess(true); HttpResponseUtils.flushResponse(resp, JSONObject.toJSONString(build)); diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/endpoint/security/PublicSecretEndpoint.java b/eiam-core/src/main/java/cn/topiam/employee/core/endpoint/security/PublicSecretEndpoint.java index 46aabb4a..51e12fff 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/endpoint/security/PublicSecretEndpoint.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/endpoint/security/PublicSecretEndpoint.java @@ -54,7 +54,7 @@ public class PublicSecretEndpoint extends HttpServlet { public static final String PUBLIC_SECRET_PATH = EiamConstants.API_PATH + "/public_secret"; - private final static String TYPE = "type"; + private static final String TYPE = "type"; /** * 获取加密key diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/logger/LogAspect.java b/eiam-core/src/main/java/cn/topiam/employee/core/logger/LogAspect.java index fc581d63..feb22067 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/logger/LogAspect.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/logger/LogAspect.java @@ -42,6 +42,7 @@ import org.springframework.web.context.request.ServletRequestAttributes; import com.alibaba.fastjson2.JSONObject; import com.beust.jcommander.internal.Maps; +import com.fasterxml.jackson.databind.ObjectMapper; import cn.topiam.employee.support.util.IpUtils; import cn.topiam.employee.support.web.useragent.UserAgent; @@ -106,6 +107,7 @@ public class LogAspect implements Ordered { log.setIp(IpUtils.getIpAddr(request)); } log.setMethod(signature.getDeclaringTypeName() + "." + signature.getName()); + ObjectMapper mapper = new ObjectMapper(); try { for (int i = 0; i < parameters.length; i++) { if (args[i] instanceof BindingResult || args[i] instanceof ServletRequest @@ -114,7 +116,7 @@ public class LogAspect implements Ordered { } parameterMap.put(parameters[i], args[i]); } - log.setParameter(replaceBlank(JSONObject.toJSONString(parameterMap))); + log.setParameter(replaceBlank(mapper.writeValueAsString(parameterMap))); } catch (Exception e) { log.setParameter(parameterMap); } @@ -126,7 +128,7 @@ public class LogAspect implements Ordered { returnValue = ""; } try { - log.setResult(replaceBlank(JSONObject.toJSONString(returnValue))); + log.setResult(replaceBlank(mapper.writeValueAsString(returnValue))); } catch (Exception e) { log.setResult(returnValue); } diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/message/mail/MailMsgEventListener.java b/eiam-core/src/main/java/cn/topiam/employee/core/message/mail/MailMsgEventListener.java index 69724448..1113dbfc 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/message/mail/MailMsgEventListener.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/message/mail/MailMsgEventListener.java @@ -31,13 +31,13 @@ import org.springframework.lang.NonNull; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; -import cn.topiam.employee.common.entity.MailSendRecordEntity; +import cn.topiam.employee.common.entity.message.MailSendRecordEntity; import cn.topiam.employee.common.entity.setting.MailTemplateEntity; import cn.topiam.employee.common.enums.MailType; import cn.topiam.employee.common.exception.MailMessageSendException; import cn.topiam.employee.common.message.mail.MailProviderSend; import cn.topiam.employee.common.message.mail.SendMailRequest; -import cn.topiam.employee.common.repository.MailSendRecordRepository; +import cn.topiam.employee.common.repository.message.MailSendRecordRepository; import cn.topiam.employee.core.message.MsgVariable; import cn.topiam.employee.core.setting.constant.MessageSettingConstants; diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/message/sms/SmsMsgEventListener.java b/eiam-core/src/main/java/cn/topiam/employee/core/message/sms/SmsMsgEventListener.java index f4e2be55..54659d82 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/message/sms/SmsMsgEventListener.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/message/sms/SmsMsgEventListener.java @@ -29,13 +29,13 @@ import org.springframework.stereotype.Component; import com.alibaba.fastjson2.JSON; -import cn.topiam.employee.common.entity.SmsSendRecordEntity; +import cn.topiam.employee.common.entity.message.SmsSendRecordEntity; import cn.topiam.employee.common.enums.MessageCategory; import cn.topiam.employee.common.exception.MessageSendException; import cn.topiam.employee.common.message.sms.SendSmsRequest; import cn.topiam.employee.common.message.sms.SmsProviderSend; import cn.topiam.employee.common.message.sms.SmsResponse; -import cn.topiam.employee.common.repository.SmsSendRecordRepository; +import cn.topiam.employee.common.repository.message.SmsSendRecordRepository; /** * 短信消息通知事件 diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/decrypt/DecryptRequestBodyAdvice.java b/eiam-core/src/main/java/cn/topiam/employee/core/security/decrypt/DecryptRequestBodyAdvice.java index ef8d1d1d..9349b688 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/decrypt/DecryptRequestBodyAdvice.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/security/decrypt/DecryptRequestBodyAdvice.java @@ -57,7 +57,7 @@ import static cn.topiam.employee.support.constant.EiamConstants.TOPIAM_ENCRYPT_S public class DecryptRequestBodyAdvice extends RequestBodyAdviceAdapter { private final Logger logger = LoggerFactory.getLogger(DecryptRequestBodyAdvice.class); - private final static String ENCRYPT = "encrypt"; + private static final String ENCRYPT = "encrypt"; @Override public boolean supports(MethodParameter methodParameter, @NonNull Type targetType, diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/jackson2/UserDetailsDeserializer.java b/eiam-core/src/main/java/cn/topiam/employee/core/security/jackson2/UserDetailsDeserializer.java index 042c73fa..123a47b3 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/jackson2/UserDetailsDeserializer.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/security/jackson2/UserDetailsDeserializer.java @@ -82,13 +82,15 @@ class UserDetailsDeserializer extends JsonDeserializer { boolean accountNonExpired = readJsonNode(jsonNode, "accountNonExpired").asBoolean(); boolean credentialsNonExpired = readJsonNode(jsonNode, "credentialsNonExpired").asBoolean(); boolean accountNonLocked = readJsonNode(jsonNode, "accountNonLocked").asBoolean(); + String authType = readJsonNode(jsonNode, "authType").asText(null); //用户类型 String userType = readJsonNode(jsonNode, "userType").asText(null); // 封装值 UserDetails result = new UserDetails(id, username, password, StringUtils.isNoneBlank(userType) ? UserType.getType(userType) : null, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); - + //认证类型 + result.setAuthType(authType); //IP地址、设备相关 result.setGeoLocation(geoLocation); result.setUserAgent(userAgent); diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/otp/OtpContextHelp.java b/eiam-core/src/main/java/cn/topiam/employee/core/security/otp/OtpContextHelp.java index 5c013a2e..b7356120 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/otp/OtpContextHelp.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/security/otp/OtpContextHelp.java @@ -174,13 +174,13 @@ public class OtpContextHelp { MessageNoticeChannel channel) { String keyPrefix = cacheProperties.getRedis().getKeyPrefix(); return keyPrefix + COLON + "otp" + COLON + prefix + COLON + channel.getCode() + COLON + type - + recipient; + + COLON + recipient; } /** * 发送验证码频繁,请稍候重试 */ - private final static String SEND_FREQUENTLY = "发送验证码频繁,请稍候重试"; + private static final String SEND_FREQUENTLY = "发送验证码频繁,请稍候重试"; /** * 验证码 code 值前缀 diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/password/validator/PasswordComplexityRuleValidator.java b/eiam-core/src/main/java/cn/topiam/employee/core/security/password/validator/PasswordComplexityRuleValidator.java index b0713e93..affef382 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/password/validator/PasswordComplexityRuleValidator.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/security/password/validator/PasswordComplexityRuleValidator.java @@ -45,7 +45,7 @@ public class PasswordComplexityRuleValidator implements //必须包含数字和字母 if (rule.equals(PasswordComplexityRule.MUST_NUMBERS_AND_LETTERS)) { //校验 - org.passay.PasswordValidator validator = new org.passay.PasswordValidator( + PasswordValidator validator = new PasswordValidator( new CharacterRule(EnglishCharacterData.Digit, 1), new CharacterRule(EnglishCharacterData.Alphabetical, 1)); RuleResult validate = validator.validate(new PasswordData(password)); @@ -57,7 +57,7 @@ public class PasswordComplexityRuleValidator implements //必须包含数字和大写字母 if (rule.equals(PasswordComplexityRule.MUST_NUMBERS_AND_CAPITAL_LETTERS)) { //校验 - org.passay.PasswordValidator validator = new org.passay.PasswordValidator( + PasswordValidator validator = new PasswordValidator( new CharacterRule(EnglishCharacterData.Digit, 1), new CharacterRule(EnglishCharacterData.UpperCase, 1)); RuleResult validate = validator.validate(new PasswordData(password)); @@ -70,7 +70,7 @@ public class PasswordComplexityRuleValidator implements if (rule.equals( PasswordComplexityRule.MUST_CONTAIN_NUMBERS_UPPERCASE_LETTERS_LOWERCASE_LETTERS_AND_SPECIAL_CHARACTERS)) { //校验 - org.passay.PasswordValidator validator = new org.passay.PasswordValidator( + PasswordValidator validator = new PasswordValidator( new CharacterRule(EnglishCharacterData.Alphabetical, 1), new CharacterRule(EnglishCharacterData.Digit, 1), new CharacterRule(EnglishCharacterData.Special, 1)); @@ -90,7 +90,7 @@ public class PasswordComplexityRuleValidator implements new CharacterRule(EnglishCharacterData.Alphabetical, 1)); rule.setNumberOfCharacteristics(2); //校验 - org.passay.PasswordValidator validator = new org.passay.PasswordValidator(rule); + PasswordValidator validator = new PasswordValidator(rule); RuleResult validate = validator.validate(new PasswordData(password)); if (!validate.isValid()) { throw new PasswordComplexityRuleException("密码至少包含数字、字母、和特殊字符中的两种"); @@ -107,7 +107,7 @@ public class PasswordComplexityRuleValidator implements new CharacterRule(EnglishCharacterData.UpperCase, 1)); rule.setNumberOfCharacteristics(3); //校验 - org.passay.PasswordValidator validator = new org.passay.PasswordValidator(rule); + PasswordValidator validator = new PasswordValidator(rule); RuleResult validate = validator.validate(new PasswordData(password)); if (!validate.isValid()) { throw new PasswordComplexityRuleException("密码至少包含数字、字母、和特殊字符中的两种"); diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/savedredirect/HttpSessionRedirectCache.java b/eiam-core/src/main/java/cn/topiam/employee/core/security/savedredirect/HttpSessionRedirectCache.java index f284c7ca..af158891 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/savedredirect/HttpSessionRedirectCache.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/security/savedredirect/HttpSessionRedirectCache.java @@ -55,7 +55,7 @@ public class HttpSessionRedirectCache implements RedirectCache { String redirectUri = request.getParameter(OAuth2ParameterNames.REDIRECT_URI); if (StringUtils.isNotBlank(redirectUri)) { //saved session - cn.topiam.employee.core.security.savedredirect.SavedRedirect redirect = new cn.topiam.employee.core.security.savedredirect.SavedRedirect(); + SavedRedirect redirect = new SavedRedirect(); int index = redirectUri.indexOf("?"); if (index > -1) { redirect.setAction(redirectUri.substring(0, index)); @@ -70,7 +70,7 @@ public class HttpSessionRedirectCache implements RedirectCache { } //REQUEST if (type.equals(RedirectType.REQUEST)) { - cn.topiam.employee.core.security.savedredirect.SavedRedirect redirect = new cn.topiam.employee.core.security.savedredirect.SavedRedirect(); + SavedRedirect redirect = new SavedRedirect(); redirect.setParameters(getParametersByArray(request.getParameterMap())); redirect.setMethod(request.getMethod()); redirect.setAction(UrlUtils.buildFullRequestUrl(request.getScheme(), @@ -86,10 +86,10 @@ public class HttpSessionRedirectCache implements RedirectCache { * @param map {@link Map} * @return {@link List} */ - public static List getParameters(Map map) { - List parameters = new ArrayList<>(); + public static List getParameters(Map map) { + List parameters = new ArrayList<>(); for (String key : map.keySet()) { - cn.topiam.employee.core.security.savedredirect.SavedRedirect.Parameter parameter = new cn.topiam.employee.core.security.savedredirect.SavedRedirect.Parameter(); + SavedRedirect.Parameter parameter = new SavedRedirect.Parameter(); parameter.setKey(key); parameter.setValue(map.get(key)); parameters.add(parameter); @@ -97,10 +97,10 @@ public class HttpSessionRedirectCache implements RedirectCache { return parameters; } - public List getParametersByArray(Map map) { - List parameters = new ArrayList<>(); + public List getParametersByArray(Map map) { + List parameters = new ArrayList<>(); for (String key : map.keySet()) { - cn.topiam.employee.core.security.savedredirect.SavedRedirect.Parameter parameter = new cn.topiam.employee.core.security.savedredirect.SavedRedirect.Parameter(); + SavedRedirect.Parameter parameter = new SavedRedirect.Parameter(); String[] paramValues = map.get(key); if (paramValues.length == 1) { String paramValue = paramValues[0]; @@ -119,11 +119,10 @@ public class HttpSessionRedirectCache implements RedirectCache { * * @param request {@link HttpServletRequest} * @param response {@link HttpServletResponse} - * @return {@link cn.topiam.employee.core.security.savedredirect.SavedRedirect} + * @return {@link SavedRedirect} */ @Override - public cn.topiam.employee.core.security.savedredirect.SavedRedirect getRedirect(HttpServletRequest request, - HttpServletResponse response) { + public SavedRedirect getRedirect(HttpServletRequest request, HttpServletResponse response) { return (SavedRedirect) request.getSession(false) .getAttribute(TOPIAM_SECURITY_SAVED_REDIRECT); } diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/savedredirect/LoginRedirectParameterFilter.java b/eiam-core/src/main/java/cn/topiam/employee/core/security/savedredirect/LoginRedirectParameterFilter.java index 032a25db..aadfded5 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/savedredirect/LoginRedirectParameterFilter.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/security/savedredirect/LoginRedirectParameterFilter.java @@ -38,8 +38,8 @@ public class LoginRedirectParameterFilter extends OncePerRequestFilter { /** * RedirectCache */ - private final cn.topiam.employee.core.security.savedredirect.RedirectCache redirectCache = new HttpSessionRedirectCache(); - private final RequestMatcher requestMatcher; + private final RedirectCache redirectCache = new HttpSessionRedirectCache(); + private final RequestMatcher requestMatcher; public LoginRedirectParameterFilter(RequestMatcher requestMatcher) { this.requestMatcher = requestMatcher; diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/savedredirect/RedirectCache.java b/eiam-core/src/main/java/cn/topiam/employee/core/security/savedredirect/RedirectCache.java index ceb0ec6e..30eedb80 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/savedredirect/RedirectCache.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/security/savedredirect/RedirectCache.java @@ -56,7 +56,7 @@ public interface RedirectCache { * * @param request {@link HttpServletRequest} * @param response {@link HttpServletResponse} - * @return {@link cn.topiam.employee.core.security.savedredirect.SavedRedirect} + * @return {@link SavedRedirect} */ SavedRedirect getRedirect(HttpServletRequest request, HttpServletResponse response); diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/session/SessionDetails.java b/eiam-core/src/main/java/cn/topiam/employee/core/security/session/SessionDetails.java index 9ae4ad14..34b63c9e 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/session/SessionDetails.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/security/session/SessionDetails.java @@ -84,4 +84,9 @@ public class SessionDetails implements Serializable { */ private UserType userType; + /** + * 认证类型 + */ + private String authType; + } diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/session/TopIamSessionBackedSessionRegistry.java b/eiam-core/src/main/java/cn/topiam/employee/core/security/session/TopIamSessionBackedSessionRegistry.java index a1de8779..8b791f9f 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/session/TopIamSessionBackedSessionRegistry.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/security/session/TopIamSessionBackedSessionRegistry.java @@ -83,7 +83,7 @@ public class TopIamSessionBackedSessionRegistry } private List getTopIamUserDetails(List infos) { - List details = new ArrayList<>(); + List details = new ArrayList<>(); for (SessionInformation information : infos) { //根据session id 获取缓存信息 Session session = sessionRepository.findById(information.getSessionId()); @@ -97,8 +97,8 @@ public class TopIamSessionBackedSessionRegistry //转为实体 UserDetails principal = (UserDetails) securityContext.getAuthentication() .getPrincipal(); - cn.topiam.employee.core.security.session.SessionDetails sessionDetails = new SessionDetails( - principal.getId(), principal.getUsername()); + SessionDetails sessionDetails = new SessionDetails(principal.getId(), + principal.getUsername()); //last request Instant instant = information.getLastRequest().toInstant(); ZoneId zoneId = ZoneId.systemDefault(); @@ -107,6 +107,8 @@ public class TopIamSessionBackedSessionRegistry sessionDetails.setLastRequestTime(lastRequestTime); //登录时间 sessionDetails.setLoginTime(principal.getLoginTime()); + //登录时间 + sessionDetails.setAuthType(principal.getAuthType()); //用户类型 sessionDetails.setUserType(principal.getUserType()); //地理位置 diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/task/UserUnlockTask.java b/eiam-core/src/main/java/cn/topiam/employee/core/security/task/UserUnlockTask.java index 33920f8b..98aa23ff 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/task/UserUnlockTask.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/security/task/UserUnlockTask.java @@ -55,7 +55,8 @@ public class UserUnlockTask { public void execute() { logger.info("用户自动解锁任务开始"); QUserEntity qUserEntity = QUserEntity.userEntity; - Predicate predicate = qUserEntity.isNotNull(); + Predicate predicate = ExpressionUtils.and(qUserEntity.isNotNull(), + qUserEntity.isDeleted.eq(Boolean.FALSE)); //查询条件 //@formatter:off predicate = ExpressionUtils.and(predicate, qUserEntity.status.eq(UserStatus.LOCKED)); diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/security/userdetails/UserDetails.java b/eiam-core/src/main/java/cn/topiam/employee/core/security/userdetails/UserDetails.java index f21cd930..13c8fe9b 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/security/userdetails/UserDetails.java +++ b/eiam-core/src/main/java/cn/topiam/employee/core/security/userdetails/UserDetails.java @@ -74,6 +74,11 @@ public class UserDetails extends User { @JsonFormat(pattern = EiamConstants.DEFAULT_DATE_TIME_FORMATTER_PATTERN) private LocalDateTime loginTime; + /** + * 身份验证类型 + */ + private String authType; + /** * 用户类型 */ diff --git a/eiam-identity-source/eiam-identity-source-core/src/main/java/cn/topiam/employee/identitysource/core/IdentitySource.java b/eiam-identity-source/eiam-identity-source-core/src/main/java/cn/topiam/employee/identitysource/core/IdentitySource.java index ede8da4b..d86d0646 100644 --- a/eiam-identity-source/eiam-identity-source-core/src/main/java/cn/topiam/employee/identitysource/core/IdentitySource.java +++ b/eiam-identity-source/eiam-identity-source-core/src/main/java/cn/topiam/employee/identitysource/core/IdentitySource.java @@ -23,6 +23,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.topiam.employee.common.enums.TriggerType; +import cn.topiam.employee.support.exception.TopIamException; /** * 身份源Provider @@ -66,6 +67,8 @@ public interface IdentitySource { * @param response {@link HttpServletResponse} * @return {@link Map} */ - Object event(HttpServletRequest request, HttpServletResponse response); + default Object event(HttpServletRequest request, HttpServletResponse response) { + throw new TopIamException("暂未实现"); + } } diff --git a/eiam-identity-source/eiam-identity-source-core/src/main/java/cn/topiam/employee/identitysource/core/processor/modal/IdentitySourceEventProcessData.java b/eiam-identity-source/eiam-identity-source-core/src/main/java/cn/topiam/employee/identitysource/core/processor/modal/IdentitySourceEventProcessData.java index 6095d5e5..9523d7aa 100644 --- a/eiam-identity-source/eiam-identity-source-core/src/main/java/cn/topiam/employee/identitysource/core/processor/modal/IdentitySourceEventProcessData.java +++ b/eiam-identity-source/eiam-identity-source-core/src/main/java/cn/topiam/employee/identitysource/core/processor/modal/IdentitySourceEventProcessData.java @@ -21,7 +21,7 @@ import java.io.Serializable; import java.time.LocalDateTime; import java.util.List; -import cn.topiam.employee.common.enums.identityprovider.IdentitySourceProvider; +import cn.topiam.employee.common.enums.identitysource.IdentitySourceProvider; import cn.topiam.employee.identitysource.core.enums.IdentitySourceEventReceiveType; import lombok.AllArgsConstructor; diff --git a/eiam-identity-source/eiam-identity-source-dingtalk/src/main/java/cn/topiam/employee/identitysource/dingtalk/DingTalkIdentitySource.java b/eiam-identity-source/eiam-identity-source-dingtalk/src/main/java/cn/topiam/employee/identitysource/dingtalk/DingTalkIdentitySource.java index 4d732194..b40de306 100644 --- a/eiam-identity-source/eiam-identity-source-dingtalk/src/main/java/cn/topiam/employee/identitysource/dingtalk/DingTalkIdentitySource.java +++ b/eiam-identity-source/eiam-identity-source-dingtalk/src/main/java/cn/topiam/employee/identitysource/dingtalk/DingTalkIdentitySource.java @@ -36,7 +36,7 @@ import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; -import cn.topiam.employee.common.enums.identityprovider.IdentitySourceProvider; +import cn.topiam.employee.common.enums.identitysource.IdentitySourceProvider; import cn.topiam.employee.common.util.RequestUtils; import cn.topiam.employee.identitysource.core.AbstractDefaultIdentitySource; import cn.topiam.employee.identitysource.core.client.IdentitySourceClient; @@ -77,7 +77,7 @@ public class DingTalkIdentitySource extends AbstractDefaultIdentitySource params = RequestUtils.getParams(request); + Map params = RequestUtils.getParams(request); String json = RequestUtils.getBody(request); if (StringUtils.isNoneBlank(json)) { String encrypt = JSON.parseObject(json).getString(ENCRYPT); @@ -99,13 +99,13 @@ public class DingTalkIdentitySource extends AbstractDefaultIdentitySource syncMap, + private Object eventCallBack(LocalDateTime eventTime, Map syncMap, String encrypt) { try { DingTalkConfig config = getConfig(); - String msgSignature = syncMap.get(MSG_SIGNATURE); - String timeStamp = syncMap.get(TIMESTAMP); - String nonce = syncMap.get(NONCE); + String msgSignature = (String) syncMap.get(MSG_SIGNATURE); + String timeStamp = (String) syncMap.get(TIMESTAMP); + String nonce = (String) syncMap.get(NONCE); DingTalkEventCryptoUtils eventCryptoUtils = new DingTalkEventCryptoUtils( config.getToken(), config.getAesKey(), config.getAppKey()); String decryptMsg = eventCryptoUtils.getDecryptMsg(msgSignature, timeStamp, nonce, diff --git a/eiam-identity-source/eiam-identity-source-feishu/src/main/java/cn/topiam/employee/identitysource/feishu/FieShuIdentitySource.java b/eiam-identity-source/eiam-identity-source-feishu/src/main/java/cn/topiam/employee/identitysource/feishu/FieShuIdentitySource.java index a8acd90a..457dc6a8 100644 --- a/eiam-identity-source/eiam-identity-source-feishu/src/main/java/cn/topiam/employee/identitysource/feishu/FieShuIdentitySource.java +++ b/eiam-identity-source/eiam-identity-source-feishu/src/main/java/cn/topiam/employee/identitysource/feishu/FieShuIdentitySource.java @@ -26,7 +26,7 @@ import javax.servlet.http.HttpServletResponse; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; -import cn.topiam.employee.common.enums.identityprovider.IdentitySourceProvider; +import cn.topiam.employee.common.enums.identitysource.IdentitySourceProvider; import cn.topiam.employee.common.util.RequestUtils; import cn.topiam.employee.identitysource.core.AbstractDefaultIdentitySource; import cn.topiam.employee.identitysource.core.client.IdentitySourceClient; diff --git a/eiam-identity-source/eiam-identity-source-wechatwork/src/main/java/cn/topiam/employee/identitysource/wechatwork/WeChatWorkIdentitySource.java b/eiam-identity-source/eiam-identity-source-wechatwork/src/main/java/cn/topiam/employee/identitysource/wechatwork/WeChatWorkIdentitySource.java index 61d99371..6dc97971 100644 --- a/eiam-identity-source/eiam-identity-source-wechatwork/src/main/java/cn/topiam/employee/identitysource/wechatwork/WeChatWorkIdentitySource.java +++ b/eiam-identity-source/eiam-identity-source-wechatwork/src/main/java/cn/topiam/employee/identitysource/wechatwork/WeChatWorkIdentitySource.java @@ -38,7 +38,7 @@ import org.springframework.web.bind.annotation.RequestMethod; import com.alibaba.fastjson2.JSON; -import cn.topiam.employee.common.enums.identityprovider.IdentitySourceProvider; +import cn.topiam.employee.common.enums.identitysource.IdentitySourceProvider; import cn.topiam.employee.common.util.RequestUtils; import cn.topiam.employee.identitysource.core.AbstractDefaultIdentitySource; import cn.topiam.employee.identitysource.core.client.IdentitySourceClient; @@ -79,7 +79,7 @@ public class WeChatWorkIdentitySource extends AbstractDefaultIdentitySource params = RequestUtils.getParams(request); + Map params = RequestUtils.getParams(request); WeChatWorkRequest weWorkResult = null; try { if (RequestMethod.POST.name().equals(request.getMethod())) { @@ -108,14 +108,14 @@ public class WeChatWorkIdentitySource extends AbstractDefaultIdentitySource params, + private String eventCallBack(LocalDateTime eventTime, Map params, WeChatWorkRequest weWorkResult) throws IllegalArgumentException { try { - String msgSignature = params.get(MSG_SIGNATURE); - String timeStamp = params.get(TIMESTAMP); - String nonce = params.get(NONCE); + String msgSignature = (String) params.get(MSG_SIGNATURE); + String timeStamp = (String) params.get(TIMESTAMP); + String nonce = (String) params.get(NONCE); if (params.containsKey(ECHOSTR)) { - String echoStr = params.get(ECHOSTR); + String echoStr = (String) params.get(ECHOSTR); return verifyUrl(msgSignature, timeStamp, nonce, echoStr); } else { EventParameter callBackDTO = processMessage(msgSignature, timeStamp, nonce, diff --git a/eiam-openapi/src/main/java/cn/topiam/employee/openapi/constants/OpenApiConstants.java b/eiam-openapi/src/main/java/cn/topiam/employee/openapi/constants/OpenApiConstants.java index 967b022f..986bc567 100644 --- a/eiam-openapi/src/main/java/cn/topiam/employee/openapi/constants/OpenApiConstants.java +++ b/eiam-openapi/src/main/java/cn/topiam/employee/openapi/constants/OpenApiConstants.java @@ -30,12 +30,12 @@ public class OpenApiConstants { /** * OpenAPI 路径 */ - public final static String OPEN_API_PATH = API_PATH + "/openapi"; + public static final String OPEN_API_PATH = API_PATH + "/openapi"; /** * 权限管理API 路径 */ - public final static String OPEN_API_PERMISSION_PATH = API_PATH + "/openapi/permission"; + public static final String OPEN_API_PERMISSION_PATH = API_PATH + "/openapi/permission"; /** * 组名称 diff --git a/eiam-core/src/main/java/cn/topiam/employee/core/configuration/EiamCaptchaValidatorConfiguration.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/configuration/EiamCaptchaValidatorConfiguration.java similarity index 85% rename from eiam-core/src/main/java/cn/topiam/employee/core/configuration/EiamCaptchaValidatorConfiguration.java rename to eiam-portal/src/main/java/cn/topiam/employee/portal/configuration/EiamCaptchaValidatorConfiguration.java index da8b81be..03fa155a 100644 --- a/eiam-core/src/main/java/cn/topiam/employee/core/configuration/EiamCaptchaValidatorConfiguration.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/configuration/EiamCaptchaValidatorConfiguration.java @@ -1,5 +1,5 @@ /* - * eiam-core - Employee Identity and Access Management Program + * eiam-portal - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.core.configuration; +package cn.topiam.employee.portal.configuration; import java.util.Objects; @@ -26,12 +26,12 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; +import cn.topiam.employee.authentication.captcha.CaptchaValidator; +import cn.topiam.employee.authentication.captcha.NoneCaptchaProvider; +import cn.topiam.employee.authentication.captcha.geetest.GeeTestCaptchaProviderConfig; +import cn.topiam.employee.authentication.captcha.geetest.GeeTestCaptchaValidator; import cn.topiam.employee.common.constants.ConfigBeanNameConstants; import cn.topiam.employee.core.security.captcha.CaptchaProviderConfig; -import cn.topiam.employee.core.security.captcha.CaptchaValidator; -import cn.topiam.employee.core.security.captcha.NoneCaptchaProvider; -import cn.topiam.employee.core.security.captcha.geetest.GeeTestCaptchaProviderConfig; -import cn.topiam.employee.core.security.captcha.geetest.GeeTestCaptchaValidator; import static cn.topiam.employee.common.enums.CaptchaProviderType.GEE_TEST; import static cn.topiam.employee.core.context.SettingContextHelp.getCaptchaProviderConfig; diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/configuration/PortalSecurityConfiguration.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/configuration/PortalSecurityConfiguration.java index 9dd43c2c..ce13dd0d 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/configuration/PortalSecurityConfiguration.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/configuration/PortalSecurityConfiguration.java @@ -29,26 +29,19 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.http.HttpMethod; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.*; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.jackson2.SecurityJackson2Modules; import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter; -import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService; -import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService; -import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; -import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.EiamOAuth2AuthorizationServerConfigurer; -import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.access.ExceptionTranslationFilter; -import org.springframework.security.web.access.intercept.RequestAuthorizationContext; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter; @@ -58,15 +51,18 @@ import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import com.fasterxml.jackson.databind.Module; -import com.fasterxml.jackson.databind.ObjectMapper; - import cn.topiam.employee.audit.event.AuditEventPublish; +import cn.topiam.employee.authentication.captcha.CaptchaValidator; +import cn.topiam.employee.authentication.captcha.filter.CaptchaValidatorFilter; import cn.topiam.employee.authentication.common.service.UserIdpService; import cn.topiam.employee.authentication.dingtalk.configurer.DingtalkOAuth2AuthenticationConfigurer; import cn.topiam.employee.authentication.dingtalk.configurer.DingtalkScanCodeAuthenticationConfigurer; +import cn.topiam.employee.authentication.feishu.configurer.FeiShuScanCodeAuthenticationConfigurer; +import cn.topiam.employee.authentication.mfa.MfaAuthenticationConfigurer; +import cn.topiam.employee.authentication.mfa.MfaAuthenticationHandler; import cn.topiam.employee.authentication.qq.configurer.QqOauthAuthenticationConfigurer; -import cn.topiam.employee.authentication.sms.configurer.SmsAuthenticationConfigurer; +import cn.topiam.employee.authentication.sms.SmsAuthenticationConfigurer; +import cn.topiam.employee.authentication.sms.SmsAuthenticationFilter; import cn.topiam.employee.authentication.wechat.configurer.WeChatScanCodeAuthenticationConfigurer; import cn.topiam.employee.authentication.wechatwork.configurer.WeChatWorkScanCodeAuthenticationConfigurer; import cn.topiam.employee.common.constants.AuthorizeConstants; @@ -74,19 +70,14 @@ import cn.topiam.employee.common.entity.setting.SettingEntity; import cn.topiam.employee.common.geo.GeoLocationService; import cn.topiam.employee.common.repository.account.UserIdpRepository; import cn.topiam.employee.common.repository.account.UserRepository; -import cn.topiam.employee.common.repository.app.AppOidcConfigRepository; import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; import cn.topiam.employee.common.repository.setting.SettingRepository; import cn.topiam.employee.core.endpoint.security.PublicSecretEndpoint; import cn.topiam.employee.core.message.mail.MailMsgEventPublish; import cn.topiam.employee.core.message.sms.SmsMsgEventPublish; import cn.topiam.employee.core.security.authentication.AuthenticationTrustResolverImpl; -import cn.topiam.employee.core.security.authentication.IdpAuthorizationManager; -import cn.topiam.employee.core.security.captcha.CaptchaValidator; -import cn.topiam.employee.core.security.captcha.CaptchaValidatorFilter; import cn.topiam.employee.core.security.form.FormLoginSecretFilter; -import cn.topiam.employee.core.security.jackson2.CoreJackson2Module; -import cn.topiam.employee.core.security.mfa.MfaAuthorizationManager; +import cn.topiam.employee.core.security.otp.OtpContextHelp; import cn.topiam.employee.core.security.password.task.PasswordExpireTask; import cn.topiam.employee.core.security.password.task.impl.PasswordExpireLockTask; import cn.topiam.employee.core.security.password.task.impl.PasswordExpireWarnTask; @@ -94,34 +85,27 @@ import cn.topiam.employee.core.security.savedredirect.LoginRedirectParameterFilt import cn.topiam.employee.core.security.task.UserExpireLockTask; import cn.topiam.employee.core.security.task.UserUnlockTask; import cn.topiam.employee.core.setting.constant.SecuritySettingConstants; -import cn.topiam.employee.portal.handler.PortalAccessDeniedHandler; -import cn.topiam.employee.portal.handler.PortalAuthenticationEntryPoint; -import cn.topiam.employee.portal.handler.PortalAuthenticationHandler; -import cn.topiam.employee.portal.handler.PortalLogoutSuccessHandler; +import cn.topiam.employee.portal.handler.*; import cn.topiam.employee.portal.idp.IdpRedirectParameterMatcher; import cn.topiam.employee.portal.idp.bind.IdpAuthenticationConfigurer; import cn.topiam.employee.portal.listener.PortalAuthenticationFailureEventListener; import cn.topiam.employee.portal.listener.PortalAuthenticationSuccessEventListener; import cn.topiam.employee.portal.listener.PortalLogoutSuccessEventListener; import cn.topiam.employee.portal.listener.PortalSessionInformationExpiredStrategy; -import cn.topiam.employee.portal.mfa.MfaAuthenticationConfigurer; import cn.topiam.employee.protocol.cas.idp.CasIdpConfigurer; -import cn.topiam.employee.protocol.oidc.authentication.EiamOAuth2AuthorizationService; -import cn.topiam.employee.protocol.oidc.repository.OidcConfigRegisteredClientRepository; -import cn.topiam.employee.protocol.oidc.token.ApplicationOpaqueTokenIntrospector; +import cn.topiam.employee.protocol.form.FormProtocolConfigurer; +import cn.topiam.employee.protocol.oidc.token.EiamOpaqueTokenIntrospector; import cn.topiam.employee.protocol.saml2.idp.Saml2IdpConfigurer; import lombok.RequiredArgsConstructor; import static org.springframework.boot.autoconfigure.security.StaticResourceLocation.*; import static org.springframework.security.config.Customizer.withDefaults; -import static cn.topiam.employee.authentication.sms.filter.SmsAuthenticationFilter.SMS_LOGIN_MATCHER; import static cn.topiam.employee.common.constants.AuthorizeConstants.*; import static cn.topiam.employee.common.constants.ConfigBeanNameConstants.*; import static cn.topiam.employee.common.constants.SessionConstants.CURRENT_STATUS; import static cn.topiam.employee.core.setting.constant.SecuritySettingConstants.SECURITY_BASIC_REMEMBER_ME_VALID_TIME; import static cn.topiam.employee.core.setting.constant.SecuritySettingConstants.SECURITY_SESSION_MAXIMUM; -import static cn.topiam.employee.portal.mfa.MfaAuthenticationFilter.MFA_LOGIN_MATCHER; import static cn.topiam.employee.support.constant.EiamConstants.*; /** @@ -134,8 +118,12 @@ import static cn.topiam.employee.support.constant.EiamConstants.*; @RequiredArgsConstructor public class PortalSecurityConfiguration { + private final AuthenticationSuccessHandler successHandler = new PortalAuthenticationSuccessHandler(); + + private final AuthenticationFailureHandler failureHandler = new PortalAuthenticationFailureHandler(); + /** - * portalSocialSecurityFilterChain + * IDP SecurityFilterChain * * @param http {@link HttpSecurity} * @return {@link SecurityFilterChain} @@ -143,40 +131,46 @@ public class PortalSecurityConfiguration { */ @Order(1) @RefreshScope - @Bean(name = SOCIAL_SECURITY_FILTER_CHAIN) - public SecurityFilterChain socialAuthenticationSecurityFilterChain(HttpSecurity http) throws Exception { + @Bean(name = IDP_SECURITY_FILTER_CHAIN) + public SecurityFilterChain idpAuthenticationSecurityFilterChain(HttpSecurity http) throws Exception { // @formatter:off List requestMatchers = new ArrayList<>(); //QQ QqOauthAuthenticationConfigurer qqOauthAuthenticationConfigurer = new QqOauthAuthenticationConfigurer<>(identityProviderRepository, userIdpService); requestMatchers.add(qqOauthAuthenticationConfigurer.getRequestMatcher()); - qqOauthAuthenticationConfigurer.successHandler(new PortalAuthenticationHandler()); - qqOauthAuthenticationConfigurer.failureHandler(new PortalAuthenticationHandler()); + qqOauthAuthenticationConfigurer.successHandler(successHandler); + qqOauthAuthenticationConfigurer.failureHandler(failureHandler); http.apply(qqOauthAuthenticationConfigurer); //微信扫码 WeChatScanCodeAuthenticationConfigurer weChatScanCodeAuthenticationConfigurer = new WeChatScanCodeAuthenticationConfigurer<>(identityProviderRepository, userIdpService); requestMatchers.add(weChatScanCodeAuthenticationConfigurer.getRequestMatcher()); - weChatScanCodeAuthenticationConfigurer.successHandler(new PortalAuthenticationHandler()); - weChatScanCodeAuthenticationConfigurer.failureHandler(new PortalAuthenticationHandler()); + weChatScanCodeAuthenticationConfigurer.successHandler(successHandler); + weChatScanCodeAuthenticationConfigurer.failureHandler(failureHandler); http.apply(weChatScanCodeAuthenticationConfigurer); //企业微信 WeChatWorkScanCodeAuthenticationConfigurer weChatWorkScanCodeAuthenticationConfigurer = new WeChatWorkScanCodeAuthenticationConfigurer<>(identityProviderRepository, userIdpService); requestMatchers.add(weChatWorkScanCodeAuthenticationConfigurer.getRequestMatcher()); - weChatWorkScanCodeAuthenticationConfigurer.successHandler(new PortalAuthenticationHandler()); - weChatWorkScanCodeAuthenticationConfigurer.failureHandler(new PortalAuthenticationHandler()); + weChatWorkScanCodeAuthenticationConfigurer.successHandler(successHandler); + weChatWorkScanCodeAuthenticationConfigurer.failureHandler(failureHandler); http.apply(weChatWorkScanCodeAuthenticationConfigurer); //钉钉OAuth2 DingtalkOAuth2AuthenticationConfigurer dingtalkOauth2AuthenticationConfigurer = new DingtalkOAuth2AuthenticationConfigurer<>(identityProviderRepository, userIdpService); requestMatchers.add(dingtalkOauth2AuthenticationConfigurer.getRequestMatcher()); - dingtalkOauth2AuthenticationConfigurer.successHandler(new PortalAuthenticationHandler()); - dingtalkOauth2AuthenticationConfigurer.failureHandler(new PortalAuthenticationHandler()); + dingtalkOauth2AuthenticationConfigurer.successHandler(successHandler); + dingtalkOauth2AuthenticationConfigurer.failureHandler(failureHandler); http.apply(dingtalkOauth2AuthenticationConfigurer); //钉钉扫码 DingtalkScanCodeAuthenticationConfigurer dingtalkScanCodeAuthenticationConfigurer = new DingtalkScanCodeAuthenticationConfigurer<>(identityProviderRepository, userIdpService); requestMatchers.add(dingtalkScanCodeAuthenticationConfigurer.getRequestMatcher()); - dingtalkScanCodeAuthenticationConfigurer.successHandler(new PortalAuthenticationHandler()); - dingtalkScanCodeAuthenticationConfigurer.failureHandler(new PortalAuthenticationHandler()); + dingtalkScanCodeAuthenticationConfigurer.successHandler(successHandler); + dingtalkScanCodeAuthenticationConfigurer.failureHandler(failureHandler); http.apply(dingtalkScanCodeAuthenticationConfigurer); + //飞书扫码 + FeiShuScanCodeAuthenticationConfigurer feiShuScanCodeAuthenticationConfigurer = new FeiShuScanCodeAuthenticationConfigurer<>(identityProviderRepository, userIdpService); + requestMatchers.add(feiShuScanCodeAuthenticationConfigurer.getRequestMatcher()); + feiShuScanCodeAuthenticationConfigurer.successHandler(successHandler); + feiShuScanCodeAuthenticationConfigurer.failureHandler(failureHandler); + http.apply(feiShuScanCodeAuthenticationConfigurer); //RequestMatcher OrRequestMatcher requestMatcher = new OrRequestMatcher(requestMatchers); @@ -218,7 +212,7 @@ public class PortalSecurityConfiguration { .authorizeHttpRequests( authorizeRequests -> authorizeRequests.anyRequest().authenticated()) .oauth2ResourceServer(configurer -> configurer.opaqueToken() - .introspector(new ApplicationOpaqueTokenIntrospector())) + .introspector(new EiamOpaqueTokenIntrospector(oAuth2AuthorizationService))) //CSRF .csrf(withCsrfConfigurerDefaults(requestMatcher)) //headers @@ -293,6 +287,38 @@ public class PortalSecurityConfiguration { //@formatter:on } + /** + * FormProtocolSecurityFilterChain + * + * @param http {@link HttpSecurity} + * @return {@link SecurityFilterChain} + * @throws Exception Exception + */ + @Order(5) + @Bean(value = FORM_PROTOCOL_SECURITY_FILTER_CHAIN) + @RefreshScope + public SecurityFilterChain formProtocolSecurityFilterChain(HttpSecurity http) throws Exception { + //@formatter:off + //Form IDP 配置 + FormProtocolConfigurer configurer = new FormProtocolConfigurer<>(); + RequestMatcher endpointsMatcher = configurer.getEndpointsMatcher(); + http.requestMatcher(endpointsMatcher) + .authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated()) + //异常处理 + .exceptionHandling(withExceptionConfigurerDefaults()) + //CSRF + .csrf(withCsrfConfigurerDefaults(endpointsMatcher)) + //headers + .headers(withHeadersConfigurerDefaults()) + //cors + .cors(withCorsConfigurerDefaults()) + //会话管理器 + .sessionManagement(withSessionManagementConfigurerDefaults(settingRepository)) + .apply(configurer); + return http.build(); + //@formatter:on + } + /** * SecurityFilterChain * @@ -300,7 +326,7 @@ public class PortalSecurityConfiguration { * @return {@link SecurityFilterChain} * @throws Exception Exception */ - @Order(5) + @Order(7) @RefreshScope @Bean(name = DEFAULT_SECURITY_FILTER_CHAIN) public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { @@ -331,17 +357,17 @@ public class PortalSecurityConfiguration { .sessionManagement(withSessionManagementConfigurerDefaults(settingRepository)); //表单登录解密过滤器 http.addFilterBefore(new FormLoginSecretFilter(), UsernamePasswordAuthenticationFilter.class); - //短信OPT认证 - SmsAuthenticationConfigurer smsAuthenticationConfigurer = new SmsAuthenticationConfigurer<>(userDetailsService); - smsAuthenticationConfigurer.successHandler(new PortalAuthenticationHandler()); - smsAuthenticationConfigurer.failureHandler(new PortalAuthenticationHandler()); + //短信OTP认证 + SmsAuthenticationConfigurer smsAuthenticationConfigurer = new SmsAuthenticationConfigurer<>(userRepository, userDetailsService, otpContextHelp); + smsAuthenticationConfigurer.successHandler(successHandler); + smsAuthenticationConfigurer.failureHandler(failureHandler); http.apply(smsAuthenticationConfigurer); //MFA - http.apply(new MfaAuthenticationConfigurer<>()); + http.apply(new MfaAuthenticationConfigurer<>(otpContextHelp, successHandler,failureHandler)); //IDP 绑定用户 http.apply(new IdpAuthenticationConfigurer<>(userIdpService, userIdpRepository, passwordEncoder,auditEventPublish)); //Form 、SMS 授权请求重定向参数过滤器 - http.addFilterBefore(new LoginRedirectParameterFilter(new OrRequestMatcher(new AntPathRequestMatcher(FORM_LOGIN), SMS_LOGIN_MATCHER,MFA_LOGIN_MATCHER)), OAuth2AuthorizationRequestRedirectFilter.class); + http.addFilterBefore(new LoginRedirectParameterFilter(new OrRequestMatcher(new AntPathRequestMatcher(FORM_LOGIN), SmsAuthenticationFilter.getRequestMatcher(),MfaAuthenticationConfigurer.getRequestMatcher())), OAuth2AuthorizationRequestRedirectFilter.class); //验证码验证过滤器 http.addFilterBefore(new CaptchaValidatorFilter(captchaValidator), FormLoginSecretFilter.class); // @formatter:on @@ -354,14 +380,8 @@ public class PortalSecurityConfiguration { * @return {@link AuthorizeHttpRequestsConfigurer} */ public Customizer.AuthorizationManagerRequestMatcherRegistry> withHttpAuthorizeRequests() { - final AuthorizationManager mfaAuthorizationManager = new MfaAuthorizationManager(); - final AuthorizationManager idpAuthorizationManager = new IdpAuthorizationManager(); //@formatter:off return registry -> { - //MFA验证 - registry.mvcMatchers(HttpMethod.POST, MFA_VALIDATE).access(mfaAuthorizationManager); - //绑定账号 - registry.mvcMatchers(HttpMethod.POST, USER_BIND_IDP).access(idpAuthorizationManager); //静态资源 registry.antMatchers( CSS.getPatterns().collect(Collectors.joining()), @@ -375,10 +395,6 @@ public class PortalSecurityConfiguration { registry.antMatchers(HttpMethod.GET, PublicSecretEndpoint.PUBLIC_SECRET_PATH).permitAll(); //登录配置 registry.antMatchers(HttpMethod.GET, LOGIN_CONFIG).permitAll(); - //MFA Provider - registry.antMatchers(HttpMethod.GET, LOGIN_MFA_FACTORS).permitAll(); - //登录 OPT - registry.antMatchers(HttpMethod.POST, LOGIN_OTP_SEND).permitAll(); //健康检查端点 registry.antMatchers(webEndpointProperties.getBasePath()+"/**").permitAll(); //其他请求认证 @@ -438,12 +454,12 @@ public class PortalSecurityConfiguration { configurer.xssProtection(xssProtection -> xssProtection.block(false)); configurer.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin); configurer.contentSecurityPolicy( - "default-src 'self'; " + - "frame-src 'self' data:; " + - "frame-ancestors 'self' https://eiam.topiam.cn data:; " + - "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; " + + "default-src 'self' data:; " + + "frame-src 'self' login.dingtalk.com open.weixin.qq.com open.work.weixin.qq.com passport.feishu.cn data:; " + + "frame-ancestors 'self' eiam.topiam.cn data:; " + + "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com sf3-cn.feishucdn.com;" + "style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net 'unsafe-inline'; " + - "img-src 'self' https://img.alicdn.com https://static-legacy.dingtalk.com https://joeschmoe.io data:; " + + "img-src 'self' https://img.alicdn.com https://static-legacy.dingtalk.com https://joeschmoe.io https://api.multiavatar.com data:; " + "font-src 'self' https://fonts.gstatic.com data:; "+ "worker-src 'self' https://storage.googleapis.com blob:;"); configurer.referrerPolicy( @@ -549,13 +565,14 @@ public class PortalSecurityConfiguration { * * @return {@link FormLoginConfigurer} */ - public static Customizer> withFormLoginConfigurerDefaults() { + public Customizer> withFormLoginConfigurerDefaults() { // @formatter:off return configurer -> { configurer.loginPage(FE_LOGIN); configurer.loginProcessingUrl(FORM_LOGIN); - configurer.successHandler(new PortalAuthenticationHandler()); - configurer.failureHandler(new PortalAuthenticationHandler()); + MfaAuthenticationHandler authenticationHandler = new MfaAuthenticationHandler(successHandler, failureHandler); + configurer.successHandler(authenticationHandler); + configurer.failureHandler(authenticationHandler); }; // @formatter:on } @@ -570,54 +587,6 @@ public class PortalSecurityConfiguration { }; } - /** - * 注册客户端 Repository - * - * @return {@link RegisteredClientRepository} - */ - @Bean - public RegisteredClientRepository registeredClientRepository(AppOidcConfigRepository appOidcConfigRepository) { - return new OidcConfigRegisteredClientRepository(appOidcConfigRepository); - } - - /** - * Authorization Service - * - * @param jdbcTemplate {@link JdbcTemplate} - * @param registeredClientRepository {@link RegisteredClientRepository} - * @return {@link OAuth2AuthorizationService} - */ - @Bean - public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, - RegisteredClientRepository registeredClientRepository) { - EiamOAuth2AuthorizationService authorizationService = new EiamOAuth2AuthorizationService( - jdbcTemplate, registeredClientRepository); - EiamOAuth2AuthorizationService.OAuth2AuthorizationRowMapper authorizationRowMapper = new JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper( - registeredClientRepository); - ClassLoader classLoader = EiamOAuth2AuthorizationService.class.getClassLoader(); - List securityModules = SecurityJackson2Modules.getModules(classLoader); - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.registerModules(securityModules); - objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module()); - objectMapper.registerModule(new CoreJackson2Module()); - authorizationRowMapper.setObjectMapper(objectMapper); - authorizationService.setAuthorizationRowMapper(authorizationRowMapper); - return authorizationService; - } - - /** - * OAuth2 Authorization Consent Service - * - * @param jdbcTemplate {@link JdbcTemplate} - * @param registeredClientRepository {@link RegisteredClientRepository} - * @return {@link OAuth2AuthorizationConsentService} - */ - @Bean - public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, - RegisteredClientRepository registeredClientRepository) { - return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository); - } - /** * 密码过期锁定任务 * @@ -672,6 +641,7 @@ public class PortalSecurityConfiguration { return new UserExpireLockTask(settingRepository, userRepository); } + private final OAuth2AuthorizationService oAuth2AuthorizationService; /** * WebEndpointProperties */ @@ -682,11 +652,21 @@ public class PortalSecurityConfiguration { */ private final CaptchaValidator captchaValidator; + /** + * UserRepository + */ + private final UserRepository userRepository; + /** * UserDetailsService */ private final UserDetailsService userDetailsService; + /** + * OtpContextHelp + */ + private final OtpContextHelp otpContextHelp; + /** * PasswordEncoder */ diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/controller/AccountController.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/controller/AccountController.java index 5fc674f7..b24aaead 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/controller/AccountController.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/controller/AccountController.java @@ -65,6 +65,17 @@ public class AccountController { return ApiRestResult.ok(accountService.changePassword(param)); } + /** + * 准备修改手机 + * + * @return {@link ApiRestResult} + */ + @Operation(summary = "准备修改手机") + @PostMapping("/prepare_change_phone") + public ApiRestResult prepareChangePhone(@DecryptRequestBody @RequestBody @Validated PrepareChangePhoneRequest param) { + return ApiRestResult.ok(accountService.prepareChangePhone(param)); + } + /** * 修改手机 * @@ -76,6 +87,17 @@ public class AccountController { return ApiRestResult.ok(accountService.changePhone(param)); } + /** + * 准备修改邮箱 + * + * @return {@link ApiRestResult} + */ + @Operation(summary = "准备修改邮箱") + @PostMapping("/prepare_change_email") + public ApiRestResult prepareChangeEmail(@DecryptRequestBody @RequestBody @Validated PrepareChangeEmailRequest param) { + return ApiRestResult.ok(accountService.prepareChangeEmail(param)); + } + /** * 修改邮箱 * diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/controller/CurrentUserController.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/controller/CurrentUserController.java index 1c39ea31..64b0302f 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/controller/CurrentUserController.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/controller/CurrentUserController.java @@ -30,8 +30,8 @@ import org.springframework.stereotype.Component; import com.alibaba.fastjson2.JSON; import com.google.common.collect.Lists; +import cn.topiam.employee.authentication.common.IdentityProviderType; import cn.topiam.employee.common.entity.account.UserEntity; -import cn.topiam.employee.common.enums.IdentityProviderType; import cn.topiam.employee.common.enums.PasswordStrength; import cn.topiam.employee.core.security.util.UserUtils; import cn.topiam.employee.support.result.ApiRestResult; diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/controller/SessionManageEndpoint.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/controller/SessionManageEndpoint.java index b601d06e..38b19923 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/controller/SessionManageEndpoint.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/controller/SessionManageEndpoint.java @@ -188,6 +188,11 @@ class OnlineSession implements Serializable { @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) private UserAgent userAgent; + /** + * 认证类型 + */ + private String authType; + /** * 登录时间 */ @@ -231,6 +236,8 @@ interface OnlineUserConverter { onlineSession.setGeoLocation(sessionDetails.getGeoLocation()); //用户代理 onlineSession.setUserAgent(sessionDetails.getUserAgent()); + //认证类型 + onlineSession.setAuthType(sessionDetails.getAuthType()); //登录时间 onlineSession.setLoginTime(sessionDetails.getLoginTime()); //最后请求时间 diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/controller/login/LoginOtpController.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/controller/login/LoginOtpController.java deleted file mode 100644 index a7827204..00000000 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/controller/login/LoginOtpController.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * eiam-portal - Employee Identity and Access Management Program - * Copyright © 2020-2023 TopIAM (support@topiam.cn) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package cn.topiam.employee.portal.controller.login; - -import java.io.Serializable; - -import javax.validation.constraints.NotNull; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import cn.topiam.employee.common.entity.account.UserEntity; -import cn.topiam.employee.common.enums.MailType; -import cn.topiam.employee.common.enums.MessageNoticeChannel; -import cn.topiam.employee.common.enums.SmsType; -import cn.topiam.employee.common.exception.LoginOtpActionNotSupportException; -import cn.topiam.employee.core.security.mfa.MfaAuthentication; -import cn.topiam.employee.core.security.otp.OtpContextHelp; -import cn.topiam.employee.core.security.userdetails.UserDetails; -import cn.topiam.employee.core.security.util.UserUtils; -import cn.topiam.employee.support.lock.Lock; -import cn.topiam.employee.support.result.ApiRestResult; - -import lombok.Data; -import lombok.extern.slf4j.Slf4j; - -import io.swagger.v3.oas.annotations.Parameter; -import static cn.topiam.employee.common.constants.AuthorizeConstants.LOGIN_PATH; - -/** - * OPT 端点 - * 短信验证码有效期2分钟 - * - * 验证码为6位纯数字 - * 每个手机号60秒内只能发送一次短信验证码,且这一规则的校验必须在服务器端执行 - * 同一个手机号在同一时间内可以有多个有效的短信验证码 - * 保存于服务器端的验证码,至多可被使用3次(无论和请求中的验证码是否匹配),随后立即作废,以防止暴力攻击 - * 短信验证码不可直接记录到日志文件 - * 集成第三方API做登录保护(可选) - * - * @author TopIAM - * Created by support@topiam.cn on 2020/12/23 20:49 - */ -@Slf4j -@RestController -@RequestMapping(value = LOGIN_PATH + "/otp") -public class LoginOtpController { - - /** - * 发送 OPT - *s - * @return {@link ApiRestResult} - */ - @PostMapping("/send") - @Lock(namespaces = "login") - public ResponseEntity> send(@Validated SendOtpRequest request, - Authentication authentication) { - if (request.getAction().equals(Action.LOGIN)) { - if (StringUtils.isBlank(request.getTarget())) { - throw new NullPointerException("目标不能为空"); - } - send(request.getTarget(), request.getChannel()); - return ResponseEntity.ok(ApiRestResult.ok()); - } - //MFA - if (request.getAction().equals(Action.MFA)) { - //非MFA对象 - if (!(authentication instanceof MfaAuthentication)) { - ResponseEntity.BodyBuilder builder = ResponseEntity.status(HttpStatus.UNAUTHORIZED); - return builder.body(ApiRestResult.ok()); - } - //MFA,从会话上下文中获取手机号及邮箱信息 - UserDetails principal = (UserDetails) ((MfaAuthentication) authentication).getFirst() - .getPrincipal(); - UserEntity user = UserUtils.getUser(principal.getId()); - String email = user.getEmail(); - if (MessageNoticeChannel.MAIL.equals(request.getChannel())) { - send(email, MessageNoticeChannel.MAIL); - return ResponseEntity.ok(ApiRestResult.ok()); - } - String phone = user.getPhone(); - if (MessageNoticeChannel.SMS.equals(request.getChannel())) { - send(phone, MessageNoticeChannel.SMS); - return ResponseEntity.ok(ApiRestResult.ok()); - } - } - throw new LoginOtpActionNotSupportException(); - } - - /** - * 发送 - * - * @param target {@link String} - * @param channel {@link MessageNoticeChannel} - */ - private void send(String target, MessageNoticeChannel channel) { - String type; - if (channel == MessageNoticeChannel.MAIL) { - type = MailType.AGAIN_VERIFY.getCode(); - } else { - type = SmsType.AGAIN_VERIFY.getCode(); - } - otpContextHelp.sendOtp(target, type, channel); - } - - /** - * 发送 OTP 请求 - */ - @Data - public static class SendOtpRequest implements Serializable { - /** - * 动作 - */ - @Parameter(description = "action") - @NotNull(message = "消息动作不能为空") - private Action action; - - /** - * 渠道 - */ - @Parameter(description = "channel") - @NotNull(message = "消息渠道不能为空") - private MessageNoticeChannel channel; - - /** - * 目标 - */ - @Parameter(description = "target") - private String target; - } - - /** - * - */ - public enum Action { - - /** - * LOGIN - */ - LOGIN, - /** - * MFA - */ - MFA - } - - private final OtpContextHelp otpContextHelp; - - public LoginOtpController(OtpContextHelp otpContextHelp) { - this.otpContextHelp = otpContextHelp; - } -} diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/converter/AccountConverter.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/converter/AccountConverter.java index d783893e..904a91b2 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/converter/AccountConverter.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/converter/AccountConverter.java @@ -111,7 +111,7 @@ public interface AccountConverter { entity.setUserId(Long.valueOf(userId)); entity.setOpenId(idpUser.getOpenId()); entity.setIdpId(idpUser.getProviderId()); - entity.setIdpType(idpUser.getProviderType()); + entity.setIdpType(idpUser.getProviderType().value()); if (MapUtils.isNotEmpty(idpUser.getAdditionalInfo())) { entity.setAdditionInfo(JSONObject.toJSONString(idpUser.getAdditionalInfo())); } diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/converter/AppConverter.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/converter/AppConverter.java index 29617910..394e83ca 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/converter/AppConverter.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/converter/AppConverter.java @@ -34,7 +34,6 @@ import cn.topiam.employee.portal.pojo.result.GetAppListResult; import cn.topiam.employee.support.context.ApplicationContextHelp; import cn.topiam.employee.support.repository.page.domain.Page; import static cn.topiam.employee.common.constants.ProtocolConstants.APP_CODE_VARIABLE; -import static cn.topiam.employee.common.enums.app.InitLoginType.APP; import static cn.topiam.employee.common.enums.app.InitLoginType.PORTAL_OR_APP; /** @@ -62,9 +61,11 @@ public interface AppConverter { result.setType(entity.getType()); result.setProtocol(entity.getProtocol()); result.setTemplate(entity.getTemplate()); - result.setIdpInit(APP.equals(entity.getInitLoginType()) | PORTAL_OR_APP.equals(entity.getInitLoginType())); + result.setInitLoginType(entity.getInitLoginType()); //登录发起URL - result.setIdpInitUrl(StringUtils.defaultString(entity.getInitLoginUrl(), getIdpInitUrl(entity.getProtocol(), entity.getCode()))); + if (PORTAL_OR_APP.equals(entity.getInitLoginType())){ + result.setInitLoginUrl(StringUtils.defaultString(entity.getInitLoginUrl(), getIdpInitUrl(entity.getProtocol(), entity.getCode()))); + } result.setIcon(entity.getIcon()); //图标未配置,所以先从模版中拿 if (StringUtils.isBlank(entity.getIcon())){ @@ -84,7 +85,7 @@ public interface AppConverter { results.add(result); } page.setList(results); - page.setPagination(cn.topiam.employee.support.repository.page.domain.Page.Pagination.builder() + page.setPagination(Page.Pagination.builder() .total(list.getTotalElements()) .totalPages(list.getTotalPages()) .current(list.getPageable().getPageNumber() + 1) diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/converter/LoginConfigConverter.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/converter/LoginConfigConverter.java index 727f8b99..1e27c217 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/converter/LoginConfigConverter.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/converter/LoginConfigConverter.java @@ -44,7 +44,7 @@ public interface LoginConfigConverter { List result = new ArrayList<>(); for (IdentityProviderEntity entity : list) { LoginConfigResult.Idps idp = new LoginConfigResult.Idps(); - idp.setId(String.valueOf(entity.getId())); + idp.setCode(entity.getCode()); idp.setName(entity.getName()); idp.setType(entity.getType()); idp.setCategory(entity.getCategory()); diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/handler/PortalAuthenticationSuccessHandler.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/handler/PortalAuthenticationSuccessHandler.java index 3d7e2d5a..a617347a 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/handler/PortalAuthenticationSuccessHandler.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/handler/PortalAuthenticationSuccessHandler.java @@ -30,8 +30,10 @@ import org.springframework.security.core.Authentication; import org.springframework.security.web.WebAttributes; import org.springframework.security.web.authentication.AbstractAuthenticationTargetUrlRequestHandler; +import cn.topiam.employee.common.constants.AuthorizeConstants; import cn.topiam.employee.common.enums.SecretType; import cn.topiam.employee.core.context.ServerContextHelp; +import cn.topiam.employee.core.security.authentication.IdpAuthentication; import cn.topiam.employee.support.result.ApiRestResult; import cn.topiam.employee.support.util.HttpResponseUtils; import cn.topiam.employee.support.util.HttpUrlUtils; @@ -52,7 +54,9 @@ public class PortalAuthenticationSuccessHandler extends implements org.springframework.security.web.authentication.AuthenticationSuccessHandler { - private final Logger logger = LoggerFactory.getLogger(PortalAuthenticationSuccessHandler.class); + private final Logger logger = LoggerFactory + .getLogger(PortalAuthenticationSuccessHandler.class); + private static final String REQUIRE_USER_BIND = "require_user_bind"; /** * Called when a user has been successfully authenticated. @@ -74,6 +78,7 @@ public class PortalAuthenticationSuccessHandler extends @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { + //@formatter:off boolean isTextHtml = acceptIncludeTextHtml(request); //Clear Authentication Attributes @@ -81,6 +86,19 @@ public class PortalAuthenticationSuccessHandler extends if (response.isCommitted()) { return; } + //TODO IDP 未关联 + if (authentication instanceof IdpAuthentication && !((IdpAuthentication) authentication).getAssociated()) { + if (!isTextHtml) { + HttpResponseUtils.flushResponseJson(response, HttpStatus.BAD_REQUEST.value(), + ApiRestResult.builder().status(REQUIRE_USER_BIND).message(REQUIRE_USER_BIND) + .build()); + return; + } + //跳转登录,前端会有接口获取状态,并进行展示绑定页面 + response.sendRedirect(HttpUrlUtils + .format(ServerContextHelp.getPortalPublicBaseUrl() + AuthorizeConstants.FE_LOGIN)); + return; + } if (!isTextHtml) { HttpResponseUtils.flushResponseJson(response, HttpStatus.OK.value(), ApiRestResult.builder().status(SUCCESS).build()); diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/idp/bind/IdpAuthenticationConfigurer.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/idp/bind/IdpAuthenticationConfigurer.java index 9d52590b..aa07d9e9 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/idp/bind/IdpAuthenticationConfigurer.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/idp/bind/IdpAuthenticationConfigurer.java @@ -27,7 +27,8 @@ import org.springframework.security.web.util.matcher.RequestMatcher; import cn.topiam.employee.audit.event.AuditEventPublish; import cn.topiam.employee.authentication.common.service.UserIdpService; import cn.topiam.employee.common.repository.account.UserIdpRepository; -import cn.topiam.employee.portal.handler.PortalAuthenticationHandler; +import cn.topiam.employee.portal.handler.PortalAuthenticationFailureHandler; +import cn.topiam.employee.portal.handler.PortalAuthenticationSuccessHandler; /** * IDP Authentication Configurer @@ -53,8 +54,8 @@ public final class IdpAuthenticationConfigurer> @Override public void init(H http) throws Exception { //设置登录成功失败处理器 - super.successHandler(new PortalAuthenticationHandler()); - super.failureHandler(new PortalAuthenticationHandler()); + super.successHandler(new PortalAuthenticationSuccessHandler()); + super.failureHandler(new PortalAuthenticationFailureHandler()); //MFA认证 IdpBindUserAuthenticationFilter loginAuthenticationFilter = new IdpBindUserAuthenticationFilter( userIdpService, userIdpRepository, passwordEncoder, auditEventPublish); @@ -70,10 +71,6 @@ public final class IdpAuthenticationConfigurer> super.configure(http); } - public RequestMatcher getRequestMatcher() { - return IdpBindUserAuthenticationFilter.getRequestMatcher(); - } - private final UserIdpService userIdpService; private final UserIdpRepository userIdpRepository; private final PasswordEncoder passwordEncoder; diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/idp/bind/IdpBindUserAuthenticationFilter.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/idp/bind/IdpBindUserAuthenticationFilter.java index 41215105..9b27f9eb 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/idp/bind/IdpBindUserAuthenticationFilter.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/idp/bind/IdpBindUserAuthenticationFilter.java @@ -17,20 +17,19 @@ */ package cn.topiam.employee.portal.idp.bind; -import java.io.IOException; import java.util.Objects; import java.util.Optional; import java.util.UUID; -import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.validation.ConstraintViolationException; import org.springframework.http.HttpMethod; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; @@ -50,14 +49,15 @@ import cn.topiam.employee.common.entity.account.po.UserIdpBindPo; import cn.topiam.employee.common.enums.SecretType; import cn.topiam.employee.common.repository.account.UserIdpRepository; import cn.topiam.employee.common.repository.account.UserRepository; -import cn.topiam.employee.common.util.RequestUtils; import cn.topiam.employee.core.security.authentication.IdpAuthentication; import cn.topiam.employee.core.security.userdetails.UserDetails; +import cn.topiam.employee.core.security.util.SecurityUtils; import cn.topiam.employee.portal.pojo.request.AccountBindIdpRequest; import cn.topiam.employee.support.context.ApplicationContextHelp; import cn.topiam.employee.support.context.ServletContextHelp; import cn.topiam.employee.support.trace.TraceUtils; import cn.topiam.employee.support.util.AesUtils; +import cn.topiam.employee.support.validation.ValidationHelp; import lombok.extern.slf4j.Slf4j; import static cn.topiam.employee.authentication.common.filter.AbstractIdpAuthenticationProcessingFilter.TOPIAM_USER_BIND_IDP; @@ -77,10 +77,6 @@ public class IdpBindUserAuthenticationFilter extends AbstractAuthenticationProce public static final RequestMatcher IDP_BIND_USER_MATCHER = new AntPathRequestMatcher( DEFAULT_FILTER_PROCESSES_URI, HttpMethod.POST.name()); - public static RequestMatcher getRequestMatcher() { - return IDP_BIND_USER_MATCHER; - } - /** * Performs actual authentication. *

@@ -103,33 +99,38 @@ public class IdpBindUserAuthenticationFilter extends AbstractAuthenticationProce */ @Override public Authentication attemptAuthentication(HttpServletRequest request, - HttpServletResponse response) throws AuthenticationException, - IOException, - ServletException { + HttpServletResponse response) throws AuthenticationException { //@formatter:off TraceUtils.put(UUID.randomUUID().toString()); - IdpAuthentication context = (IdpAuthentication) SecurityContextHolder.getContext(); - UserDetails principal = (UserDetails) context.getPrincipal(); + SecurityContext securityContext = SecurityUtils.getSecurityContext(); + Authentication authentication = securityContext.getAuthentication(); + if (!(authentication instanceof IdpAuthentication)){ + return null; + } Object value = request.getSession().getAttribute(TOPIAM_USER_BIND_IDP); + AccountBindIdpRequest idpRequest = new AccountBindIdpRequest(request.getParameter("username"),request.getParameter("password")); + ValidationHelp.ValidationResult requestValidationResult = ValidationHelp.validateEntity(idpRequest); + if (requestValidationResult.isHasErrors()){ + throw new ConstraintViolationException(requestValidationResult.getConstraintViolations()); + } //参数为空 if (Objects.isNull(value)) { - String content = "用户 [" + principal.getUsername() + "] 绑定 IDP 失败, 参数无效"; + String content = "用户 [" + idpRequest.getUsername() + "] 绑定 IDP 失败, 参数无效"; log.error(content); auditEventPublish.publish(EventType.BIND_IDP_USER, content, EventStatus.SUCCESS); throw new UserBindIdpException("user_bind_idp_invalid_argument_error", content); } - AccountBindIdpRequest idpRequest = JSONObject.parseObject(RequestUtils.getBody(request), AccountBindIdpRequest.class); idpRequest.setPassword(idpRequest.getPassword()); //会话上下文数据转 UserInfo IdpUser idpUserInfo = JSONObject.parseObject((String) value, IdpUser.class); //验证 - UserEntity user = authnUserBindValidate(idpRequest, idpUserInfo.getProviderId(),idpUserInfo.getOpenId(),principal); + UserEntity user = authnUserBindValidate(idpRequest, idpUserInfo.getProviderId(),idpUserInfo.getOpenId()); //认证 Boolean bind = userIdpService.bindUserIdp(user.getId().toString(),idpUserInfo); if (bind){ - String content="用户 ["+principal.getUsername()+"] 绑定 IDP 成功"; + String content="用户 ["+idpRequest.getUsername()+"] 绑定 IDP 成功"; UserDetails userDetails = userIdpService.getUserDetails(idpUserInfo.getOpenId(), idpUserInfo.getProviderId()); - IdpAuthentication token = new IdpAuthentication(userDetails, idpUserInfo.getProviderType().getCode(), idpUserInfo.getProviderId(), true, userDetails.getAuthorities()); + IdpAuthentication token = new IdpAuthentication(userDetails, idpUserInfo.getProviderType().value(), idpUserInfo.getProviderId(), true, userDetails.getAuthorities()); // Allow subclasses to set the "details" property token.setDetails(this.authenticationDetailsSource.buildDetails(request)); removeState(request,response); @@ -153,13 +154,13 @@ public class IdpBindUserAuthenticationFilter extends AbstractAuthenticationProce } private UserEntity authnUserBindValidate(AccountBindIdpRequest request, String providerId, - String openId, UserDetails principal) { + String openId) { HttpServletRequest servletRequest = ServletContextHelp.getRequest(); //根据用户名查询用户 UserRepository userRepository = ApplicationContextHelp.getBean(UserRepository.class); - UserEntity user = userRepository.findByUsername(principal.getUsername()); + UserEntity user = userRepository.findByUsername(request.getUsername()); if (Objects.isNull(user)) { - String content = "用户 [" + principal.getUsername() + "] 绑定 IDP 失败, 未查询到用户信息"; + String content = "用户 [" + request.getUsername() + "] 绑定 IDP 失败, 未查询到用户信息"; log.error(content); auditEventPublish.publish(EventType.BIND_IDP_USER, content, EventStatus.SUCCESS); throw new UsernameNotFoundException("用户名或密码错误"); @@ -170,14 +171,14 @@ public class IdpBindUserAuthenticationFilter extends AbstractAuthenticationProce .getAttribute(SecretType.LOGIN.getKey()); request.setPassword(AesUtils.decrypt(request.getPassword(), secret)); } catch (Exception exception) { - String content = "用户 [" + principal.getUsername() + "] 绑定 IDP 失败, 密码解密异常"; + String content = "用户 [" + request.getUsername() + "] 绑定 IDP 失败, 密码解密异常"; log.error(content, exception); auditEventPublish.publish(EventType.BIND_IDP_USER, content, EventStatus.SUCCESS); throw new UserBindIdpException(); } boolean matches = passwordEncoder.matches(request.getPassword(), user.getPassword()); if (!matches) { - String content = "用户 [" + principal.getUsername() + "] 绑定 IDP 失败, 用户密码验证失败"; + String content = "用户 [" + request.getUsername() + "] 绑定 IDP 失败, 用户密码验证失败"; log.error(content); auditEventPublish.publish(EventType.BIND_IDP_USER, content, EventStatus.SUCCESS); throw new UsernameNotFoundException("用户名或密码错误"); @@ -186,7 +187,7 @@ public class IdpBindUserAuthenticationFilter extends AbstractAuthenticationProce Optional bindEntity = userIdpRepository.findByIdpIdAndUserId(providerId, user.getId()); if (bindEntity.isPresent()) { - String content = "用户 [" + principal.getUsername() + "] 绑定 IDP 失败, 用户已存在绑定"; + String content = "用户 [" + request.getUsername() + "] 绑定 IDP 失败, 用户已存在绑定"; log.error(content); auditEventPublish.publish(EventType.BIND_IDP_USER, content, EventStatus.SUCCESS); throw new UsernameNotFoundException("用户已存在绑定"); @@ -194,7 +195,7 @@ public class IdpBindUserAuthenticationFilter extends AbstractAuthenticationProce //是否绑定 bindEntity = userIdpRepository.findByIdpIdAndOpenId(providerId, openId); if (bindEntity.isPresent()) { - String content = "用户 [" + principal.getUsername() + "] 绑定 IDP 失败, 已存在其他用户绑定"; + String content = "用户 [" + request.getUsername() + "] 绑定 IDP 失败, 已存在其他用户绑定"; log.error(content); auditEventPublish.publish(EventType.BIND_IDP_USER, content, EventStatus.SUCCESS); throw new UsernameNotFoundException("已存在其他用户绑定"); diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/listener/PortalAuthenticationSuccessEventListener.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/listener/PortalAuthenticationSuccessEventListener.java index cc388931..6e30bc08 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/listener/PortalAuthenticationSuccessEventListener.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/listener/PortalAuthenticationSuccessEventListener.java @@ -21,11 +21,11 @@ import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.List; -import java.util.Objects; import org.springframework.context.ApplicationListener; import org.springframework.lang.NonNull; import org.springframework.security.authentication.event.AuthenticationSuccessEvent; +import org.springframework.security.core.Authentication; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -35,13 +35,13 @@ import cn.topiam.employee.audit.entity.Target; import cn.topiam.employee.audit.enums.EventStatus; import cn.topiam.employee.audit.enums.TargetType; import cn.topiam.employee.audit.event.AuditEventPublish; +import cn.topiam.employee.authentication.common.util.AuthenticationUtils; import cn.topiam.employee.common.geo.GeoLocationService; +import cn.topiam.employee.common.repository.account.UserRepository; import cn.topiam.employee.core.security.userdetails.UserDetails; import cn.topiam.employee.support.context.ApplicationContextHelp; import cn.topiam.employee.support.context.ServletContextHelp; import cn.topiam.employee.support.util.IpUtils; -import cn.topiam.employee.support.web.useragent.UserAgent; -import cn.topiam.employee.support.web.useragent.UserAgentUtils; import lombok.AllArgsConstructor; import static cn.topiam.employee.audit.enums.EventType.LOGIN_PORTAL; @@ -67,19 +67,20 @@ public class PortalAuthenticationSuccessEventListener implements .getRequestAttributes(); AuditEventPublish auditEventPublish = ApplicationContextHelp .getBean(AuditEventPublish.class); + UserRepository userRepository = ApplicationContextHelp.getBean(UserRepository.class); + Authentication authentication = event.getAuthentication(); Object principal = event.getAuthentication().getPrincipal(); //@formatter:off if (principal instanceof UserDetails) { - //登录事件 - ((UserDetails) principal).setLoginTime(LocalDateTime.now()); + //认证类型 + ((UserDetails) principal).setAuthType(AuthenticationUtils.geAuthType(authentication)); //区域 ((UserDetails) principal).setGeoLocation(geoLocationService.getGeoLocation(IpUtils.getIpAddr(ServletContextHelp.getRequest()))); - //浏览器 - UserAgent agent = UserAgentUtils - .getUserAgent(Objects.requireNonNull(attributes).getRequest()); - ((UserDetails) principal).setUserAgent(agent); //登录时间 ((UserDetails) principal).setLoginTime(getDateTimeOfTimestamp(event.getTimestamp())); + //认证次数+1 + userRepository.updateAuthSucceedInfo(((UserDetails) principal).getId() + ,((UserDetails) principal).getGeoLocation().getIp(),((UserDetails) principal).getLoginTime()); // 审计事件 List targets= Lists.newArrayList(Target.builder().type(TargetType.PORTAL).build()); auditEventPublish.publish(LOGIN_PORTAL, event.getAuthentication(), EventStatus.SUCCESS,targets); diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/endpoint/MfaFactorsEndpoint.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/endpoint/MfaFactorsEndpoint.java deleted file mode 100644 index 9c60f69b..00000000 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/mfa/endpoint/MfaFactorsEndpoint.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * eiam-portal - Employee Identity and Access Management Program - * Copyright © 2020-2023 TopIAM (support@topiam.cn) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package cn.topiam.employee.portal.mfa.endpoint; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; - -import cn.topiam.employee.common.entity.account.UserEntity; -import cn.topiam.employee.common.enums.MfaFactor; -import cn.topiam.employee.core.security.util.UserUtils; -import cn.topiam.employee.portal.pojo.result.LoginMfaFactorResult; -import cn.topiam.employee.support.result.ApiRestResult; -import cn.topiam.employee.support.util.DesensitizationUtil; - -import lombok.extern.slf4j.Slf4j; - -import io.swagger.v3.oas.annotations.tags.Tag; -import static cn.topiam.employee.common.constants.AuthorizeConstants.LOGIN_MFA_FACTORS; -import static cn.topiam.employee.core.context.SettingContextHelp.getMfaFactors; - -/** - * MFA 提供类型 - * - * @author TopIAM - * Created by support@topiam.cn on 2022/8/13 19:17 - */ -@Tag(name = "MFA 因素") -@Slf4j -@RestController -@RequestMapping(value = LOGIN_MFA_FACTORS, method = RequestMethod.GET) -public class MfaFactorsEndpoint { - - /** - * 获取MFA 提供者 - * - * @return {@link LoginMfaFactorResult} - */ - @GetMapping - public ApiRestResult> getLoginMfaFactors() { - UserEntity user = UserUtils.getUser(); - List list = new ArrayList<>(); - List factors = getMfaFactors(); - for (MfaFactor provider : factors) { - LoginMfaFactorResult result = LoginMfaFactorResult.builder().build(); - result.setFactor(provider); - result.setUsable(false); - //sms - if (provider.equals(MfaFactor.SMS_OTP) && StringUtils.isNotBlank(user.getPhone())) { - result.setTarget(DesensitizationUtil.phoneEncrypt(user.getPhone())); - result.setUsable(true); - } - //otp - if (provider.equals(MfaFactor.EMAIL_OTP) && StringUtils.isNotBlank(user.getEmail())) { - result.setTarget(DesensitizationUtil.emailEncrypt(user.getEmail())); - result.setUsable(true); - } - //totp - if (provider.equals(MfaFactor.APP_TOTP) && user.getTotpBind()) { - result.setUsable(true); - } - list.add(result); - } - return ApiRestResult.ok(list); - } -} diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/request/AccountBindIdpRequest.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/request/AccountBindIdpRequest.java index cb1ef761..56814006 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/request/AccountBindIdpRequest.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/request/AccountBindIdpRequest.java @@ -22,6 +22,7 @@ import java.io.Serializable; import javax.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; import lombok.Data; import io.swagger.v3.oas.annotations.media.Schema; @@ -31,6 +32,7 @@ import io.swagger.v3.oas.annotations.media.Schema; * Created by support@topiam.cn on 2022/4/3 22:22 */ @Data +@AllArgsConstructor @Schema(description = "账户绑定IDP入参") public class AccountBindIdpRequest implements Serializable { diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/request/PrepareChangeEmailRequest.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/request/PrepareChangeEmailRequest.java new file mode 100644 index 00000000..a4f70550 --- /dev/null +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/request/PrepareChangeEmailRequest.java @@ -0,0 +1,56 @@ +/* + * eiam-portal - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.portal.pojo.request; + +import java.io.Serial; +import java.io.Serializable; + +import javax.validation.constraints.NotEmpty; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * + * @author TopIAM + * Created by support@topiam.cn on 2022/8/8 19:15 + */ +@Data +@Schema(description = "准备更改电子邮件入参") +public class PrepareChangeEmailRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 5681761697876754485L; + + /** + * 邮箱 + */ + @NotEmpty(message = "邮箱不能为空") + @Parameter(description = "邮箱") + private String email; + + /** + * 密码 + */ + @NotEmpty(message = "密码不能为空") + @Parameter(description = "密码") + private String password; + +} diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/request/PrepareChangePhoneRequest.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/request/PrepareChangePhoneRequest.java new file mode 100644 index 00000000..794e45d7 --- /dev/null +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/request/PrepareChangePhoneRequest.java @@ -0,0 +1,64 @@ +/* + * eiam-portal - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.portal.pojo.request; + +import java.io.Serial; +import java.io.Serializable; + +import javax.validation.constraints.NotEmpty; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; + +/** + *准备更改手机号入参 + * + * @author TopIAM + * Created by support@topiam.cn on 2022/8/8 19:15 + */ +@Data +@Schema(description = "准备更改手机号入参") +public class PrepareChangePhoneRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 5681761697876754485L; + + /** + * 手机号 + */ + @NotEmpty(message = "手机号不能为空") + @Parameter(description = "手机号") + private String phone; + + /** + * 手机号区域 + */ + @NotEmpty(message = "手机号区域不能为空") + @Parameter(description = "手机号区域") + private String phoneRegion; + + /** + * 密码 + */ + @NotEmpty(message = "密码不能为空") + @Parameter(description = "密码") + private String password; + +} diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/result/GetAppListResult.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/result/GetAppListResult.java index 45ce499f..a9885a6e 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/result/GetAppListResult.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/result/GetAppListResult.java @@ -22,6 +22,7 @@ import java.io.Serializable; import cn.topiam.employee.common.enums.app.AppProtocol; import cn.topiam.employee.common.enums.app.AppType; +import cn.topiam.employee.common.enums.app.InitLoginType; import lombok.Data; @@ -82,16 +83,16 @@ public class GetAppListResult implements Serializable { private String icon; /** - * IDP发起 + * Sso 发起方 */ - @Parameter(description = "IDP发起") - private Boolean idpInit; + @Parameter(description = "SSO 发起方") + private InitLoginType initLoginType; /** - * IDP发起URL + * SSO 发起URL */ - @Parameter(description = "IDP发起URL") - private String idpInitUrl; + @Parameter(description = "SSO 发起URL") + private String initLoginUrl; /** * 应用描述 diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/result/LoginConfigResult.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/result/LoginConfigResult.java index fba9929a..6369dd25 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/result/LoginConfigResult.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/pojo/result/LoginConfigResult.java @@ -22,8 +22,6 @@ import java.io.Serializable; import java.util.List; import cn.topiam.employee.common.enums.CaptchaProviderType; -import cn.topiam.employee.common.enums.IdentityProviderCategory; -import cn.topiam.employee.common.enums.IdentityProviderType; import lombok.Builder; import lombok.Data; @@ -72,30 +70,30 @@ public class LoginConfigResult implements Serializable { @Data public static class Idps implements Serializable { @Serial - private static final long serialVersionUID = -6482651783349719888L; + private static final long serialVersionUID = -6482651783349719888L; /** - * ID + * CODE */ - @Schema(description = "ID") - private String id; + @Schema(description = "CODE") + private String code; /** * name */ @Parameter(description = "名称") - private String name; + private String name; /** * 提供商 */ @Parameter(description = "提供商") - private IdentityProviderType type; + private String type; /** * 提供商类型 */ @Parameter(description = "提供商类型") - private IdentityProviderCategory category; + private String category; } } diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/service/AccountService.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/service/AccountService.java index 6cfeb4aa..c49094c4 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/service/AccountService.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/service/AccountService.java @@ -44,6 +44,14 @@ public interface AccountService { */ Boolean changePassword(ChangePasswordRequest param); + /** + * 修改手机 + * + * @param param {@link PrepareChangePhoneRequest} + * @return {@link Boolean} + */ + Boolean prepareChangePhone(PrepareChangePhoneRequest param); + /** * 修改手机 * @@ -52,6 +60,14 @@ public interface AccountService { */ Boolean changePhone(ChangePhoneRequest param); + /** + * 准备修改邮箱 + * + * @param param {@link PrepareChangeEmailRequest} + * @return {@link Boolean} + */ + Boolean prepareChangeEmail(PrepareChangeEmailRequest param); + /** * 修改邮箱 * diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/service/impl/AccountServiceImpl.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/service/impl/AccountServiceImpl.java index 0ab079f6..ef08cbab 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/service/impl/AccountServiceImpl.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/service/impl/AccountServiceImpl.java @@ -35,6 +35,9 @@ import org.springframework.transaction.annotation.Transactional; import cn.topiam.employee.common.entity.account.UserDetailEntity; import cn.topiam.employee.common.entity.account.UserEntity; +import cn.topiam.employee.common.enums.MailType; +import cn.topiam.employee.common.enums.MessageNoticeChannel; +import cn.topiam.employee.common.enums.SmsType; import cn.topiam.employee.common.exception.BindMfaNotFoundSecretException; import cn.topiam.employee.common.exception.InvalidMfaCodeException; import cn.topiam.employee.common.exception.PasswordValidatedFailException; @@ -43,6 +46,7 @@ import cn.topiam.employee.common.repository.account.UserDetailRepository; import cn.topiam.employee.common.repository.account.UserRepository; import cn.topiam.employee.core.context.ServerContextHelp; import cn.topiam.employee.core.security.mfa.provider.TotpAuthenticator; +import cn.topiam.employee.core.security.otp.OtpContextHelp; import cn.topiam.employee.core.security.session.SessionDetails; import cn.topiam.employee.core.security.session.TopIamSessionBackedSessionRegistry; import cn.topiam.employee.core.security.util.SecurityUtils; @@ -50,6 +54,7 @@ import cn.topiam.employee.portal.converter.AccountConverter; import cn.topiam.employee.portal.pojo.request.*; import cn.topiam.employee.portal.pojo.result.PrepareBindMfaResult; import cn.topiam.employee.portal.service.AccountService; +import cn.topiam.employee.support.context.ApplicationContextHelp; import cn.topiam.employee.support.context.ServletContextHelp; import cn.topiam.employee.support.util.BeanUtils; @@ -120,6 +125,23 @@ public class AccountServiceImpl implements AccountService { return true; } + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean prepareChangePhone(PrepareChangePhoneRequest param) { + UserEntity user = validatedPassword(param.getPassword()); + OtpContextHelp otpContextHelp = ApplicationContextHelp.getApplicationContext() + .getBean(OtpContextHelp.class); + // 发送短信验证码 + if (StringUtils.isNotBlank(user.getPhone())) { + otpContextHelp.sendOtp(param.getPhone(), SmsType.UPDATE_PHONE.getCode(), + MessageNoticeChannel.SMS); + } else { + otpContextHelp.sendOtp(param.getPhone(), SmsType.BIND_PHONE.getCode(), + MessageNoticeChannel.SMS); + } + return true; + } + /** * 修改手机 * @@ -206,6 +228,28 @@ public class AccountServiceImpl implements AccountService { return false; } + /** + * 准备修改邮箱 + * + * @param param {@link PrepareChangeEmailRequest} + * @return {@link Boolean} + */ + @Override + public Boolean prepareChangeEmail(PrepareChangeEmailRequest param) { + UserEntity user = validatedPassword(param.getPassword()); + OtpContextHelp otpContextHelp = ApplicationContextHelp.getApplicationContext() + .getBean(OtpContextHelp.class); + // 发送邮箱验证码 + if (StringUtils.isNotBlank(user.getPhone())) { + otpContextHelp.sendOtp(param.getEmail(), MailType.UPDATE_BIND_MAIL.getCode(), + MessageNoticeChannel.MAIL); + } else { + otpContextHelp.sendOtp(param.getEmail(), MailType.BIND_EMAIL.getCode(), + MessageNoticeChannel.MAIL); + } + return true; + } + /** * 更改邮箱 * diff --git a/eiam-portal/src/main/java/cn/topiam/employee/portal/service/impl/LoginConfigServiceImpl.java b/eiam-portal/src/main/java/cn/topiam/employee/portal/service/impl/LoginConfigServiceImpl.java index b7f31bb1..7726136a 100644 --- a/eiam-portal/src/main/java/cn/topiam/employee/portal/service/impl/LoginConfigServiceImpl.java +++ b/eiam-portal/src/main/java/cn/topiam/employee/portal/service/impl/LoginConfigServiceImpl.java @@ -22,11 +22,11 @@ import java.util.Objects; import org.springframework.stereotype.Service; +import cn.topiam.employee.authentication.captcha.geetest.GeeTestCaptchaProviderConfig; import cn.topiam.employee.common.entity.authentication.IdentityProviderEntity; import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository; import cn.topiam.employee.common.repository.setting.SettingRepository; import cn.topiam.employee.core.security.captcha.CaptchaProviderConfig; -import cn.topiam.employee.core.security.captcha.geetest.GeeTestCaptchaProviderConfig; import cn.topiam.employee.portal.converter.LoginConfigConverter; import cn.topiam.employee.portal.pojo.result.LoginConfigResult; import cn.topiam.employee.portal.service.LoginConfigService; diff --git a/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/endpoint/CasIdpSingleSignOnEndpointFilter.java b/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/endpoint/CasIdpSingleSignOnEndpointFilter.java index 4fa55834..ad4b0cb8 100644 --- a/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/endpoint/CasIdpSingleSignOnEndpointFilter.java +++ b/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/endpoint/CasIdpSingleSignOnEndpointFilter.java @@ -38,12 +38,12 @@ import org.springframework.web.util.UriComponentsBuilder; import cn.topiam.employee.application.ApplicationService; import cn.topiam.employee.application.ApplicationServiceLoader; -import cn.topiam.employee.application.CasApplicationService; +import cn.topiam.employee.application.cas.CasApplicationService; +import cn.topiam.employee.application.cas.model.CasSsoModel; import cn.topiam.employee.application.context.ApplicationContext; import cn.topiam.employee.application.context.ApplicationContextHolder; import cn.topiam.employee.common.constants.ProtocolConstants; import cn.topiam.employee.core.context.ServerContextHelp; -import cn.topiam.employee.core.protocol.CasSsoModel; import cn.topiam.employee.core.security.savedredirect.HttpSessionRedirectCache; import cn.topiam.employee.core.security.savedredirect.RedirectCache; import cn.topiam.employee.core.security.util.SecurityUtils; @@ -125,7 +125,7 @@ public class CasIdpSingleSignOnEndpointFilter extends OncePerRequestFilter ServiceTicket serviceTicket = centralAuthenticationService .grantServiceTicket(ticketGrantingTicket.getId(), service); - response.sendRedirect(UriComponentsBuilder.fromHttpUrl(ssoModel.getSsoCallbackUrl()) + response.sendRedirect(UriComponentsBuilder.fromHttpUrl(ssoModel.getClientServiceUrl()) .queryParam(TICKET, serviceTicket.getId()).build().toString()); } filterChain.doFilter(request, response); diff --git a/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/tickets/DefaultTicketFactory.java b/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/tickets/DefaultTicketFactory.java index 33e35fde..fe8115a8 100644 --- a/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/tickets/DefaultTicketFactory.java +++ b/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/tickets/DefaultTicketFactory.java @@ -32,7 +32,7 @@ public class DefaultTicketFactory implements TicketFactory { public void initialize() { serviceTicketFactory = new DefaultServiceTicketFactory(); ticketGrantingTicketFactory = new DefaultTicketGrantingTicketFactory(); - factoryMap = new HashMap<>(); + factoryMap = new HashMap<>(16); factoryMap.put(TicketGrantingTicket.class.getCanonicalName(), this.ticketGrantingTicketFactory); factoryMap.put(ServiceTicket.class.getCanonicalName(), this.serviceTicketFactory); diff --git a/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/tickets/TicketGrantingTicketImpl.java b/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/tickets/TicketGrantingTicketImpl.java index 2e79a1c2..b044cb47 100644 --- a/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/tickets/TicketGrantingTicketImpl.java +++ b/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/tickets/TicketGrantingTicketImpl.java @@ -49,6 +49,7 @@ public class TicketGrantingTicketImpl implements TicketGrantingTicket { return false; } + @Override public UserDetails getUserDetails() { return this.userDetails; } diff --git a/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/xml/Response10GeneratorImpl.java b/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/xml/Response10GeneratorImpl.java index c9e320e5..5b681af7 100644 --- a/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/xml/Response10GeneratorImpl.java +++ b/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/xml/Response10GeneratorImpl.java @@ -32,7 +32,7 @@ import org.slf4j.LoggerFactory; */ public class Response10GeneratorImpl implements ResponseGenerator { - private final static Logger logger = LoggerFactory + private static final Logger logger = LoggerFactory .getLogger(Response20GeneratorImpl.class); private final HttpServletResponse response; diff --git a/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/xml/Response20GeneratorImpl.java b/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/xml/Response20GeneratorImpl.java index 7b884da5..9a84849b 100644 --- a/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/xml/Response20GeneratorImpl.java +++ b/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/xml/Response20GeneratorImpl.java @@ -46,7 +46,7 @@ import static cn.topiam.employee.protocol.cas.idp.constant.ProtocolConstants.*; */ public class Response20GeneratorImpl implements ResponseGenerator { - private final static Logger logger = LoggerFactory + private static final Logger logger = LoggerFactory .getLogger(Response20GeneratorImpl.class); private final HttpServletResponse response; @@ -124,7 +124,8 @@ public class Response20GeneratorImpl implements ResponseGenerator { ByteArrayOutputStream bos = new ByteArrayOutputStream(); TransformerFactory transFactory = TransformerFactory.newInstance(); Transformer transformer = transFactory.newTransformer(); - transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");//序列化不保留标头 + //序列化不保留标头 + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); DOMSource domSource = new DOMSource(node); transformer.transform(domSource, new StreamResult(bos)); return bos.toString(StandardCharsets.UTF_8); diff --git a/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/xml/Response30GeneratorImpl.java b/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/xml/Response30GeneratorImpl.java index f4f2a6d4..2cddc4b1 100644 --- a/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/xml/Response30GeneratorImpl.java +++ b/eiam-protocol/eiam-protocol-cas/src/main/java/cn/topiam/employee/protocol/cas/idp/xml/Response30GeneratorImpl.java @@ -31,7 +31,7 @@ import org.slf4j.LoggerFactory; */ public class Response30GeneratorImpl extends Response20GeneratorImpl { - private final static Logger logger = LoggerFactory.getLogger(Response20GeneratorImpl.class); + private static final Logger logger = LoggerFactory.getLogger(Response20GeneratorImpl.class); public Response30GeneratorImpl(DocumentBuilder documentBuilder, HttpServletResponse response) { super(documentBuilder, response); diff --git a/eiam-protocol/eiam-protocol-cas/src/main/resources/templates/form_redirect.ftlh b/eiam-protocol/eiam-protocol-cas/src/main/resources/templates/form_redirect.ftlh deleted file mode 100644 index 2f480981..00000000 --- a/eiam-protocol/eiam-protocol-cas/src/main/resources/templates/form_redirect.ftlh +++ /dev/null @@ -1,198 +0,0 @@ - - - - Redirect - TopIAM - - - - - - -

- Note: Since your browser does not support JavaScript, you must press the - Continue button once to proceed. -

- -
-
-
-
- - - - - - -
-
-
-
- - - diff --git a/eiam-protocol/eiam-protocol-cas/src/main/resources/templates/jwt_redirect.ftlh b/eiam-protocol/eiam-protocol-cas/src/main/resources/templates/jwt_redirect.ftlh deleted file mode 100644 index b802a686..00000000 --- a/eiam-protocol/eiam-protocol-cas/src/main/resources/templates/jwt_redirect.ftlh +++ /dev/null @@ -1,198 +0,0 @@ - - - - Redirecting - TopIAM - - - - - - - -
-
-
-
- - - - - - -
-
-
-
- - - diff --git a/eiam-protocol/eiam-protocol-core/src/main/java/cn/topiam/employee/protocol/cas/util/ProtocolUtils.java b/eiam-protocol/eiam-protocol-core/src/main/java/cn/topiam/employee/protocol/cas/util/ProtocolUtils.java new file mode 100644 index 00000000..032a593e --- /dev/null +++ b/eiam-protocol/eiam-protocol-core/src/main/java/cn/topiam/employee/protocol/cas/util/ProtocolUtils.java @@ -0,0 +1,45 @@ +/* + * eiam-protocol-core - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.protocol.cas.util; + +import org.springframework.context.ApplicationContext; +import org.springframework.security.config.annotation.web.HttpSecurityBuilder; + +import cn.topiam.employee.application.ApplicationServiceLoader; + +/** + * + * @author SanLi + * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2022/12/21 16:35 + */ +public class ProtocolUtils { + + public static > ApplicationServiceLoader getApplicationServiceLoader(B builder) { + ApplicationServiceLoader applicationServiceLoader = builder + .getSharedObject(ApplicationServiceLoader.class); + if (applicationServiceLoader == null) { + applicationServiceLoader = getBean(builder, ApplicationServiceLoader.class); + builder.setSharedObject(ApplicationServiceLoader.class, applicationServiceLoader); + } + return applicationServiceLoader; + } + + public static , T> T getBean(B builder, Class type) { + return builder.getSharedObject(ApplicationContext.class).getBean(type); + } +} diff --git a/eiam-protocol/eiam-protocol-core/src/main/resources/templates/form_redirect.ftlh b/eiam-protocol/eiam-protocol-core/src/main/resources/templates/form_redirect.ftlh deleted file mode 100644 index 2f480981..00000000 --- a/eiam-protocol/eiam-protocol-core/src/main/resources/templates/form_redirect.ftlh +++ /dev/null @@ -1,198 +0,0 @@ - - - - Redirect - TopIAM - - - - - - - -
-
-
-
- - - - - - -
-
-
-
- - - diff --git a/eiam-protocol/eiam-protocol-core/src/main/resources/templates/jwt_redirect.ftlh b/eiam-protocol/eiam-protocol-core/src/main/resources/templates/jwt_redirect.ftlh deleted file mode 100644 index b802a686..00000000 --- a/eiam-protocol/eiam-protocol-core/src/main/resources/templates/jwt_redirect.ftlh +++ /dev/null @@ -1,198 +0,0 @@ - - - - Redirecting - TopIAM - - - - - - - -
-
-
-
- - - - - - -
-
-
-
- - - diff --git a/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/FormInitSingleSignOnEndpoint.java b/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/FormInitSingleSignOnEndpoint.java deleted file mode 100644 index c1a83899..00000000 --- a/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/FormInitSingleSignOnEndpoint.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * eiam-protocol-form - Employee Identity and Access Management Program - * Copyright © 2020-2023 TopIAM (support@topiam.cn) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package cn.topiam.employee.protocol.form; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Controller; -import org.springframework.util.AlternativeJdkIdGenerator; -import org.springframework.util.IdGenerator; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.servlet.ModelAndView; - -import lombok.AllArgsConstructor; -import static cn.topiam.employee.protocol.form.constant.ProtocolConstants.IDP_FORM_SSO_INITIATOR; - -/** - * Form 单点登陆 - * - * @author TopIAM - * Created by support@topiam.cn on 2022/5/7 22:46 - */ -@Controller -@RequestMapping(IDP_FORM_SSO_INITIATOR) -@AllArgsConstructor -public class FormInitSingleSignOnEndpoint { - private final Logger logger = LoggerFactory.getLogger(FormInitSingleSignOnEndpoint.class); - - /** - * SSO - * - * @return {@link ModelAndView} - */ - @PostMapping - public ModelAndView sso(@PathVariable String appId) { - IdGenerator idGenerator = new AlternativeJdkIdGenerator(); - ModelAndView view = new ModelAndView("form_redirect"); - //目标地址 - view.addObject("target", ""); - //随机数 - view.addObject("nonce", idGenerator.generateId()); - return view; - } -} diff --git a/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/FormProtocolConfigurer.java b/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/FormProtocolConfigurer.java new file mode 100644 index 00000000..c4b0b678 --- /dev/null +++ b/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/FormProtocolConfigurer.java @@ -0,0 +1,69 @@ +/* + * eiam-protocol-form - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.protocol.form; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.security.config.annotation.web.HttpSecurityBuilder; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.util.matcher.OrRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; + +import cn.topiam.employee.application.ApplicationServiceLoader; +import cn.topiam.employee.protocol.form.endpoint.FormInitSingleSignOnEndpointFilter; +import cn.topiam.employee.protocol.form.endpoint.FormSingleSignOnEndpointFilter; +import cn.topiam.employee.support.context.ApplicationContextHelp; + +import freemarker.template.Configuration; +import static cn.topiam.employee.protocol.cas.util.ProtocolUtils.getApplicationServiceLoader; + +/** + * 认证配置 + * + * @author TopIAM + * Created by support@topiam.cn on 2021/9/10 22:58 + */ +public final class FormProtocolConfigurer> extends + AbstractHttpConfigurer, B> { + + @Override + public void configure(B http) { + ApplicationServiceLoader applicationServiceLoader = getApplicationServiceLoader(http); + Configuration configuration = ApplicationContextHelp.getBean(Configuration.class); + //Form 单点登录地址 + http.addFilterAfter( + new FormSingleSignOnEndpointFilter(applicationServiceLoader, configuration), + UsernamePasswordAuthenticationFilter.class); + //发起Form表单登录过滤器 + http.addFilterAfter( + new FormInitSingleSignOnEndpointFilter(applicationServiceLoader, configuration), + FormSingleSignOnEndpointFilter.class); + } + + public RequestMatcher getEndpointsMatcher() { + List requestMatchers = new ArrayList<>(); + //Form 门户端发起登录 + requestMatchers.add(FormSingleSignOnEndpointFilter.getRequestMatcher()); + //Form 服务端发起登录 + requestMatchers.add(FormInitSingleSignOnEndpointFilter.getRequestMatcher()); + return new OrRequestMatcher(requestMatchers); + } + +} diff --git a/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/endpoint/AbstractFormEndpointFilter.java b/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/endpoint/AbstractFormEndpointFilter.java new file mode 100644 index 00000000..9c9c192e --- /dev/null +++ b/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/endpoint/AbstractFormEndpointFilter.java @@ -0,0 +1,148 @@ +/* + * eiam-protocol-form - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.protocol.form.endpoint; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import javax.servlet.FilterChain; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.compress.utils.CharsetNames; +import org.apache.http.entity.ContentType; +import org.springframework.boot.web.servlet.filter.OrderedFilter; +import org.springframework.core.Ordered; +import org.springframework.lang.NonNull; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.web.filter.OncePerRequestFilter; + +import cn.topiam.employee.application.ApplicationServiceLoader; +import cn.topiam.employee.application.exception.AppNotExistException; +import cn.topiam.employee.application.form.FormApplicationService; +import cn.topiam.employee.application.form.model.FormProtocolConfig; +import cn.topiam.employee.common.crypto.EncryptContextHelp; +import cn.topiam.employee.common.entity.app.AppAccountEntity; +import cn.topiam.employee.common.entity.app.AppFormConfigEntity; +import cn.topiam.employee.common.enums.app.AppProtocol; +import cn.topiam.employee.core.context.ServerContextHelp; +import cn.topiam.employee.core.security.savedredirect.HttpSessionRedirectCache; +import cn.topiam.employee.core.security.savedredirect.RedirectCache; +import cn.topiam.employee.core.security.util.SecurityUtils; + +import lombok.AllArgsConstructor; +import lombok.SneakyThrows; + +import freemarker.template.Configuration; +import freemarker.template.Template; +import static cn.topiam.employee.common.constants.AuthorizeConstants.FE_LOGIN; +import static cn.topiam.employee.common.constants.ProtocolConstants.APP_CODE; +import static cn.topiam.employee.core.security.util.SecurityUtils.isAuthenticated; + +/** + * IDP 发起单点登录端点 + * + * @author TopIAM + * Created by support@topiam.cn on 2022/5/7 22:46 + */ +@SuppressWarnings("DuplicatedCode") +@AllArgsConstructor +public abstract class AbstractFormEndpointFilter extends OncePerRequestFilter + implements OrderedFilter { + + private final RedirectCache redirectCache = new HttpSessionRedirectCache(); + + /** + * + * + * @param requestMatcher {@link RequestMatcher} + * @param request {@link HttpServletRequest} + * @param response {@link HttpServletResponse} + * @param filterChain {@link FilterChain} + */ + @SneakyThrows + protected void doFilter(@NonNull RequestMatcher requestMatcher, + @NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain) { + if (!isAuthenticated()) { + //Saved Redirect + redirectCache.saveRedirect(request, response, RedirectCache.RedirectType.REQUEST); + //跳转登录 + response.sendRedirect(ServerContextHelp.getPortalPublicBaseUrl() + FE_LOGIN); + return; + } + //@formatter:off + if (requestMatcher.matches(request)) { + //获取应用编码 + Map variables = requestMatcher.matcher(request).getVariables(); + String appCode = variables.get(APP_CODE); + //获取应用配置 + FormApplicationService applicationService = (FormApplicationService) applicationServiceLoader.getApplicationService(AppProtocol.FORM.getCode()); + FormProtocolConfig config = applicationService.getProtocolConfig(appCode); + if (Objects.isNull(config)) { + throw new AppNotExistException(); + } + AppAccountEntity appAccount = applicationService.getAppAccount(Long.valueOf(config.getAppId()), + Long.valueOf(SecurityUtils.getCurrentUserId())); + response.setCharacterEncoding(CharsetNames.UTF_8); + response.setContentType(ContentType.TEXT_HTML.getMimeType()); + Template template = cfg.getTemplate("form_redirect.ftlh"); + Map data = new HashMap<>(16); + data.put("nonce", System.currentTimeMillis()); + data.put("loginUrl", config.getLoginUrl()); + data.put("submitType", config.getSubmitType()); + data.put("usernameField", config.getUsernameField()); + data.put("passwordField", config.getPasswordField()); + data.put("account", appAccount.getAccount()); + data.put("password", EncryptContextHelp.decrypt(appAccount.getPassword())); + List otherField = config.getOtherField(); + data.put("otherFields", otherField); + template.process(data, response.getWriter()); + return; + } + filterChain.doFilter(request, response); + //@formatter:on + } + + /** + * Get the order value of this object. + *

Higher values are interpreted as lower priority. As a consequence, + * the object with the lowest value has the highest priority (somewhat + * analogous to Servlet {@code load-on-startup} values). + *

Same order values will result in arbitrary sort positions for the + * affected objects. + * + * @return the order value + * @see #HIGHEST_PRECEDENCE + * @see #LOWEST_PRECEDENCE + */ + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; + } + + /** + * Form 应用配置 + */ + private final ApplicationServiceLoader applicationServiceLoader; + + private final Configuration cfg; +} diff --git a/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/endpoint/FormInitSingleSignOnEndpointFilter.java b/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/endpoint/FormInitSingleSignOnEndpointFilter.java new file mode 100644 index 00000000..a99f6c12 --- /dev/null +++ b/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/endpoint/FormInitSingleSignOnEndpointFilter.java @@ -0,0 +1,75 @@ +/* + * eiam-protocol-form - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.protocol.form.endpoint; + +import javax.servlet.FilterChain; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.http.HttpMethod; +import org.springframework.lang.NonNull; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; + +import cn.topiam.employee.application.ApplicationServiceLoader; + +import lombok.SneakyThrows; + +import freemarker.template.Configuration; +import static cn.topiam.employee.common.constants.ProtocolConstants.FormEndpointConstants.IDP_FORM_SSO_INITIATOR; + +/** + * IDP 发起单点登录端点 + * + * @author TopIAM + * Created by support@topiam.cn on 2022/5/7 22:46 + */ +@SuppressWarnings("DuplicatedCode") +public class FormInitSingleSignOnEndpointFilter extends AbstractFormEndpointFilter { + + private static final RequestMatcher FORM_INIT_SINGLE_SIGN_MATCHER = new AntPathRequestMatcher( + IDP_FORM_SSO_INITIATOR, HttpMethod.POST.name()); + + public FormInitSingleSignOnEndpointFilter(ApplicationServiceLoader applicationServiceLoader, + Configuration cfg) { + super(applicationServiceLoader, cfg); + } + + /** + * Same contract as for {@code doFilter}, but guaranteed to be + * just invoked once per request within a single request thread. + * See {@link #shouldNotFilterAsyncDispatch()} for details. + *

Provides HttpServletRequest and HttpServletResponse arguments instead of the + * default ServletRequest and ServletResponse ones. + * + * @param request {@link HttpServletRequest} + * @param response {@link HttpServletResponse} + * @param filterChain {@link FilterChain} + */ + @SneakyThrows + @Override + protected void doFilterInternal(@NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain) { + doFilter(FORM_INIT_SINGLE_SIGN_MATCHER, request, response, filterChain); + } + + public static RequestMatcher getRequestMatcher() { + return FORM_INIT_SINGLE_SIGN_MATCHER; + } +} diff --git a/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/endpoint/FormSingleSignOnEndpointFilter.java b/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/endpoint/FormSingleSignOnEndpointFilter.java new file mode 100644 index 00000000..140a0437 --- /dev/null +++ b/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/form/endpoint/FormSingleSignOnEndpointFilter.java @@ -0,0 +1,70 @@ +/* + * eiam-protocol-form - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.protocol.form.endpoint; + +import javax.servlet.FilterChain; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.lang.NonNull; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; + +import cn.topiam.employee.application.ApplicationServiceLoader; +import cn.topiam.employee.common.constants.ProtocolConstants; + +import freemarker.template.Configuration; + +/** + * Saml 接受SP发起登录端点 + * + * @author TopIAM + * Created by support@topiam.cn on 2022/5/7 22:46 + */ +@SuppressWarnings("DuplicatedCode") +public class FormSingleSignOnEndpointFilter extends AbstractFormEndpointFilter { + private static final RequestMatcher REQUEST_MATCHER = new AntPathRequestMatcher( + ProtocolConstants.FormEndpointConstants.FORM_SSO_PATH); + + public FormSingleSignOnEndpointFilter(ApplicationServiceLoader applicationServiceLoader, + Configuration cfg) { + super(applicationServiceLoader, cfg); + } + + /** + * Same contract as for {@code doFilter}, but guaranteed to be + * just invoked once per request within a single request thread. + * See {@link #shouldNotFilterAsyncDispatch()} for details. + *

Provides HttpServletRequest and HttpServletResponse arguments instead of the + * default ServletRequest and ServletResponse ones. + * + * @param request {@link HttpServletRequest} + * @param response {@link HttpServletResponse} + * @param filterChain {@link FilterChain} + */ + @Override + protected void doFilterInternal(@NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain) { + doFilter(REQUEST_MATCHER, request, response, filterChain); + } + + public static RequestMatcher getRequestMatcher() { + return REQUEST_MATCHER; + } +} diff --git a/eiam-protocol/eiam-protocol-form/src/main/resources/templates/form_redirect.ftlh b/eiam-protocol/eiam-protocol-form/src/main/resources/templates/form_redirect.ftlh index 2f480981..42e4a280 100644 --- a/eiam-protocol/eiam-protocol-form/src/main/resources/templates/form_redirect.ftlh +++ b/eiam-protocol/eiam-protocol-form/src/main/resources/templates/form_redirect.ftlh @@ -10,156 +10,7 @@ />

-
-
-
- - - - - - -
-
-
+
diff --git a/eiam-protocol/eiam-protocol-form/src/main/resources/templates/jwt_redirect.ftlh b/eiam-protocol/eiam-protocol-form/src/main/resources/templates/jwt_redirect.ftlh deleted file mode 100644 index b802a686..00000000 --- a/eiam-protocol/eiam-protocol-form/src/main/resources/templates/jwt_redirect.ftlh +++ /dev/null @@ -1,198 +0,0 @@ - - - - Redirecting - TopIAM - - - - - - - -
-
-
-
- - - - - - -
-
-
-
- - - diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/resources/templates/form_redirect.ftlh b/eiam-protocol/eiam-protocol-jwt/src/main/resources/templates/form_redirect.ftlh deleted file mode 100644 index 2f480981..00000000 --- a/eiam-protocol/eiam-protocol-jwt/src/main/resources/templates/form_redirect.ftlh +++ /dev/null @@ -1,198 +0,0 @@ - - - - Redirect - TopIAM - - - - - - - -
-
-
-
- - - - - - -
-
-
-
- - - diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/EiamOAuth2AuthorizationService.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/EiamOAuth2AuthorizationService.java deleted file mode 100644 index ca14963c..00000000 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/EiamOAuth2AuthorizationService.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * eiam-protocol-oidc - Employee Identity and Access Management Program - * Copyright © 2020-2023 TopIAM (support@topiam.cn) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package cn.topiam.employee.protocol.oidc.authentication; - -import org.springframework.jdbc.core.JdbcOperations; -import org.springframework.jdbc.support.lob.LobHandler; -import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService; -import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; -import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; -import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; - -/** - * 扩展 JdbcOAuth2AuthorizationService 集合数据库及Redis - * - * @author TopIAM - * Created by support@topiam.cn on 2022/10/28 22:39 - */ -@SuppressWarnings({ "unused", "AlibabaClassNamingShouldBeCamel" }) -public final class EiamOAuth2AuthorizationService extends JdbcOAuth2AuthorizationService { - - @Override - public void save(OAuth2Authorization authorization) { - super.save(authorization); - } - - @Override - public void remove(OAuth2Authorization authorization) { - super.remove(authorization); - } - - @Override - public OAuth2Authorization findById(String id) { - return super.findById(id); - } - - @Override - public OAuth2Authorization findByToken(String token, OAuth2TokenType tokenType) { - return super.findByToken(token, tokenType); - } - - public EiamOAuth2AuthorizationService(JdbcOperations jdbcOperations, - RegisteredClientRepository registeredClientRepository) { - super(jdbcOperations, registeredClientRepository); - } - - public EiamOAuth2AuthorizationService(JdbcOperations jdbcOperations, - RegisteredClientRepository registeredClientRepository, - LobHandler lobHandler) { - super(jdbcOperations, registeredClientRepository, lobHandler); - } -} diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/EiamOAuth2InitSingleSignOnEndpointFilter.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/EiamOAuth2InitSingleSignOnEndpointFilter.java index 1a42418d..dd6a33e0 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/EiamOAuth2InitSingleSignOnEndpointFilter.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/EiamOAuth2InitSingleSignOnEndpointFilter.java @@ -39,7 +39,6 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken; @@ -100,11 +99,11 @@ public class EiamOAuth2InitSingleSignOnEndpointFilter extends OncePerRequestFilt /** * 授权成功处理器 */ - private AuthenticationSuccessHandler authenticationSuccessHandler = this::sendAuthorizationResponse; + private final AuthenticationSuccessHandler authenticationSuccessHandler = this::sendAuthorizationResponse; /** * 授权失败处理器 */ - private AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse; + private final AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse; /** * 认证管理器 */ @@ -239,50 +238,6 @@ public class EiamOAuth2InitSingleSignOnEndpointFilter extends OncePerRequestFilt //@formatter:on } - /** - * Sets the {@link AuthenticationDetailsSource} used for building an authentication details instance from {@link HttpServletRequest}. - * - * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for building an authentication details instance from {@link HttpServletRequest} - * @since 0.3.1 - */ - public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) { - Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null"); - this.authenticationDetailsSource = authenticationDetailsSource; - } - - /** - * Sets the {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest} - * to an instance of {@link OAuth2AuthorizationCodeRequestAuthenticationToken} used for authenticating the request. - * - * @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest} - */ - public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) { - Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); - this.authenticationConverter = authenticationConverter; - } - - /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} - * and returning the {@link OAuth2AuthorizationResponse Authorization Response}. - * - * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} - */ - public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) { - Assert.notNull(authenticationSuccessHandler, "authenticationSuccessHandler cannot be null"); - this.authenticationSuccessHandler = authenticationSuccessHandler; - } - - /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException} - */ - public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) { - Assert.notNull(authenticationFailureHandler, "authenticationFailureHandler cannot be null"); - this.authenticationFailureHandler = authenticationFailureHandler; - } - /** * Get the order value of this object. *

Higher values are interpreted as lower priority. As a consequence, diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/EiamOidcAuthorizationServerContextFilter.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/EiamOidcAuthorizationServerContextFilter.java index 954dbaf7..a084ed24 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/EiamOidcAuthorizationServerContextFilter.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/EiamOidcAuthorizationServerContextFilter.java @@ -41,14 +41,12 @@ import org.springframework.web.util.UriComponentsBuilder; import cn.topiam.employee.application.context.ApplicationContext; import cn.topiam.employee.application.context.ApplicationContextHolder; import cn.topiam.employee.application.exception.AppNotExistException; -import cn.topiam.employee.common.constants.ProtocolConstants; import cn.topiam.employee.common.entity.app.po.AppOidcConfigPO; import cn.topiam.employee.common.repository.app.AppOidcConfigRepository; import cn.topiam.employee.core.context.ServerContextHelp; import cn.topiam.employee.support.util.HttpUrlUtils; import static cn.topiam.employee.common.constants.ProtocolConstants.APP_CODE; -import static cn.topiam.employee.common.constants.ProtocolConstants.OidcEndpointConstants.OIDC_AUTHORIZE_BASE_PATH; -import static cn.topiam.employee.common.constants.ProtocolConstants.OidcEndpointConstants.OIDC_AUTHORIZE_PATH; +import static cn.topiam.employee.common.constants.ProtocolConstants.OidcEndpointConstants.*; /** * A {@code Filter} that associates the {@link AuthorizationServerContext} to the {@link AuthorizationServerContextHolder}. @@ -92,13 +90,13 @@ public final class EiamOidcAuthorizationServerContextFilter extends OncePerReque StringSubstitutor sub = new StringSubstitutor(variables, "{", "}"); AuthorizationServerSettings providerSettings = AuthorizationServerSettings.builder() .issuer(sub.replace(HttpUrlUtils.format(ServerContextHelp.getPortalPublicBaseUrl() + OIDC_AUTHORIZE_PATH))) - .authorizationEndpoint(asUrl(ServerContextHelp.getPortalPublicBaseUrl(), sub.replace(ProtocolConstants.OidcEndpointConstants.AUTHORIZATION_ENDPOINT))) - .tokenEndpoint(asUrl(ServerContextHelp.getPortalPublicBaseUrl(), sub.replace(ProtocolConstants.OidcEndpointConstants.TOKEN_ENDPOINT))) - .jwkSetEndpoint(asUrl(ServerContextHelp.getPortalPublicBaseUrl(), sub.replace(ProtocolConstants.OidcEndpointConstants.JWK_SET_ENDPOINT))) - .oidcClientRegistrationEndpoint(asUrl(ServerContextHelp.getPortalPublicBaseUrl(), sub.replace(ProtocolConstants.OidcEndpointConstants.OIDC_CLIENT_REGISTRATION_ENDPOINT))) - .tokenIntrospectionEndpoint(asUrl(ServerContextHelp.getPortalPublicBaseUrl(), sub.replace(ProtocolConstants.OidcEndpointConstants.TOKEN_INTROSPECTION_ENDPOINT))) - .tokenRevocationEndpoint(asUrl(ServerContextHelp.getPortalPublicBaseUrl(), sub.replace(ProtocolConstants.OidcEndpointConstants.TOKEN_REVOCATION_ENDPOINT))) - .oidcUserInfoEndpoint(asUrl(ServerContextHelp.getPortalPublicBaseUrl(), sub.replace(ProtocolConstants.OidcEndpointConstants.OIDC_USER_INFO_ENDPOINT))) + .authorizationEndpoint(asUrl(ServerContextHelp.getPortalPublicBaseUrl(), sub.replace(AUTHORIZATION_ENDPOINT))) + .tokenEndpoint(asUrl(ServerContextHelp.getPortalPublicBaseUrl(), sub.replace(TOKEN_ENDPOINT))) + .jwkSetEndpoint(asUrl(ServerContextHelp.getPortalPublicBaseUrl(), sub.replace(JWK_SET_ENDPOINT))) + .oidcClientRegistrationEndpoint(asUrl(ServerContextHelp.getPortalPublicBaseUrl(), sub.replace(OIDC_CLIENT_REGISTRATION_ENDPOINT))) + .tokenIntrospectionEndpoint(asUrl(ServerContextHelp.getPortalPublicBaseUrl(), sub.replace(TOKEN_INTROSPECTION_ENDPOINT))) + .tokenRevocationEndpoint(asUrl(ServerContextHelp.getPortalPublicBaseUrl(), sub.replace(TOKEN_REVOCATION_ENDPOINT))) + .oidcUserInfoEndpoint(asUrl(ServerContextHelp.getPortalPublicBaseUrl(), sub.replace(OIDC_USER_INFO_ENDPOINT))) .build(); AuthorizationServerContext providerContext = new AuthorizationServerContext() { @Override diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/authentication/EiamOAuth2AuthorizationCodeAuthenticationProvider.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/authentication/EiamOAuth2AuthorizationCodeAuthenticationProvider.java new file mode 100644 index 00000000..3c0da7c7 --- /dev/null +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/authentication/EiamOAuth2AuthorizationCodeAuthenticationProvider.java @@ -0,0 +1,271 @@ +/* + * eiam-protocol-oidc - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.protocol.oidc.authentication.authentication; + +import java.security.Principal; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.core.log.LogMessage; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.core.*; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.oauth2.core.oidc.OidcIdToken; +import org.springframework.security.oauth2.core.oidc.OidcScopes; +import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; +import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode; +import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; +import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken; +import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; +import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder; +import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext; +import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext; +import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import cn.topiam.employee.audit.context.AuditContext; +import static cn.topiam.employee.protocol.oidc.util.EiamOAuth2Utils.getAuthenticatedClientElseThrowInvalidClient; +import static cn.topiam.employee.protocol.oidc.util.EiamOAuth2Utils.invalidate; + +/** + * Eiam OAuth 2 授权代码身份验证提供程序 + * + * @see org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationProvider + * + * @author SanLi + * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2022/12/25 14:47 + */ +@SuppressWarnings({ "unused", "AlibabaClassNamingShouldBeCamel", "AlibabaMethodTooLong" }) +public final class EiamOAuth2AuthorizationCodeAuthenticationProvider implements + AuthenticationProvider { + private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; + private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType( + OAuth2ParameterNames.CODE); + private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType( + OidcParameterNames.ID_TOKEN); + private final Log logger = LogFactory + .getLog(getClass()); + private final OAuth2AuthorizationService authorizationService; + private final OAuth2TokenGenerator tokenGenerator; + + /** + * Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the provided parameters. + * + * @param authorizationService the authorization service + * @param tokenGenerator the token generator + * @since 0.2.3 + */ + public EiamOAuth2AuthorizationCodeAuthenticationProvider(OAuth2AuthorizationService authorizationService, + OAuth2TokenGenerator tokenGenerator) { + Assert.notNull(authorizationService, "authorizationService cannot be null"); + Assert.notNull(tokenGenerator, "tokenGenerator cannot be null"); + this.authorizationService = authorizationService; + this.tokenGenerator = tokenGenerator; + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = (OAuth2AuthorizationCodeAuthenticationToken) authentication; + + OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient( + authorizationCodeAuthentication); + RegisteredClient registeredClient = clientPrincipal.getRegisteredClient(); + + if (this.logger.isTraceEnabled()) { + this.logger.trace("Retrieved registered client"); + } + + OAuth2Authorization authorization = this.authorizationService + .findByToken(authorizationCodeAuthentication.getCode(), AUTHORIZATION_CODE_TOKEN_TYPE); + if (authorization == null) { + throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT); + } + + if (this.logger.isTraceEnabled()) { + this.logger.trace("Retrieved authorization with authorization code"); + } + + OAuth2Authorization.Token authorizationCode = authorization + .getToken(OAuth2AuthorizationCode.class); + + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + + if (!registeredClient.getClientId().equals(authorizationRequest.getClientId())) { + if (!authorizationCode.isInvalidated()) { + // Invalidate the authorization code given that a different client is attempting to use it + authorization = invalidate(authorization, authorizationCode.getToken()); + this.authorizationService.save(authorization); + if (this.logger.isWarnEnabled()) { + this.logger.warn(LogMessage.format( + "Invalidated authorization code used by registered client '%s'", + registeredClient.getId())); + } + } + throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT); + } + + if (StringUtils.hasText(authorizationRequest.getRedirectUri()) && !authorizationRequest + .getRedirectUri().equals(authorizationCodeAuthentication.getRedirectUri())) { + throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT); + } + + if (!authorizationCode.isActive()) { + throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT); + } + + if (this.logger.isTraceEnabled()) { + this.logger.trace("Validated token request parameters"); + } + + // @formatter:off + DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder() + .registeredClient(registeredClient) + .principal(authorization.getAttribute(Principal.class.getName())) + .authorizationServerContext(AuthorizationServerContextHolder.getContext()) + .authorization(authorization) + .authorizedScopes(authorization.getAuthorizedScopes()) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .authorizationGrant(authorizationCodeAuthentication); + // @formatter:on + + OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.from(authorization); + + // ----- Access token ----- + OAuth2TokenContext tokenContext = tokenContextBuilder + .tokenType(OAuth2TokenType.ACCESS_TOKEN).build(); + OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext); + if (generatedAccessToken == null) { + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, + "The token generator failed to generate the access token.", ERROR_URI); + throw new OAuth2AuthenticationException(error); + } + + if (this.logger.isTraceEnabled()) { + this.logger.trace("Generated access token"); + } + + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, + generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(), + generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes()); + if (generatedAccessToken instanceof ClaimAccessor) { + authorizationBuilder.token(accessToken, + (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, + ((ClaimAccessor) generatedAccessToken).getClaims())); + } else { + authorizationBuilder.accessToken(accessToken); + } + + // ----- Refresh token ----- + OAuth2RefreshToken refreshToken = null; + if (registeredClient.getAuthorizationGrantTypes() + .contains(AuthorizationGrantType.REFRESH_TOKEN) && + // Do not issue refresh token to public client + !clientPrincipal.getClientAuthenticationMethod() + .equals(ClientAuthenticationMethod.NONE)) { + + tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build(); + OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext); + if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) { + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, + "The token generator failed to generate the refresh token.", ERROR_URI); + throw new OAuth2AuthenticationException(error); + } + + if (this.logger.isTraceEnabled()) { + this.logger.trace("Generated refresh token"); + } + + refreshToken = (OAuth2RefreshToken) generatedRefreshToken; + authorizationBuilder.refreshToken(refreshToken); + } + + // ----- ID token ----- + OidcIdToken idToken; + if (authorizationRequest.getScopes().contains(OidcScopes.OPENID)) { + // @formatter:off + tokenContext = tokenContextBuilder + .tokenType(ID_TOKEN_TOKEN_TYPE) + // ID token customizer may need access to the access token and/or refresh token + .authorization(authorizationBuilder.build()) + .build(); + // @formatter:on + OAuth2Token generatedIdToken = this.tokenGenerator.generate(tokenContext); + if (!(generatedIdToken instanceof Jwt)) { + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, + "The token generator failed to generate the ID token.", ERROR_URI); + throw new OAuth2AuthenticationException(error); + } + + if (this.logger.isTraceEnabled()) { + this.logger.trace("Generated id token"); + } + + idToken = new OidcIdToken(generatedIdToken.getTokenValue(), + generatedIdToken.getIssuedAt(), generatedIdToken.getExpiresAt(), + ((Jwt) generatedIdToken).getClaims()); + authorizationBuilder.token(idToken, (metadata) -> metadata + .put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())); + } else { + idToken = null; + } + + authorization = authorizationBuilder.build(); + + // Invalidate the authorization code as it can only be used once + authorization = invalidate(authorization, authorizationCode.getToken()); + + this.authorizationService.save(authorization); + + if (this.logger.isTraceEnabled()) { + this.logger.trace("Saved authorization"); + } + + Map additionalParameters = Collections.emptyMap(); + if (idToken != null) { + additionalParameters = new HashMap<>(16); + additionalParameters.put(OidcParameterNames.ID_TOKEN, idToken.getTokenValue()); + } + + if (this.logger.isTraceEnabled()) { + this.logger.trace("Authenticated token request"); + } + //放入审计上下文中 + AuditContext.setAuthorization(authorization.getAttribute(Principal.class.getName())); + return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, + accessToken, refreshToken, additionalParameters); + } + + @Override + public boolean supports(Class authentication) { + return OAuth2AuthorizationCodeAuthenticationToken.class.isAssignableFrom(authentication); + } + +} diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/authentication/EiamOAuth2RefreshTokenAuthenticationProvider.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/authentication/EiamOAuth2RefreshTokenAuthenticationProvider.java new file mode 100644 index 00000000..ed1cddb9 --- /dev/null +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/authentication/EiamOAuth2RefreshTokenAuthenticationProvider.java @@ -0,0 +1,260 @@ +/* + * eiam-protocol-oidc - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.protocol.oidc.authentication.authentication; + +import java.security.Principal; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.core.*; +import org.springframework.security.oauth2.core.oidc.OidcIdToken; +import org.springframework.security.oauth2.core.oidc.OidcScopes; +import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; +import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; +import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationToken; +import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; +import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder; +import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext; +import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext; +import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator; +import org.springframework.util.Assert; + +import cn.topiam.employee.audit.context.AuditContext; +import static cn.topiam.employee.protocol.oidc.util.EiamOAuth2Utils.getAuthenticatedClientElseThrowInvalidClient; + +/** + * EiamOAuth2RefreshTokenAuthenticationProvider + * + * @see org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationProvider + + * @author SanLi + * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2022/12/25 21:05 + */ +@SuppressWarnings({ "unused", "AlibabaClassNamingShouldBeCamel", "AlibabaMethodTooLong" }) +public final class EiamOAuth2RefreshTokenAuthenticationProvider implements AuthenticationProvider { + private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; + private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType( + OidcParameterNames.ID_TOKEN); + private final Log logger = LogFactory + .getLog(getClass()); + private final OAuth2AuthorizationService authorizationService; + private final OAuth2TokenGenerator tokenGenerator; + + /** + * Constructs an {@code OAuth2RefreshTokenAuthenticationProvider} using the provided parameters. + * + * @param authorizationService the authorization service + * @param tokenGenerator the token generator + * @since 0.2.3 + */ + public EiamOAuth2RefreshTokenAuthenticationProvider(OAuth2AuthorizationService authorizationService, + OAuth2TokenGenerator tokenGenerator) { + Assert.notNull(authorizationService, "authorizationService cannot be null"); + Assert.notNull(tokenGenerator, "tokenGenerator cannot be null"); + this.authorizationService = authorizationService; + this.tokenGenerator = tokenGenerator; + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + OAuth2RefreshTokenAuthenticationToken refreshTokenAuthentication = (OAuth2RefreshTokenAuthenticationToken) authentication; + + OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient( + refreshTokenAuthentication); + RegisteredClient registeredClient = clientPrincipal.getRegisteredClient(); + + if (this.logger.isTraceEnabled()) { + this.logger.trace("Retrieved registered client"); + } + + OAuth2Authorization authorization = this.authorizationService.findByToken( + refreshTokenAuthentication.getRefreshToken(), OAuth2TokenType.REFRESH_TOKEN); + if (authorization == null) { + throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT); + } + + if (this.logger.isTraceEnabled()) { + this.logger.trace("Retrieved authorization with refresh token"); + } + + if (!registeredClient.getId().equals(authorization.getRegisteredClientId())) { + throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT); + } + + if (!registeredClient.getAuthorizationGrantTypes() + .contains(AuthorizationGrantType.REFRESH_TOKEN)) { + throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT); + } + + OAuth2Authorization.Token refreshToken = authorization + .getRefreshToken(); + if (!refreshToken.isActive()) { + // As per https://tools.ietf.org/html/rfc6749#section-5.2 + // invalid_grant: The provided authorization grant (e.g., authorization code, + // resource owner credentials) or refresh token is invalid, expired, revoked [...]. + throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT); + } + + // As per https://tools.ietf.org/html/rfc6749#section-6 + // The requested scope MUST NOT include any scope not originally granted by the resource owner, + // and if omitted is treated as equal to the scope originally granted by the resource owner. + Set scopes = refreshTokenAuthentication.getScopes(); + Set authorizedScopes = authorization.getAuthorizedScopes(); + if (!authorizedScopes.containsAll(scopes)) { + throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE); + } + + if (this.logger.isTraceEnabled()) { + this.logger.trace("Validated token request parameters"); + } + + if (scopes.isEmpty()) { + scopes = authorizedScopes; + } + + // @formatter:off + DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder() + .registeredClient(registeredClient) + .principal(authorization.getAttribute(Principal.class.getName())) + .authorizationServerContext(AuthorizationServerContextHolder.getContext()) + .authorization(authorization) + .authorizedScopes(scopes) + .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) + .authorizationGrant(refreshTokenAuthentication); + // @formatter:on + + OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.from(authorization); + + // ----- Access token ----- + OAuth2TokenContext tokenContext = tokenContextBuilder + .tokenType(OAuth2TokenType.ACCESS_TOKEN).build(); + OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext); + if (generatedAccessToken == null) { + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, + "The token generator failed to generate the access token.", ERROR_URI); + throw new OAuth2AuthenticationException(error); + } + + if (this.logger.isTraceEnabled()) { + this.logger.trace("Generated access token"); + } + + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, + generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(), + generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes()); + if (generatedAccessToken instanceof ClaimAccessor) { + authorizationBuilder.token(accessToken, (metadata) -> { + metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, + ((ClaimAccessor) generatedAccessToken).getClaims()); + metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, false); + }); + } else { + authorizationBuilder.accessToken(accessToken); + } + + // ----- Refresh token ----- + OAuth2RefreshToken currentRefreshToken = refreshToken.getToken(); + if (!registeredClient.getTokenSettings().isReuseRefreshTokens()) { + tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build(); + OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext); + if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) { + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, + "The token generator failed to generate the refresh token.", ERROR_URI); + throw new OAuth2AuthenticationException(error); + } + + if (this.logger.isTraceEnabled()) { + this.logger.trace("Generated refresh token"); + } + + currentRefreshToken = (OAuth2RefreshToken) generatedRefreshToken; + authorizationBuilder.refreshToken(currentRefreshToken); + } + + // ----- ID token ----- + OidcIdToken idToken; + if (authorizedScopes.contains(OidcScopes.OPENID)) { + // @formatter:off + tokenContext = tokenContextBuilder + .tokenType(ID_TOKEN_TOKEN_TYPE) + // ID token customizer may need access to the access token and/or refresh token + .authorization(authorizationBuilder.build()) + .build(); + // @formatter:on + OAuth2Token generatedIdToken = this.tokenGenerator.generate(tokenContext); + if (!(generatedIdToken instanceof Jwt)) { + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, + "The token generator failed to generate the ID token.", ERROR_URI); + throw new OAuth2AuthenticationException(error); + } + + if (this.logger.isTraceEnabled()) { + this.logger.trace("Generated id token"); + } + + idToken = new OidcIdToken(generatedIdToken.getTokenValue(), + generatedIdToken.getIssuedAt(), generatedIdToken.getExpiresAt(), + ((Jwt) generatedIdToken).getClaims()); + authorizationBuilder.token(idToken, (metadata) -> metadata + .put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())); + } else { + idToken = null; + } + + authorization = authorizationBuilder.build(); + + this.authorizationService.save(authorization); + + if (this.logger.isTraceEnabled()) { + this.logger.trace("Saved authorization"); + } + + Map additionalParameters = Collections.emptyMap(); + if (idToken != null) { + additionalParameters = new HashMap<>(16); + additionalParameters.put(OidcParameterNames.ID_TOKEN, idToken.getTokenValue()); + } + + if (this.logger.isTraceEnabled()) { + this.logger.trace("Authenticated token request"); + } + + //审计 + AuditContext.setAuthorization(authorization.getAttribute(Principal.class.getName())); + return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, + accessToken, currentRefreshToken, additionalParameters); + } + + @Override + public boolean supports(Class authentication) { + return OAuth2RefreshTokenAuthenticationToken.class.isAssignableFrom(authentication); + } + +} diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/consent/DefaultConsentPage.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/consent/DefaultConsentPage.java new file mode 100644 index 00000000..f5d06e67 --- /dev/null +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/consent/DefaultConsentPage.java @@ -0,0 +1,161 @@ +/* + * eiam-protocol-oidc - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.protocol.oidc.authentication.consent; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.http.MediaType; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.core.oidc.OidcScopes; + +/** + * 默认重定向地址 + * + * @author SanLi + * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2022/12/17 21:47 + */ +@SuppressWarnings("AlibabaMethodTooLong") +public class DefaultConsentPage { + private static final MediaType TEXT_HTML_UTF8 = new MediaType("text", "html", + StandardCharsets.UTF_8); + + public static void displayConsent(HttpServletRequest request, HttpServletResponse response, + String clientId, Authentication principal, + Set requestedScopes, Set authorizedScopes, + String state) throws IOException { + String consentPage = generateConsentPage(request, clientId, principal, requestedScopes, + authorizedScopes, state); + response.setContentType(TEXT_HTML_UTF8.toString()); + response.setContentLength(consentPage.getBytes(StandardCharsets.UTF_8).length); + response.getWriter().write(consentPage); + } + + private static String generateConsentPage(HttpServletRequest request, String clientId, + Authentication principal, Set requestedScopes, + Set authorizedScopes, String state) { + Set scopesToAuthorize = new HashSet<>(); + Set scopesPreviouslyAuthorized = new HashSet<>(); + for (String scope : requestedScopes) { + if (authorizedScopes.contains(scope)) { + scopesPreviouslyAuthorized.add(scope); + // openid scope does not require consent + } else if (!scope.equals(OidcScopes.OPENID)) { + scopesToAuthorize.add(scope); + } + } + + StringBuilder builder = new StringBuilder(); + + builder.append(""); + builder.append(""); + builder.append(""); + builder.append(" "); + builder.append( + " "); + builder.append( + " "); + builder.append(" Consent required"); + builder.append(" "); + builder.append(""); + builder.append(""); + builder.append("

"); + builder.append("
"); + builder.append("

Consent required

"); + builder.append("
"); + builder.append("
"); + builder.append("
"); + builder.append("

") + .append(clientId) + .append(" wants to access your account ") + .append(principal.getName()).append("

"); + builder.append("
"); + builder.append("
"); + builder.append("
"); + builder.append("
"); + builder.append( + "

The following permissions are requested by the above app.
Please review these and consent if you approve.

"); + builder.append("
"); + builder.append("
"); + builder.append("
"); + builder.append("
"); + builder.append("
"); + builder.append(" "); + builder.append(" "); + + for (String scope : scopesToAuthorize) { + builder.append("
"); + builder.append( + " "); + builder.append(" "); + builder.append("
"); + } + + if (!scopesPreviouslyAuthorized.isEmpty()) { + builder.append( + "

You have already granted the following permissions to the above app:

"); + for (String scope : scopesPreviouslyAuthorized) { + builder.append("
"); + builder.append( + " "); + builder.append(" "); + builder.append("
"); + } + } + + builder.append("
"); + builder.append( + " "); + builder.append("
"); + builder.append("
"); + builder.append( + " "); + builder.append("
"); + builder.append("
"); + builder.append("
"); + builder.append("
"); + builder.append("
"); + builder.append("
"); + builder.append( + "

Your consent to provide access is required.
If you do not approve, click Cancel, in which case no information will be shared with the app.

"); + builder.append("
"); + builder.append("
"); + builder.append("
"); + builder.append(""); + builder.append(""); + + return builder.toString(); + } +} diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/consent/EiamOAuth2AuthorizationConsentEndpointFilter.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/consent/EiamOAuth2AuthorizationConsentEndpointFilter.java new file mode 100644 index 00000000..24282c3e --- /dev/null +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/consent/EiamOAuth2AuthorizationConsentEndpointFilter.java @@ -0,0 +1,81 @@ +/* + * eiam-protocol-oidc - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.protocol.oidc.authentication.consent; + +import java.io.IOException; +import java.util.Set; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.lang.NonNull; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.web.util.matcher.AndRequestMatcher; +import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.util.Assert; + +import com.google.common.collect.Sets; + +import cn.topiam.employee.common.repository.app.AppOidcConfigRepository; +import cn.topiam.employee.core.security.util.SecurityUtils; +import cn.topiam.employee.protocol.oidc.endpoint.AbstractEiamEndpointFilter; + +/** + * 授权同意Endpoint + * + * @author SanLi + * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2022/12/17 21:25 + */ +@SuppressWarnings("AlibabaClassNamingShouldBeCamel") +public class EiamOAuth2AuthorizationConsentEndpointFilter extends AbstractEiamEndpointFilter { + + @Override + protected void doFilterInternal(@NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain) throws IOException, + ServletException { + if (requestMatcher.matches(request)) { + Authentication principal = SecurityUtils.getSecurityContext().getAuthentication(); + String clientId = request.getParameter(OAuth2ParameterNames.CLIENT_ID); + String state = request.getParameter(OAuth2ParameterNames.STATE); + Set requestedScopes = Sets + .newHashSet(request.getParameter(OAuth2ParameterNames.SCOPE).split(" ")); + //查询应用具有的权限 + Set authorizedScopes = Sets.newHashSet(); + DefaultConsentPage.displayConsent(request, response, clientId, principal, + requestedScopes, authorizedScopes, state); + return; + } + filterChain.doFilter(request, response); + } + + private final RequestMatcher requestMatcher; + + public EiamOAuth2AuthorizationConsentEndpointFilter(AppOidcConfigRepository appOidcConfigRepository, + RequestMatcher requestMatcher) { + super(appOidcConfigRepository); + Assert.notNull(appOidcConfigRepository, "appOidcConfigRepository must not be null"); + this.requestMatcher = new AndRequestMatcher(requestMatcher, + new RequestHeaderRequestMatcher("Location")); + } + +} diff --git a/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/package-info.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/consent/package-info.java similarity index 85% rename from eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/package-info.java rename to eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/consent/package-info.java index 0518a7e9..e763e954 100644 --- a/eiam-protocol/eiam-protocol-form/src/main/java/cn/topiam/employee/protocol/package-info.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/consent/package-info.java @@ -1,5 +1,5 @@ /* - * eiam-protocol-form - Employee Identity and Access Management Program + * eiam-protocol-oidc - Employee Identity and Access Management Program * Copyright © 2020-2023 TopIAM (support@topiam.cn) * * This program is free software: you can redistribute it and/or modify @@ -15,4 +15,4 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.protocol; \ No newline at end of file +package cn.topiam.employee.protocol.oidc.authentication.consent; \ No newline at end of file diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/EiamOAuth2AuthenticationImplicitAuthenticationProvider.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/EiamOAuth2AuthenticationImplicitAuthenticationProvider.java index 64b894df..e84b3d6c 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/EiamOAuth2AuthenticationImplicitAuthenticationProvider.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/EiamOAuth2AuthenticationImplicitAuthenticationProvider.java @@ -18,7 +18,9 @@ package cn.topiam.employee.protocol.oidc.authentication.implicit; import java.security.Principal; -import java.util.*; +import java.util.Base64; +import java.util.Map; +import java.util.Set; import java.util.function.Consumer; import org.springframework.security.authentication.AnonymousAuthenticationToken; @@ -35,7 +37,10 @@ import org.springframework.security.oauth2.core.oidc.OidcScopes; import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.server.authorization.*; -import org.springframework.security.oauth2.server.authorization.authentication.*; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationValidator; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken; import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder; @@ -46,6 +51,8 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; import com.google.common.collect.Maps; + +import cn.topiam.employee.audit.context.AuditContext; import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.REDIRECT_URI; /** @@ -57,6 +64,8 @@ import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterN @SuppressWarnings("AlibabaClassNamingShouldBeCamel") public class EiamOAuth2AuthenticationImplicitAuthenticationProvider implements AuthenticationProvider { + public static final AuthorizationGrantType IMPLICIT = new AuthorizationGrantType( + "implicit"); private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1"; private static final StringKeyGenerator DEFAULT_STATE_GENERATOR = new Base64StringKeyGenerator( @@ -91,6 +100,7 @@ public class EiamOAuth2AuthenticationImplicitAuthenticationProvider implements this.tokenGenerator = tokenGenerator; } + @SuppressWarnings("AlibabaMethodTooLong") @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { EiamOAuth2AuthorizationImplicitAuthenticationToken authorizationImplicitRequestAuthentication = (EiamOAuth2AuthorizationImplicitAuthenticationToken) authentication; @@ -109,8 +119,7 @@ public class EiamOAuth2AuthenticationImplicitAuthenticationProvider implements .build(); this.authenticationValidator.accept(authenticationContext); - if (!registeredClient.getAuthorizationGrantTypes() - .contains(AuthorizationGrantType.IMPLICIT)) { + if (!registeredClient.getAuthorizationGrantTypes().contains(IMPLICIT)) { throwError(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT, OAuth2ParameterNames.CLIENT_ID, authorizationImplicitRequestAuthentication, registeredClient); } @@ -252,6 +261,8 @@ public class EiamOAuth2AuthenticationImplicitAuthenticationProvider implements OAuth2AccessTokenAuthenticationToken token = new OAuth2AccessTokenAuthenticationToken( registeredClient, clientPrincipal, accessToken, refreshToken, additionalParameters); token.setAuthenticated(true); + //放入审计上下文中 + AuditContext.setAuthorization(authorization.getAttribute(Principal.class.getName())); return token; } @@ -259,7 +270,6 @@ public class EiamOAuth2AuthenticationImplicitAuthenticationProvider implements public boolean supports(Class authentication) { return authentication .isAssignableFrom(EiamOAuth2AuthorizationImplicitAuthenticationToken.class); - } private static boolean requireAuthorizationConsent(RegisteredClient registeredClient, diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/EiamOAuth2AuthorizationImplicitAuthenticationEndpointFilter.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/EiamOAuth2AuthorizationImplicitAuthenticationEndpointFilter.java index 6e6650d8..af1dcd05 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/EiamOAuth2AuthorizationImplicitAuthenticationEndpointFilter.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/EiamOAuth2AuthorizationImplicitAuthenticationEndpointFilter.java @@ -18,7 +18,6 @@ package cn.topiam.employee.protocol.oidc.authentication.implicit; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.time.temporal.ChronoUnit; import java.util.*; @@ -30,7 +29,6 @@ import javax.servlet.http.HttpServletResponse; import org.springframework.core.log.LogMessage; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.lang.NonNull; import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.authentication.AuthenticationManager; @@ -60,10 +58,22 @@ import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.UriComponentsBuilder; +import com.google.common.collect.Lists; + +import cn.topiam.employee.application.context.ApplicationContext; +import cn.topiam.employee.application.context.ApplicationContextHolder; +import cn.topiam.employee.audit.context.AuditContext; +import cn.topiam.employee.audit.entity.Target; +import cn.topiam.employee.audit.enums.EventStatus; +import cn.topiam.employee.audit.enums.EventType; +import cn.topiam.employee.audit.enums.TargetType; +import cn.topiam.employee.audit.event.AuditEventPublish; import cn.topiam.employee.common.constants.ProtocolConstants; import cn.topiam.employee.core.context.ServerContextHelp; import cn.topiam.employee.core.security.savedredirect.HttpSessionRedirectCache; import cn.topiam.employee.core.security.savedredirect.RedirectCache; +import cn.topiam.employee.protocol.oidc.authentication.consent.DefaultConsentPage; +import cn.topiam.employee.support.context.ApplicationContextHelp; import static org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames.TOKEN_TYPE; import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.*; @@ -73,7 +83,7 @@ import static cn.topiam.employee.protocol.oidc.util.EiamOAuth2Utils.appendUrl; /** * OAuth2 授权过滤器 * - * 用于支持 授权码模式和隐式授权模式 + * 用于支持隐式授权模式 * * @author TopIAM * Created by support@topiam.cn on 2022/10/28 23:26 @@ -281,6 +291,15 @@ public final class EiamOAuth2AuthorizationImplicitAuthenticationEndpointFilter e vars.put(OAuth2ParameterNames.STATE, state); } String append = appendUrl(redirectUri, vars, keys, true); + //审计 + ApplicationContext applicationContext = ApplicationContextHolder.getApplicationContext(); + Target target = Target.builder().id(applicationContext.getAppId().toString()) + .type(TargetType.APPLICATION).build(); + ArrayList targets = Lists.newArrayList(target); + + AuditEventPublish publish = ApplicationContextHelp.getBean(AuditEventPublish.class); + publish.publish(EventType.APP_SSO, AuditContext.getAuthorization(), EventStatus.SUCCESS, + Lists.newArrayList(target)); this.redirectStrategy.sendRedirect(request, response, append); } @@ -328,6 +347,17 @@ public final class EiamOAuth2AuthorizationImplicitAuthenticationEndpointFilter e uriBuilder.queryParam(OAuth2ParameterNames.STATE, authorizationImplicitRequestAuthenticationToken.getState()); } + + //审计 + ApplicationContext applicationContext = ApplicationContextHolder.getApplicationContext(); + Target target = Target.builder().id(applicationContext.getAppId().toString()) + .type(TargetType.APPLICATION).build(); + ArrayList targets = Lists.newArrayList(target); + + AuditEventPublish publish = ApplicationContextHelp.getBean(AuditEventPublish.class); + publish.publish(EventType.APP_SSO, AuditContext.getAuthorization(), EventStatus.FAIL, + targets, error.toString()); + this.redirectStrategy.sendRedirect(request, response, uriBuilder.toUriString()); } @@ -374,133 +404,4 @@ public final class EiamOAuth2AuthorizationImplicitAuthenticationEndpointFilter e this.consentPage = consentPage; } - /** - * For internal use only. - */ - @SuppressWarnings("AlibabaMethodTooLong") - private static class DefaultConsentPage { - private static final MediaType TEXT_HTML_UTF8 = new MediaType("text", "html", - StandardCharsets.UTF_8); - - private static void displayConsent(HttpServletRequest request, HttpServletResponse response, - String clientId, Authentication principal, - Set requestedScopes, - Set authorizedScopes, - String state) throws IOException { - - String consentPage = generateConsentPage(request, clientId, principal, requestedScopes, - authorizedScopes, state); - response.setContentType(TEXT_HTML_UTF8.toString()); - response.setContentLength(consentPage.getBytes(StandardCharsets.UTF_8).length); - response.getWriter().write(consentPage); - } - - private static String generateConsentPage(HttpServletRequest request, String clientId, - Authentication principal, - Set requestedScopes, - Set authorizedScopes, String state) { - Set scopesToAuthorize = new HashSet<>(); - Set scopesPreviouslyAuthorized = new HashSet<>(); - for (String scope : requestedScopes) { - if (authorizedScopes.contains(scope)) { - scopesPreviouslyAuthorized.add(scope); - } else if (!scope.equals(OidcScopes.OPENID)) { // openid scope does not require consent - scopesToAuthorize.add(scope); - } - } - - StringBuilder builder = new StringBuilder(); - - builder.append(""); - builder.append(""); - builder.append(""); - builder.append(" "); - builder.append( - " "); - builder.append( - " "); - builder.append(" Consent required"); - builder.append(" "); - builder.append(""); - builder.append(""); - builder.append("
"); - builder.append("
"); - builder.append("

Consent required

"); - builder.append("
"); - builder.append("
"); - builder.append("
"); - builder - .append("

" + clientId - + " wants to access your account " - + principal.getName() + "

"); - builder.append("
"); - builder.append("
"); - builder.append("
"); - builder.append("
"); - builder.append( - "

The following permissions are requested by the above app.
Please review these and consent if you approve.

"); - builder.append("
"); - builder.append("
"); - builder.append("
"); - builder.append("
"); - builder.append("
"); - builder.append(" "); - builder.append( - " "); - - for (String scope : scopesToAuthorize) { - builder.append("
"); - builder.append( - " "); - builder.append(" "); - builder.append("
"); - } - - if (!scopesPreviouslyAuthorized.isEmpty()) { - builder.append( - "

You have already granted the following permissions to the above app:

"); - for (String scope : scopesPreviouslyAuthorized) { - builder.append("
"); - builder.append( - " "); - builder.append(" "); - builder.append("
"); - } - } - - builder.append("
"); - builder.append( - " "); - builder.append("
"); - builder.append("
"); - builder.append( - " "); - builder.append("
"); - builder.append("
"); - builder.append("
"); - builder.append("
"); - builder.append("
"); - builder.append("
"); - builder.append( - "

Your consent to provide access is required.
If you do not approve, click Cancel, in which case no information will be shared with the app.

"); - builder.append("
"); - builder.append("
"); - builder.append("
"); - builder.append(""); - builder.append(""); - - return builder.toString(); - } - } } diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/OAuth2AuthorizationImplicitConsentAuthenticationConverter.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/EiamOAuth2AuthorizationImplicitConsentAuthenticationConverter.java similarity index 97% rename from eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/OAuth2AuthorizationImplicitConsentAuthenticationConverter.java rename to eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/EiamOAuth2AuthorizationImplicitConsentAuthenticationConverter.java index adbcb108..649286d7 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/OAuth2AuthorizationImplicitConsentAuthenticationConverter.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/EiamOAuth2AuthorizationImplicitConsentAuthenticationConverter.java @@ -44,8 +44,8 @@ import static cn.topiam.employee.protocol.oidc.util.EiamOAuth2Utils.getParameter * Created by support@topiam.cn on 2022/11/10 22:53 */ @SuppressWarnings("All") -public final class OAuth2AuthorizationImplicitConsentAuthenticationConverter implements - AuthenticationConverter { +public final class EiamOAuth2AuthorizationImplicitConsentAuthenticationConverter implements + AuthenticationConverter { private static final String DEFAULT_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1"; private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken( "anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/EiamOAuth2AuthorizationImplicitConsentAuthenticationToken.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/EiamOAuth2AuthorizationImplicitConsentAuthenticationToken.java index aa291481..f790f6c6 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/EiamOAuth2AuthorizationImplicitConsentAuthenticationToken.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/implicit/EiamOAuth2AuthorizationImplicitConsentAuthenticationToken.java @@ -17,11 +17,7 @@ */ package cn.topiam.employee.protocol.oidc.authentication.implicit; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; import org.springframework.lang.Nullable; import org.springframework.security.authentication.AbstractAuthenticationToken; diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/password/EiamOAuth2AuthorizationPasswordAuthenticationConverter.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/password/EiamOAuth2AuthorizationPasswordAuthenticationConverter.java index 32d11a0a..281ae17a 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/password/EiamOAuth2AuthorizationPasswordAuthenticationConverter.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/password/EiamOAuth2AuthorizationPasswordAuthenticationConverter.java @@ -27,7 +27,8 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.core.*; +import org.springframework.security.oauth2.core.AuthorizationGrantType; +import org.springframework.security.oauth2.core.OAuth2ErrorCodes; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.util.MultiValueMap; @@ -47,7 +48,7 @@ import static cn.topiam.employee.protocol.oidc.util.EiamOAuth2Utils.getParameter @SuppressWarnings({ "AlibabaClassNamingShouldBeCamel" }) public class EiamOAuth2AuthorizationPasswordAuthenticationConverter implements AuthenticationConverter { - public final static String DEFAULT_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; + public static final String DEFAULT_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; @Override public Authentication convert(HttpServletRequest request) { diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/password/EiamOAuth2AuthorizationPasswordAuthenticationProvider.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/password/EiamOAuth2AuthorizationPasswordAuthenticationProvider.java index 74a160ea..8227872b 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/password/EiamOAuth2AuthorizationPasswordAuthenticationProvider.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/authentication/password/EiamOAuth2AuthorizationPasswordAuthenticationProvider.java @@ -50,6 +50,8 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; + +import cn.topiam.employee.audit.context.AuditContext; import static cn.topiam.employee.protocol.oidc.util.EiamOAuth2Utils.getAuthenticatedClientElseThrowInvalidClient; /** @@ -58,7 +60,7 @@ import static cn.topiam.employee.protocol.oidc.util.EiamOAuth2Utils.getAuthentic * @author TopIAM * Created by support@topiam.cn on 2022/10/27 22:48 */ -@SuppressWarnings("AlibabaClassNamingShouldBeCamel") +@SuppressWarnings({ "AlibabaClassNamingShouldBeCamel", "AlibabaMethodTooLong" }) public class EiamOAuth2AuthorizationPasswordAuthenticationProvider extends DaoAuthenticationProvider { private static final Logger LOGGER = LogManager @@ -205,7 +207,8 @@ public class EiamOAuth2AuthorizationPasswordAuthenticationProvider extends Map additionalParameters = Collections.emptyMap(); LOGGER.debug("returning OAuth2AccessTokenAuthenticationToken"); - + //放入审计上下文中 + AuditContext.setAuthorization(authorization.getAttribute(Principal.class.getName())); return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken, additionalParameters); // @formatter:on diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/configuration/OidcConfiguration.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/configuration/OidcConfiguration.java new file mode 100644 index 00000000..4646f2bf --- /dev/null +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/configuration/OidcConfiguration.java @@ -0,0 +1,80 @@ +/* + * eiam-protocol-oidc - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.protocol.oidc.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService; +import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; +import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; +import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext; +import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer; + +import cn.topiam.employee.common.repository.app.AppOidcConfigRepository; +import cn.topiam.employee.protocol.oidc.jwt.EiamOAuth2TokenCustomizer; +import cn.topiam.employee.protocol.oidc.repository.OidcConfigRegisteredClientRepository; +import cn.topiam.employee.protocol.oidc.service.EiamRedisOAuth2AuthorizationConsentService; +import cn.topiam.employee.protocol.oidc.service.EiamRedisOAuth2AuthorizationService; + +/** + * + * @author SanLi + * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2022/12/25 23:06 + */ +@Configuration +public class OidcConfiguration { + + /** + * 注册客户端 Repository + * + * @return {@link RegisteredClientRepository} + */ + @Bean + public RegisteredClientRepository registeredClientRepository(AppOidcConfigRepository appOidcConfigRepository) { + return new OidcConfigRegisteredClientRepository(appOidcConfigRepository); + } + + /** + * Authorization Service + * + * @param redisTemplate {@link JdbcTemplate} + * @return {@link OAuth2AuthorizationService} + */ + @Bean + public OAuth2AuthorizationService authorizationService(RedisTemplate redisTemplate) { + return new EiamRedisOAuth2AuthorizationService(redisTemplate); + } + + /** + * OAuth2 Authorization Consent Service + * + * @param redisTemplate {@link RedisTemplate} + * @return {@link OAuth2AuthorizationConsentService} + */ + @Bean + public OAuth2AuthorizationConsentService authorizationConsentService(RedisTemplate redisTemplate) { + return new EiamRedisOAuth2AuthorizationConsentService(redisTemplate); + } + + @Bean + public OAuth2TokenCustomizer jwtCustomizer() { + return new EiamOAuth2TokenCustomizer(); + } +} diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/context/package-info.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/configuration/package-info.java similarity index 93% rename from eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/context/package-info.java rename to eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/configuration/package-info.java index b47dde8e..ddb7961e 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/context/package-info.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/configuration/package-info.java @@ -15,4 +15,4 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package cn.topiam.employee.protocol.oidc.context; \ No newline at end of file +package cn.topiam.employee.protocol.oidc.configuration; \ No newline at end of file diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/handler/PortalOAuth2AuthenticationEntryPoint.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/handler/PortalOAuth2AuthenticationEntryPoint.java deleted file mode 100644 index 22f84b85..00000000 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/handler/PortalOAuth2AuthenticationEntryPoint.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * eiam-protocol-oidc - Employee Identity and Access Management Program - * Copyright © 2020-2023 TopIAM (support@topiam.cn) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package cn.topiam.employee.protocol.oidc.handler; - -import java.io.IOException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.core.AuthenticationException; - -import cn.topiam.employee.core.context.ServerContextHelp; -import cn.topiam.employee.core.security.savedredirect.HttpSessionRedirectCache; -import cn.topiam.employee.core.security.savedredirect.RedirectCache; -import cn.topiam.employee.support.result.ApiRestResult; -import cn.topiam.employee.support.util.HttpResponseUtils; -import static org.springframework.http.HttpStatus.UNAUTHORIZED; - -import static cn.topiam.employee.common.constants.AuthorizeConstants.FE_LOGIN; -import static cn.topiam.employee.support.context.ServletContextHelp.acceptIncludeTextHtml; - -/** - * 认证入口点 - * - * @author TopIAM - * Created by support@topiam.cn on 2020/9/2 22:11 - */ -@SuppressWarnings({ "AlibabaClassNamingShouldBeCamel", "DuplicatedCode" }) -public class PortalOAuth2AuthenticationEntryPoint implements - org.springframework.security.web.AuthenticationEntryPoint { - /** - * 日志 - */ - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - - private final RedirectCache redirectCache = new HttpSessionRedirectCache(); - - /** - * Commences an authentication scheme. - *

- * ExceptionTranslationFilter will populate the HttpSession - * attribute named - * AbstractAuthenticationProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY - * with the requested target URL before calling this method. - *

- * Implementations should modify the headers on the ServletResponse as - * necessary to commence the authentication process. - * - * @param request that resulted in an AuthenticationException - * @param response so that the user agent can begin authentication - * @param authException that caused the invocation - */ - @Override - public void commence(HttpServletRequest request, HttpServletResponse response, - AuthenticationException authException) throws IOException { - logger.info("----------------------------------------------------------"); - logger.info("未登录, 或登录过期"); - //记录 - redirectCache.saveRedirect(request, response, RedirectCache.RedirectType.REQUEST); - //判断请求 - boolean isTextHtml = acceptIncludeTextHtml(request); - //JSON - if (!isTextHtml) { - ApiRestResult result = ApiRestResult.builder() - .status(String.valueOf(UNAUTHORIZED.value())).message(StringUtils - .defaultString(authException.getMessage(), UNAUTHORIZED.getReasonPhrase())) - .build(); - HttpResponseUtils.flushResponseJson(response, UNAUTHORIZED.value(), result); - } - // HTML - else { - //跳转前端SESSION过期路由 - response.sendRedirect(ServerContextHelp.getPortalPublicBaseUrl() + FE_LOGIN); - } - logger.info("----------------------------------------------------------"); - } -} diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/jwk/Jwks.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/jwk/Jwks.java deleted file mode 100644 index fe7087d8..00000000 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/jwk/Jwks.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * eiam-protocol-oidc - Employee Identity and Access Management Program - * Copyright © 2020-2023 TopIAM (support@topiam.cn) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package cn.topiam.employee.protocol.oidc.jwk; - -import java.security.KeyPair; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.security.interfaces.RSAPrivateKey; -import java.security.interfaces.RSAPublicKey; -import java.util.UUID; - -import javax.crypto.SecretKey; - -import com.nimbusds.jose.jwk.Curve; -import com.nimbusds.jose.jwk.ECKey; -import com.nimbusds.jose.jwk.OctetSequenceKey; -import com.nimbusds.jose.jwk.RSAKey; - -/** - * JWK - * - * @author TopIAM - * Created by support@topiam.cn on 2022/7/3 22:57 - */ -public final class Jwks { - - private Jwks() { - } - - public static RSAKey generateRsa() { - KeyPair keyPair = KeyGeneratorUtils.generateRsaKey(); - RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); - RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); - // @formatter:off - return new RSAKey.Builder(publicKey) - .privateKey(privateKey) - .keyID(UUID.randomUUID().toString()) - .build(); - // @formatter:on - } - - public static ECKey generateEc() { - KeyPair keyPair = KeyGeneratorUtils.generateEcKey(); - ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic(); - ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate(); - Curve curve = Curve.forECParameterSpec(publicKey.getParams()); - // @formatter:off - return new ECKey.Builder(curve, publicKey) - .privateKey(privateKey) - .keyID(UUID.randomUUID().toString()) - .build(); - // @formatter:on - } - - public static OctetSequenceKey generateSecret() { - SecretKey secretKey = KeyGeneratorUtils.generateSecretKey(); - // @formatter:off - return new OctetSequenceKey.Builder(secretKey) - .keyID(UUID.randomUUID().toString()) - .build(); - // @formatter:on - } -} diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/jwk/KeyGeneratorUtils.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/jwk/KeyGeneratorUtils.java deleted file mode 100644 index 44cbe635..00000000 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/jwk/KeyGeneratorUtils.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * eiam-protocol-oidc - Employee Identity and Access Management Program - * Copyright © 2020-2023 TopIAM (support@topiam.cn) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package cn.topiam.employee.protocol.oidc.jwk; - -import java.math.BigInteger; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.spec.ECFieldFp; -import java.security.spec.ECParameterSpec; -import java.security.spec.ECPoint; -import java.security.spec.EllipticCurve; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; - -/** - * KeyGeneratorUtils - * - * @author TopIAM - * Created by support@topiam.cn on 2022/7/3 22:57 - */ -public final class KeyGeneratorUtils { - - private KeyGeneratorUtils() { - } - - static SecretKey generateSecretKey() { - SecretKey hmacKey; - try { - hmacKey = KeyGenerator.getInstance("HmacSha256").generateKey(); - } catch (Exception ex) { - throw new IllegalStateException(ex); - } - return hmacKey; - } - - static KeyPair generateRsaKey() { - KeyPair keyPair; - try { - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); - keyPairGenerator.initialize(2048); - keyPair = keyPairGenerator.generateKeyPair(); - } catch (Exception ex) { - throw new IllegalStateException(ex); - } - return keyPair; - } - - static KeyPair generateEcKey() { - EllipticCurve ellipticCurve = new EllipticCurve( - new ECFieldFp(new BigInteger( - "115792089210356248762697446949407573530086143415290314195533631308867097853951")), - new BigInteger( - "115792089210356248762697446949407573530086143415290314195533631308867097853948"), - new BigInteger( - "41058363725152142129326129780047268409114441015993725554835256314039467401291")); - ECPoint ecPoint = new ECPoint( - new BigInteger( - "48439561293906451759052585252797914202762949526041747995844080717082404635286"), - new BigInteger( - "36134250956749795798585127919587881956611106672985015071877198253568414405109")); - ECParameterSpec ecParameterSpec = new ECParameterSpec(ellipticCurve, ecPoint, - new BigInteger( - "115792089210356248762697446949407573529996955224135760342422259061068512044369"), - 1); - - KeyPair keyPair; - try { - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC"); - keyPairGenerator.initialize(ecParameterSpec); - keyPair = keyPairGenerator.generateKeyPair(); - } catch (Exception ex) { - throw new IllegalStateException(ex); - } - return keyPair; - } -} diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/jwt/ApplicationJwtDecoder.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/jwt/ApplicationJwtDecoder.java index ca1f5154..dfb65da3 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/jwt/ApplicationJwtDecoder.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/jwt/ApplicationJwtDecoder.java @@ -46,7 +46,6 @@ public class ApplicationJwtDecoder implements JwtDecoder { public Jwt decode(String token) throws JwtException { ApplicationContext applicationContext = ApplicationContextHolder.getApplicationContext(); Long appId = applicationContext.getAppId(); - String appCode = applicationContext.getAppCode(); try { Optional certOptional = appCertRepository.findByAppIdAndUsingType(appId, AppCertUsingType.OIDC_JWK); diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/jwt/EiamOAuth2TokenCustomizer.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/jwt/EiamOAuth2TokenCustomizer.java new file mode 100644 index 00000000..d8011cf2 --- /dev/null +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/jwt/EiamOAuth2TokenCustomizer.java @@ -0,0 +1,84 @@ +/* + * eiam-protocol-oidc - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.protocol.oidc.jwt; + +import java.security.Principal; +import java.util.Objects; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.core.oidc.StandardClaimNames; +import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames; +import org.springframework.security.oauth2.jwt.JwsHeader; +import org.springframework.security.oauth2.jwt.JwtClaimsSet; +import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; +import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext; +import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer; + +import cn.topiam.employee.application.context.ApplicationContext; +import cn.topiam.employee.application.context.ApplicationContextHolder; +import cn.topiam.employee.common.entity.account.UserEntity; +import cn.topiam.employee.common.repository.account.UserRepository; +import cn.topiam.employee.core.security.userdetails.UserDetails; +import cn.topiam.employee.support.context.ApplicationContextHelp; +import static org.springframework.security.oauth2.core.oidc.OidcScopes.*; + +import static cn.topiam.employee.support.constant.EiamConstants.DEFAULT_DATE_TIME_FORMATTER; + +/** + * 令牌定制器 + * + * @author SanLi + * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2022/12/26 16:44 + */ +@SuppressWarnings({ "unused", "AlibabaClassNamingShouldBeCamel" }) +public class EiamOAuth2TokenCustomizer implements OAuth2TokenCustomizer { + + @Override + public void customize(JwtEncodingContext context) { + //@formatter:off + Set authorizedScopes = context.getAuthorizedScopes(); + JwsHeader.Builder headers = context.getJwsHeader(); + JwtClaimsSet.Builder claims = context.getClaims(); + if (context.getTokenType().getValue().equals(OidcParameterNames.ID_TOKEN)) { + OAuth2Authorization auth2Authorization = context.getAuthorization(); + Authentication authentication = auth2Authorization.getAttribute(Principal.class.getName()); + UserDetails principal = (UserDetails) authentication.getPrincipal(); + ApplicationContext applicationContext = ApplicationContextHolder.getApplicationContext(); + Long appId = applicationContext.getAppId(); + UserRepository userRepository = ApplicationContextHelp.getBean(UserRepository.class); + UserEntity user = userRepository.findByUsername(principal.getUsername()); + // Customize headers/claims for id_token + if (authorizedScopes.contains(EMAIL)) { + claims.claim(StandardClaimNames.EMAIL, StringUtils.defaultString(user.getEmail(), "")); + claims.claim(StandardClaimNames.EMAIL_VERIFIED, !Objects.isNull(user.getEmailVerified()) && user.getEmailVerified()); + } + if (authorizedScopes.contains(PHONE)) { + claims.claim(StandardClaimNames.PHONE_NUMBER, StringUtils.defaultString(user.getPhone(), "")); + claims.claim(StandardClaimNames.PHONE_NUMBER_VERIFIED, !Objects.isNull(user.getPhoneVerified()) && user.getPhoneVerified()); + } + if (authorizedScopes.contains(PROFILE)) { + claims.claim(StandardClaimNames.NAME, StringUtils.defaultString(user.getFullName(), "")); + claims.claim(StandardClaimNames.NICKNAME, StringUtils.defaultString(user.getNickName(), "")); + claims.claim(StandardClaimNames.UPDATED_AT, user.getUpdateTime().format(DEFAULT_DATE_TIME_FORMATTER)); + } + } + //@formatter:on + } +} diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/service/RedisOAuth2AuthorizationConsentService.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/service/EiamRedisOAuth2AuthorizationConsentService.java similarity index 81% rename from eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/service/RedisOAuth2AuthorizationConsentService.java rename to eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/service/EiamRedisOAuth2AuthorizationConsentService.java index 66e9f605..7f53247c 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/service/RedisOAuth2AuthorizationConsentService.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/service/EiamRedisOAuth2AuthorizationConsentService.java @@ -20,11 +20,10 @@ package cn.topiam.employee.protocol.oidc.service; import java.util.concurrent.TimeUnit; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService; import org.springframework.util.Assert; - -import lombok.RequiredArgsConstructor; import static cn.topiam.employee.support.constant.EiamConstants.COLON; /** @@ -33,17 +32,22 @@ import static cn.topiam.employee.support.constant.EiamConstants.COLON; * Created by support@topiam.cn on 2022/10/31 20:41 */ @SuppressWarnings("ALL") -@RequiredArgsConstructor -public class RedisOAuth2AuthorizationConsentService implements OAuth2AuthorizationConsentService { +public class EiamRedisOAuth2AuthorizationConsentService implements + OAuth2AuthorizationConsentService { private final RedisTemplate redisTemplate; - private final static Long TIMEOUT = 10L; + private static final Long TIMEOUT = 10L; + + public EiamRedisOAuth2AuthorizationConsentService(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + this.redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); + this.redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); + } @Override public void save(OAuth2AuthorizationConsent authorizationConsent) { Assert.notNull(authorizationConsent, "authorizationConsent cannot be null"); - redisTemplate.opsForValue().set(buildKey(authorizationConsent), authorizationConsent, TIMEOUT, TimeUnit.MINUTES); diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/service/RedisOAuth2AuthorizationService.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/service/EiamRedisOAuth2AuthorizationService.java similarity index 89% rename from eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/service/RedisOAuth2AuthorizationService.java rename to eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/service/EiamRedisOAuth2AuthorizationService.java index 47f226f4..4990e682 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/service/RedisOAuth2AuthorizationService.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/service/EiamRedisOAuth2AuthorizationService.java @@ -24,7 +24,7 @@ import java.util.Objects; import java.util.concurrent.TimeUnit; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.lang.Nullable; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2RefreshToken; @@ -34,8 +34,7 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; import org.springframework.util.Assert; - -import lombok.RequiredArgsConstructor; +import static cn.topiam.employee.support.constant.EiamConstants.COLON; /** * RedisOAuth2AuthorizationService @@ -44,22 +43,27 @@ import lombok.RequiredArgsConstructor; * Created by support@topiam.cn on 2022/10/31 20:41 */ @SuppressWarnings("ALL") -@RequiredArgsConstructor -public class RedisOAuth2AuthorizationService implements OAuth2AuthorizationService { +public class EiamRedisOAuth2AuthorizationService implements OAuth2AuthorizationService { - private final static Long TIMEOUT = 10L; + private static final Long TIMEOUT = 10L; private static final String AUTHORIZATION = "token"; private final RedisTemplate redisTemplate; + public EiamRedisOAuth2AuthorizationService(RedisTemplate redisTemplate) { + redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); + redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); + this.redisTemplate = redisTemplate; + } + @Override public void save(OAuth2Authorization authorization) { Assert.notNull(authorization, "authorization cannot be null"); if (isState(authorization)) { String token = authorization.getAttribute(OAuth2ParameterNames.STATE); - redisTemplate.setValueSerializer(RedisSerializer.java()); + redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.STATE, token), authorization, TIMEOUT, TimeUnit.MINUTES); } @@ -70,7 +74,7 @@ public class RedisOAuth2AuthorizationService implements OAuth2AuthorizationServi OAuth2AuthorizationCode authorizationCodeToken = authorizationCode.getToken(); long between = ChronoUnit.MINUTES.between(authorizationCodeToken.getIssuedAt(), authorizationCodeToken.getExpiresAt()); - redisTemplate.setValueSerializer(RedisSerializer.java()); + redisTemplate.opsForValue().set( buildKey(OAuth2ParameterNames.CODE, authorizationCodeToken.getTokenValue()), authorization, between, TimeUnit.MINUTES); @@ -80,7 +84,7 @@ public class RedisOAuth2AuthorizationService implements OAuth2AuthorizationServi OAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken(); long between = ChronoUnit.SECONDS.between(refreshToken.getIssuedAt(), refreshToken.getExpiresAt()); - redisTemplate.setValueSerializer(RedisSerializer.java()); + redisTemplate.opsForValue().set( buildKey(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken.getTokenValue()), authorization, between, TimeUnit.SECONDS); @@ -90,7 +94,7 @@ public class RedisOAuth2AuthorizationService implements OAuth2AuthorizationServi OAuth2AccessToken accessToken = authorization.getAccessToken().getToken(); long between = ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()); - redisTemplate.setValueSerializer(RedisSerializer.java()); + redisTemplate.opsForValue().set( buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()), authorization, between, TimeUnit.SECONDS); @@ -137,13 +141,13 @@ public class RedisOAuth2AuthorizationService implements OAuth2AuthorizationServi public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) { Assert.hasText(token, "token cannot be empty"); Assert.notNull(tokenType, "tokenType cannot be empty"); - redisTemplate.setValueSerializer(RedisSerializer.java()); + return (OAuth2Authorization) redisTemplate.opsForValue() .get(buildKey(tokenType.getValue(), token)); } - private String buildKey(String type, String id) { - return String.format("%s::%s::%s", AUTHORIZATION, type, id); + private String buildKey(String type, String token) { + return String.format("%s" + COLON + "%s" + COLON + "%s", AUTHORIZATION, type, token); } private static boolean isState(OAuth2Authorization authorization) { diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/token/EiamOAuth2TokenGenerator.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/token/EiamOAuth2TokenGenerator.java deleted file mode 100644 index 6a530bc5..00000000 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/token/EiamOAuth2TokenGenerator.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * eiam-protocol-oidc - Employee Identity and Access Management Program - * Copyright © 2020-2023 TopIAM (support@topiam.cn) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package cn.topiam.employee.protocol.oidc.token; - -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Collections; -import java.util.Set; - -import org.springframework.lang.Nullable; -import org.springframework.security.oauth2.core.AuthorizationGrantType; -import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; -import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; -import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames; -import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames; -import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; -import org.springframework.security.oauth2.jwt.*; -import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; -import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; -import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat; -import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext; -import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext; -import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer; -import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -import cn.topiam.employee.application.context.ApplicationContext; -import cn.topiam.employee.application.context.ApplicationContextHolder; -import cn.topiam.employee.common.entity.account.UserEntity; -import cn.topiam.employee.core.security.userdetails.UserDetails; -import cn.topiam.employee.core.security.util.UserUtils; -import static org.springframework.security.oauth2.core.oidc.OidcScopes.*; - -/** - * EiamOAuth2TokenGenerator - * - * @author TopIAM - * Created by support@topiam.cn on 2022/10/27 00:06 - */ -@SuppressWarnings({ "unused", "AlibabaClassNamingShouldBeCamel", "AlibabaAvoidComplexCondition", - "AlibabaMethodTooLong" }) -public final class EiamOAuth2TokenGenerator implements OAuth2TokenGenerator { - private final JwtEncoder jwtEncoder; - private OAuth2TokenCustomizer jwtCustomizer; - - /** - * Constructs a {@code JwtGenerator} using the provided parameters. - * - * @param jwtEncoder the jwt encoder - */ - public EiamOAuth2TokenGenerator(JwtEncoder jwtEncoder) { - Assert.notNull(jwtEncoder, "jwtEncoder cannot be null"); - this.jwtEncoder = jwtEncoder; - } - - @Nullable - @Override - public Jwt generate(OAuth2TokenContext context) { - // @formatter:off - if (context.getTokenType() == null || (!OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType()) && !OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue()))) { - return null; - } - if (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType()) && !OAuth2TokenFormat.SELF_CONTAINED.equals(context.getRegisteredClient().getTokenSettings().getAccessTokenFormat())) { - return null; - } - - String issuer = null; - if (context.getAuthorizationServerContext() != null) { - issuer = context.getAuthorizationServerContext().getIssuer(); - } - RegisteredClient registeredClient = context.getRegisteredClient(); - - Instant issuedAt = Instant.now(); - Instant expiresAt; - if (OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue())) { - // TODO Allow configuration for ID Token time-to-live - // TODO ID token 默认为 30 分钟,通过上下文拿配置倒是也可以,但是更想从 RegisteredClient 拿配置, - // 等 https://github.com/spring-projects/spring-authorization-server/issues/790 支持后支持 - expiresAt = issuedAt.plus(30, ChronoUnit.MINUTES); - } else { - expiresAt = issuedAt.plus(registeredClient.getTokenSettings().getAccessTokenTimeToLive()); - } - - // @formatter:off - JwtClaimsSet.Builder claimsBuilder = JwtClaimsSet.builder(); - if (StringUtils.hasText(issuer)) { - claimsBuilder.issuer(issuer); - } - UserDetails principal = (UserDetails) context.getPrincipal().getPrincipal(); - UserEntity user = UserUtils.getUser(principal.getId()); - Set scopes = context.getAuthorizedScopes(); - claimsBuilder - .subject(principal.getId()) - .audience(Collections.singletonList(registeredClient.getClientId())) - .issuedAt(issuedAt) - .expiresAt(expiresAt); - if (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) { - claimsBuilder.notBefore(issuedAt); - if (!CollectionUtils.isEmpty(scopes)) { - claimsBuilder.claim(OAuth2ParameterNames.SCOPE, scopes); - } - } - //根据配置封装ID Token - else if (OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue())) { - claimsBuilder.claim(IdTokenClaimNames.AZP, registeredClient.getClientId()); - ApplicationContext applicationContext = ApplicationContextHolder.getApplicationContext(); - //手机号 - if (scopes.contains(PHONE) && StringUtils.hasText(user.getPhone())){ - claimsBuilder.claim(PHONE, user.getPhone()); - } - //邮箱 - if (scopes.contains(EMAIL) && StringUtils.hasText(user.getEmail())){ - claimsBuilder.claim(EMAIL, user.getEmail()); - } - //profile - if (scopes.contains(PROFILE)){ - - } - if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(context.getAuthorizationGrantType())) { - OAuth2AuthorizationRequest authorizationRequest = context.getAuthorization().getAttribute(OAuth2AuthorizationRequest.class.getName()); - String nonce = (String) authorizationRequest.getAdditionalParameters().get(OidcParameterNames.NONCE); - if (StringUtils.hasText(nonce)) { - claimsBuilder.claim(IdTokenClaimNames.NONCE, nonce); - } - } - // TODO Add 'auth_time' claim - } - // @formatter:on - - JwsHeader.Builder headersBuilder = JwsHeader.with(SignatureAlgorithm.RS256); - - if (this.jwtCustomizer != null) { - // @formatter:off - JwtEncodingContext.Builder jwtContextBuilder = JwtEncodingContext.with(headersBuilder, claimsBuilder) - .registeredClient(context.getRegisteredClient()) - .principal(context.getPrincipal()) - .authorizationServerContext(context.getAuthorizationServerContext()) - .authorizedScopes(context.getAuthorizedScopes()) - .tokenType(context.getTokenType()) - .authorizationGrantType(context.getAuthorizationGrantType()); - if (context.getAuthorization() != null) { - jwtContextBuilder.authorization(context.getAuthorization()); - } - if (context.getAuthorizationGrant() != null) { - jwtContextBuilder.authorizationGrant(context.getAuthorizationGrant()); - } - // @formatter:on - - JwtEncodingContext jwtContext = jwtContextBuilder.build(); - this.jwtCustomizer.customize(jwtContext); - } - - JwsHeader headers = headersBuilder.build(); - JwtClaimsSet claims = claimsBuilder.build(); - - return this.jwtEncoder.encode(JwtEncoderParameters.from(headers, claims)); - } - - /** - * Sets the {@link OAuth2TokenCustomizer} that customizes the - * {@link JwtEncodingContext#getJwsHeader()} () headers} and/or - * {@link JwtEncodingContext#getClaims() claims} for the generated {@link Jwt}. - * - * @param jwtCustomizer the {@link OAuth2TokenCustomizer} that customizes the headers and/or claims for the generated {@code Jwt} - */ - public void setJwtCustomizer(OAuth2TokenCustomizer jwtCustomizer) { - Assert.notNull(jwtCustomizer, "jwtCustomizer cannot be null"); - this.jwtCustomizer = jwtCustomizer; - } - -} diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/token/ApplicationOpaqueTokenIntrospector.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/token/EiamOpaqueTokenIntrospector.java similarity index 94% rename from eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/token/ApplicationOpaqueTokenIntrospector.java rename to eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/token/EiamOpaqueTokenIntrospector.java index 6808cde6..f1f39374 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/token/ApplicationOpaqueTokenIntrospector.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/token/EiamOpaqueTokenIntrospector.java @@ -37,8 +37,6 @@ import org.springframework.util.CollectionUtils; import com.alibaba.fastjson2.JSON; -import cn.topiam.employee.support.context.ApplicationContextHelp; - import lombok.RequiredArgsConstructor; import static org.springframework.security.oauth2.server.authorization.OAuth2TokenType.ACCESS_TOKEN; @@ -48,12 +46,12 @@ import static org.springframework.security.oauth2.server.authorization.OAuth2Tok * Created by support@topiam.cn on 2022/10/29 20:27 */ @RequiredArgsConstructor -public class ApplicationOpaqueTokenIntrospector implements OpaqueTokenIntrospector { +public class EiamOpaqueTokenIntrospector implements OpaqueTokenIntrospector { private static final String AUTHORITY_PREFIX = "SCOPE_"; private final Logger logger = LoggerFactory - .getLogger(ApplicationOpaqueTokenIntrospector.class); + .getLogger(EiamOpaqueTokenIntrospector.class); /** * introspect @@ -63,8 +61,6 @@ public class ApplicationOpaqueTokenIntrospector implements OpaqueTokenIntrospect */ @Override public OAuth2AuthenticatedPrincipal introspect(String token) { - OAuth2AuthorizationService authorizationService = ApplicationContextHelp - .getBean(OAuth2AuthorizationService.class); OAuth2Authorization authorization = authorizationService.findByToken(token, ACCESS_TOKEN); if (authorization == null) { return null; @@ -135,4 +131,6 @@ public class ApplicationOpaqueTokenIntrospector implements OpaqueTokenIntrospect }); return new OAuth2IntrospectionAuthenticatedPrincipal(claims, authorities); } + + private final OAuth2AuthorizationService authorizationService; } diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/util/EiamOAuth2Utils.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/util/EiamOAuth2Utils.java index ccea21e0..8c54795f 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/util/EiamOAuth2Utils.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/employee/protocol/oidc/util/EiamOAuth2Utils.java @@ -53,7 +53,6 @@ import cn.topiam.employee.common.repository.app.AppOidcConfigRepository; import cn.topiam.employee.protocol.oidc.authentication.implicit.EiamOAuth2AuthorizationImplicitAuthenticationException; import cn.topiam.employee.protocol.oidc.jwk.ApplicationJwkSource; import cn.topiam.employee.protocol.oidc.jwt.ApplicationJwtDecoder; -import cn.topiam.employee.protocol.oidc.token.EiamOAuth2TokenGenerator; /** * EiamOAuth2Utils @@ -150,7 +149,7 @@ public class EiamOAuth2Utils { if (tokenGenerator == null) { tokenGenerator = getOptionalBean(builder, OAuth2TokenGenerator.class); if (tokenGenerator == null) { - EiamOAuth2TokenGenerator jwtGenerator = getJwtGenerator(builder); + JwtGenerator jwtGenerator = getJwtGenerator(builder); OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator(); OAuth2TokenCustomizer accessTokenCustomizer = getAccessTokenCustomizer( builder); @@ -171,18 +170,17 @@ public class EiamOAuth2Utils { return tokenGenerator; } - private static > EiamOAuth2TokenGenerator getJwtGenerator(B builder) { - EiamOAuth2TokenGenerator jwtGenerator = builder - .getSharedObject(EiamOAuth2TokenGenerator.class); + private static > JwtGenerator getJwtGenerator(B builder) { + JwtGenerator jwtGenerator = builder.getSharedObject(JwtGenerator.class); if (jwtGenerator == null) { JwtEncoder jwtEncoder = getJwtEncoder(builder); if (jwtEncoder != null) { - jwtGenerator = new EiamOAuth2TokenGenerator(jwtEncoder); + jwtGenerator = new JwtGenerator(jwtEncoder); OAuth2TokenCustomizer jwtCustomizer = getJwtCustomizer(builder); if (jwtCustomizer != null) { jwtGenerator.setJwtCustomizer(jwtCustomizer); } - builder.setSharedObject(EiamOAuth2TokenGenerator.class, jwtGenerator); + builder.setSharedObject(JwtGenerator.class, jwtGenerator); } } return jwtGenerator; diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2AuthorizationCodeEndpointConfigurer.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2AuthorizationCodeEndpointConfigurer.java new file mode 100644 index 00000000..c0fd0cb0 --- /dev/null +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2AuthorizationCodeEndpointConfigurer.java @@ -0,0 +1,170 @@ +/* + * eiam-protocol-oidc - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.config.annotation.ObjectPostProcessor; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationContext; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationProvider; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationValidator; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationProvider; +import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter; +import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter; +import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeRequestAuthenticationConverter; +import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationConsentAuthenticationConverter; +import org.springframework.security.web.DefaultRedirectStrategy; +import org.springframework.security.web.RedirectStrategy; +import org.springframework.security.web.authentication.AuthenticationConverter; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.OrRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; + +import cn.topiam.employee.protocol.oidc.authentication.implicit.EiamOAuth2AuthorizationImplicitAuthenticationEndpointFilter; +import static cn.topiam.employee.common.constants.ProtocolConstants.OidcEndpointConstants.AUTHORIZATION_ENDPOINT; + +/** + * OAuth2 授权码端点配置 + * + * @author TopIAM + * Created by support@topiam.cn on 2022/10/26 19:12 + */ +@SuppressWarnings({ "All" }) +public final class EiamOAuth2AuthorizationCodeEndpointConfigurer extends AbstractOAuth2Configurer { + + protected final Log logger = LogFactory + .getLog(getClass()); + + private RequestMatcher requestMatcher; + private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); + private final List authorizationRequestConverters = new ArrayList<>(); + private Consumer> authorizationRequestConvertersConsumer = (authorizationRequestConverters) -> { + }; + private final List authenticationProviders = new ArrayList<>(); + private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { + }; + private String consentPage; + + private Consumer authorizationCodeRequestAuthenticationValidator; + + /** + * Restrict for internal use only. + */ + EiamOAuth2AuthorizationCodeEndpointConfigurer(ObjectPostProcessor objectPostProcessor) { + super(objectPostProcessor); + } + + void addAuthorizationCodeRequestAuthenticationValidator(Consumer authenticationValidator) { + this.authorizationCodeRequestAuthenticationValidator = this.authorizationCodeRequestAuthenticationValidator == null + ? authenticationValidator + : this.authorizationCodeRequestAuthenticationValidator.andThen(authenticationValidator); + } + + @Override + void init(HttpSecurity httpSecurity) { + this.requestMatcher = new OrRequestMatcher( + new AntPathRequestMatcher(AUTHORIZATION_ENDPOINT, HttpMethod.GET.name()), + new AntPathRequestMatcher(AUTHORIZATION_ENDPOINT, HttpMethod.POST.name())); + + List authenticationProviders = createDefaultAuthenticationProviders( + httpSecurity); + if (!this.authenticationProviders.isEmpty()) { + authenticationProviders.addAll(0, this.authenticationProviders); + } + this.authenticationProvidersConsumer.accept(authenticationProviders); + authenticationProviders.forEach(authenticationProvider -> httpSecurity + .authenticationProvider(postProcess(authenticationProvider))); + } + + @Override + void configure(HttpSecurity httpSecurity) { + AuthenticationManager authenticationManager = httpSecurity + .getSharedObject(AuthenticationManager.class); + OAuth2AuthorizationEndpointFilter authorizationEndpointFilter = new OAuth2AuthorizationEndpointFilter( + authenticationManager, AUTHORIZATION_ENDPOINT); + List authenticationConverters = createDefaultAuthenticationConverters(); + if (!this.authorizationRequestConverters.isEmpty()) { + authenticationConverters.addAll(0, this.authorizationRequestConverters); + } + this.authorizationRequestConvertersConsumer.accept(authenticationConverters); + authorizationEndpointFilter.setAuthenticationConverter( + new DelegatingAuthenticationConverter(authenticationConverters)); + //确认请求页面地址 + if (this.consentPage != null) { + authorizationEndpointFilter.setConsentPage(consentPage); + } + httpSecurity.addFilterAfter(postProcess(authorizationEndpointFilter), + EiamOAuth2AuthorizationImplicitAuthenticationEndpointFilter.class); + } + + @Override + RequestMatcher getRequestMatcher() { + return this.requestMatcher; + } + + /** + * 创建默认身份验证转换器 + * + * @return {@link List} + */ + private static List createDefaultAuthenticationConverters() { + List authenticationConverters = new ArrayList<>(); + //授权码模式请求转换器 + authenticationConverters.add(new OAuth2AuthorizationCodeRequestAuthenticationConverter()); + //OAuth2授权同意认证转换器 + authenticationConverters.add(new OAuth2AuthorizationConsentAuthenticationConverter()); + return authenticationConverters; + } + + private List createDefaultAuthenticationProviders(HttpSecurity httpSecurity) { + List authenticationProviders = new ArrayList<>(); + //OAuth2授权码请求身份验证程序 + OAuth2AuthorizationCodeRequestAuthenticationProvider authorizationCodeRequestAuthenticationProvider = new OAuth2AuthorizationCodeRequestAuthenticationProvider( + OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), + OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity), + OAuth2ConfigurerUtils.getAuthorizationConsentService(httpSecurity)); + if (this.authorizationCodeRequestAuthenticationValidator != null) { + authorizationCodeRequestAuthenticationProvider.setAuthenticationValidator( + new OAuth2AuthorizationCodeRequestAuthenticationValidator() + .andThen(this.authorizationCodeRequestAuthenticationValidator)); + } + authenticationProviders.add(authorizationCodeRequestAuthenticationProvider); + + //OAuth2授权码同意身份验证提供程序 + OAuth2AuthorizationConsentAuthenticationProvider authorizationConsentAuthenticationProvider = new OAuth2AuthorizationConsentAuthenticationProvider( + OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), + OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity), + OAuth2ConfigurerUtils.getAuthorizationConsentService(httpSecurity)); + authenticationProviders.add(authorizationConsentAuthenticationProvider); + return authenticationProviders; + } + + public EiamOAuth2AuthorizationCodeEndpointConfigurer consentPage(String consentPage) { + this.consentPage = consentPage; + return this; + } + +} diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2AuthorizationEndpointConfigurer.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2AuthorizationEndpointConfigurer.java deleted file mode 100644 index ef820365..00000000 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2AuthorizationEndpointConfigurer.java +++ /dev/null @@ -1,290 +0,0 @@ -/* - * eiam-protocol-oidc - Employee Identity and Access Management Program - * Copyright © 2020-2023 TopIAM (support@topiam.cn) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -import javax.servlet.http.HttpServletRequest; - -import org.springframework.http.HttpMethod; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.config.annotation.ObjectPostProcessor; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse; -import org.springframework.security.oauth2.server.authorization.authentication.*; -import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings; -import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter; -import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter; -import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeRequestAuthenticationConverter; -import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationConsentAuthenticationConverter; -import org.springframework.security.web.authentication.AuthenticationConverter; -import org.springframework.security.web.authentication.AuthenticationFailureHandler; -import org.springframework.security.web.authentication.AuthenticationSuccessHandler; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.security.web.util.matcher.OrRequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -import cn.topiam.employee.common.constants.ProtocolConstants; -import cn.topiam.employee.protocol.oidc.authentication.implicit.EiamOAuth2AuthorizationImplicitAuthenticationEndpointFilter; - -/** - * OAuth2 授权码端点配置 - * - * @author TopIAM - * Created by support@topiam.cn on 2022/10/26 19:12 - */ -@SuppressWarnings({ "All" }) -public final class EiamOAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2Configurer { - private RequestMatcher requestMatcher; - - private final List authorizationRequestConverters = new ArrayList<>(); - private Consumer> authorizationRequestConvertersConsumer = (authorizationRequestConverters) -> { - }; - private final List authenticationProviders = new ArrayList<>(); - private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { - }; - private AuthenticationSuccessHandler authorizationResponseHandler; - private AuthenticationFailureHandler errorResponseHandler; - private String consentPage; - - private Consumer authorizationCodeRequestAuthenticationValidator; - - /** - * Restrict for internal use only. - */ - EiamOAuth2AuthorizationEndpointConfigurer(ObjectPostProcessor objectPostProcessor) { - super(objectPostProcessor); - } - - /** - * Adds an {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest} - * to an instance of {@link OAuth2AuthorizationCodeRequestAuthenticationToken} or {@link OAuth2AuthorizationConsentAuthenticationToken} - * used for authenticating the request. - * - * @param authorizationRequestConverter an {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest} - * @return the {@link EiamOAuth2AuthorizationEndpointConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationEndpointConfigurer authorizationRequestConverter(AuthenticationConverter authorizationRequestConverter) { - Assert.notNull(authorizationRequestConverter, - "authorizationRequestConverter cannot be null"); - this.authorizationRequestConverters.add(authorizationRequestConverter); - return this; - } - - /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authorizationRequestConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param authorizationRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s - * @return the {@link EiamOAuth2AuthorizationEndpointConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOAuth2AuthorizationEndpointConfigurer authorizationRequestConverters(Consumer> authorizationRequestConvertersConsumer) { - Assert.notNull(authorizationRequestConvertersConsumer, - "authorizationRequestConvertersConsumer cannot be null"); - this.authorizationRequestConvertersConsumer = authorizationRequestConvertersConsumer; - return this; - } - - /** - * Adds an {@link AuthenticationProvider} used for authenticating an {@link OAuth2AuthorizationCodeRequestAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} - * @return the {@link EiamOAuth2AuthorizationEndpointConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) { - Assert.notNull(authenticationProvider, "authenticationProvider cannot be null"); - this.authenticationProviders.add(authenticationProvider); - return this; - } - - /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s - * @return the {@link EiamOAuth2AuthorizationEndpointConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOAuth2AuthorizationEndpointConfigurer authenticationProviders(Consumer> authenticationProvidersConsumer) { - Assert.notNull(authenticationProvidersConsumer, - "authenticationProvidersConsumer cannot be null"); - this.authenticationProvidersConsumer = authenticationProvidersConsumer; - return this; - } - - /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} - * and returning the {@link OAuth2AuthorizationResponse Authorization Response}. - * - * @param authorizationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} - * @return the {@link EiamOAuth2AuthorizationEndpointConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationEndpointConfigurer authorizationResponseHandler(AuthenticationSuccessHandler authorizationResponseHandler) { - this.authorizationResponseHandler = authorizationResponseHandler; - return this; - } - - /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException} - * @return the {@link EiamOAuth2AuthorizationEndpointConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) { - this.errorResponseHandler = errorResponseHandler; - return this; - } - - /** - * Specify the URI to redirect Resource Owners to if consent is required during - * the {@code authorization_code} flow. A default consent page will be generated when - * this attribute is not specified. - * - * If a URI is specified, applications are required to process the specified URI to generate - * a consent page. The query string will contain the following parameters: - * - *
    - *
  • {@code client_id} - the client identifier
  • - *
  • {@code scope} - a space-delimited list of scopes present in the authorization request
  • - *
  • {@code state} - a CSRF protection token
  • - *
- * - * In general, the consent page should create a form that submits - * a request with the following requirements: - * - *
    - *
  • It must be an HTTP POST
  • - *
  • It must be submitted to {@link AuthorizationServerSettings#getAuthorizationEndpoint()}
  • - *
  • It must include the received {@code client_id} as an HTTP parameter
  • - *
  • It must include the received {@code state} as an HTTP parameter
  • - *
  • It must include the list of {@code scope}s the {@code Resource Owner} - * consented to as an HTTP parameter
  • - *
- * - * @param consentPage the URI of the custom consent page to redirect to if consent is required (e.g. "/oauth2/consent") - * @return the {@link EiamOAuth2AuthorizationEndpointConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationEndpointConfigurer consentPage(String consentPage) { - this.consentPage = consentPage; - return this; - } - - void addAuthorizationCodeRequestAuthenticationValidator(Consumer authenticationValidator) { - this.authorizationCodeRequestAuthenticationValidator = this.authorizationCodeRequestAuthenticationValidator == null - ? authenticationValidator - : this.authorizationCodeRequestAuthenticationValidator.andThen(authenticationValidator); - } - - @Override - void init(HttpSecurity httpSecurity) { - this.requestMatcher = new OrRequestMatcher(new AntPathRequestMatcher( - ProtocolConstants.OidcEndpointConstants.AUTHORIZATION_ENDPOINT, HttpMethod.GET.name()), - new AntPathRequestMatcher( - ProtocolConstants.OidcEndpointConstants.AUTHORIZATION_ENDPOINT, - HttpMethod.POST.name())); - - List authenticationProviders = createDefaultAuthenticationProviders( - httpSecurity); - if (!this.authenticationProviders.isEmpty()) { - authenticationProviders.addAll(0, this.authenticationProviders); - } - this.authenticationProvidersConsumer.accept(authenticationProviders); - authenticationProviders.forEach(authenticationProvider -> httpSecurity - .authenticationProvider(postProcess(authenticationProvider))); - } - - @Override - void configure(HttpSecurity httpSecurity) { - AuthenticationManager authenticationManager = httpSecurity - .getSharedObject(AuthenticationManager.class); - OAuth2AuthorizationEndpointFilter authorizationEndpointFilter = new OAuth2AuthorizationEndpointFilter( - authenticationManager, ProtocolConstants.OidcEndpointConstants.AUTHORIZATION_ENDPOINT); - List authenticationConverters = createDefaultAuthenticationConverters(); - if (!this.authorizationRequestConverters.isEmpty()) { - authenticationConverters.addAll(0, this.authorizationRequestConverters); - } - this.authorizationRequestConvertersConsumer.accept(authenticationConverters); - authorizationEndpointFilter.setAuthenticationConverter( - new DelegatingAuthenticationConverter(authenticationConverters)); - if (this.authorizationResponseHandler != null) { - authorizationEndpointFilter - .setAuthenticationSuccessHandler(this.authorizationResponseHandler); - } - if (this.errorResponseHandler != null) { - authorizationEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler); - } - if (StringUtils.hasText(this.consentPage)) { - authorizationEndpointFilter.setConsentPage(this.consentPage); - } - httpSecurity.addFilterAfter(postProcess(authorizationEndpointFilter), - EiamOAuth2AuthorizationImplicitAuthenticationEndpointFilter.class); - } - - @Override - RequestMatcher getRequestMatcher() { - return this.requestMatcher; - } - - /** - * 创建默认身份验证转换器 - * - * @return {@link List} - */ - private static List createDefaultAuthenticationConverters() { - List authenticationConverters = new ArrayList<>(); - //授权码模式请求转换器 - authenticationConverters.add(new OAuth2AuthorizationCodeRequestAuthenticationConverter()); - //OAuth2授权同意认证转换器 - authenticationConverters.add(new OAuth2AuthorizationConsentAuthenticationConverter()); - return authenticationConverters; - } - - private List createDefaultAuthenticationProviders(HttpSecurity httpSecurity) { - List authenticationProviders = new ArrayList<>(); - //OAuth2授权码请求身份验证程序 - OAuth2AuthorizationCodeRequestAuthenticationProvider authorizationCodeRequestAuthenticationProvider = new OAuth2AuthorizationCodeRequestAuthenticationProvider( - OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), - OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity), - OAuth2ConfigurerUtils.getAuthorizationConsentService(httpSecurity)); - if (this.authorizationCodeRequestAuthenticationValidator != null) { - authorizationCodeRequestAuthenticationProvider.setAuthenticationValidator( - new OAuth2AuthorizationCodeRequestAuthenticationValidator() - .andThen(this.authorizationCodeRequestAuthenticationValidator)); - } - authenticationProviders.add(authorizationCodeRequestAuthenticationProvider); - - //OAuth2授权码同意身份验证提供程序 - OAuth2AuthorizationConsentAuthenticationProvider authorizationConsentAuthenticationProvider = new OAuth2AuthorizationConsentAuthenticationProvider( - OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), - OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity), - OAuth2ConfigurerUtils.getAuthorizationConsentService(httpSecurity)); - authenticationProviders.add(authorizationConsentAuthenticationProvider); - return authenticationProviders; - } - -} diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2AuthorizationImplicitEndpointConfigurer.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2AuthorizationImplicitEndpointConfigurer.java index cbbe4eb1..38a3ab07 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2AuthorizationImplicitEndpointConfigurer.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2AuthorizationImplicitEndpointConfigurer.java @@ -21,33 +21,25 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -import javax.servlet.http.HttpServletRequest; - import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2Token; -import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse; -import org.springframework.security.oauth2.server.authorization.authentication.*; -import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings; import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator; import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter; import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; -import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; -import cn.topiam.employee.common.constants.ProtocolConstants; +import cn.topiam.employee.protocol.oidc.authentication.consent.EiamOAuth2AuthorizationConsentEndpointFilter; import cn.topiam.employee.protocol.oidc.authentication.implicit.*; import cn.topiam.employee.protocol.oidc.util.EiamOAuth2Utils; +import static cn.topiam.employee.common.constants.ProtocolConstants.OidcEndpointConstants.AUTHORIZATION_ENDPOINT; /** * OAuth2 授权码端点配置 @@ -68,10 +60,11 @@ public final class EiamOAuth2AuthorizationImplicitEndpointConfigurer extends }; private AuthenticationSuccessHandler authorizationResponseHandler; private AuthenticationFailureHandler errorResponseHandler; - private String consentPage; private Consumer authorizationImplicitRequestAuthenticationContextConsumer; + private String consentPage; + /** * Restrict for internal use only. */ @@ -79,123 +72,6 @@ public final class EiamOAuth2AuthorizationImplicitEndpointConfigurer extends super(objectPostProcessor); } - /** - * Adds an {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest} - * to an instance of {@link OAuth2AuthorizationCodeRequestAuthenticationToken} or {@link OAuth2AuthorizationConsentAuthenticationToken} - * used for authenticating the request. - * - * @param authorizationRequestConverter an {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest} - * @return the {@link EiamOAuth2AuthorizationImplicitEndpointConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationImplicitEndpointConfigurer authorizationRequestConverter(AuthenticationConverter authorizationRequestConverter) { - Assert.notNull(authorizationRequestConverter, - "authorizationRequestConverter cannot be null"); - this.authorizationRequestConverters.add(authorizationRequestConverter); - return this; - } - - /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authorizationRequestConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param authorizationRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s - * @return the {@link EiamOAuth2AuthorizationImplicitEndpointConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOAuth2AuthorizationImplicitEndpointConfigurer authorizationRequestConverters(Consumer> authorizationRequestConvertersConsumer) { - Assert.notNull(authorizationRequestConvertersConsumer, - "authorizationRequestConvertersConsumer cannot be null"); - this.authorizationRequestConvertersConsumer = authorizationRequestConvertersConsumer; - return this; - } - - /** - * Adds an {@link AuthenticationProvider} used for authenticating an {@link OAuth2AuthorizationCodeRequestAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} - * @return the {@link EiamOAuth2AuthorizationImplicitEndpointConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationImplicitEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) { - Assert.notNull(authenticationProvider, "authenticationProvider cannot be null"); - this.authenticationProviders.add(authenticationProvider); - return this; - } - - /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s - * @return the {@link EiamOAuth2AuthorizationImplicitEndpointConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOAuth2AuthorizationImplicitEndpointConfigurer authenticationProviders(Consumer> authenticationProvidersConsumer) { - Assert.notNull(authenticationProvidersConsumer, - "authenticationProvidersConsumer cannot be null"); - this.authenticationProvidersConsumer = authenticationProvidersConsumer; - return this; - } - - /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} - * and returning the {@link OAuth2AuthorizationResponse Authorization Response}. - * - * @param authorizationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} - * @return the {@link EiamOAuth2AuthorizationImplicitEndpointConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationImplicitEndpointConfigurer authorizationResponseHandler(AuthenticationSuccessHandler authorizationResponseHandler) { - this.authorizationResponseHandler = authorizationResponseHandler; - return this; - } - - /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException} - * @return the {@link EiamOAuth2AuthorizationImplicitEndpointConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationImplicitEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) { - this.errorResponseHandler = errorResponseHandler; - return this; - } - - /** - * Specify the URI to redirect Resource Owners to if consent is required during - * the {@code authorization_code} flow. A default consent page will be generated when - * this attribute is not specified. - * - * If a URI is specified, applications are required to process the specified URI to generate - * a consent page. The query string will contain the following parameters: - * - *
    - *
  • {@code client_id} - the client identifier
  • - *
  • {@code scope} - a space-delimited list of scopes present in the authorization request
  • - *
  • {@code state} - a CSRF protection token
  • - *
- * - * In general, the consent page should create a form that submits - * a request with the following requirements: - * - *
    - *
  • It must be an HTTP POST
  • - *
  • It must be submitted to {@link AuthorizationServerSettings#getAuthorizationEndpoint()}
  • - *
  • It must include the received {@code client_id} as an HTTP parameter
  • - *
  • It must include the received {@code state} as an HTTP parameter
  • - *
  • It must include the list of {@code scope}s the {@code Resource Owner} - * consented to as an HTTP parameter
  • - *
- * - * @param consentPage the URI of the custom consent page to redirect to if consent is required (e.g. "/oauth2/consent") - * @return the {@link EiamOAuth2AuthorizationImplicitEndpointConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationImplicitEndpointConfigurer consentPage(String consentPage) { - this.consentPage = consentPage; - return this; - } - void addAuthorizationImplicitRequestAuthenticationValidator(Consumer authenticationValidator) { this.authorizationImplicitRequestAuthenticationContextConsumer = this.authorizationImplicitRequestAuthenticationContextConsumer == null ? authenticationValidator @@ -205,11 +81,9 @@ public final class EiamOAuth2AuthorizationImplicitEndpointConfigurer extends @Override void init(HttpSecurity httpSecurity) { - this.requestMatcher = new OrRequestMatcher(new AntPathRequestMatcher( - ProtocolConstants.OidcEndpointConstants.AUTHORIZATION_ENDPOINT, HttpMethod.GET.name()), - new AntPathRequestMatcher( - ProtocolConstants.OidcEndpointConstants.AUTHORIZATION_ENDPOINT, - HttpMethod.POST.name())); + this.requestMatcher = new OrRequestMatcher( + new AntPathRequestMatcher(AUTHORIZATION_ENDPOINT, HttpMethod.GET.name()), + new AntPathRequestMatcher(AUTHORIZATION_ENDPOINT, HttpMethod.POST.name())); List authenticationProviders = createDefaultAuthenticationProviders( httpSecurity); @@ -226,7 +100,7 @@ public final class EiamOAuth2AuthorizationImplicitEndpointConfigurer extends AuthenticationManager authenticationManager = httpSecurity .getSharedObject(AuthenticationManager.class); EiamOAuth2AuthorizationImplicitAuthenticationEndpointFilter authorizationEndpointFilter = new EiamOAuth2AuthorizationImplicitAuthenticationEndpointFilter( - authenticationManager, ProtocolConstants.OidcEndpointConstants.AUTHORIZATION_ENDPOINT); + authenticationManager, AUTHORIZATION_ENDPOINT); List authenticationConverters = createDefaultAuthenticationConverters(); if (!this.authorizationRequestConverters.isEmpty()) { authenticationConverters.addAll(0, this.authorizationRequestConverters); @@ -234,18 +108,12 @@ public final class EiamOAuth2AuthorizationImplicitEndpointConfigurer extends this.authorizationRequestConvertersConsumer.accept(authenticationConverters); authorizationEndpointFilter.setAuthenticationConverter( new DelegatingAuthenticationConverter(authenticationConverters)); - if (this.authorizationResponseHandler != null) { - authorizationEndpointFilter - .setAuthenticationSuccessHandler(this.authorizationResponseHandler); - } - if (this.errorResponseHandler != null) { - authorizationEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler); - } - if (StringUtils.hasText(this.consentPage)) { - authorizationEndpointFilter.setConsentPage(this.consentPage); + //确认请求页面地址 + if (this.consentPage != null) { + authorizationEndpointFilter.setConsentPage(consentPage); } httpSecurity.addFilterAfter(postProcess(authorizationEndpointFilter), - AbstractPreAuthenticatedProcessingFilter.class); + EiamOAuth2AuthorizationConsentEndpointFilter.class); } @Override @@ -264,7 +132,7 @@ public final class EiamOAuth2AuthorizationImplicitEndpointConfigurer extends authenticationConverters.add(new EiamOAuth2AuthenticationImplicitAuthenticationConverter()); //OAuth2授权同意认证转换器 authenticationConverters - .add(new OAuth2AuthorizationImplicitConsentAuthenticationConverter()); + .add(new EiamOAuth2AuthorizationImplicitConsentAuthenticationConverter()); return authenticationConverters; } @@ -293,4 +161,9 @@ public final class EiamOAuth2AuthorizationImplicitEndpointConfigurer extends return authenticationProviders; } + public EiamOAuth2AuthorizationImplicitEndpointConfigurer consentPage(String consentPage) { + this.consentPage = consentPage; + return this; + } + } diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2AuthorizationServerConfigurer.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2AuthorizationServerConfigurer.java index 39d2b090..72f88fe7 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2AuthorizationServerConfigurer.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2AuthorizationServerConfigurer.java @@ -17,8 +17,19 @@ */ package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers; -import java.util.*; +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.security.authentication.AuthenticationManager; @@ -26,38 +37,37 @@ import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer; +import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2ErrorCodes; -import org.springframework.security.oauth2.core.OAuth2Token; import org.springframework.security.oauth2.core.oidc.OidcScopes; -import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService; -import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; -import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings; -import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator; import org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter; import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter; -import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter; -import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeAuthenticationConverter; -import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientCredentialsAuthenticationConverter; -import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2RefreshTokenAuthenticationConverter; +import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.HttpStatusEntryPoint; import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; import org.springframework.security.web.context.SecurityContextHolderFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.util.Assert; import cn.topiam.employee.common.constants.ProtocolConstants; +import cn.topiam.employee.core.context.ServerContextHelp; +import cn.topiam.employee.core.security.savedredirect.HttpSessionRedirectCache; +import cn.topiam.employee.core.security.savedredirect.RedirectCache; import cn.topiam.employee.protocol.oidc.authentication.EiamOAuth2InitSingleSignOnEndpointFilter; import cn.topiam.employee.protocol.oidc.authentication.EiamOidcAuthorizationServerContextFilter; -import cn.topiam.employee.protocol.oidc.authentication.password.EiamOAuth2AuthorizationPasswordAuthenticationConverter; -import cn.topiam.employee.protocol.oidc.handler.PortalOAuth2AuthenticationEntryPoint; import cn.topiam.employee.protocol.oidc.util.EiamOAuth2Utils; +import cn.topiam.employee.support.result.ApiRestResult; +import cn.topiam.employee.support.util.HttpResponseUtils; +import static org.springframework.http.HttpStatus.UNAUTHORIZED; + +import static cn.topiam.employee.common.constants.AuthorizeConstants.FE_LOGIN; import static cn.topiam.employee.protocol.oidc.util.EiamOAuth2Utils.getAppOidcConfigRepository; +import static cn.topiam.employee.support.context.ServletContextHelp.acceptIncludeTextHtml; /** * An {@link AbstractHttpConfigurer} for OAuth 2.0 Authorization Server support. @@ -65,137 +75,13 @@ import static cn.topiam.employee.protocol.oidc.util.EiamOAuth2Utils.getAppOidcCo * @author TopIAM * Created by support@topiam.cn on 2022/10/26 19:32 */ -@SuppressWarnings("AlibabaClassNamingShouldBeCamel") +@SuppressWarnings({ "AlibabaClassNamingShouldBeCamel", "DuplicatedCode" }) public final class EiamOAuth2AuthorizationServerConfigurer extends AbstractHttpConfigurer { private final Map, AbstractOAuth2Configurer> configurers = createConfigurers(); private RequestMatcher endpointsMatcher; - /** - * Sets the repository of registered clients. - * - * @param registeredClientRepository the repository of registered clients - * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationServerConfigurer registeredClientRepository(RegisteredClientRepository registeredClientRepository) { - Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null"); - getBuilder().setSharedObject(RegisteredClientRepository.class, registeredClientRepository); - return this; - } - - /** - * Sets the authorization service. - * - * @param authorizationService the authorization service - * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationServerConfigurer authorizationService(OAuth2AuthorizationService authorizationService) { - Assert.notNull(authorizationService, "authorizationService cannot be null"); - getBuilder().setSharedObject(OAuth2AuthorizationService.class, authorizationService); - return this; - } - - /** - * Sets the authorization consent service. - * - * @param authorizationConsentService the authorization consent service - * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationServerConfigurer authorizationConsentService(OAuth2AuthorizationConsentService authorizationConsentService) { - Assert.notNull(authorizationConsentService, "authorizationConsentService cannot be null"); - getBuilder().setSharedObject(OAuth2AuthorizationConsentService.class, - authorizationConsentService); - return this; - } - - /** - * Sets the authorization server settings. - * - * @param authorizationServerSettings the authorization server settings - * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationServerConfigurer authorizationServerSettings(AuthorizationServerSettings authorizationServerSettings) { - Assert.notNull(authorizationServerSettings, "authorizationServerSettings cannot be null"); - getBuilder().setSharedObject(AuthorizationServerSettings.class, - authorizationServerSettings); - return this; - } - - /** - * Sets the token generator. - * - * @param tokenGenerator the token generator - * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration - * @since 0.2.3 - */ - public EiamOAuth2AuthorizationServerConfigurer tokenGenerator(OAuth2TokenGenerator tokenGenerator) { - Assert.notNull(tokenGenerator, "tokenGenerator cannot be null"); - getBuilder().setSharedObject(OAuth2TokenGenerator.class, tokenGenerator); - return this; - } - - /** - * Configures OAuth 2.0 Client Authentication. - * - * @param clientAuthenticationCustomizer the {@link Customizer} providing access to the {@link OAuth2ClientAuthenticationConfigurer} - * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationServerConfigurer clientAuthentication(Customizer clientAuthenticationCustomizer) { - clientAuthenticationCustomizer - .customize(getConfigurer(OAuth2ClientAuthenticationConfigurer.class)); - return this; - } - - /** - * Configures the OAuth 2.0 Authorization Endpoint. - * - * @param authorizationEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2AuthorizationEndpointConfigurer} - * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationServerConfigurer authorizationEndpoint(Customizer authorizationEndpointCustomizer) { - authorizationEndpointCustomizer - .customize(getConfigurer(OAuth2AuthorizationEndpointConfigurer.class)); - return this; - } - - /** - * Configures the OAuth 2.0 Token Endpoint. - * - * @param tokenEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2TokenEndpointConfigurer} - * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration - */ - public EiamOAuth2AuthorizationServerConfigurer tokenEndpoint(Customizer tokenEndpointCustomizer) { - tokenEndpointCustomizer.customize(getConfigurer(OAuth2TokenEndpointConfigurer.class)); - return this; - } - - /** - * Configures the OAuth 2.0 Token Introspection Endpoint. - * - * @param tokenIntrospectionEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2TokenIntrospectionEndpointConfigurer} - * @return the {@link EiamOAuth2AuthorizationServerConfigurer} for further configuration - * @since 0.2.3 - */ - public EiamOAuth2AuthorizationServerConfigurer tokenIntrospectionEndpoint(Customizer tokenIntrospectionEndpointCustomizer) { - tokenIntrospectionEndpointCustomizer - .customize(getConfigurer(OAuth2TokenIntrospectionEndpointConfigurer.class)); - return this; - } - - /** - * Configures the OAuth 2.0 Token Revocation Endpoint. - * - * @param tokenRevocationEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2TokenRevocationEndpointConfigurer} - * @return the {@link EiamOAuth2AuthorizationServerConfigurer} for further configuration - * @since 0.2.2 - */ - public EiamOAuth2AuthorizationServerConfigurer tokenRevocationEndpoint(Customizer tokenRevocationEndpointCustomizer) { - tokenRevocationEndpointCustomizer - .customize(getConfigurer(OAuth2TokenRevocationEndpointConfigurer.class)); - return this; - } - /** * Configures OpenID Connect 1.0 support (disabled by default). * @@ -205,7 +91,8 @@ public final class EiamOAuth2AuthorizationServerConfigurer extends public EiamOAuth2AuthorizationServerConfigurer oidc(Customizer oidcCustomizer) { EiamOidcConfigurer oidcConfigurer = getConfigurer(EiamOidcConfigurer.class); if (oidcConfigurer == null) { - addConfigurer(EiamOidcConfigurer.class, new EiamOidcConfigurer(this::postProcess)); + this.configurers.put(EiamOidcConfigurer.class, + new EiamOidcConfigurer(this::postProcess)); oidcConfigurer = getConfigurer(EiamOidcConfigurer.class); } oidcCustomizer.customize(oidcConfigurer); @@ -230,8 +117,8 @@ public final class EiamOAuth2AuthorizationServerConfigurer extends if (oidcConfigurer == null) { // OpenID Connect is disabled. // Add an authentication validator that rejects authentication requests. - EiamOAuth2AuthorizationEndpointConfigurer authorizationEndpointConfigurer = - getConfigurer(EiamOAuth2AuthorizationEndpointConfigurer.class); + EiamOAuth2AuthorizationCodeEndpointConfigurer authorizationEndpointConfigurer = + getConfigurer(EiamOAuth2AuthorizationCodeEndpointConfigurer.class); //添加授权码请求身份验证验证器 authorizationEndpointConfigurer.addAuthorizationCodeRequestAuthenticationValidator((authenticationContext) -> { OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = @@ -264,7 +151,7 @@ public final class EiamOAuth2AuthorizationServerConfigurer extends //配置 this.configurers.values().forEach(configurer -> { configurer.init(httpSecurity); - configurer.init(httpSecurity); + //添加 RequestMatchers requestMatchers.add(configurer.getRequestMatcher()); }); requestMatchers.add(new AntPathRequestMatcher(ProtocolConstants.OidcEndpointConstants.JWK_SET_ENDPOINT, HttpMethod.GET.name())); @@ -274,8 +161,8 @@ public final class EiamOAuth2AuthorizationServerConfigurer extends if (exceptionHandling != null) { //身份验证入口点 exceptionHandling.defaultAuthenticationEntryPointFor( - new PortalOAuth2AuthenticationEntryPoint(), - new OrRequestMatcher(getRequestMatcher(EiamOAuth2AuthorizationEndpointConfigurer.class)) + authenticationEntryPoint, + new OrRequestMatcher(getRequestMatcher(EiamOAuth2AuthorizationCodeEndpointConfigurer.class)) ); exceptionHandling.defaultAuthenticationEntryPointFor( new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), @@ -309,21 +196,22 @@ public final class EiamOAuth2AuthorizationServerConfigurer extends Map, AbstractOAuth2Configurer> configurers = new LinkedHashMap<>(); configurers.put(EiamOAuth2ClientAuthenticationConfigurer.class, new EiamOAuth2ClientAuthenticationConfigurer(this::postProcess)); + //OAuth2 同意端点配置器 + String consentEndpoint = ProtocolConstants.OidcEndpointConstants.AUTHORIZATION_CONSENT_ENDPOINT; + EiamOAuth2ConsentEndpointConfigurer consentEndpointConfigurer = new EiamOAuth2ConsentEndpointConfigurer(this::postProcess); + consentEndpointConfigurer.consentPage(consentEndpoint); + configurers.put(EiamOAuth2ConsentEndpointConfigurer.class,consentEndpointConfigurer); //OAuth2 隐式模式端点配置器 - configurers.put(EiamOAuth2AuthorizationImplicitEndpointConfigurer.class,new EiamOAuth2AuthorizationImplicitEndpointConfigurer(this::postProcess) ); + EiamOAuth2AuthorizationImplicitEndpointConfigurer implicitEndpointConfigurer = new EiamOAuth2AuthorizationImplicitEndpointConfigurer(this::postProcess); + implicitEndpointConfigurer.consentPage(consentEndpoint); + configurers.put(EiamOAuth2AuthorizationImplicitEndpointConfigurer.class,implicitEndpointConfigurer); //OAuth2 授权码端点配置器 - configurers.put(EiamOAuth2AuthorizationEndpointConfigurer.class,new EiamOAuth2AuthorizationEndpointConfigurer(this::postProcess) ); + EiamOAuth2AuthorizationCodeEndpointConfigurer codeEndpointConfigurer = new EiamOAuth2AuthorizationCodeEndpointConfigurer(this::postProcess); + codeEndpointConfigurer.consentPage(consentEndpoint); + configurers.put(EiamOAuth2AuthorizationCodeEndpointConfigurer.class,codeEndpointConfigurer); //token端点配置器 - EiamOAuth2TokenEndpointConfigurer configurer = new EiamOAuth2TokenEndpointConfigurer(this::postProcess); - DelegatingAuthenticationConverter authenticationConverter = new DelegatingAuthenticationConverter( - Arrays.asList( - //密码模式认证转换器 - new EiamOAuth2AuthorizationPasswordAuthenticationConverter(), - new OAuth2AuthorizationCodeAuthenticationConverter(), - new OAuth2RefreshTokenAuthenticationConverter(), - new OAuth2ClientCredentialsAuthenticationConverter())); - configurer.accessTokenRequestConverter(authenticationConverter); - configurers.put(EiamOAuth2TokenEndpointConfigurer.class, configurer); + EiamOAuth2TokenEndpointConfigurer tokenEndpointConfigurer = new EiamOAuth2TokenEndpointConfigurer(this::postProcess); + configurers.put(EiamOAuth2TokenEndpointConfigurer.class, tokenEndpointConfigurer); configurers.put(EiamOAuth2TokenIntrospectionEndpointConfigurer.class, new EiamOAuth2TokenIntrospectionEndpointConfigurer(this::postProcess)); configurers.put(EiamOAuth2TokenRevocationEndpointConfigurer.class, new EiamOAuth2TokenRevocationEndpointConfigurer(this::postProcess)); //@formatter:no @@ -335,11 +223,40 @@ public final class EiamOAuth2AuthorizationServerConfigurer extends return (T) this.configurers.get(type); } - private void addConfigurer(Class configurerType, T configurer) { - this.configurers.put(configurerType, configurer); - } private RequestMatcher getRequestMatcher(Class configurerType) { return getConfigurer(configurerType).getRequestMatcher(); } + private final AuthenticationEntryPoint authenticationEntryPoint= new AuthenticationEntryPoint() { + /** + * 日志 + */ + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final RedirectCache redirectCache = new HttpSessionRedirectCache(); + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + logger.info("----------------------------------------------------------"); + logger.info("未登录, 或登录过期"); + //记录 + redirectCache.saveRedirect(request, response, RedirectCache.RedirectType.REQUEST); + //判断请求 + boolean isTextHtml = acceptIncludeTextHtml(request); + //JSON + if (!isTextHtml) { + ApiRestResult result = ApiRestResult.builder() + .status(String.valueOf(UNAUTHORIZED.value())).message(StringUtils + .defaultString(authException.getMessage(), UNAUTHORIZED.getReasonPhrase())) + .build(); + HttpResponseUtils.flushResponseJson(response, UNAUTHORIZED.value(), result); + } + // HTML + else { + //跳转前端SESSION过期路由 + response.sendRedirect(ServerContextHelp.getPortalPublicBaseUrl() + FE_LOGIN); + } + logger.info("----------------------------------------------------------"); + } + }; } diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2ClientAuthenticationConfigurer.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2ClientAuthenticationConfigurer.java index 2a76cff7..c5aa8fd0 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2ClientAuthenticationConfigurer.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2ClientAuthenticationConfigurer.java @@ -21,32 +21,24 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -import javax.servlet.http.HttpServletRequest; - import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.core.context.SecurityContext; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; import org.springframework.security.oauth2.server.authorization.authentication.ClientSecretAuthenticationProvider; import org.springframework.security.oauth2.server.authorization.authentication.JwtClientAssertionAuthenticationProvider; -import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken; import org.springframework.security.oauth2.server.authorization.authentication.PublicClientAuthenticationProvider; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; import org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter; import org.springframework.security.oauth2.server.authorization.web.authentication.*; import org.springframework.security.web.authentication.AuthenticationConverter; -import org.springframework.security.web.authentication.AuthenticationFailureHandler; -import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.util.Assert; import cn.topiam.employee.common.constants.ProtocolConstants; @@ -65,8 +57,6 @@ public final class EiamOAuth2ClientAuthenticationConfigurer extends AbstractOAut private final List authenticationProviders = new ArrayList<>(); private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { }; - private AuthenticationSuccessHandler authenticationSuccessHandler; - private AuthenticationFailureHandler errorResponseHandler; /** * Restrict for internal use only. @@ -75,87 +65,6 @@ public final class EiamOAuth2ClientAuthenticationConfigurer extends AbstractOAut super(objectPostProcessor); } - /** - * Adds an {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest} - * to an instance of {@link OAuth2ClientAuthenticationToken} used for authenticating the client. - * - * @param authenticationConverter an {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest} - * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration - */ - public EiamOAuth2ClientAuthenticationConfigurer authenticationConverter(AuthenticationConverter authenticationConverter) { - Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); - this.authenticationConverters.add(authenticationConverter); - return this; - } - - /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param authenticationConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s - * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOAuth2ClientAuthenticationConfigurer authenticationConverters(Consumer> authenticationConvertersConsumer) { - Assert.notNull(authenticationConvertersConsumer, - "authenticationConvertersConsumer cannot be null"); - this.authenticationConvertersConsumer = authenticationConvertersConsumer; - return this; - } - - /** - * Adds an {@link AuthenticationProvider} used for authenticating an {@link OAuth2ClientAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OAuth2ClientAuthenticationToken} - * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration - */ - public EiamOAuth2ClientAuthenticationConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) { - Assert.notNull(authenticationProvider, "authenticationProvider cannot be null"); - this.authenticationProviders.add(authenticationProvider); - return this; - } - - /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s - * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOAuth2ClientAuthenticationConfigurer authenticationProviders(Consumer> authenticationProvidersConsumer) { - Assert.notNull(authenticationProvidersConsumer, - "authenticationProvidersConsumer cannot be null"); - this.authenticationProvidersConsumer = authenticationProvidersConsumer; - return this; - } - - /** - * Sets the {@link AuthenticationSuccessHandler} used for handling a successful client authentication - * and associating the {@link OAuth2ClientAuthenticationToken} to the {@link SecurityContext}. - * - * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling a successful client authentication - * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration - */ - public EiamOAuth2ClientAuthenticationConfigurer authenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) { - this.authenticationSuccessHandler = authenticationSuccessHandler; - return this; - } - - /** - * Sets the {@link AuthenticationFailureHandler} used for handling a failed client authentication - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling a failed client authentication - * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration - */ - public EiamOAuth2ClientAuthenticationConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) { - this.errorResponseHandler = errorResponseHandler; - return this; - } - @Override void init(HttpSecurity httpSecurity) { this.requestMatcher = new OrRequestMatcher( @@ -192,14 +101,6 @@ public final class EiamOAuth2ClientAuthenticationConfigurer extends AbstractOAut this.authenticationConvertersConsumer.accept(authenticationConverters); clientAuthenticationFilter.setAuthenticationConverter( new DelegatingAuthenticationConverter(authenticationConverters)); - if (this.authenticationSuccessHandler != null) { - clientAuthenticationFilter - .setAuthenticationSuccessHandler(this.authenticationSuccessHandler); - } - if (this.errorResponseHandler != null) { - clientAuthenticationFilter.setAuthenticationFailureHandler(this.errorResponseHandler); - } - builder.addFilterAfter(postProcess(clientAuthenticationFilter), AbstractPreAuthenticatedProcessingFilter.class); } diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2ConsentEndpointConfigurer.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2ConsentEndpointConfigurer.java new file mode 100644 index 00000000..fbca5e04 --- /dev/null +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2ConsentEndpointConfigurer.java @@ -0,0 +1,87 @@ +/* + * eiam-protocol-oidc - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers; + +import java.util.Objects; +import java.util.function.Consumer; + +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.ObjectPostProcessor; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.oauth2.server.authorization.oidc.OidcProviderConfiguration; +import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; + +import cn.topiam.employee.common.constants.ProtocolConstants; +import cn.topiam.employee.protocol.oidc.authentication.consent.EiamOAuth2AuthorizationConsentEndpointFilter; +import cn.topiam.employee.protocol.oidc.util.EiamOAuth2Utils; + +/** + * 提供商端点适配器 + * + * @author TopIAM + * Created by support@topiam.cn on 2022/11/9 22:53 + */ +@SuppressWarnings("AlibabaClassNamingShouldBeCamel") +public final class EiamOAuth2ConsentEndpointConfigurer extends AbstractOAuth2Configurer { + private RequestMatcher requestMatcher; + private Consumer defaultProviderConfigurationCustomizer; + + private String consentPage; + + /** + * Restrict for internal use only. + */ + EiamOAuth2ConsentEndpointConfigurer(ObjectPostProcessor objectPostProcessor) { + super(objectPostProcessor); + } + + void addDefaultProviderConfigurationCustomizer(Consumer defaultProviderConfigurationCustomizer) { + this.defaultProviderConfigurationCustomizer = this.defaultProviderConfigurationCustomizer == null + ? defaultProviderConfigurationCustomizer + : this.defaultProviderConfigurationCustomizer + .andThen(defaultProviderConfigurationCustomizer); + } + + @Override + void init(HttpSecurity httpSecurity) { + this.requestMatcher = new AntPathRequestMatcher( + Objects.requireNonNullElse(consentPage, + ProtocolConstants.OidcEndpointConstants.AUTHORIZATION_CONSENT_ENDPOINT), + HttpMethod.GET.name()); + } + + @Override + void configure(HttpSecurity httpSecurity) { + EiamOAuth2AuthorizationConsentEndpointFilter consentEndpointFilter = new EiamOAuth2AuthorizationConsentEndpointFilter( + EiamOAuth2Utils.getAppOidcConfigRepository(httpSecurity), this.requestMatcher); + httpSecurity.addFilterAfter(postProcess(consentEndpointFilter), + AbstractPreAuthenticatedProcessingFilter.class); + } + + @Override + RequestMatcher getRequestMatcher() { + return this.requestMatcher; + } + + public void consentPage(String consentPage) { + this.consentPage = consentPage; + } + +} diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2TokenEndpointConfigurer.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2TokenEndpointConfigurer.java index d74c8749..e20be07e 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2TokenEndpointConfigurer.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2TokenEndpointConfigurer.java @@ -17,25 +17,35 @@ */ package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers; +import java.io.IOException; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.function.Consumer; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.OAuth2Token; +import org.springframework.security.oauth2.core.*; import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; +import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter; +import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; -import org.springframework.security.oauth2.server.authorization.authentication.*; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationProvider; import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator; import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter; import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter; @@ -48,11 +58,25 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import com.google.common.collect.Lists; + +import cn.topiam.employee.application.context.ApplicationContext; +import cn.topiam.employee.application.context.ApplicationContextHolder; +import cn.topiam.employee.audit.context.AuditContext; +import cn.topiam.employee.audit.entity.Target; +import cn.topiam.employee.audit.enums.EventStatus; +import cn.topiam.employee.audit.enums.EventType; +import cn.topiam.employee.audit.enums.TargetType; +import cn.topiam.employee.audit.event.AuditEventPublish; import cn.topiam.employee.common.constants.ProtocolConstants; +import cn.topiam.employee.protocol.oidc.authentication.authentication.EiamOAuth2AuthorizationCodeAuthenticationProvider; +import cn.topiam.employee.protocol.oidc.authentication.authentication.EiamOAuth2RefreshTokenAuthenticationProvider; +import cn.topiam.employee.protocol.oidc.authentication.password.EiamOAuth2AuthorizationPasswordAuthenticationConverter; import cn.topiam.employee.protocol.oidc.authentication.password.EiamOAuth2AuthorizationPasswordAuthenticationProvider; import cn.topiam.employee.protocol.oidc.util.EiamOAuth2Utils; +import cn.topiam.employee.support.context.ApplicationContextHelp; /** * 配置OAuth2 token端点 @@ -62,14 +86,11 @@ import cn.topiam.employee.protocol.oidc.util.EiamOAuth2Utils; @SuppressWarnings("AlibabaClassNamingShouldBeCamel") public final class EiamOAuth2TokenEndpointConfigurer extends AbstractOAuth2Configurer { private RequestMatcher requestMatcher; - private final List accessTokenRequestConverters = new ArrayList<>(); private Consumer> accessTokenRequestConvertersConsumer = (accessTokenRequestConverters) -> { }; private final List authenticationProviders = new ArrayList<>(); private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { }; - private AuthenticationSuccessHandler accessTokenResponseHandler; - private AuthenticationFailureHandler errorResponseHandler; /** * Restrict for internal use only. @@ -78,87 +99,6 @@ public final class EiamOAuth2TokenEndpointConfigurer extends AbstractOAuth2Confi super(objectPostProcessor); } - /** - * Adds an {@link AuthenticationConverter} used when attempting to extract an Access Token Request from {@link HttpServletRequest} - * to an instance of {@link OAuth2AuthorizationGrantAuthenticationToken} used for authenticating the authorization grant. - * - * @param accessTokenRequestConverter an {@link AuthenticationConverter} used when attempting to extract an Access Token Request from {@link HttpServletRequest} - * @return the {@link OAuth2TokenEndpointConfigurer} for further configuration - */ - public EiamOAuth2TokenEndpointConfigurer accessTokenRequestConverter(AuthenticationConverter accessTokenRequestConverter) { - Assert.notNull(accessTokenRequestConverter, "accessTokenRequestConverter cannot be null"); - this.accessTokenRequestConverters.add(accessTokenRequestConverter); - return this; - } - - /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #accessTokenRequestConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param accessTokenRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s - * @return the {@link EiamOAuth2TokenEndpointConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOAuth2TokenEndpointConfigurer accessTokenRequestConverters(Consumer> accessTokenRequestConvertersConsumer) { - Assert.notNull(accessTokenRequestConvertersConsumer, - "accessTokenRequestConvertersConsumer cannot be null"); - this.accessTokenRequestConvertersConsumer = accessTokenRequestConvertersConsumer; - return this; - } - - /** - * Adds an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2AuthorizationGrantAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2AuthorizationGrantAuthenticationToken} - * @return the {@link EiamOAuth2TokenEndpointConfigurer} for further configuration - */ - public EiamOAuth2TokenEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) { - Assert.notNull(authenticationProvider, "authenticationProvider cannot be null"); - this.authenticationProviders.add(authenticationProvider); - return this; - } - - /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s - * @return the {@link EiamOAuth2TokenEndpointConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOAuth2TokenEndpointConfigurer authenticationProviders(Consumer> authenticationProvidersConsumer) { - Assert.notNull(authenticationProvidersConsumer, - "authenticationProvidersConsumer cannot be null"); - this.authenticationProvidersConsumer = authenticationProvidersConsumer; - return this; - } - - /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AccessTokenAuthenticationToken} - * and returning the {@link OAuth2AccessTokenResponse Access Token Response}. - * - * @param accessTokenResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AccessTokenAuthenticationToken} - * @return the {@link EiamOAuth2TokenEndpointConfigurer} for further configuration - */ - public EiamOAuth2TokenEndpointConfigurer accessTokenResponseHandler(AuthenticationSuccessHandler accessTokenResponseHandler) { - this.accessTokenResponseHandler = accessTokenResponseHandler; - return this; - } - - /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * @return the {@link EiamOAuth2TokenEndpointConfigurer} for further configuration - */ - public EiamOAuth2TokenEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) { - this.errorResponseHandler = errorResponseHandler; - return this; - } - @Override void init(HttpSecurity httpSecurity) { this.requestMatcher = new AntPathRequestMatcher( @@ -181,19 +121,12 @@ public final class EiamOAuth2TokenEndpointConfigurer extends AbstractOAuth2Confi OAuth2TokenEndpointFilter tokenEndpointFilter = new OAuth2TokenEndpointFilter( authenticationManager, ProtocolConstants.OidcEndpointConstants.TOKEN_ENDPOINT); + tokenEndpointFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler); + tokenEndpointFilter.setAuthenticationFailureHandler(authenticationFailureHandler); List authenticationConverters = createDefaultAuthenticationConverters(); - if (!this.accessTokenRequestConverters.isEmpty()) { - authenticationConverters.addAll(0, this.accessTokenRequestConverters); - } this.accessTokenRequestConvertersConsumer.accept(authenticationConverters); tokenEndpointFilter.setAuthenticationConverter( new DelegatingAuthenticationConverter(authenticationConverters)); - if (this.accessTokenResponseHandler != null) { - tokenEndpointFilter.setAuthenticationSuccessHandler(this.accessTokenResponseHandler); - } - if (this.errorResponseHandler != null) { - tokenEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler); - } httpSecurity.addFilterAfter(postProcess(tokenEndpointFilter), FilterSecurityInterceptor.class); } @@ -209,6 +142,8 @@ public final class EiamOAuth2TokenEndpointConfigurer extends AbstractOAuth2Confi authenticationConverters.add(new OAuth2AuthorizationCodeAuthenticationConverter()); authenticationConverters.add(new OAuth2RefreshTokenAuthenticationConverter()); authenticationConverters.add(new OAuth2ClientCredentialsAuthenticationConverter()); + //密码模式认证转换器 + authenticationConverters.add(new EiamOAuth2AuthorizationPasswordAuthenticationConverter()); return authenticationConverters; } @@ -226,10 +161,10 @@ public final class EiamOAuth2TokenEndpointConfigurer extends AbstractOAuth2Confi OAuth2TokenGenerator tokenGenerator = EiamOAuth2Utils.getTokenGenerator(builder); - OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider(authorizationService, tokenGenerator); + EiamOAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider = new EiamOAuth2AuthorizationCodeAuthenticationProvider(authorizationService, tokenGenerator); authenticationProviders.add(authorizationCodeAuthenticationProvider); - OAuth2RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider = new OAuth2RefreshTokenAuthenticationProvider(authorizationService, tokenGenerator); + EiamOAuth2RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider = new EiamOAuth2RefreshTokenAuthenticationProvider(authorizationService, tokenGenerator); authenticationProviders.add(refreshTokenAuthenticationProvider); OAuth2ClientCredentialsAuthenticationProvider clientCredentialsAuthenticationProvider = new OAuth2ClientCredentialsAuthenticationProvider(authorizationService, tokenGenerator); @@ -246,4 +181,68 @@ public final class EiamOAuth2TokenEndpointConfigurer extends AbstractOAuth2Confi //@formatter:on } + private final AuthenticationSuccessHandler authenticationSuccessHandler = this::sendAccessTokenResponse; + private final AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse; + + private void sendAccessTokenResponse(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws IOException { + + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) authentication; + + OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken(); + OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken(); + Map additionalParameters = accessTokenAuthentication + .getAdditionalParameters(); + + OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse + .withToken(accessToken.getTokenValue()).tokenType(accessToken.getTokenType()) + .scopes(accessToken.getScopes()); + if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) { + builder.expiresIn( + ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt())); + } + if (refreshToken != null) { + builder.refreshToken(refreshToken.getTokenValue()); + } + if (!CollectionUtils.isEmpty(additionalParameters)) { + builder.additionalParameters(additionalParameters); + } + OAuth2AccessTokenResponse accessTokenResponse = builder.build(); + ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); + + //审计 + ApplicationContext applicationContext = ApplicationContextHolder.getApplicationContext(); + Target target = Target.builder().id(applicationContext.getAppId().toString()) + .type(TargetType.APPLICATION).build(); + ArrayList targets = Lists.newArrayList(target); + + AuditEventPublish publish = ApplicationContextHelp.getBean(AuditEventPublish.class); + publish.publish(EventType.APP_SSO, AuditContext.getAuthorization(), EventStatus.SUCCESS, + targets); + + this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse); + } + + private void sendErrorResponse(HttpServletRequest request, HttpServletResponse response, + AuthenticationException exception) throws IOException { + + OAuth2Error error = ((OAuth2AuthenticationException) exception).getError(); + ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); + httpResponse.setStatusCode(HttpStatus.BAD_REQUEST); + + //审计 + ApplicationContext applicationContext = ApplicationContextHolder.getApplicationContext(); + Target target = Target.builder().id(applicationContext.getAppId().toString()) + .type(TargetType.APPLICATION).build(); + ArrayList targets = Lists.newArrayList(target); + + AuditEventPublish publish = ApplicationContextHelp.getBean(AuditEventPublish.class); + publish.publish(EventType.APP_SSO, AuditContext.getAuthorization(), EventStatus.FAIL, + targets, error.toString()); + this.errorHttpResponseConverter.write(error, null, httpResponse); + } + + private final HttpMessageConverter accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter(); + private final HttpMessageConverter errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter(); + } diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2TokenIntrospectionEndpointConfigurer.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2TokenIntrospectionEndpointConfigurer.java index feb1c21e..20b07c8c 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2TokenIntrospectionEndpointConfigurer.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2TokenIntrospectionEndpointConfigurer.java @@ -21,27 +21,19 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -import javax.servlet.http.HttpServletRequest; - import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationProvider; -import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationToken; import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter; import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter; import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2TokenIntrospectionAuthenticationConverter; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; import org.springframework.security.web.authentication.AuthenticationConverter; -import org.springframework.security.web.authentication.AuthenticationFailureHandler; -import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.util.Assert; import cn.topiam.employee.common.constants.ProtocolConstants; @@ -60,8 +52,6 @@ public final class EiamOAuth2TokenIntrospectionEndpointConfigurer extends Abstra }; private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { }; - private AuthenticationSuccessHandler introspectionResponseHandler; - private AuthenticationFailureHandler errorResponseHandler; /** * Restrict for internal use only. @@ -89,87 +79,6 @@ public final class EiamOAuth2TokenIntrospectionEndpointConfigurer extends Abstra return authenticationProviders; } - /** - * Adds an {@link AuthenticationConverter} used when attempting to extract an Introspection Request from {@link HttpServletRequest} - * to an instance of {@link OAuth2TokenIntrospectionAuthenticationToken} used for authenticating the request. - * - * @param introspectionRequestConverter an {@link AuthenticationConverter} used when attempting to extract an Introspection Request from {@link HttpServletRequest} - * @return the {@link EiamOAuth2TokenEndpointConfigurer} for further configuration - */ - public EiamOAuth2TokenIntrospectionEndpointConfigurer introspectionRequestConverter(AuthenticationConverter introspectionRequestConverter) { - Assert.notNull(introspectionRequestConverter, - "introspectionRequestConverter cannot be null"); - this.introspectionRequestConverters.add(introspectionRequestConverter); - return this; - } - - /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #introspectionRequestConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param introspectionRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s - * @return the {@link EiamOAuth2TokenEndpointConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOAuth2TokenIntrospectionEndpointConfigurer introspectionRequestConverters(Consumer> introspectionRequestConvertersConsumer) { - Assert.notNull(introspectionRequestConvertersConsumer, - "introspectionRequestConvertersConsumer cannot be null"); - this.introspectionRequestConvertersConsumer = introspectionRequestConvertersConsumer; - return this; - } - - /** - * Adds an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2TokenIntrospectionAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2TokenIntrospectionAuthenticationToken} - * @return the {@link EiamOAuth2TokenEndpointConfigurer} for further configuration - */ - public EiamOAuth2TokenIntrospectionEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) { - Assert.notNull(authenticationProvider, "authenticationProvider cannot be null"); - this.authenticationProviders.add(authenticationProvider); - return this; - } - - /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s - * @return the {@link EiamOAuth2TokenEndpointConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOAuth2TokenIntrospectionEndpointConfigurer authenticationProviders(Consumer> authenticationProvidersConsumer) { - Assert.notNull(authenticationProvidersConsumer, - "authenticationProvidersConsumer cannot be null"); - this.authenticationProvidersConsumer = authenticationProvidersConsumer; - return this; - } - - /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenIntrospectionAuthenticationToken}. - * - * @param introspectionResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenIntrospectionAuthenticationToken} - * @return the {@link EiamOAuth2TokenEndpointConfigurer} for further configuration - */ - public EiamOAuth2TokenIntrospectionEndpointConfigurer introspectionResponseHandler(AuthenticationSuccessHandler introspectionResponseHandler) { - this.introspectionResponseHandler = introspectionResponseHandler; - return this; - } - - /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * @return the {@link EiamOAuth2TokenEndpointConfigurer} for further configuration - */ - public EiamOAuth2TokenIntrospectionEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) { - this.errorResponseHandler = errorResponseHandler; - return this; - } - @Override void init(HttpSecurity httpSecurity) { this.requestMatcher = new AntPathRequestMatcher( @@ -201,13 +110,6 @@ public final class EiamOAuth2TokenIntrospectionEndpointConfigurer extends Abstra this.introspectionRequestConvertersConsumer.accept(authenticationConverters); introspectionEndpointFilter.setAuthenticationConverter( new DelegatingAuthenticationConverter(authenticationConverters)); - if (this.introspectionResponseHandler != null) { - introspectionEndpointFilter - .setAuthenticationSuccessHandler(this.introspectionResponseHandler); - } - if (this.errorResponseHandler != null) { - introspectionEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler); - } httpSecurity.addFilterAfter(postProcess(introspectionEndpointFilter), FilterSecurityInterceptor.class); } diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2TokenRevocationEndpointConfigurer.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2TokenRevocationEndpointConfigurer.java index c3580cba..75f9f4ad 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2TokenRevocationEndpointConfigurer.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOAuth2TokenRevocationEndpointConfigurer.java @@ -17,21 +17,28 @@ */ package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2Error; +import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationProvider; -import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationToken; import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter; import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter; import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2TokenRevocationAuthenticationConverter; @@ -41,7 +48,6 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.util.Assert; import cn.topiam.employee.common.constants.ProtocolConstants; @@ -60,8 +66,9 @@ public final class EiamOAuth2TokenRevocationEndpointConfigurer extends AbstractO private final List authenticationProviders = new ArrayList<>(); private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { }; - private AuthenticationSuccessHandler revocationResponseHandler; - private AuthenticationFailureHandler errorResponseHandler; + private final HttpMessageConverter errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter(); + private final AuthenticationSuccessHandler authenticationSuccessHandler = this::sendRevocationSuccessResponse; + private final AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse; /** * Restrict for internal use only. @@ -70,86 +77,6 @@ public final class EiamOAuth2TokenRevocationEndpointConfigurer extends AbstractO super(objectPostProcessor); } - /** - * Adds an {@link AuthenticationConverter} used when attempting to extract a Revoke Token Request from {@link HttpServletRequest} - * to an instance of {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the request. - * - * @param revocationRequestConverter an {@link AuthenticationConverter} used when attempting to extract a Revoke Token Request from {@link HttpServletRequest} - * @return the {@link EiamOAuth2TokenRevocationEndpointConfigurer} for further configuration - */ - public EiamOAuth2TokenRevocationEndpointConfigurer revocationRequestConverter(AuthenticationConverter revocationRequestConverter) { - Assert.notNull(revocationRequestConverter, "revocationRequestConverter cannot be null"); - this.revocationRequestConverters.add(revocationRequestConverter); - return this; - } - - /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #revocationRequestConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param revocationRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s - * @return the {@link EiamOAuth2TokenRevocationEndpointConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOAuth2TokenRevocationEndpointConfigurer revocationRequestConverters(Consumer> revocationRequestConvertersConsumer) { - Assert.notNull(revocationRequestConvertersConsumer, - "revocationRequestConvertersConsumer cannot be null"); - this.revocationRequestConvertersConsumer = revocationRequestConvertersConsumer; - return this; - } - - /** - * Adds an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2TokenRevocationAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2TokenRevocationAuthenticationToken} - * @return the {@link EiamOAuth2TokenRevocationEndpointConfigurer} for further configuration - */ - public EiamOAuth2TokenRevocationEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) { - Assert.notNull(authenticationProvider, "authenticationProvider cannot be null"); - this.authenticationProviders.add(authenticationProvider); - return this; - } - - /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s - * @return the {@link EiamOAuth2TokenRevocationEndpointConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOAuth2TokenRevocationEndpointConfigurer authenticationProviders(Consumer> authenticationProvidersConsumer) { - Assert.notNull(authenticationProvidersConsumer, - "authenticationProvidersConsumer cannot be null"); - this.authenticationProvidersConsumer = authenticationProvidersConsumer; - return this; - } - - /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenRevocationAuthenticationToken}. - * - * @param revocationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenRevocationAuthenticationToken} - * @return the {@link EiamOAuth2TokenRevocationEndpointConfigurer} for further configuration - */ - public EiamOAuth2TokenRevocationEndpointConfigurer revocationResponseHandler(AuthenticationSuccessHandler revocationResponseHandler) { - this.revocationResponseHandler = revocationResponseHandler; - return this; - } - - /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * @return the {@link EiamOAuth2TokenRevocationEndpointConfigurer} for further configuration - */ - public EiamOAuth2TokenRevocationEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) { - this.errorResponseHandler = errorResponseHandler; - return this; - } - @Override void init(HttpSecurity httpSecurity) { this.requestMatcher = new AntPathRequestMatcher( @@ -174,6 +101,8 @@ public final class EiamOAuth2TokenRevocationEndpointConfigurer extends AbstractO OAuth2TokenRevocationEndpointFilter revocationEndpointFilter = new OAuth2TokenRevocationEndpointFilter( authenticationManager, ProtocolConstants.OidcEndpointConstants.TOKEN_REVOCATION_ENDPOINT); + revocationEndpointFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler); + revocationEndpointFilter.setAuthenticationFailureHandler(authenticationFailureHandler); List authenticationConverters = createDefaultAuthenticationConverters(); if (!this.revocationRequestConverters.isEmpty()) { authenticationConverters.addAll(0, this.revocationRequestConverters); @@ -181,13 +110,6 @@ public final class EiamOAuth2TokenRevocationEndpointConfigurer extends AbstractO this.revocationRequestConvertersConsumer.accept(authenticationConverters); revocationEndpointFilter.setAuthenticationConverter( new DelegatingAuthenticationConverter(authenticationConverters)); - if (this.revocationResponseHandler != null) { - revocationEndpointFilter - .setAuthenticationSuccessHandler(this.revocationResponseHandler); - } - if (this.errorResponseHandler != null) { - revocationEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler); - } httpSecurity.addFilterAfter(postProcess(revocationEndpointFilter), FilterSecurityInterceptor.class); } @@ -215,4 +137,32 @@ public final class EiamOAuth2TokenRevocationEndpointConfigurer extends AbstractO return authenticationProviders; } + /** + * 撤销成功 + * + * @param request {@link HttpServletRequest} + * @param response {@link HttpServletResponse} + * @param authentication {@link Authentication} + */ + private void sendRevocationSuccessResponse(HttpServletRequest request, + HttpServletResponse response, + Authentication authentication) { + response.setStatus(HttpStatus.OK.value()); + } + + /** + * 响应失败 + * + * @param request {@link HttpServletRequest} + * @param response {@link HttpServletResponse} + * @param exception {@link AuthenticationException} + * @throws IOException IOException + */ + private void sendErrorResponse(HttpServletRequest request, HttpServletResponse response, + AuthenticationException exception) throws IOException { + OAuth2Error error = ((OAuth2AuthenticationException) exception).getError(); + ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); + httpResponse.setStatusCode(HttpStatus.BAD_REQUEST); + this.errorHttpResponseConverter.write(error, null, httpResponse); + } } diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOidcClientRegistrationEndpointConfigurer.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOidcClientRegistrationEndpointConfigurer.java deleted file mode 100644 index 62afe9bf..00000000 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOidcClientRegistrationEndpointConfigurer.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * eiam-protocol-oidc - Employee Identity and Access Management Program - * Copyright © 2020-2023 TopIAM (support@topiam.cn) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -import javax.servlet.http.HttpServletRequest; - -import org.springframework.http.HttpMethod; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.config.annotation.ObjectPostProcessor; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration; -import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientConfigurationAuthenticationProvider; -import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationProvider; -import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationToken; -import org.springframework.security.oauth2.server.authorization.oidc.web.OidcClientRegistrationEndpointFilter; -import org.springframework.security.oauth2.server.authorization.oidc.web.authentication.OidcClientRegistrationAuthenticationConverter; -import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter; -import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; -import org.springframework.security.web.authentication.AuthenticationConverter; -import org.springframework.security.web.authentication.AuthenticationFailureHandler; -import org.springframework.security.web.authentication.AuthenticationSuccessHandler; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.security.web.util.matcher.OrRequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.util.Assert; - -import cn.topiam.employee.common.constants.ProtocolConstants; - -/** - * Configurer for OpenID Connect Dynamic Client Registration 1.0 Endpoint. - * - * @author TopIAM - * Created by support@topiam.cn on 2022/10/26 19:21 - */ -public final class EiamOidcClientRegistrationEndpointConfigurer extends AbstractOAuth2Configurer { - private RequestMatcher requestMatcher; - private final List clientRegistrationRequestConverters = new ArrayList<>(); - private Consumer> clientRegistrationRequestConvertersConsumer = (clientRegistrationRequestConverters) -> { - }; - private final List authenticationProviders = new ArrayList<>(); - private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { - }; - private AuthenticationSuccessHandler clientRegistrationResponseHandler; - private AuthenticationFailureHandler errorResponseHandler; - - /** - * Restrict for internal use only. - */ - EiamOidcClientRegistrationEndpointConfigurer(ObjectPostProcessor objectPostProcessor) { - super(objectPostProcessor); - } - - /** - * Adds an {@link AuthenticationConverter} used when attempting to extract a Client Registration Request from {@link HttpServletRequest} - * to an instance of {@link OidcClientRegistrationAuthenticationToken} used for authenticating the request. - * - * @param clientRegistrationRequestConverter an {@link AuthenticationConverter} used when attempting to extract a Client Registration Request from {@link HttpServletRequest} - * @return the {@link EiamOidcClientRegistrationEndpointConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOidcClientRegistrationEndpointConfigurer clientRegistrationRequestConverter(AuthenticationConverter clientRegistrationRequestConverter) { - Assert.notNull(clientRegistrationRequestConverter, - "clientRegistrationRequestConverter cannot be null"); - this.clientRegistrationRequestConverters.add(clientRegistrationRequestConverter); - return this; - } - - /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #clientRegistrationRequestConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param clientRegistrationRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s - * @return the {@link EiamOidcClientRegistrationEndpointConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOidcClientRegistrationEndpointConfigurer clientRegistrationRequestConverters(Consumer> clientRegistrationRequestConvertersConsumer) { - Assert.notNull(clientRegistrationRequestConvertersConsumer, - "clientRegistrationRequestConvertersConsumer cannot be null"); - this.clientRegistrationRequestConvertersConsumer = clientRegistrationRequestConvertersConsumer; - return this; - } - - /** - * Adds an {@link AuthenticationProvider} used for authenticating an {@link OidcClientRegistrationAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OidcClientRegistrationAuthenticationToken} - * @return the {@link EiamOidcClientRegistrationEndpointConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOidcClientRegistrationEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) { - Assert.notNull(authenticationProvider, "authenticationProvider cannot be null"); - this.authenticationProviders.add(authenticationProvider); - return this; - } - - /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s - * @return the {@link EiamOidcClientRegistrationEndpointConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOidcClientRegistrationEndpointConfigurer authenticationProviders(Consumer> authenticationProvidersConsumer) { - Assert.notNull(authenticationProvidersConsumer, - "authenticationProvidersConsumer cannot be null"); - this.authenticationProvidersConsumer = authenticationProvidersConsumer; - return this; - } - - /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OidcClientRegistrationAuthenticationToken} - * and returning the {@link OidcClientRegistration Client Registration Response}. - * - * @param clientRegistrationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OidcClientRegistrationAuthenticationToken} - * @return the {@link EiamOidcClientRegistrationEndpointConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOidcClientRegistrationEndpointConfigurer clientRegistrationResponseHandler(AuthenticationSuccessHandler clientRegistrationResponseHandler) { - this.clientRegistrationResponseHandler = clientRegistrationResponseHandler; - return this; - } - - /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * @return the {@link EiamOidcClientRegistrationEndpointConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOidcClientRegistrationEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) { - this.errorResponseHandler = errorResponseHandler; - return this; - } - - @Override - void init(HttpSecurity httpSecurity) { - this.requestMatcher = new OrRequestMatcher( - new AntPathRequestMatcher( - ProtocolConstants.OidcEndpointConstants.OIDC_CLIENT_REGISTRATION_ENDPOINT, - HttpMethod.POST.name()), - new AntPathRequestMatcher( - ProtocolConstants.OidcEndpointConstants.OIDC_CLIENT_REGISTRATION_ENDPOINT, - HttpMethod.GET.name())); - - List authenticationProviders = createDefaultAuthenticationProviders( - httpSecurity); - if (!this.authenticationProviders.isEmpty()) { - authenticationProviders.addAll(0, this.authenticationProviders); - } - this.authenticationProvidersConsumer.accept(authenticationProviders); - authenticationProviders.forEach(authenticationProvider -> httpSecurity - .authenticationProvider(postProcess(authenticationProvider))); - } - - @Override - void configure(HttpSecurity httpSecurity) { - AuthenticationManager authenticationManager = httpSecurity - .getSharedObject(AuthenticationManager.class); - - OidcClientRegistrationEndpointFilter oidcClientRegistrationEndpointFilter = new OidcClientRegistrationEndpointFilter( - authenticationManager, - ProtocolConstants.OidcEndpointConstants.OIDC_CLIENT_REGISTRATION_ENDPOINT); - - List authenticationConverters = createDefaultAuthenticationConverters(); - if (!this.clientRegistrationRequestConverters.isEmpty()) { - authenticationConverters.addAll(0, this.clientRegistrationRequestConverters); - } - this.clientRegistrationRequestConvertersConsumer.accept(authenticationConverters); - oidcClientRegistrationEndpointFilter.setAuthenticationConverter( - new DelegatingAuthenticationConverter(authenticationConverters)); - if (this.clientRegistrationResponseHandler != null) { - oidcClientRegistrationEndpointFilter - .setAuthenticationSuccessHandler(this.clientRegistrationResponseHandler); - } - if (this.errorResponseHandler != null) { - oidcClientRegistrationEndpointFilter - .setAuthenticationFailureHandler(this.errorResponseHandler); - } - httpSecurity.addFilterAfter(postProcess(oidcClientRegistrationEndpointFilter), - FilterSecurityInterceptor.class); - } - - @Override - RequestMatcher getRequestMatcher() { - return this.requestMatcher; - } - - private static List createDefaultAuthenticationConverters() { - List authenticationConverters = new ArrayList<>(); - - authenticationConverters.add(new OidcClientRegistrationAuthenticationConverter()); - - return authenticationConverters; - } - - private static List createDefaultAuthenticationProviders(HttpSecurity httpSecurity) { - List authenticationProviders = new ArrayList<>(); - - OidcClientRegistrationAuthenticationProvider oidcClientRegistrationAuthenticationProvider = new OidcClientRegistrationAuthenticationProvider( - OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), - OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity), - OAuth2ConfigurerUtils.getTokenGenerator(httpSecurity)); - authenticationProviders.add(oidcClientRegistrationAuthenticationProvider); - - OidcClientConfigurationAuthenticationProvider oidcClientConfigurationAuthenticationProvider = new OidcClientConfigurationAuthenticationProvider( - OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), - OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity)); - authenticationProviders.add(oidcClientConfigurationAuthenticationProvider); - - return authenticationProviders; - } - -} diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOidcConfigurer.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOidcConfigurer.java index dcc5b9bb..12e823c2 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOidcConfigurer.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOidcConfigurer.java @@ -22,12 +22,10 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; -import static cn.topiam.employee.common.constants.ProtocolConstants.OidcEndpointConstants.OIDC_CLIENT_REGISTRATION_ENDPOINT; /** * Configurer for OpenID Connect 1.0 support. @@ -50,50 +48,6 @@ public final class EiamOidcConfigurer extends AbstractOAuth2Configurer { new EiamOidcUserInfoEndpointConfigurer(objectPostProcessor)); } - /** - * Configures the OpenID Connect 1.0 Provider Configuration Endpoint. - * - * @param providerConfigurationEndpointCustomizer the {@link Customizer} providing access to the {@link EiamOidcProviderConfigurationEndpointConfigurer} - * @return the {@link EiamOidcConfigurer} for further configuration - * @since 0.4.0 - */ - public EiamOidcConfigurer providerConfigurationEndpoint(Customizer providerConfigurationEndpointCustomizer) { - providerConfigurationEndpointCustomizer - .customize(getConfigurer(EiamOidcProviderConfigurationEndpointConfigurer.class)); - return this; - } - - /** - * Configures the OpenID Connect Dynamic Client Registration 1.0 Endpoint. - * - * @param clientRegistrationEndpointCustomizer the {@link Customizer} providing access to the {@link EiamOidcClientRegistrationEndpointConfigurer} - * @return the {@link EiamOidcConfigurer} for further configuration - */ - public EiamOidcConfigurer clientRegistrationEndpoint(Customizer clientRegistrationEndpointCustomizer) { - EiamOidcClientRegistrationEndpointConfigurer clientRegistrationEndpointConfigurer = getConfigurer( - EiamOidcClientRegistrationEndpointConfigurer.class); - if (clientRegistrationEndpointConfigurer == null) { - addConfigurer(EiamOidcClientRegistrationEndpointConfigurer.class, - new EiamOidcClientRegistrationEndpointConfigurer(getObjectPostProcessor())); - clientRegistrationEndpointConfigurer = getConfigurer( - EiamOidcClientRegistrationEndpointConfigurer.class); - } - clientRegistrationEndpointCustomizer.customize(clientRegistrationEndpointConfigurer); - return this; - } - - /** - * Configures the OpenID Connect 1.0 UserInfo Endpoint. - * - * @param userInfoEndpointCustomizer the {@link Customizer} providing access to the {@link EiamOidcUserInfoEndpointConfigurer} - * @return the {@link OidcConfigurer} for further configuration - */ - public EiamOidcConfigurer userInfoEndpoint(Customizer userInfoEndpointCustomizer) { - userInfoEndpointCustomizer - .customize(getConfigurer(EiamOidcUserInfoEndpointConfigurer.class)); - return this; - } - @Override void init(HttpSecurity httpSecurity) { List requestMatchers = new ArrayList<>(); @@ -106,16 +60,6 @@ public final class EiamOidcConfigurer extends AbstractOAuth2Configurer { @Override void configure(HttpSecurity httpSecurity) { - EiamOidcClientRegistrationEndpointConfigurer clientRegistrationEndpointConfigurer = getConfigurer( - EiamOidcClientRegistrationEndpointConfigurer.class); - if (clientRegistrationEndpointConfigurer != null) { - EiamOidcProviderConfigurationEndpointConfigurer providerConfigurationEndpointConfigurer = getConfigurer( - EiamOidcProviderConfigurationEndpointConfigurer.class); - - providerConfigurationEndpointConfigurer.addDefaultProviderConfigurationCustomizer( - (builder) -> builder.clientRegistrationEndpoint(OIDC_CLIENT_REGISTRATION_ENDPOINT)); - } - this.configurers.values().forEach(configurer -> configurer.configure(httpSecurity)); } diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOidcUserInfoEndpointConfigurer.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOidcUserInfoEndpointConfigurer.java index 4a3d8141..90f05310 100644 --- a/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOidcUserInfoEndpointConfigurer.java +++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/EiamOidcUserInfoEndpointConfigurer.java @@ -17,18 +17,11 @@ */ package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers; -import java.util.function.Function; - import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.oauth2.core.OAuth2AccessToken; -import org.springframework.security.oauth2.core.oidc.OidcIdToken; -import org.springframework.security.oauth2.core.oidc.OidcUserInfo; -import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationContext; import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationProvider; -import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationToken; import org.springframework.security.oauth2.server.authorization.oidc.web.OidcUserInfoEndpointFilter; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @@ -44,8 +37,7 @@ import cn.topiam.employee.common.constants.ProtocolConstants; * Created by support@topiam.cn on 2022/10/26 19:24 */ public final class EiamOidcUserInfoEndpointConfigurer extends AbstractOAuth2Configurer { - private RequestMatcher requestMatcher; - private Function userInfoMapper; + private RequestMatcher requestMatcher; /** * Restrict for internal use only. @@ -54,27 +46,6 @@ public final class EiamOidcUserInfoEndpointConfigurer extends AbstractOAuth2Conf super(objectPostProcessor); } - /** - * Sets the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext} - * to an instance of {@link OidcUserInfo} for the UserInfo response. - * - *

- * The {@link OidcUserInfoAuthenticationContext} gives the mapper access to the {@link OidcUserInfoAuthenticationToken}, - * as well as, the following context attributes: - *

    - *
  • {@link OidcUserInfoAuthenticationContext#getAccessToken()} containing the bearer token used to make the request.
  • - *
  • {@link OidcUserInfoAuthenticationContext#getAuthorization()} containing the {@link OidcIdToken} and - * {@link OAuth2AccessToken} associated with the bearer token used to make the request.
  • - *
- * - * @param userInfoMapper the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo} - * @return the {@link EiamOidcUserInfoEndpointConfigurer} for further configuration - */ - public EiamOidcUserInfoEndpointConfigurer userInfoMapper(Function userInfoMapper) { - this.userInfoMapper = userInfoMapper; - return this; - } - @Override void init(HttpSecurity httpSecurity) { String userInfoEndpointUri = ProtocolConstants.OidcEndpointConstants.OIDC_USER_INFO_ENDPOINT; @@ -84,9 +55,6 @@ public final class EiamOidcUserInfoEndpointConfigurer extends AbstractOAuth2Conf OidcUserInfoAuthenticationProvider oidcUserInfoAuthenticationProvider = new OidcUserInfoAuthenticationProvider( OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity)); - if (this.userInfoMapper != null) { - oidcUserInfoAuthenticationProvider.setUserInfoMapper(this.userInfoMapper); - } httpSecurity.authenticationProvider(postProcess(oidcUserInfoAuthenticationProvider)); } diff --git a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/Saml2IdpConfigurer.java b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/Saml2IdpConfigurer.java index 2587aaa4..7ccf7054 100644 --- a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/Saml2IdpConfigurer.java +++ b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/Saml2IdpConfigurer.java @@ -34,7 +34,9 @@ import cn.topiam.employee.protocol.saml2.idp.endpoint.Saml2IdpSingleSignOnEndpoi import cn.topiam.employee.protocol.saml2.idp.endpoint.Saml2IdpSingleSignOutEndpointFilter; import cn.topiam.employee.protocol.saml2.idp.endpoint.Saml2InitSingleSignOnEndpointFilter; import cn.topiam.employee.protocol.saml2.idp.filter.EiamSaml2AuthorizationServerContextFilter; -import static cn.topiam.employee.protocol.saml2.idp.util.Saml2Utils.*; +import static cn.topiam.employee.protocol.cas.util.ProtocolUtils.getApplicationServiceLoader; +import static cn.topiam.employee.protocol.saml2.idp.util.Saml2Utils.getAppSaml2ConfigRepository; +import static cn.topiam.employee.protocol.saml2.idp.util.Saml2Utils.getSessionRegistry; /** * 认证配置 diff --git a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/Saml2IdpMetadataEndpointFilter.java b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/Saml2IdpMetadataEndpointFilter.java index 88679ddc..55d459ed 100644 --- a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/Saml2IdpMetadataEndpointFilter.java +++ b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/Saml2IdpMetadataEndpointFilter.java @@ -38,11 +38,11 @@ import org.springframework.web.filter.OncePerRequestFilter; import cn.topiam.employee.application.ApplicationService; import cn.topiam.employee.application.ApplicationServiceLoader; -import cn.topiam.employee.application.Saml2ApplicationService; import cn.topiam.employee.application.context.ApplicationContext; import cn.topiam.employee.application.context.ApplicationContextHolder; +import cn.topiam.employee.application.saml2.Saml2ApplicationService; +import cn.topiam.employee.application.saml2.model.Saml2ProtocolConfig; import cn.topiam.employee.common.constants.ProtocolConstants; -import cn.topiam.employee.core.protocol.Saml2ProtocolConfig; import static cn.topiam.employee.common.util.SamlUtils.initOpenSaml; import static cn.topiam.employee.common.util.SamlUtils.transformSamlObject2String; import static cn.topiam.employee.protocol.saml2.idp.util.Saml2Utils.getEntityDescriptor; diff --git a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/Saml2IdpSingleSignOnEndpointFilter.java b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/Saml2IdpSingleSignOnEndpointFilter.java index 23ae2431..793b7ce5 100644 --- a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/Saml2IdpSingleSignOnEndpointFilter.java +++ b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/Saml2IdpSingleSignOnEndpointFilter.java @@ -56,16 +56,16 @@ import com.google.common.collect.Lists; import cn.topiam.employee.application.ApplicationService; import cn.topiam.employee.application.ApplicationServiceLoader; -import cn.topiam.employee.application.Saml2ApplicationService; import cn.topiam.employee.application.context.ApplicationContext; import cn.topiam.employee.application.context.ApplicationContextHolder; +import cn.topiam.employee.application.saml2.Saml2ApplicationService; +import cn.topiam.employee.application.saml2.model.Saml2SsoModel; import cn.topiam.employee.audit.entity.Target; import cn.topiam.employee.audit.enums.EventStatus; import cn.topiam.employee.audit.enums.TargetType; import cn.topiam.employee.audit.event.AuditEventPublish; import cn.topiam.employee.common.util.SamlUtils; import cn.topiam.employee.core.context.ServerContextHelp; -import cn.topiam.employee.core.protocol.Saml2SsoModel; import cn.topiam.employee.core.security.savedredirect.HttpSessionRedirectCache; import cn.topiam.employee.core.security.savedredirect.RedirectCache; import cn.topiam.employee.protocol.saml2.idp.endpoint.xml.ResponseGenerator; @@ -82,7 +82,8 @@ import static org.springframework.util.StringUtils.hasText; import static cn.topiam.employee.audit.enums.EventType.APP_SSO; import static cn.topiam.employee.common.constants.AuthorizeConstants.FE_LOGIN; -import static cn.topiam.employee.common.constants.ProtocolConstants.*; +import static cn.topiam.employee.common.constants.ProtocolConstants.APP_CODE_VARIABLE; +import static cn.topiam.employee.common.constants.ProtocolConstants.Saml2EndpointConstants; import static cn.topiam.employee.common.util.SamlKeyStoreProvider.getKeyStoreCredentialResolver; import static cn.topiam.employee.common.util.SamlUtils.getMessageContext; import static cn.topiam.employee.core.security.util.SecurityUtils.isAuthenticated; @@ -99,7 +100,7 @@ public class Saml2IdpSingleSignOnEndpointFilter extends OncePerRequestFilter implements OrderedFilter { private static final Logger logger = LoggerFactory .getLogger(Saml2IdpSingleSignOnEndpointFilter.class); - private final static RequestMatcher REQUEST_MATCHER = new AntPathRequestMatcher( + private static final RequestMatcher REQUEST_MATCHER = new AntPathRequestMatcher( Saml2EndpointConstants.SAML_SSO_PATH); private final RedirectCache redirectCache = new HttpSessionRedirectCache(); diff --git a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/Saml2IdpSingleSignOutEndpointFilter.java b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/Saml2IdpSingleSignOutEndpointFilter.java index 71351beb..77d1bdaf 100644 --- a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/Saml2IdpSingleSignOutEndpointFilter.java +++ b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/Saml2IdpSingleSignOutEndpointFilter.java @@ -17,10 +17,13 @@ */ package cn.topiam.employee.protocol.saml2.idp.endpoint; +import java.util.Objects; + import javax.servlet.FilterChain; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; import org.opensaml.messaging.context.MessageContext; import org.opensaml.saml.saml2.core.LogoutRequest; import org.opensaml.security.credential.CredentialResolver; @@ -31,6 +34,7 @@ import org.springframework.core.Ordered; import org.springframework.http.HttpMethod; import org.springframework.lang.NonNull; import org.springframework.security.core.session.SessionRegistry; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.web.filter.OncePerRequestFilter; @@ -39,9 +43,10 @@ import com.google.common.collect.Lists; import cn.topiam.employee.application.ApplicationService; import cn.topiam.employee.application.ApplicationServiceLoader; -import cn.topiam.employee.application.Saml2ApplicationService; import cn.topiam.employee.application.context.ApplicationContext; import cn.topiam.employee.application.context.ApplicationContextHolder; +import cn.topiam.employee.application.saml2.Saml2ApplicationService; +import cn.topiam.employee.application.saml2.model.Saml2ProtocolConfig; import cn.topiam.employee.audit.entity.Target; import cn.topiam.employee.audit.enums.EventStatus; import cn.topiam.employee.audit.enums.TargetType; @@ -49,7 +54,6 @@ import cn.topiam.employee.audit.event.AuditEventPublish; import cn.topiam.employee.common.constants.ProtocolConstants; import cn.topiam.employee.common.util.SamlUtils; import cn.topiam.employee.core.context.ServerContextHelp; -import cn.topiam.employee.core.protocol.Saml2ProtocolConfig; import cn.topiam.employee.protocol.saml2.idp.endpoint.xml.Saml2ValidatorSuite; import cn.topiam.employee.support.context.ApplicationContextHelp; import cn.topiam.employee.support.exception.TopIamException; @@ -73,7 +77,7 @@ public class Saml2IdpSingleSignOutEndpointFilter extends OncePerRequestFilter private final Logger logger = LoggerFactory .getLogger(Saml2IdpSingleSignOutEndpointFilter.class); - private final static RequestMatcher REQUEST_MATCHER = new AntPathRequestMatcher( + private static final RequestMatcher REQUEST_MATCHER = new AntPathRequestMatcher( ProtocolConstants.Saml2EndpointConstants.SAML_LOGOUT_PATH); public static RequestMatcher getRequestMatcher() { @@ -104,13 +108,19 @@ public class Saml2IdpSingleSignOutEndpointFilter extends OncePerRequestFilter LogoutRequest logoutRequest = (LogoutRequest) messageContext.getMessage(); logger.info("LogoutRequest: "); SamlUtils.logSamlObject(logoutRequest); - CredentialResolver credentialResolver = getKeyStoreCredentialResolver(protocolConfig.getSpEntityId(), protocolConfig.getSpSignCert()); - Saml2ValidatorSuite.verifySignatureUsingSignatureValidator(logoutRequest.getSignature(), credentialResolver, protocolConfig.getSpEntityId()); - Saml2ValidatorSuite.verifySignatureUsingMessageHandler(messageContext, credentialResolver, protocolConfig.getSpEntityId()); + if (protocolConfig.getSpRequestsSigned()){ + CredentialResolver credentialResolver = getKeyStoreCredentialResolver(protocolConfig.getSpEntityId(), protocolConfig.getSpSignCert()); + Saml2ValidatorSuite.verifySignatureUsingSignatureValidator(logoutRequest.getSignature(), credentialResolver, protocolConfig.getSpEntityId()); + Saml2ValidatorSuite.verifySignatureUsingMessageHandler(messageContext, credentialResolver, protocolConfig.getSpEntityId()); + } //根据 SessionIndexes 清除会话 - logoutRequest.getSessionIndexes().forEach((i) -> sessionRegistry.removeSessionInformation(i.getValue())); + Objects.requireNonNull(logoutRequest).getSessionIndexes().forEach((i) -> sessionRegistry.removeSessionInformation(i.getValue())); //跳转登录 - response.sendRedirect(ServerContextHelp.getPortalPublicBaseUrl() + FE_LOGIN); + StringBuilder loginUrl = new StringBuilder(ServerContextHelp.getPortalPublicBaseUrl() + FE_LOGIN); + if(StringUtils.isNotBlank(logoutRequest.getDestination())) { + loginUrl.append("?").append(OAuth2ParameterNames.REDIRECT_URI+"=").append(logoutRequest.getDestination()); + } + response.sendRedirect(loginUrl.toString()); } catch (Exception e) { success=false; result=e.getMessage(); diff --git a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/Saml2InitSingleSignOnEndpointFilter.java b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/Saml2InitSingleSignOnEndpointFilter.java index 8e4890f0..8eab2274 100644 --- a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/Saml2InitSingleSignOnEndpointFilter.java +++ b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/Saml2InitSingleSignOnEndpointFilter.java @@ -37,15 +37,15 @@ import com.google.common.collect.Lists; import cn.topiam.employee.application.ApplicationService; import cn.topiam.employee.application.ApplicationServiceLoader; -import cn.topiam.employee.application.Saml2ApplicationService; import cn.topiam.employee.application.context.ApplicationContext; import cn.topiam.employee.application.context.ApplicationContextHolder; +import cn.topiam.employee.application.saml2.Saml2ApplicationService; +import cn.topiam.employee.application.saml2.model.Saml2SsoModel; import cn.topiam.employee.audit.entity.Target; import cn.topiam.employee.audit.enums.EventStatus; import cn.topiam.employee.audit.enums.TargetType; import cn.topiam.employee.audit.event.AuditEventPublish; import cn.topiam.employee.core.context.ServerContextHelp; -import cn.topiam.employee.core.protocol.Saml2SsoModel; import cn.topiam.employee.core.security.savedredirect.HttpSessionRedirectCache; import cn.topiam.employee.core.security.savedredirect.RedirectCache; import cn.topiam.employee.support.context.ApplicationContextHelp; diff --git a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/AssertionGenerator.java b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/AssertionGenerator.java index d0d667cc..05fd01a0 100644 --- a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/AssertionGenerator.java +++ b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/AssertionGenerator.java @@ -39,11 +39,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; +import cn.topiam.employee.application.saml2.model.Saml2SsoModel; import cn.topiam.employee.common.enums.app.AuthnContextClassRefType; import cn.topiam.employee.common.enums.app.SamlEncryptAssertAlgorithmType; import cn.topiam.employee.common.enums.app.SamlNameIdFormatType; import cn.topiam.employee.common.enums.app.SamlSignAssertAlgorithmType; -import cn.topiam.employee.core.protocol.Saml2SsoModel; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -60,7 +60,7 @@ import static cn.topiam.employee.common.util.SamlUtils.generateSecureRandomId; @RequiredArgsConstructor @Getter public class AssertionGenerator { - private final static Logger logger = LoggerFactory + private static final Logger logger = LoggerFactory .getLogger(AssertionGenerator.class); /** diff --git a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/AttributeStatementGenerator.java b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/AttributeStatementGenerator.java index a04b34ea..8ef15737 100644 --- a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/AttributeStatementGenerator.java +++ b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/AttributeStatementGenerator.java @@ -29,7 +29,7 @@ import org.opensaml.saml.saml2.core.impl.AttributeStatementBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import cn.topiam.employee.core.protocol.Saml2SsoModel; +import cn.topiam.employee.application.saml2.model.Saml2SsoModel; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -43,7 +43,7 @@ import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @Getter public class AttributeStatementGenerator { - private final static Logger logger = LoggerFactory + private static final Logger logger = LoggerFactory .getLogger(AttributeStatementGenerator.class); /** * 断言属性 diff --git a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/AuthnStatementGenerator.java b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/AuthnStatementGenerator.java index 7952a405..51bade5e 100644 --- a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/AuthnStatementGenerator.java +++ b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/AuthnStatementGenerator.java @@ -38,7 +38,7 @@ import cn.topiam.employee.support.context.ServletContextHelp; * Created by support@topiam.cn on 2022/6/1 22:52 */ public class AuthnStatementGenerator { - private final static Logger logger = LoggerFactory + private static final Logger logger = LoggerFactory .getLogger(AuthnStatementGenerator.class); private final AuthnContextClassRefType authnContextClassRefType; diff --git a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/ConditionsGenerator.java b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/ConditionsGenerator.java index 9b4a0734..6e2d389d 100644 --- a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/ConditionsGenerator.java +++ b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/ConditionsGenerator.java @@ -38,7 +38,7 @@ import lombok.AllArgsConstructor; */ @AllArgsConstructor public class ConditionsGenerator { - private final static Logger logger = LoggerFactory.getLogger(ConditionsGenerator.class); + private static final Logger logger = LoggerFactory.getLogger(ConditionsGenerator.class); /** * audienceUri */ diff --git a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/ResponseGenerator.java b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/ResponseGenerator.java index 04958c59..86c5fe5a 100644 --- a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/ResponseGenerator.java +++ b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/ResponseGenerator.java @@ -36,11 +36,11 @@ import org.opensaml.xmlsec.signature.support.SignatureConstants; import org.opensaml.xmlsec.signature.support.SignatureException; import org.opensaml.xmlsec.signature.support.Signer; +import cn.topiam.employee.application.saml2.model.Saml2SsoModel; import cn.topiam.employee.common.enums.app.AuthnContextClassRefType; import cn.topiam.employee.common.enums.app.SamlEncryptAssertAlgorithmType; import cn.topiam.employee.common.enums.app.SamlNameIdFormatType; import cn.topiam.employee.common.enums.app.SamlSignAssertAlgorithmType; -import cn.topiam.employee.core.protocol.Saml2SsoModel; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/Saml2ValidatorSuite.java b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/Saml2ValidatorSuite.java index 3807d2a8..3f28d68b 100644 --- a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/Saml2ValidatorSuite.java +++ b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/endpoint/xml/Saml2ValidatorSuite.java @@ -27,7 +27,6 @@ import org.opensaml.saml.common.messaging.context.SAMLProtocolContext; import org.opensaml.saml.common.xml.SAMLConstants; import org.opensaml.saml.criterion.EntityRoleCriterion; import org.opensaml.saml.criterion.ProtocolCriterion; -import org.opensaml.saml.saml2.core.AuthnRequest; import org.opensaml.saml.saml2.metadata.SPSSODescriptor; import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator; import org.opensaml.security.credential.Credential; @@ -56,7 +55,7 @@ public class Saml2ValidatorSuite { /** * 验证签名 * - * @param authnRequest {@link AuthnRequest} + * @param signature {@link Signature} * @param credentialResolver {@link CredentialResolver} * @throws Exception Exception */ diff --git a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/util/Saml2Utils.java b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/util/Saml2Utils.java index 26dbba58..7b1c5615 100644 --- a/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/util/Saml2Utils.java +++ b/eiam-protocol/eiam-protocol-saml2/src/main/java/cn/topiam/employee/protocol/saml2/idp/util/Saml2Utils.java @@ -24,20 +24,20 @@ import org.opensaml.saml.saml2.core.NameIDType; import org.opensaml.saml.saml2.metadata.*; import org.opensaml.saml.saml2.metadata.impl.*; import org.opensaml.security.credential.UsageType; -import org.springframework.context.ApplicationContext; import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.core.session.SessionRegistry; -import cn.topiam.employee.application.ApplicationServiceLoader; +import cn.topiam.employee.application.saml2.model.Saml2ProtocolConfig; import cn.topiam.employee.common.repository.app.AppSaml2ConfigRepository; import cn.topiam.employee.common.util.SamlKeyStoreProvider; import cn.topiam.employee.core.context.ServerContextHelp; -import cn.topiam.employee.core.protocol.Saml2ProtocolConfig; import cn.topiam.employee.support.exception.TopIamException; import static org.opensaml.saml.common.xml.SAMLConstants.SAML2_POST_BINDING_URI; import static org.opensaml.saml.common.xml.SAMLConstants.SAML2_REDIRECT_BINDING_URI; -import static cn.topiam.employee.common.constants.ProtocolConstants.*; +import static cn.topiam.employee.common.constants.ProtocolConstants.APP_CODE_VARIABLE; +import static cn.topiam.employee.common.constants.ProtocolConstants.Saml2EndpointConstants; +import static cn.topiam.employee.protocol.cas.util.ProtocolUtils.getBean; /** * @@ -65,20 +65,6 @@ public class Saml2Utils { return sessionRegistry; } - public static > ApplicationServiceLoader getApplicationServiceLoader(B builder) { - ApplicationServiceLoader applicationServiceLoader = builder - .getSharedObject(ApplicationServiceLoader.class); - if (applicationServiceLoader == null) { - applicationServiceLoader = getBean(builder, ApplicationServiceLoader.class); - builder.setSharedObject(ApplicationServiceLoader.class, applicationServiceLoader); - } - return applicationServiceLoader; - } - - public static , T> T getBean(B builder, Class type) { - return builder.getSharedObject(ApplicationContext.class).getBean(type); - } - /** * 获取EntityDescriptor * diff --git a/eiam-protocol/eiam-protocol-saml2/src/main/resources/templates/form_redirect.ftlh b/eiam-protocol/eiam-protocol-saml2/src/main/resources/templates/form_redirect.ftlh deleted file mode 100644 index 2f480981..00000000 --- a/eiam-protocol/eiam-protocol-saml2/src/main/resources/templates/form_redirect.ftlh +++ /dev/null @@ -1,198 +0,0 @@ - - - - Redirect - TopIAM - - - - - - - -
-
-
-
- - - - - - -
-
-
-
- - - diff --git a/eiam-protocol/eiam-protocol-saml2/src/main/resources/templates/jwt_redirect.ftlh b/eiam-protocol/eiam-protocol-saml2/src/main/resources/templates/jwt_redirect.ftlh deleted file mode 100644 index b802a686..00000000 --- a/eiam-protocol/eiam-protocol-saml2/src/main/resources/templates/jwt_redirect.ftlh +++ /dev/null @@ -1,198 +0,0 @@ - - - - Redirecting - TopIAM - - - - - - - -
-
-
-
- - - - - - -
-
-
-
- - - diff --git a/eiam-support/src/main/java/cn/topiam/employee/support/repository/LogicDeleteRepository.java b/eiam-support/src/main/java/cn/topiam/employee/support/repository/LogicDeleteRepository.java new file mode 100644 index 00000000..5b228c42 --- /dev/null +++ b/eiam-support/src/main/java/cn/topiam/employee/support/repository/LogicDeleteRepository.java @@ -0,0 +1,38 @@ +/* + * eiam-support - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.support.repository; + +import java.io.Serializable; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.NoRepositoryBean; + +import cn.topiam.employee.support.repository.domain.LogicDeleteEntity; + +/** + * LogicDeleteRepository + * + * @author TopIAM + * + * @param + * @param + */ +@NoRepositoryBean +public interface LogicDeleteRepository, PK extends Serializable> + extends JpaRepository { +} diff --git a/eiam-support/src/main/java/cn/topiam/employee/support/repository/domain/BaseTenantEntity.java b/eiam-support/src/main/java/cn/topiam/employee/support/repository/domain/BaseTenantEntity.java index 230bb56b..fb558afd 100644 --- a/eiam-support/src/main/java/cn/topiam/employee/support/repository/domain/BaseTenantEntity.java +++ b/eiam-support/src/main/java/cn/topiam/employee/support/repository/domain/BaseTenantEntity.java @@ -37,7 +37,7 @@ import lombok.ToString; @Setter @ToString @MappedSuperclass -public abstract class BaseTenantEntity extends BaseEntity { +public abstract class BaseTenantEntity extends LogicDeleteEntity { @Serial private static final long serialVersionUID = 4720107236271252583L; /** diff --git a/eiam-support/src/main/java/cn/topiam/employee/support/repository/domain/LogicDeleteEntity.java b/eiam-support/src/main/java/cn/topiam/employee/support/repository/domain/LogicDeleteEntity.java new file mode 100644 index 00000000..1c700b4c --- /dev/null +++ b/eiam-support/src/main/java/cn/topiam/employee/support/repository/domain/LogicDeleteEntity.java @@ -0,0 +1,48 @@ +/* + * eiam-support - Employee Identity and Access Management Program + * Copyright © 2020-2023 TopIAM (support@topiam.cn) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package cn.topiam.employee.support.repository.domain; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.MappedSuperclass; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import lombok.Getter; +import lombok.Setter; + +/** + * LogicDeleteEntity + * + * @author TopIAM + * + * @param + */ +@MappedSuperclass +@Setter +@Getter +public class LogicDeleteEntity extends BaseEntity { + public static final String DELETE_FIELD = "is_deleted"; + public static final String SOFT_DELETE_WHERE = "is_deleted = 0"; + public static final String SOFT_DELETE_SET = "is_deleted = null"; + public static final String SOFT_DELETE_HQL_SET = "isDeleted = null"; + @JsonIgnore + @Column(name = "is_deleted") + private Boolean isDeleted = Boolean.FALSE; +} diff --git a/eiam-support/src/main/java/cn/topiam/employee/support/util/AesUtils.java b/eiam-support/src/main/java/cn/topiam/employee/support/util/AesUtils.java index ae671d39..4844118f 100644 --- a/eiam-support/src/main/java/cn/topiam/employee/support/util/AesUtils.java +++ b/eiam-support/src/main/java/cn/topiam/employee/support/util/AesUtils.java @@ -37,7 +37,11 @@ import lombok.SneakyThrows; */ public class AesUtils { private static final String ALGORITHM = "AES"; - private static final String KEY = "fDx/JA3Aw9BIQClUSOddjA=="; + private final String KEY; + + public AesUtils(String key) { + this.KEY = key; + } /** * 生成秘钥 @@ -63,7 +67,7 @@ public class AesUtils { * 加密 */ @SneakyThrows - public static String encrypt(String content) { + public String encrypt(String content) { if (StringUtils.hasText(content)) { return encrypt(content, KEY); } @@ -88,7 +92,7 @@ public class AesUtils { * 解密 */ @SneakyThrows - public static String decrypt(String content) { + public String decrypt(String content) { if (StringUtils.hasText(content)) { return decrypt(content, KEY); } diff --git a/eiam-support/src/main/java/cn/topiam/employee/support/util/HttpClientUtils.java b/eiam-support/src/main/java/cn/topiam/employee/support/util/HttpClientUtils.java index 85ee46e3..a3115e07 100644 --- a/eiam-support/src/main/java/cn/topiam/employee/support/util/HttpClientUtils.java +++ b/eiam-support/src/main/java/cn/topiam/employee/support/util/HttpClientUtils.java @@ -43,6 +43,7 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.springframework.http.*; @@ -159,7 +160,7 @@ public class HttpClientUtils { * @param paramMap paramMap * @return String */ - public static String get(String url, Map paramMap) { + public static String get(String url, Map paramMap, BasicHeader... basicHeader) { String httpEntityContent; try { @@ -170,6 +171,7 @@ public class HttpClientUtils { RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(5000) .setConnectionRequestTimeout(1000).setSocketTimeout(60000).build(); httpGet.setConfig(requestConfig); + httpGet.setHeaders(basicHeader); List formParams = setHttpParams(paramMap); String param = URLEncodedUtils.format(formParams, UTF8); URL urL = new URL(url + "?" + param); diff --git a/eiam-support/src/main/java/cn/topiam/employee/support/util/HttpResponseUtils.java b/eiam-support/src/main/java/cn/topiam/employee/support/util/HttpResponseUtils.java index a7d03e9b..a8be087d 100644 --- a/eiam-support/src/main/java/cn/topiam/employee/support/util/HttpResponseUtils.java +++ b/eiam-support/src/main/java/cn/topiam/employee/support/util/HttpResponseUtils.java @@ -30,6 +30,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import com.alibaba.fastjson2.JSON; +import com.fasterxml.jackson.databind.ObjectMapper; /** * HttpResponseUtils @@ -38,7 +39,9 @@ import com.alibaba.fastjson2.JSON; * Created by support@topiam.cn on 2020/9/3 21:25 */ public class HttpResponseUtils { - private static final String NULL = "null"; + private static final String NULL = "null"; + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); /** * 将数据刷新写回web端 @@ -79,7 +82,9 @@ public class HttpResponseUtils { response.setStatus(status); PrintWriter writer = response.getWriter(); if (ObjectUtils.isNotEmpty(data)) { - writer.write(JSON.toJSONString(data)); + String value = OBJECT_MAPPER.writeValueAsString(data); + // 指定序列化输入的类型 + writer.write(value); } else { writer.write(""); } diff --git a/eiam-support/src/main/java/cn/topiam/employee/support/util/IpUtils.java b/eiam-support/src/main/java/cn/topiam/employee/support/util/IpUtils.java index 7dc3dc58..8d0e0862 100644 --- a/eiam-support/src/main/java/cn/topiam/employee/support/util/IpUtils.java +++ b/eiam-support/src/main/java/cn/topiam/employee/support/util/IpUtils.java @@ -24,12 +24,15 @@ import javax.servlet.http.HttpServletRequest; import com.google.common.net.InetAddresses; +import lombok.extern.slf4j.Slf4j; + /** * IpUtil * * @author TopIAM * Created by support@topiam.cn on 2019-01-10 19:50 */ +@Slf4j public class IpUtils { private static final String UNKNOWN = "Unknown"; diff --git a/eiam-support/src/main/java/cn/topiam/employee/support/util/UrlTestUtils.java b/eiam-support/src/main/java/cn/topiam/employee/support/util/UrlTestUtils.java index 4da389bc..5cefdb26 100644 --- a/eiam-support/src/main/java/cn/topiam/employee/support/util/UrlTestUtils.java +++ b/eiam-support/src/main/java/cn/topiam/employee/support/util/UrlTestUtils.java @@ -31,7 +31,7 @@ import org.slf4j.LoggerFactory; */ public class UrlTestUtils { - private final static Logger logger = LoggerFactory.getLogger(UrlTestUtils.class); + private static final Logger logger = LoggerFactory.getLogger(UrlTestUtils.class); public static boolean testUrlWithTimeOut(String urlString, int timeOutMillSeconds) { try { diff --git a/eiam-synchronizer/src/main/java/cn/topiam/employee/synchronizer/configuration/IdentitySourceBeanRegistry.java b/eiam-synchronizer/src/main/java/cn/topiam/employee/synchronizer/configuration/IdentitySourceBeanRegistry.java index 5fbfa96c..ed047b45 100644 --- a/eiam-synchronizer/src/main/java/cn/topiam/employee/synchronizer/configuration/IdentitySourceBeanRegistry.java +++ b/eiam-synchronizer/src/main/java/cn/topiam/employee/synchronizer/configuration/IdentitySourceBeanRegistry.java @@ -45,7 +45,7 @@ import com.cronutils.model.CronType; import cn.topiam.employee.common.constants.SettingConstants; import cn.topiam.employee.common.entity.identitysource.IdentitySourceEntity; import cn.topiam.employee.common.enums.TriggerType; -import cn.topiam.employee.common.enums.identityprovider.IdentitySourceProvider; +import cn.topiam.employee.common.enums.identitysource.IdentitySourceProvider; import cn.topiam.employee.common.repository.identitysource.IdentitySourceRepository; import cn.topiam.employee.identitysource.core.IdentitySource; import cn.topiam.employee.identitysource.core.IdentitySourceConfig; @@ -69,7 +69,7 @@ import cn.topiam.employee.support.trace.TraceUtils; import cn.topiam.employee.synchronizer.task.IdentitySourceSyncTask; import lombok.extern.slf4j.Slf4j; -import static cn.topiam.employee.common.enums.identityprovider.IdentitySourceProvider.DINGTALK; +import static cn.topiam.employee.common.enums.identitysource.IdentitySourceProvider.DINGTALK; import static cn.topiam.employee.support.lock.LockAspect.getTopiamLockKeyPrefix; import static cn.topiam.employee.synchronizer.configuration.IdentitySourceBeanUtils.getSourceBeanName; diff --git a/eiam-synchronizer/src/main/java/cn/topiam/employee/synchronizer/processor/AbstractIdentitySourcePostProcessor.java b/eiam-synchronizer/src/main/java/cn/topiam/employee/synchronizer/processor/AbstractIdentitySourcePostProcessor.java index 427c2d7a..8f567957 100644 --- a/eiam-synchronizer/src/main/java/cn/topiam/employee/synchronizer/processor/AbstractIdentitySourcePostProcessor.java +++ b/eiam-synchronizer/src/main/java/cn/topiam/employee/synchronizer/processor/AbstractIdentitySourcePostProcessor.java @@ -39,7 +39,7 @@ import cn.topiam.employee.common.enums.DataOrigin; import cn.topiam.employee.common.enums.MailType; import cn.topiam.employee.common.enums.SmsType; import cn.topiam.employee.common.enums.UserStatus; -import cn.topiam.employee.common.enums.identityprovider.IdentitySourceProvider; +import cn.topiam.employee.common.enums.identitysource.IdentitySourceProvider; import cn.topiam.employee.common.repository.identitysource.IdentitySourceRepository; import cn.topiam.employee.common.repository.identitysource.IdentitySourceSyncHistoryRepository; import cn.topiam.employee.common.repository.identitysource.IdentitySourceSyncRecordRepository; @@ -55,7 +55,7 @@ import lombok.extern.slf4j.Slf4j; import static cn.topiam.employee.common.constants.CommonConstants.SYSTEM_DEFAULT_USER_NAME; import static cn.topiam.employee.common.enums.UserStatus.DISABLE; import static cn.topiam.employee.common.enums.UserStatus.ENABLE; -import static cn.topiam.employee.common.enums.identityprovider.IdentitySourceProvider.*; +import static cn.topiam.employee.common.enums.identitysource.IdentitySourceProvider.*; import static cn.topiam.employee.core.message.sms.SmsMsgEventPublish.PASSWORD; import static cn.topiam.employee.core.message.sms.SmsMsgEventPublish.USERNAME; diff --git a/eiam-synchronizer/src/main/java/cn/topiam/employee/synchronizer/processor/DefaultIdentitySourceEventPostProcessor.java b/eiam-synchronizer/src/main/java/cn/topiam/employee/synchronizer/processor/DefaultIdentitySourceEventPostProcessor.java index 18f29cab..7ce9c3d2 100644 --- a/eiam-synchronizer/src/main/java/cn/topiam/employee/synchronizer/processor/DefaultIdentitySourceEventPostProcessor.java +++ b/eiam-synchronizer/src/main/java/cn/topiam/employee/synchronizer/processor/DefaultIdentitySourceEventPostProcessor.java @@ -42,7 +42,9 @@ import cn.topiam.employee.common.entity.account.OrganizationMemberEntity; import cn.topiam.employee.common.entity.account.UserEntity; import cn.topiam.employee.common.entity.identitysource.IdentitySourceEntity; import cn.topiam.employee.common.entity.identitysource.IdentitySourceEventRecordEntity; -import cn.topiam.employee.common.enums.*; +import cn.topiam.employee.common.enums.OrganizationType; +import cn.topiam.employee.common.enums.SyncStatus; +import cn.topiam.employee.common.enums.UserStatus; import cn.topiam.employee.common.enums.identitysource.IdentitySourceActionType; import cn.topiam.employee.common.enums.identitysource.IdentitySourceObjectType; import cn.topiam.employee.common.repository.account.OrganizationMemberRepository; diff --git a/eiam-synchronizer/src/main/java/cn/topiam/employee/synchronizer/processor/DefaultIdentitySourceUserPostProcessor.java b/eiam-synchronizer/src/main/java/cn/topiam/employee/synchronizer/processor/DefaultIdentitySourceUserPostProcessor.java index ac649928..f0b906bb 100644 --- a/eiam-synchronizer/src/main/java/cn/topiam/employee/synchronizer/processor/DefaultIdentitySourceUserPostProcessor.java +++ b/eiam-synchronizer/src/main/java/cn/topiam/employee/synchronizer/processor/DefaultIdentitySourceUserPostProcessor.java @@ -116,7 +116,7 @@ public class DefaultIdentitySourceUserPostProcessor extends AbstractIdentitySour //删除用户 userRepository.deleteAllById(deleteUserIds); //删除用户详情 - userDetailRepository.deleteAllByUserId(deleteUserIds); + userDetailRepository.deleteAllByUserIds(deleteUserIds); //删除组织用户关联关系 organizationMemberRepository.deleteAllByUserId(deleteUserIds); //删除用户组关系 diff --git a/pom.xml b/pom.xml index acab3121..f2ab3625 100644 --- a/pom.xml +++ b/pom.xml @@ -57,7 +57,7 @@ 1.0.11.RELEASE 31.1-jre 3.2.0 - 3.19.1 + 3.19.3 3.6.7 2.21.1 2.11.0 @@ -65,7 +65,7 @@ 1.6.14 2.0.23 4.3.0 - 1.3.14 + 1.4.0 4.0.0 1.0.5 1.5.3.Final @@ -74,17 +74,17 @@ 1.70 0.2.0 7.12.1 - 3.1.681 - 1.5.30 + 3.1.688 + 1.5.38 2.0.0 - 0.2.8 + 0.2.9 2.0.23 3.1.5 2.14.2 1.6.2 2.7.8 3.16.0 - 5.6.129 + 5.6.131 8.5.1 9.2.0 2.5.1 @@ -93,7 +93,7 @@ 0.10.2 1.22 1.10.0 - 8.13.4 + 8.13.5 0.4.0 1.1.3 @@ -121,6 +121,10 @@ + + org.springframework.boot + spring-boot-loader + com.googlecode.libphonenumber