mirror of https://gitee.com/topiam/eiam
Merge branch 'develop' of https://gitee.com/hbnuosc/eiam into develop
commit
cf7ad75926
13
README.md
13
README.md
|
@ -195,7 +195,7 @@ application.yml文件依次修改以下配置
|
|||
|
||||
## 加入社群
|
||||
|
||||

|
||||

|
||||
|
||||
## 参与贡献
|
||||
|
||||
|
@ -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+端口)
|
||||
- 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 清理缓存(可选操作)
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
|
@ -15,19 +15,17 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ public class ApplicationServiceLoader implements ApplicationContextAware {
|
|||
* 用于保存接口实现类名及对应的类
|
||||
*/
|
||||
private Map<String, ApplicationService> 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<String> beanNameList) {
|
||||
Map<String, ApplicationService> applicationServiceMap = new HashMap<>(16);
|
||||
for (String beanName : beanNameList) {
|
||||
applicationServiceMap.put(beanName,
|
||||
applicationContext.getBean(beanName, ApplicationService.class));
|
||||
}
|
||||
loadMap.putAll(applicationServiceMap);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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);
|
||||
}
|
|
@ -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<String, Object> 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<AppFormSaveConfigParam> validationResult = ValidationHelp.validateEntity(model);
|
||||
if (validationResult.isHasErrors()) {
|
||||
throw new ConstraintViolationException(validationResult.getConstraintViolations());
|
||||
}
|
||||
//@formatter:on
|
||||
//1、修改基本信息
|
||||
Optional<AppEntity> 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<String, String> 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<AppFormConfigEntity> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<AppFormConfigEntity.OtherField> 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<String,String> 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
|
||||
}
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<AppFormConfigEntity.OtherField> otherField;
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<AppFormConfigEntity.OtherField> otherField;
|
||||
|
||||
/**
|
||||
* 协议端点
|
||||
*/
|
||||
@Schema(description = "协议端点")
|
||||
private AppFormProtocolEndpoint protocolEndpoint;
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<AppFormConfigEntity.OtherField> otherField;
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<String,String> variables = new HashMap<>(16);
|
||||
variables.put(APP_CODE,appCode);
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<String> authGrantTypes;
|
||||
private Set<String> authGrantTypes;
|
||||
|
||||
/**
|
||||
* 客户端认证方式
|
||||
*/
|
||||
@Parameter(description = "客户端认证方式")
|
||||
private Set<String> clientAuthMethods;
|
||||
private Set<String> clientAuthMethods;
|
||||
|
||||
/**
|
||||
* 重定向URI
|
||||
*/
|
||||
@Parameter(description = "重定向URI")
|
||||
private Set<String> redirectUris;
|
||||
private Set<String> redirectUris;
|
||||
/**
|
||||
* scopes
|
||||
*/
|
||||
@Parameter(description = "授权范围")
|
||||
private Set<String> grantScopes;
|
||||
private Set<String> 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;
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.application.oidc.model;
|
||||
package cn.topiam.employee.application.oidc.pojo;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.application;
|
||||
package cn.topiam.employee.application.oidc.pojo;
|
|
@ -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")
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* 应用接口
|
|
@ -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<String, Object> config) {
|
||||
AppSaml2StandardSaveConfigParam model;
|
||||
try {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String value = mapper.writeValueAsString(config);
|
||||
// 指定序列化输入的类型
|
||||
mapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.application;
|
||||
package cn.topiam.employee.application.saml2;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.protocol;
|
||||
package cn.topiam.employee.application.saml2.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.protocol;
|
||||
package cn.topiam.employee.application.saml2.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* 模版配置
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.application.saml2.model;
|
||||
package cn.topiam.employee.application.saml2.pojo;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
|
@ -15,10 +15,11 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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();
|
||||
//元数据端点
|
|
@ -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<String> CONTENT = new TransmittableThreadLocal<>();
|
||||
|
||||
/**
|
||||
* Authentication
|
||||
*/
|
||||
private static final TransmittableThreadLocal<Authentication> 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Long> {
|
||||
@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<Long> {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -3119319193111206582L;
|
||||
|
@ -136,4 +144,10 @@ public class AuditEntity extends BaseEntity<Long> {
|
|||
*/
|
||||
@Column(name = "actor_type")
|
||||
private UserType actorType;
|
||||
|
||||
/**
|
||||
* 身份验证类型
|
||||
*/
|
||||
@Column(name = "actor_auth_type")
|
||||
private String actorAuthType;
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ public class Event implements Serializable {
|
|||
/**
|
||||
* 事件内容
|
||||
*/
|
||||
@Field(type = FieldType.Text, name = "content")
|
||||
@Field(type = FieldType.Object, name = "content")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Target> 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<String,String> 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
|
||||
}
|
||||
|
||||
|
|
|
@ -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<AuditEntity, Long>,
|
||||
public interface AuditRepository extends LogicDeleteRepository<AuditEntity, Long>,
|
||||
QuerydslPredicateExecutor<AuditEntity> {
|
||||
|
||||
/**
|
||||
|
|
|
@ -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<AuditListResult> searchHitsConvertToAuditListResult(SearchHits<AuditElasticSearchEntity> 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<UserEntity> 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<UserGroupEntity> 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<IdentitySourceEntity> 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> organizationEntity = organizationRepository
|
||||
.findByIdContainsDeleted(id);
|
||||
if (organizationEntity.isPresent()) {
|
||||
name = organizationEntity.get().getName();
|
||||
}
|
||||
}
|
||||
|
||||
if (TargetType.APPLICATION.equals(targetType)) {
|
||||
AppRepository appRepository = ApplicationContextHelp.getBean(AppRepository.class);
|
||||
Optional<AppEntity> 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> 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> 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> 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> 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> identityProviderEntity = identityProviderRepository
|
||||
.findByIdContainsDeleted(Long.valueOf(id));
|
||||
if (identityProviderEntity.isPresent()) {
|
||||
name = identityProviderEntity.get().getName();
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -63,6 +63,12 @@
|
|||
<artifactId>eiam-authentication-wechatwork</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- captcha-->
|
||||
<dependency>
|
||||
<groupId>cn.topiam</groupId>
|
||||
<artifactId>eiam-authentication-captcha</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- sms-->
|
||||
<dependency>
|
||||
<groupId>cn.topiam</groupId>
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>eiam-authentication</artifactId>
|
||||
<groupId>cn.topiam</groupId>
|
||||
<version>1.0.0-beta1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>eiam-authentication-captcha</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<dependencies>
|
||||
<!-- common -->
|
||||
<dependency>
|
||||
<groupId>cn.topiam</groupId>
|
||||
<artifactId>eiam-authentication-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.security.captcha;
|
||||
package cn.topiam.employee.authentication.captcha;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.security.captcha;
|
||||
package cn.topiam.employee.authentication.captcha;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.authentication.captcha.configurer;
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.security.captcha.geetest;
|
||||
package cn.topiam.employee.authentication.captcha.filter;
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.security.captcha.geetest;
|
||||
package cn.topiam.employee.authentication.captcha.geetest;
|
||||
|
||||
import java.io.Serial;
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<String, String> getParams = RequestUtils.getParams(request);
|
||||
Map<String, Object> 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
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.authentication.captcha.geetest;
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.security.captcha;
|
||||
package cn.topiam.employee.authentication.sms;
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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
|
|
@ -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
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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";
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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<IdentityProviderEntity> optional = getIdentityProviderRepository()
|
||||
.findByIdAndEnabledIsTrue(Long.valueOf(providerId));
|
||||
.findByCodeAndEnabledIsTrue(code);
|
||||
if (optional.isEmpty()) {
|
||||
//无效身份提供商
|
||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_IDP);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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("未知认证对象");
|
||||
}
|
||||
}
|
|
@ -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<String, String> variables = matcher.getVariables();
|
||||
String providerId = variables.get(PROVIDER_ID);
|
||||
String providerCode = variables.get(PROVIDER_CODE);
|
||||
Optional<IdentityProviderEntity> 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");
|
||||
|
|
|
@ -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<String, String> 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<String, String> 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<String, String> 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<String, String> 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<String, String> variables = matcher.getVariables();
|
||||
//校验身份提供商
|
||||
String providerId = variables.get(PROVIDER_ID);
|
||||
String providerCode = variables.get(PROVIDER_CODE);
|
||||
Optional<IdentityProviderEntity> 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 -> {
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<H extends HttpSecurityBuilder<H>> extends
|
||||
AbstractAuthenticationFilterConfigurer<H, FeiShuScanCodeAuthenticationConfigurer<H>, 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());
|
||||
}
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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";
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<OAuth2AuthorizationRequest> 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<String, String> variables = matcher.getVariables();
|
||||
String providerCode = variables.get(PROVIDER_CODE);
|
||||
Optional<IdentityProviderEntity> 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<String, Object> 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;
|
||||
}
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<String, String> 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<String, String> 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("(?<!(http:|https:))/+", "/");
|
||||
}
|
||||
|
||||
public static RequestMatcher getRequestMatcher() {
|
||||
return REQUEST_MATCHER;
|
||||
}
|
||||
}
|
|
@ -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,15 +15,18 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<H extends HttpSecurityBuilder<H>> extends
|
||||
AbstractAuthenticationFilterConfigurer<H, MfaAuthenticationConfigurer<H>, 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<H extends HttpSecurityBuilder<H>>
|
|||
@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<H extends HttpSecurityBuilder<H>>
|
|||
|
||||
@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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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();
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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()
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<MfaFactorResult> list = new ArrayList<>();
|
||||
List<MfaFactor> 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;
|
||||
}
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<String, Object> params = RequestUtils.getParams(request);
|
||||
String value = OBJECT_MAPPER.writeValueAsString(params);
|
||||
SendOtpRequest sendOtpRequest = OBJECT_MAPPER.readValue(value, SendOtpRequest.class);
|
||||
ValidationHelp.ValidationResult<SendOtpRequest> 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;
|
||||
}
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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";
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.authentication.sms.configurer;
|
||||
package cn.topiam.employee.authentication.mfa.constant;
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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();
|
||||
}
|
|
@ -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<String, String> variables = matcher.getVariables();
|
||||
String providerId = variables.get(PROVIDER_ID);
|
||||
String providerCode = variables.get(PROVIDER_CODE);
|
||||
Optional<IdentityProviderEntity> 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);
|
||||
});
|
||||
|
|
|
@ -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<String, String> 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));
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<H extends HttpSecurityBuilder<H>> extends
|
||||
AbstractAuthenticationFilterConfigurer<H, SmsAuthenticationConfigurer<H>, 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<H extends HttpSecurityBuilder<H>>
|
|||
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<H extends HttpSecurityBuilder<H>>
|
|||
|
||||
@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();
|
||||
}
|
||||
}
|
|
@ -15,12 +15,14 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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";
|
||||
|
||||
}
|
|
@ -15,4 +15,4 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.authentication.sms.filter;
|
||||
package cn.topiam.employee.authentication.sms.constant;
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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<String, String> variables = matcher.getVariables();
|
||||
String providerId = variables.get(PROVIDER_ID);
|
||||
String providerCode = variables.get(PROVIDER_CODE);
|
||||
Optional<IdentityProviderEntity> 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() {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue