mirror of
https://gitee.com/dromara/dax-pay
synced 2026-08-13 07:45:41 +08:00
refactor: 社交登录架构重构与端点配置功能
- capability-social 业务层(config/endpoint/cache/bind/login)迁移至 service-iam - SPI 接口(SocialBindStore/SocialLoginHandler)合并到实现类, capability-social 精简为纯协议层 - service-system 配置模块拆分为 Oss/Security/Url 三个独立子模块 - 社交登录回调地址改由端点配置自动生成, 移除 redirectUri 手动配置 - 修复 exchange 阶段 redirect_uri 与 authorize 阶段不一致问题
This commit is contained in:
@@ -18,3 +18,7 @@ ALTER TABLE iam_social_config DROP COLUMN IF EXISTS frontend_callback_url;
|
||||
-- 字段长度 VARCHAR(256) 可容纳密文(appSecret 多为 32~64 字符, 密文 < 200 字符), 无需调整
|
||||
-- 开发阶段无历史明文数据, 跳过迁移; 生产环境启用前需写一次性逻辑加密历史明文
|
||||
COMMENT ON COLUMN iam_social_config.client_secret IS '客户端密钥(加密存储)';
|
||||
|
||||
-- 回调地址不再由社交配置维护, 改由端点配置(PlatformUrlConfig)的 baseUrl 自动生成
|
||||
-- 实际回调地址为 {adminBaseUrl|merchantBaseUrl}/auth/oauth-callback/{source}
|
||||
ALTER TABLE iam_social_config DROP COLUMN IF EXISTS redirect_uri;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</parent>
|
||||
|
||||
<artifactId>capability-social</artifactId>
|
||||
<description>第三方社交登录能力模块(参考JustAuth重新实现)</description>
|
||||
<description>第三方社交登录通用 OAuth 协议能力模块(参考JustAuth重新实现)</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- web -->
|
||||
@@ -62,12 +62,6 @@
|
||||
<artifactId>common-config</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- mybatis-plus (社交平台配置表) -->
|
||||
<dependency>
|
||||
<groupId>cn.daxpay.open</groupId>
|
||||
<artifactId>common-mybatis-plus</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- hutool http (OAuth2 接口调用) -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
package cn.daxpay.open.platform.capability.social;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
/// # 第三方社交登录能力模块自动配置
|
||||
///
|
||||
/// 仅负责通用 OAuth 协议层的组件扫描(justauth 包下的 Request/Factory).
|
||||
/// 业务部分(配置管理、SocialEndpoint、绑定关系等)已迁移至 service-iam.
|
||||
///
|
||||
@Slf4j
|
||||
@AutoConfiguration
|
||||
@ComponentScan
|
||||
@ConfigurationPropertiesScan
|
||||
@MapperScan(annotationClass = Mapper.class)
|
||||
public class SocialAutoConfiguration {
|
||||
}
|
||||
|
||||
@@ -9,40 +9,19 @@ import cn.daxpay.open.platform.capability.social.justauth.request.QqRequest;
|
||||
import cn.daxpay.open.platform.capability.social.justauth.request.SocialAuthRequest;
|
||||
import cn.daxpay.open.platform.capability.social.justauth.request.WeComRequest;
|
||||
import cn.daxpay.open.platform.capability.social.justauth.request.WechatMpRequest;
|
||||
import cn.daxpay.open.platform.capability.social.config.entity.SocialConfig;
|
||||
import cn.daxpay.open.platform.capability.social.config.service.SocialConfigService;
|
||||
import cn.daxpay.open.platform.capability.social.justauth.SocialAuthConfig;
|
||||
import cn.daxpay.open.platform.capability.social.justauth.SocialSourceEnum;
|
||||
import cn.daxpay.open.platform.core.exception.operation.OperationFailException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/// # 社交授权请求工厂
|
||||
///
|
||||
/// 根据平台来源构建对应的 SocialAuthRequest, 配置从 iam_social_config 表按终端加载
|
||||
/// 仅负责按平台枚举创建对应的 [SocialAuthRequest] 实现.
|
||||
/// 配置加载由调用方(SocialEndpoint)通过 SocialConfigService 完成, 工厂不再耦合业务配置.
|
||||
///
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SocialAuthRequestFactory {
|
||||
|
||||
private final SocialConfigService socialConfigService;
|
||||
|
||||
/// 根据平台构建授权请求(自动加载已配置且启用的配置)
|
||||
public SocialAuthRequest build(String sourceName) {
|
||||
SocialSourceEnum source = SocialSourceEnum.of(sourceName);
|
||||
if (source == null) {
|
||||
// 社交登录: 不支持的平台
|
||||
throw new OperationFailException("error.social.unsupportedSource");
|
||||
}
|
||||
SocialConfig config = socialConfigService.findEnabledBySource(sourceName);
|
||||
if (config == null) {
|
||||
// 社交登录: 平台未配置或未启用
|
||||
throw new OperationFailException("error.social.configNotExist");
|
||||
}
|
||||
SocialAuthConfig authConfig = socialConfigService.buildAuthConfig(config);
|
||||
return this.create(source, authConfig);
|
||||
}
|
||||
|
||||
/// 根据平台来源创建对应的请求实现
|
||||
public SocialAuthRequest create(SocialSourceEnum source, SocialAuthConfig config) {
|
||||
return switch (source) {
|
||||
@@ -56,4 +35,14 @@ public class SocialAuthRequestFactory {
|
||||
case DOUYIN -> new DouyinRequest(config);
|
||||
};
|
||||
}
|
||||
|
||||
/// 根据平台来源创建对应的请求实现, 平台不支持时抛错
|
||||
public SocialAuthRequest create(String sourceName, SocialAuthConfig config) {
|
||||
SocialSourceEnum source = SocialSourceEnum.of(sourceName);
|
||||
if (source == null) {
|
||||
// 社交登录: 不支持的平台
|
||||
throw new OperationFailException("error.social.unsupportedSource");
|
||||
}
|
||||
return this.create(source, config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package cn.daxpay.open.platform.capability.social.bind;
|
||||
|
||||
import cn.daxpay.open.platform.capability.social.bind.result.SocialBindResult;
|
||||
import cn.daxpay.open.platform.capability.social.justauth.model.AuthUser;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 社交账号绑定存储接口
|
||||
///
|
||||
/// capability-social 层只定义绑定关系的访问契约, 具体实现由 service-iam 提供(操作 iam_user_social 表),
|
||||
/// 以保证依赖方向: 业务服务层依赖能力层, 而非相反
|
||||
///
|
||||
public interface SocialBindStore {
|
||||
|
||||
/// 根据平台来源和平台用户标识查询绑定的本地用户ID
|
||||
Optional<Long> findUserIdBySourceAndOpenId(String source, String openId);
|
||||
|
||||
/// 判断指定平台账号是否已被绑定
|
||||
boolean existsBind(String source, String openId);
|
||||
|
||||
/// 保存绑定关系
|
||||
/// @param userId 本地用户ID
|
||||
/// @param clientCode 终端编码
|
||||
/// @param authUser 平台返回的用户信息
|
||||
void saveBind(Long userId, String clientCode, AuthUser authUser);
|
||||
|
||||
/// 查询指定用户已绑定的所有第三方账号
|
||||
List<SocialBindResult> findBindsByUserId(Long userId);
|
||||
|
||||
/// 解除指定用户的某个平台绑定
|
||||
/// @return 是否解绑成功
|
||||
boolean removeBind(Long userId, String source);
|
||||
}
|
||||
@@ -16,7 +16,7 @@ import java.util.Arrays;
|
||||
public enum SocialSourceEnum implements I18nSupport {
|
||||
|
||||
|
||||
/// 微信公众号
|
||||
/// 微信开放平台
|
||||
WECHAT_MP(
|
||||
"weChat",
|
||||
"https://open.weixin.qq.com/connect/oauth2/authorize",
|
||||
|
||||
@@ -40,7 +40,7 @@ public abstract class AbstractSocialAuthRequest implements SocialAuthRequest {
|
||||
return SocialUrlBuilder.ofBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("redirect_uri", this.buildRedirectUri())
|
||||
.queryParam("state", state)
|
||||
.build();
|
||||
}
|
||||
@@ -70,7 +70,7 @@ public abstract class AbstractSocialAuthRequest implements SocialAuthRequest {
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("client_secret", config.getClientSecret())
|
||||
.queryParam("grant_type", "authorization_code")
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("redirect_uri", this.buildRedirectUri())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -81,6 +81,24 @@ public abstract class AbstractSocialAuthRequest implements SocialAuthRequest {
|
||||
.build();
|
||||
}
|
||||
|
||||
/// 构建回调地址(配置基础路径 + 平台编码, 形如 .../oauth-callback/{source})
|
||||
/// authorize 与 accessToken 两处必须一致, 集中在此方法保证.
|
||||
/// 配置项 redirect_uri 约定为前端回调基础路径(不含 source), 如:
|
||||
/// http://127.0.0.1:13333/auth/oauth-callback
|
||||
/// 实际传给第三方的为:
|
||||
/// http://127.0.0.1:13333/auth/oauth-callback/gitee
|
||||
protected String buildRedirectUri() {
|
||||
String base = config.getRedirectUri();
|
||||
if (base == null) {
|
||||
base = "";
|
||||
}
|
||||
// 去掉末尾斜杠, 避免出现 //gitee
|
||||
if (base.endsWith("/")) {
|
||||
base = base.substring(0, base.length() - 1);
|
||||
}
|
||||
return base + "/" + source.getCode();
|
||||
}
|
||||
|
||||
/// GET 请求
|
||||
protected String doGet(String url) {
|
||||
return this.doGet(url, null);
|
||||
|
||||
@@ -23,7 +23,7 @@ public class DingTalkRequest extends AbstractSocialAuthRequest {
|
||||
return SocialUrlBuilder.ofBaseUrl(this.getSource().authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", this.getConfig().getClientId())
|
||||
.queryParam("redirect_uri", this.getConfig().getRedirectUri())
|
||||
.queryParam("redirect_uri", this.buildRedirectUri())
|
||||
.queryParam("prompt", "consent")
|
||||
.queryParam("state", state)
|
||||
.build();
|
||||
|
||||
@@ -26,7 +26,7 @@ public class DouyinRequest extends AbstractSocialAuthRequest {
|
||||
.queryParam("client_key", this.getConfig().getClientId())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("scope", "user_info")
|
||||
.queryParam("redirect_uri", this.getConfig().getRedirectUri())
|
||||
.queryParam("redirect_uri", this.buildRedirectUri())
|
||||
.queryParam("state", state)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public class FeishuRequest extends AbstractSocialAuthRequest {
|
||||
public String authorize(String state) {
|
||||
return SocialUrlBuilder.ofBaseUrl(this.getSource().authorize())
|
||||
.queryParam("app_id", this.getConfig().getClientId())
|
||||
.queryParam("redirect_uri", this.encode(this.getConfig().getRedirectUri()))
|
||||
.queryParam("redirect_uri", this.encode(this.buildRedirectUri()))
|
||||
.queryParam("state", state)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public class WeComRequest extends AbstractSocialAuthRequest {
|
||||
return SocialUrlBuilder.ofBaseUrl(this.getSource().authorize())
|
||||
.queryParam("appid", this.getConfig().getClientId())
|
||||
.queryParam("agentid", this.getConfig().getAgentId())
|
||||
.queryParam("redirect_uri", this.getConfig().getRedirectUri())
|
||||
.queryParam("redirect_uri", this.buildRedirectUri())
|
||||
.queryParam("state", state)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public class WechatMpRequest extends AbstractSocialAuthRequest {
|
||||
public String authorize(String state) {
|
||||
return SocialUrlBuilder.ofBaseUrl(this.getSource().authorize())
|
||||
.queryParam("appid", this.getConfig().getClientId())
|
||||
.queryParam("redirect_uri", this.encode(this.getConfig().getRedirectUri()))
|
||||
.queryParam("redirect_uri", this.encode(this.buildRedirectUri()))
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("scope", "snsapi_userinfo")
|
||||
.queryParam("state", state.concat("#wechat_redirect"))
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package cn.daxpay.open.platform.capability.social.login;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/// # 社交登录处理器接口
|
||||
///
|
||||
/// capability-social 层在 LOGIN 场景下通过绑定关系确认了用户身份后, 委托 service-iam 完成实际的登录签发,
|
||||
/// 以保证 session 中的 UserDetail 完整(复用平台既有登录成功流程)
|
||||
///
|
||||
public interface SocialLoginHandler {
|
||||
|
||||
/// 使用已确认身份的 userId 完成登录(含 session 填充与登录成功回调), 返回 token
|
||||
/// @param userId 本地用户ID
|
||||
/// @param clientCode 终端编码
|
||||
String login(Long userId, String clientCode, HttpServletRequest request, HttpServletResponse response);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
-- 第三方平台登录配置表
|
||||
-- 配置页采用"枚举驱动 + 读时初始化"模式: 首次访问时为每个 SocialSource 枚举平台
|
||||
-- 插入占位记录(configured=false, 业务字段留空), 用户保存配置后才置 configured=true.
|
||||
-- 因此 client_id/client_secret/redirect_uri 等业务字段允许为空.
|
||||
-- 因此 client_id/client_secret 等业务字段允许为空.
|
||||
DROP TABLE IF EXISTS iam_social_config;
|
||||
CREATE TABLE IF NOT EXISTS iam_social_config (
|
||||
id BIGINT NOT NULL,
|
||||
@@ -14,7 +14,6 @@ CREATE TABLE IF NOT EXISTS iam_social_config (
|
||||
source VARCHAR(32) NOT NULL,
|
||||
client_id VARCHAR(128),
|
||||
client_secret VARCHAR(256),
|
||||
redirect_uri VARCHAR(256),
|
||||
extra JSONB DEFAULT '{}'::jsonb,
|
||||
configured BOOLEAN DEFAULT FALSE,
|
||||
enabled BOOLEAN DEFAULT FALSE,
|
||||
@@ -33,7 +32,6 @@ COMMENT ON COLUMN iam_social_config.deleted IS '逻辑删除';
|
||||
COMMENT ON COLUMN iam_social_config.source IS '平台编码';
|
||||
COMMENT ON COLUMN iam_social_config.client_id IS '客户端ID';
|
||||
COMMENT ON COLUMN iam_social_config.client_secret IS '客户端密钥';
|
||||
COMMENT ON COLUMN iam_social_config.redirect_uri IS '回调地址';
|
||||
COMMENT ON COLUMN iam_social_config.extra IS '平台特有配置';
|
||||
COMMENT ON COLUMN iam_social_config.configured IS '是否已完成配置';
|
||||
COMMENT ON COLUMN iam_social_config.enabled IS '是否启用';
|
||||
|
||||
@@ -15,9 +15,6 @@
|
||||
"clientSecret": {
|
||||
"notBlank": "Client secret cannot be blank"
|
||||
},
|
||||
"redirectUri": {
|
||||
"notBlank": "Redirect URI cannot be blank"
|
||||
},
|
||||
"clientCode": {
|
||||
"notBlank": "Client code cannot be blank"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"weChat": "微信公众号",
|
||||
"weChat": "微信开放平台",
|
||||
"weCom": "企业微信",
|
||||
"qq": "QQ",
|
||||
"github": "GitHub",
|
||||
|
||||
@@ -15,9 +15,6 @@
|
||||
"clientSecret": {
|
||||
"notBlank": "客户端密钥不能为空"
|
||||
},
|
||||
"redirectUri": {
|
||||
"notBlank": "回调地址不能为空"
|
||||
},
|
||||
"clientCode": {
|
||||
"notBlank": "终端不能为空"
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package cn.daxpay.open.platform.iam.auth.service;
|
||||
import cn.daxpay.open.platform.system.entity.config.platform.security.PlatformLoginSecurityConfig;
|
||||
import cn.daxpay.open.platform.system.entity.config.platform.security.PlatformPasswordPolicyConfig;
|
||||
import cn.daxpay.open.platform.system.entity.config.platform.security.PlatformSessionManagementConfig;
|
||||
import cn.daxpay.open.platform.system.service.config.PlatformConfigService;
|
||||
import cn.daxpay.open.platform.system.service.config.PlatformSecurityConfigService;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -15,21 +15,21 @@ import org.springframework.stereotype.Service;
|
||||
@RequiredArgsConstructor
|
||||
public class IamSecurityConfigService {
|
||||
|
||||
private final PlatformConfigService platformConfigService;
|
||||
private final PlatformSecurityConfigService platformSecurityConfigService;
|
||||
|
||||
/// 获取密码策略配置
|
||||
public PlatformPasswordPolicyConfig getPasswordPolicy() {
|
||||
return platformConfigService.getPasswordPolicyConfig();
|
||||
return platformSecurityConfigService.getPasswordPolicyConfig();
|
||||
}
|
||||
|
||||
/// 获取登录安全配置
|
||||
public PlatformLoginSecurityConfig getLoginSecurity() {
|
||||
return platformConfigService.getLoginSecurityConfig();
|
||||
return platformSecurityConfigService.getLoginSecurityConfig();
|
||||
}
|
||||
|
||||
/// 获取会话管理配置
|
||||
public PlatformSessionManagementConfig getSessionManagement() {
|
||||
return platformConfigService.getSessionManagementConfig();
|
||||
return platformSecurityConfigService.getSessionManagementConfig();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package cn.daxpay.open.platform.capability.social.config.controller;
|
||||
package cn.daxpay.open.platform.iam.controller.social;
|
||||
|
||||
import cn.daxpay.open.platform.capability.social.config.param.SocialConfigParam;
|
||||
import cn.daxpay.open.platform.capability.social.config.result.SocialConfigResult;
|
||||
import cn.daxpay.open.platform.capability.social.config.service.SocialConfigService;
|
||||
import cn.daxpay.open.platform.iam.param.social.SocialConfigParam;
|
||||
import cn.daxpay.open.platform.iam.result.social.SocialConfigResult;
|
||||
import cn.daxpay.open.platform.iam.service.social.SocialConfigService;
|
||||
import cn.daxpay.open.platform.core.annotation.PermCode;
|
||||
import cn.daxpay.open.platform.core.rest.Res;
|
||||
import cn.daxpay.open.platform.core.rest.result.Result;
|
||||
@@ -1,11 +1,11 @@
|
||||
package cn.daxpay.open.platform.capability.social.config.convert;
|
||||
package cn.daxpay.open.platform.iam.convert.social;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.daxpay.open.platform.capability.social.config.entity.SocialConfig;
|
||||
import cn.daxpay.open.platform.capability.social.config.param.SocialConfigParam;
|
||||
import cn.daxpay.open.platform.capability.social.config.result.SocialConfigResult;
|
||||
import cn.daxpay.open.platform.iam.entity.social.SocialConfig;
|
||||
import cn.daxpay.open.platform.iam.param.social.SocialConfigParam;
|
||||
import cn.daxpay.open.platform.iam.result.social.SocialConfigResult;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import org.mapstruct.Mapper;
|
||||
@@ -1,9 +1,9 @@
|
||||
package cn.daxpay.open.platform.capability.social.config.dao;
|
||||
package cn.daxpay.open.platform.iam.dao.social;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import cn.daxpay.open.platform.capability.social.config.entity.SocialConfig;
|
||||
import cn.daxpay.open.platform.iam.entity.social.SocialConfig;
|
||||
import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.daxpay.open.platform.capability.social.config.dao;
|
||||
package cn.daxpay.open.platform.iam.dao.social;
|
||||
|
||||
import cn.daxpay.open.platform.capability.social.config.entity.SocialConfig;
|
||||
import cn.daxpay.open.platform.iam.entity.social.SocialConfig;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
package cn.daxpay.open.platform.capability.social.endpoint;
|
||||
package cn.daxpay.open.platform.iam.endpoint.social;
|
||||
|
||||
import cn.daxpay.open.platform.capability.auth.util.SecurityUtil;
|
||||
import cn.daxpay.open.platform.capability.social.auth.SocialAuthRequestFactory;
|
||||
import cn.daxpay.open.platform.capability.social.bind.SocialBindStore;
|
||||
import cn.daxpay.open.platform.capability.social.bind.result.SocialBindResult;
|
||||
import cn.daxpay.open.platform.capability.social.cache.RedisSocialStateCache;
|
||||
import cn.daxpay.open.platform.capability.social.cache.SocialAuthContext;
|
||||
import cn.daxpay.open.platform.capability.social.cache.SocialAuthMode;
|
||||
import cn.daxpay.open.platform.capability.social.config.entity.SocialConfig;
|
||||
import cn.daxpay.open.platform.capability.social.config.service.SocialConfigService;
|
||||
import cn.daxpay.open.platform.capability.social.justauth.SocialAuthConfig;
|
||||
import cn.daxpay.open.platform.capability.social.justauth.SocialSourceEnum;
|
||||
import cn.daxpay.open.platform.capability.social.justauth.model.AuthCallback;
|
||||
import cn.daxpay.open.platform.capability.social.justauth.model.AuthUser;
|
||||
import cn.daxpay.open.platform.capability.social.justauth.request.SocialAuthRequest;
|
||||
import cn.daxpay.open.platform.capability.social.login.SocialLoginHandler;
|
||||
import cn.daxpay.open.platform.core.annotation.IgnoreAuth;
|
||||
import cn.daxpay.open.platform.core.exception.operation.OperationFailException;
|
||||
import cn.daxpay.open.platform.core.rest.Res;
|
||||
import cn.daxpay.open.platform.core.rest.result.Result;
|
||||
import cn.daxpay.open.platform.iam.result.social.SocialBindResult;
|
||||
import cn.daxpay.open.platform.iam.result.social.SocialEnabledPlatformResult;
|
||||
import cn.daxpay.open.platform.iam.result.social.SocialExchangeResult;
|
||||
import cn.daxpay.open.platform.iam.service.social.IamSocialLoginHandler;
|
||||
import cn.daxpay.open.platform.iam.service.social.IamUserSocialBindStore;
|
||||
import cn.daxpay.open.platform.iam.service.social.SocialConfigService;
|
||||
import cn.daxpay.open.platform.iam.service.social.cache.RedisSocialStateCache;
|
||||
import cn.daxpay.open.platform.iam.service.social.cache.SocialAuthContext;
|
||||
import cn.daxpay.open.platform.iam.service.social.cache.SocialAuthMode;
|
||||
import cn.daxpay.open.platform.system.entity.config.platform.PlatformUrlConfig;
|
||||
import cn.daxpay.open.platform.iam.enums.SocialClientEnum;
|
||||
import cn.daxpay.open.platform.system.service.config.PlatformUrlConfigService;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -35,13 +40,13 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/// # 第三方社交登录端点
|
||||
///
|
||||
/// 提供 OAuth2 授权(render)、授权码兑换(exchange)、绑定管理(bind list/unbind)等接口.
|
||||
/// 采用前端回调模式: 第三方平台直接重定向到前端回调页, 前端拿到 code+state 后
|
||||
/// 调用 exchange API 完成换 token, 后端不做 302 跳转.
|
||||
/// state 超时使用系统默认常量.
|
||||
///
|
||||
/// # 第三方社交登录端点
|
||||
///
|
||||
/// 提供 OAuth2 授权(render)、授权码兑换(exchange)、绑定管理(bind list/unbind)等接口.
|
||||
/// 采用前端回调模式: 第三方平台直接重定向到前端回调页, 前端拿到 code+state 后
|
||||
/// 调用 exchange API 完成换 token, 后端不做 302 跳转.
|
||||
/// state 超时使用系统默认常量.
|
||||
///
|
||||
@Slf4j
|
||||
@IgnoreAuth
|
||||
@Tag(name = "第三方社交登录")
|
||||
@@ -57,15 +62,25 @@ public class SocialEndpoint {
|
||||
|
||||
private final RedisSocialStateCache redisSocialStateCache;
|
||||
|
||||
private final SocialBindStore socialBindStore;
|
||||
private final IamUserSocialBindStore socialBindStore;
|
||||
|
||||
private final SocialLoginHandler socialLoginHandler;
|
||||
private final IamSocialLoginHandler socialLoginHandler;
|
||||
|
||||
private final SocialConfigService socialConfigService;
|
||||
|
||||
private final PlatformUrlConfigService platformUrlConfigService;
|
||||
|
||||
/// 查询已启用的第三方登录平台(登录页公开接口)
|
||||
/// 仅返回平台编码列表, 不含任何敏感字段, 供登录页动态渲染第三方登录按钮.
|
||||
@Operation(summary = "查询已启用的第三方登录平台")
|
||||
@GetMapping("/enabled-list")
|
||||
public Result<List<SocialEnabledPlatformResult>> enabledList() {
|
||||
return Res.ok(socialConfigService.findEnabledList());
|
||||
}
|
||||
|
||||
/// 生成授权地址并缓存上下文(前端拿到后跳转)
|
||||
/// @param source 平台来源
|
||||
/// @param client 终端编码
|
||||
/// @param client 终端编码(admin/merchant), 用于解析端点配置中的 baseUrl
|
||||
/// @param mode 授权场景(不传则按登录态判断: 已登录=绑定, 未登录=登录)
|
||||
/// @param redirect 成功后前端跳转路径(可选)
|
||||
@Operation(summary = "生成授权地址")
|
||||
@@ -76,7 +91,7 @@ public class SocialEndpoint {
|
||||
@RequestParam(required = false) String mode,
|
||||
@RequestParam(required = false) String redirect) {
|
||||
// 加载平台配置(全局唯一)
|
||||
SocialConfig config = socialConfigService.findEnabledBySource(source);
|
||||
var config = this.socialConfigService.findEnabledBySource(source);
|
||||
if (config == null) {
|
||||
// 社交登录: 平台未配置或未启用
|
||||
throw new OperationFailException("error.social.configNotExist");
|
||||
@@ -86,6 +101,14 @@ public class SocialEndpoint {
|
||||
// 社交登录: 不支持的平台
|
||||
throw new OperationFailException("error.social.unsupportedSource");
|
||||
}
|
||||
// 按 client 解析前端 baseUrl(用于 redirectUri 自动生成)
|
||||
PlatformUrlConfig urlConfig = platformUrlConfigService.getUrlConfig();
|
||||
String baseUrl = SocialClientEnum.of(client).resolveBaseUrl(urlConfig);
|
||||
// 回调地址由端点配置的 baseUrl 自动生成, 必须配置 baseUrl
|
||||
if (StrUtil.isBlank(baseUrl)) {
|
||||
// 社交登录: 端点配置缺失
|
||||
throw new OperationFailException("error.social.endpointNotConfigured");
|
||||
}
|
||||
SocialAuthMode authMode = this.resolveMode(mode);
|
||||
// 构建 state 并缓存上下文(含平台来源, 供 exchange 阶段使用)
|
||||
String state = IdUtil.fastSimpleUUID();
|
||||
@@ -96,11 +119,11 @@ public class SocialEndpoint {
|
||||
.setSource(source);
|
||||
if (authMode == SocialAuthMode.BIND) {
|
||||
// 绑定场景必须已登录, 用户ID从登录态获取
|
||||
context.setUserId(cn.daxpay.open.platform.capability.auth.util.SecurityUtil.getUserId());
|
||||
context.setUserId(SecurityUtil.getUserId());
|
||||
}
|
||||
redisSocialStateCache.cache(state, context, STATE_TIMEOUT_SECONDS);
|
||||
// 构建授权请求并生成授权地址
|
||||
SocialAuthConfig authConfig = socialConfigService.buildAuthConfig(config);
|
||||
SocialAuthConfig authConfig = socialConfigService.buildAuthConfig(config, baseUrl);
|
||||
SocialAuthRequest request = socialAuthRequestFactory.create(socialSource, authConfig);
|
||||
String authorizeUrl = request.authorize(state);
|
||||
return Res.ok(authorizeUrl);
|
||||
@@ -125,8 +148,17 @@ public class SocialEndpoint {
|
||||
String source = context.getSource();
|
||||
String clientCode = context.getClientCode();
|
||||
try {
|
||||
// 用授权码换取用户信息
|
||||
SocialAuthRequest authRequest = socialAuthRequestFactory.build(source);
|
||||
// 加载平台配置 + 创建对应 Request
|
||||
cn.daxpay.open.platform.iam.entity.social.SocialConfig config = socialConfigService.findEnabledBySource(source);
|
||||
if (config == null) {
|
||||
return Res.ok(new SocialExchangeResult().setError("oauth_failed"));
|
||||
}
|
||||
// exchange 阶段的 redirect_uri 必须与 authorize 阶段一致, 从端点配置按 client 解析 baseUrl
|
||||
PlatformUrlConfig urlConfig = platformUrlConfigService.getUrlConfig();
|
||||
String baseUrl = SocialClientEnum.of(clientCode).resolveBaseUrl(urlConfig);
|
||||
SocialAuthConfig authConfig = socialConfigService.buildAuthConfig(config, baseUrl);
|
||||
SocialSourceEnum socialSource = SocialSourceEnum.of(source);
|
||||
SocialAuthRequest authRequest = socialAuthRequestFactory.create(socialSource, authConfig);
|
||||
AuthUser authUser = authRequest.login(AuthCallback.of(code, state));
|
||||
// 按场景处理
|
||||
if (context.getMode() == SocialAuthMode.BIND) {
|
||||
@@ -153,7 +185,7 @@ public class SocialEndpoint {
|
||||
@Operation(summary = "已绑定的第三方账号列表")
|
||||
@GetMapping("/bind/list")
|
||||
public Result<List<SocialBindResult>> bindList() {
|
||||
Long userId = cn.daxpay.open.platform.capability.auth.util.SecurityUtil.getUserId();
|
||||
Long userId = SecurityUtil.getUserId();
|
||||
return Res.ok(socialBindStore.findBindsByUserId(userId));
|
||||
}
|
||||
|
||||
@@ -162,7 +194,7 @@ public class SocialEndpoint {
|
||||
@Operation(summary = "解除第三方账号绑定")
|
||||
@PostMapping("/unbind")
|
||||
public Result<Void> unbind(@RequestParam String source) {
|
||||
Long userId = cn.daxpay.open.platform.capability.auth.util.SecurityUtil.getUserId();
|
||||
Long userId = SecurityUtil.getUserId();
|
||||
boolean success = socialBindStore.removeBind(userId, source);
|
||||
if (!success) {
|
||||
// 社交登录: 未绑定该平台, 无需解绑
|
||||
@@ -180,7 +212,7 @@ public class SocialEndpoint {
|
||||
}
|
||||
}
|
||||
// 默认: 已登录走绑定, 未登录走登录
|
||||
boolean login = cn.daxpay.open.platform.capability.auth.util.SecurityUtil.isLogin();
|
||||
boolean login = SecurityUtil.isLogin();
|
||||
return login ? SocialAuthMode.BIND : SocialAuthMode.LOGIN;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.daxpay.open.platform.capability.social.config.entity;
|
||||
package cn.daxpay.open.platform.iam.entity.social;
|
||||
|
||||
import cn.daxpay.open.platform.capability.social.config.convert.SocialConfigConvert;
|
||||
import cn.daxpay.open.platform.capability.social.config.result.SocialConfigResult;
|
||||
import cn.daxpay.open.platform.iam.convert.social.SocialConfigConvert;
|
||||
import cn.daxpay.open.platform.iam.result.social.SocialConfigResult;
|
||||
import cn.daxpay.open.platform.capability.social.justauth.SocialSourceEnum;
|
||||
import cn.daxpay.open.platform.common.mybatisplus.base.MpBaseEntity;
|
||||
import cn.daxpay.open.platform.common.mybatisplus.function.ToResult;
|
||||
@@ -15,7 +15,8 @@ import lombok.experimental.Accessors;
|
||||
|
||||
/// # 第三方平台登录配置
|
||||
///
|
||||
/// 记录各社交平台(appId/appSecret/回调地址等)的配置, 全局唯一(按 source 区分), 管理端可动态维护.
|
||||
/// 记录各社交平台(appId/appSecret 等)的配置, 全局唯一(按 source 区分), 管理端可动态维护.
|
||||
/// 回调地址不再单独配置, 由端点配置(PlatformUrlConfig)的 baseUrl 自动生成: {baseUrl}/auth/oauth-callback/{source}.
|
||||
/// 平台特有参数(如企业微信 agentId)统一存放在 extra jsonb 字段, 避免表结构随平台扩展频繁变更.
|
||||
/// `configured` 标识是否已完成配置: 配置页内存合并时缺失项为 false, 用户保存配置后才为 true.
|
||||
///
|
||||
@@ -37,9 +38,6 @@ public class SocialConfig extends MpBaseEntity implements ToResult<SocialConfigR
|
||||
@TableField(typeHandler = DataEncryptTypeHandler.class)
|
||||
private String clientSecret;
|
||||
|
||||
/// 回调地址
|
||||
private String redirectUri;
|
||||
|
||||
/// 平台特有配置(如企业微信 agentId), 以 jsonb 存储, 此处为原始 JSON 文本
|
||||
@TableField(typeHandler = JsonbStringTypeHandler.class)
|
||||
private String extra;
|
||||
@@ -0,0 +1,47 @@
|
||||
package cn.daxpay.open.platform.iam.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import cn.daxpay.open.platform.system.entity.config.platform.PlatformUrlConfig;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/// # 社交登录终端编码
|
||||
///
|
||||
/// 用于按 client(admin/merchant) 解析端点配置中对应的前端 baseUrl.
|
||||
/// 默认值 ADMIN: client 参数无法识别时回退到管理端配置.
|
||||
///
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum SocialClientEnum {
|
||||
|
||||
/// 运营管理端
|
||||
ADMIN("admin") {
|
||||
@Override
|
||||
public String resolveBaseUrl(PlatformUrlConfig config) {
|
||||
return config.getAdminBaseUrl();
|
||||
}
|
||||
},
|
||||
|
||||
/// 商户管理端
|
||||
MERCHANT("merchant") {
|
||||
@Override
|
||||
public String resolveBaseUrl(PlatformUrlConfig config) {
|
||||
return config.getMerchantBaseUrl();
|
||||
}
|
||||
};
|
||||
|
||||
/// 终端编码
|
||||
private final String code;
|
||||
|
||||
/// 从端点配置中解析当前终端对应的 baseUrl
|
||||
public abstract String resolveBaseUrl(PlatformUrlConfig config);
|
||||
|
||||
/// 根据编码查找枚举, 无法识别时回退到 ADMIN(容错)
|
||||
public static SocialClientEnum of(String code) {
|
||||
return Arrays.stream(values())
|
||||
.filter(e -> e.code.equalsIgnoreCase(code))
|
||||
.findFirst()
|
||||
.orElse(ADMIN);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.daxpay.open.platform.capability.social.config.param;
|
||||
package cn.daxpay.open.platform.iam.param.social;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -28,10 +28,6 @@ public class SocialConfigParam {
|
||||
@Schema(description = "客户端密钥(编辑未修改不传, 修改传新值)")
|
||||
private String clientSecret;
|
||||
|
||||
@Schema(description = "回调地址")
|
||||
@NotBlank(message = "{validation.field.redirectUri.notBlank}")
|
||||
private String redirectUri;
|
||||
|
||||
/// 平台特有配置(如企业微信 agentId)
|
||||
@Schema(description = "平台特有配置(如企业微信 agentId)")
|
||||
private Map<String, String> extra;
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.daxpay.open.platform.capability.social.bind.result;
|
||||
package cn.daxpay.open.platform.iam.result.social;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.daxpay.open.platform.capability.social.config.result;
|
||||
package cn.daxpay.open.platform.iam.result.social;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -28,9 +28,6 @@ public class SocialConfigResult {
|
||||
@Schema(description = "客户端密钥(脱敏)")
|
||||
private String clientSecret;
|
||||
|
||||
@Schema(description = "回调地址")
|
||||
private String redirectUri;
|
||||
|
||||
/// 平台特有配置(如企业微信 agentId)
|
||||
@Schema(description = "平台特有配置(如企业微信 agentId)")
|
||||
private Map<String, String> extra;
|
||||
@@ -0,0 +1,21 @@
|
||||
package cn.daxpay.open.platform.iam.result.social;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 已启用的第三方登录平台
|
||||
///
|
||||
/// 登录页未登录场景下的最小公开返回, 仅暴露平台编码(source),
|
||||
/// 不含 clientId/clientSecret/redirectUri/extra 等任何敏感字段.
|
||||
/// 平台显示名/图标/品牌色由前端本地映射表(socialEnum.ts)决定.
|
||||
///
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "已启用的第三方登录平台")
|
||||
public class SocialEnabledPlatformResult {
|
||||
|
||||
/// 平台编码(weChat/weCom/qq/github/gitee/feishu/dingTalk/douyin)
|
||||
@Schema(description = "平台编码")
|
||||
private String source;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.daxpay.open.platform.capability.social.endpoint;
|
||||
package cn.daxpay.open.platform.iam.result.social;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@@ -1,6 +1,5 @@
|
||||
package cn.daxpay.open.platform.iam.service.social;
|
||||
|
||||
import cn.daxpay.open.platform.capability.social.login.SocialLoginHandler;
|
||||
import cn.daxpay.open.platform.core.code.CommonCode;
|
||||
import cn.daxpay.open.platform.core.entity.UserDetail;
|
||||
import cn.daxpay.open.platform.iam.result.user.UserInfoResult;
|
||||
@@ -14,18 +13,21 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/// # 社交登录处理器实现
|
||||
/// # 社交登录处理器
|
||||
///
|
||||
/// 在 LOGIN 场景下, 通过绑定关系确认用户身份后, 委托本类完成 Sa-Token 登录签发与 session 填充
|
||||
/// 在 LOGIN 场景下, 通过绑定关系确认用户身份后, 完成本地 Sa-Token 登录签发与 session 填充.
|
||||
/// 被 SocialEndpoint 直接注入使用(同模块, 无需 SPI 抽象).
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class IamSocialLoginHandler implements SocialLoginHandler {
|
||||
public class IamSocialLoginHandler {
|
||||
|
||||
private final UserQueryService userQueryService;
|
||||
|
||||
@Override
|
||||
/// 使用已确认身份的 userId 完成登录(含 session 填充), 返回 token
|
||||
/// @param userId 本地用户ID
|
||||
/// @param clientCode 终端编码
|
||||
public String login(Long userId, String clientCode, HttpServletRequest request, HttpServletResponse response) {
|
||||
// 加载用户信息并构建会话对象
|
||||
UserInfoResult userInfo = userQueryService.findById(userId);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package cn.daxpay.open.platform.iam.service.social;
|
||||
|
||||
import cn.daxpay.open.platform.capability.social.bind.SocialBindStore;
|
||||
import cn.daxpay.open.platform.capability.social.bind.result.SocialBindResult;
|
||||
import cn.daxpay.open.platform.iam.result.social.SocialBindResult;
|
||||
import cn.daxpay.open.platform.capability.social.justauth.model.AuthUser;
|
||||
import cn.daxpay.open.platform.iam.dao.social.IamUserSocialManager;
|
||||
import cn.daxpay.open.platform.iam.entity.social.IamUserSocial;
|
||||
@@ -14,19 +13,19 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/// # 用户第三方账号绑定存储实现
|
||||
/// # 用户第三方账号绑定存储
|
||||
///
|
||||
/// 实现 capability-social 定义的 SocialBindStore 契约, 操作 iam_user_social 表,
|
||||
/// 被 SocialEndpoint 通过接口注入使用(无需 capability-social 反向依赖 service-iam)
|
||||
/// 操作 iam_user_social 表, 提供"按平台+openId 查用户/保存绑定/列绑定/解绑"等能力,
|
||||
/// 被 SocialEndpoint 直接注入使用(同模块, 无需 SPI 抽象).
|
||||
///
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class IamUserSocialBindStore implements SocialBindStore {
|
||||
public class IamUserSocialBindStore {
|
||||
|
||||
private final IamUserSocialManager iamUserSocialManager;
|
||||
|
||||
@Override
|
||||
/// 根据平台来源和平台用户标识查询绑定的本地用户ID
|
||||
public Optional<Long> findUserIdBySourceAndOpenId(String source, String openId) {
|
||||
return iamUserSocialManager.lambdaQuery()
|
||||
.eq(IamUserSocial::getSource, source)
|
||||
@@ -35,7 +34,7 @@ public class IamUserSocialBindStore implements SocialBindStore {
|
||||
.map(IamUserSocial::getUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
/// 判断指定平台账号是否已被绑定
|
||||
public boolean existsBind(String source, String openId) {
|
||||
return iamUserSocialManager.lambdaQuery()
|
||||
.eq(IamUserSocial::getSource, source)
|
||||
@@ -43,7 +42,10 @@ public class IamUserSocialBindStore implements SocialBindStore {
|
||||
.exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
/// 保存绑定关系
|
||||
/// @param userId 本地用户ID
|
||||
/// @param clientCode 终端编码
|
||||
/// @param authUser 平台返回的用户信息
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveBind(Long userId, String clientCode, AuthUser authUser) {
|
||||
String source = authUser.getSource();
|
||||
@@ -84,7 +86,7 @@ public class IamUserSocialBindStore implements SocialBindStore {
|
||||
iamUserSocialManager.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
/// 查询指定用户已绑定的所有第三方账号
|
||||
public List<SocialBindResult> findBindsByUserId(Long userId) {
|
||||
return iamUserSocialManager.lambdaQuery()
|
||||
.eq(IamUserSocial::getUserId, userId)
|
||||
@@ -93,7 +95,8 @@ public class IamUserSocialBindStore implements SocialBindStore {
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
/// 解除指定用户的某个平台绑定
|
||||
/// @return 是否解绑成功
|
||||
public boolean removeBind(Long userId, String source) {
|
||||
return iamUserSocialManager.lambdaUpdate()
|
||||
.eq(IamUserSocial::getUserId, userId)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.daxpay.open.platform.capability.social.config.service;
|
||||
package cn.daxpay.open.platform.iam.service.social;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -6,11 +6,12 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import cn.daxpay.open.platform.capability.social.config.convert.SocialConfigConvert;
|
||||
import cn.daxpay.open.platform.capability.social.config.dao.SocialConfigManager;
|
||||
import cn.daxpay.open.platform.capability.social.config.entity.SocialConfig;
|
||||
import cn.daxpay.open.platform.capability.social.config.param.SocialConfigParam;
|
||||
import cn.daxpay.open.platform.capability.social.config.result.SocialConfigResult;
|
||||
import cn.daxpay.open.platform.iam.convert.social.SocialConfigConvert;
|
||||
import cn.daxpay.open.platform.iam.dao.social.SocialConfigManager;
|
||||
import cn.daxpay.open.platform.iam.entity.social.SocialConfig;
|
||||
import cn.daxpay.open.platform.iam.param.social.SocialConfigParam;
|
||||
import cn.daxpay.open.platform.iam.result.social.SocialConfigResult;
|
||||
import cn.daxpay.open.platform.iam.result.social.SocialEnabledPlatformResult;
|
||||
import cn.daxpay.open.platform.capability.social.justauth.SocialAuthConfig;
|
||||
import cn.daxpay.open.platform.capability.social.justauth.SocialSourceEnum;
|
||||
import cn.daxpay.open.platform.core.exception.operation.OperationFailException;
|
||||
@@ -104,8 +105,24 @@ public class SocialConfigService {
|
||||
return socialConfigManager.findEnabledBySource(source).orElse(null);
|
||||
}
|
||||
|
||||
/// 查询所有已配置且启用的平台(登录页公开接口使用)
|
||||
/// 仅返回平台编码(source), 不暴露任何敏感字段(clientId/clientSecret/redirectUri/extra 等).
|
||||
public List<SocialEnabledPlatformResult> findEnabledList() {
|
||||
return socialConfigManager.findAllEnabled().stream()
|
||||
.map(c -> new SocialEnabledPlatformResult().setSource(c.getSource()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// 将配置实体转换为授权配置
|
||||
public SocialAuthConfig buildAuthConfig(SocialConfig entity) {
|
||||
/// @param baseUrl 端点解析出的前端基础地址, 用于 redirectUri 自动生成(exchange 阶段传 null 表示不需要 redirectUri)
|
||||
public SocialAuthConfig buildAuthConfig(SocialConfig entity, String baseUrl) {
|
||||
// 回调地址由端点配置自动生成: {baseUrl}/auth/oauth-callback(exchange 阶段 baseUrl 为 null 时跳过)
|
||||
String redirectUri = null;
|
||||
if (StrUtil.isNotBlank(baseUrl)) {
|
||||
// 末尾斜杠归一化由 AbstractSocialAuthRequest.buildRedirectUri 处理
|
||||
String base = StrUtil.removeSuffix(baseUrl, "/");
|
||||
redirectUri = base + "/auth/oauth-callback";
|
||||
}
|
||||
// 企业微信 agentId 等平台特有参数从 extra(jsonb) 读取
|
||||
String agentId = null;
|
||||
if (StrUtil.isNotBlank(entity.getExtra())) {
|
||||
@@ -114,7 +131,7 @@ public class SocialConfigService {
|
||||
return new SocialAuthConfig()
|
||||
.setClientId(entity.getClientId())
|
||||
.setClientSecret(entity.getClientSecret())
|
||||
.setRedirectUri(entity.getRedirectUri())
|
||||
.setRedirectUri(redirectUri)
|
||||
.setAgentId(agentId);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.daxpay.open.platform.capability.social.cache;
|
||||
package cn.daxpay.open.platform.iam.service.social.cache;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.daxpay.open.platform.capability.social.cache;
|
||||
package cn.daxpay.open.platform.iam.service.social.cache;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.daxpay.open.platform.capability.social.cache;
|
||||
package cn.daxpay.open.platform.iam.service.social.cache;
|
||||
|
||||
/// # 社交登录授权场景
|
||||
///
|
||||
@@ -0,0 +1,41 @@
|
||||
package cn.daxpay.open.platform.system.controller.config;
|
||||
|
||||
import cn.daxpay.open.platform.core.annotation.PermCode;
|
||||
import cn.daxpay.open.platform.core.rest.Res;
|
||||
import cn.daxpay.open.platform.core.rest.result.Result;
|
||||
import cn.daxpay.open.platform.system.param.config.PlatformOssConfigParam;
|
||||
import cn.daxpay.open.platform.system.result.config.platform.PlatformOssConfigResult;
|
||||
import cn.daxpay.open.platform.system.service.config.PlatformOssConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/// # 平台OSS配置
|
||||
///
|
||||
/// 管理对象存储配置
|
||||
@PermCode(menuCode = "system:platform:config")
|
||||
@Validated
|
||||
@Tag(name = "平台OSS配置")
|
||||
@RestController
|
||||
@RequestMapping("/platform/config/oss")
|
||||
@RequiredArgsConstructor
|
||||
public class PlatformOssConfigController {
|
||||
private final PlatformOssConfigService platformOssConfigService;
|
||||
|
||||
@PermCode(code = "platformConfig:view", nameCn = "平台配置查看", nameEn = "Platform Config View")
|
||||
@Operation(summary = "获取OSS配置")
|
||||
@GetMapping("/get")
|
||||
public Result<PlatformOssConfigResult> getOssConfig() {
|
||||
return Res.ok(platformOssConfigService.findOssConfig());
|
||||
}
|
||||
|
||||
@PermCode(code = "platformConfig:manage", nameCn = "平台配置管理", nameEn = "Platform Config Manage")
|
||||
@Operation(summary = "更新OSS配置")
|
||||
@PostMapping("/update")
|
||||
public Result<Void> updateOssConfig(@RequestBody @Validated PlatformOssConfigParam param) {
|
||||
platformOssConfigService.updateOssConfig(param);
|
||||
return Res.ok();
|
||||
}
|
||||
}
|
||||
@@ -4,122 +4,106 @@ import cn.daxpay.open.platform.core.annotation.IgnoreAuth;
|
||||
import cn.daxpay.open.platform.core.annotation.PermCode;
|
||||
import cn.daxpay.open.platform.core.rest.Res;
|
||||
import cn.daxpay.open.platform.core.rest.result.Result;
|
||||
import cn.daxpay.open.platform.system.param.config.*;
|
||||
import cn.daxpay.open.platform.system.param.config.security.*;
|
||||
import cn.daxpay.open.platform.system.result.config.platform.*;
|
||||
import cn.daxpay.open.platform.system.service.config.PlatformConfigService;
|
||||
import cn.daxpay.open.platform.system.service.config.PlatformSecurityConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
||||
/// # 平台配置
|
||||
/// # 平台安全配置
|
||||
///
|
||||
/// 管理密码策略、登录安全、会话管理、异常登录检测、双因素认证等安全类配置
|
||||
@PermCode(menuCode = "system:security:config")
|
||||
@Validated
|
||||
@Tag(name = "平台配置")
|
||||
@Tag(name = "平台安全配置")
|
||||
@RestController
|
||||
@RequestMapping("/platform/config")
|
||||
@RequestMapping("/platform/config/security")
|
||||
@RequiredArgsConstructor
|
||||
public class PlatformConfigController {
|
||||
private final PlatformConfigService platformConfigService;
|
||||
public class PlatformSecurityConfigController {
|
||||
private final PlatformSecurityConfigService platformSecurityConfigService;
|
||||
|
||||
@PermCode(code = "security:view", nameCn = "安全配置查看", nameEn = "Security View")
|
||||
@Operation(summary = "获取密码策略配置")
|
||||
@GetMapping("/security/password-policy/get")
|
||||
@GetMapping("/password-policy/get")
|
||||
public Result<PlatformPasswordPolicyConfigResult> getPasswordPolicyConfig() {
|
||||
return Res.ok(platformConfigService.findPasswordPolicyConfig());
|
||||
return Res.ok(platformSecurityConfigService.findPasswordPolicyConfig());
|
||||
}
|
||||
|
||||
@IgnoreAuth
|
||||
@Operation(summary = "获取密码策略校验配置(供前端校验使用)")
|
||||
@GetMapping("/security/password-policy/validate-config")
|
||||
@GetMapping("/password-policy/validate-config")
|
||||
public Result<PlatformPasswordPolicyConfigResult> getPasswordPolicyValidateConfig() {
|
||||
return Res.ok(platformConfigService.findPasswordPolicyConfig());
|
||||
return Res.ok(platformSecurityConfigService.findPasswordPolicyConfig());
|
||||
}
|
||||
|
||||
@PermCode(code = "security:manage", nameCn = "安全配置管理", nameEn = "Security Manage")
|
||||
@Operation(summary = "更新密码策略配置")
|
||||
@PostMapping("/security/password-policy/update")
|
||||
@PostMapping("/password-policy/update")
|
||||
public Result<Void> updatePasswordPolicyConfig(@RequestBody @Validated PlatformPasswordPolicyConfigParam param) {
|
||||
platformConfigService.updatePasswordPolicyConfig(param);
|
||||
platformSecurityConfigService.updatePasswordPolicyConfig(param);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@PermCode(code = "security:view", nameCn = "安全配置查看", nameEn = "Security View")
|
||||
@Operation(summary = "获取登录安全配置")
|
||||
@GetMapping("/security/login/get")
|
||||
@GetMapping("/login/get")
|
||||
public Result<PlatformLoginSecurityConfigResult> getLoginSecurityConfig() {
|
||||
return Res.ok(platformConfigService.findLoginSecurityConfig());
|
||||
return Res.ok(platformSecurityConfigService.findLoginSecurityConfig());
|
||||
}
|
||||
|
||||
@PermCode(code = "security:manage", nameCn = "安全配置管理", nameEn = "Security Manage")
|
||||
@Operation(summary = "更新登录安全配置")
|
||||
@PostMapping("/security/login/update")
|
||||
@PostMapping("/login/update")
|
||||
public Result<Void> updateLoginSecurityConfig(@RequestBody @Validated PlatformLoginSecurityConfigParam param) {
|
||||
platformConfigService.updateLoginSecurityConfig(param);
|
||||
platformSecurityConfigService.updateLoginSecurityConfig(param);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@PermCode(code = "security:view", nameCn = "安全配置查看", nameEn = "Security View")
|
||||
@Operation(summary = "获取会话管理配置")
|
||||
@GetMapping("/security/session/get")
|
||||
@GetMapping("/session/get")
|
||||
public Result<PlatformSessionManagementConfigResult> getSessionManagementConfig() {
|
||||
return Res.ok(platformConfigService.findSessionManagementConfig());
|
||||
return Res.ok(platformSecurityConfigService.findSessionManagementConfig());
|
||||
}
|
||||
|
||||
@PermCode(code = "security:manage", nameCn = "安全配置管理", nameEn = "Security Manage")
|
||||
@Operation(summary = "更新会话管理配置")
|
||||
@PostMapping("/security/session/update")
|
||||
@PostMapping("/session/update")
|
||||
public Result<Void> updateSessionManagementConfig(@RequestBody @Validated PlatformSessionManagementConfigParam param) {
|
||||
platformConfigService.updateSessionManagementConfig(param);
|
||||
platformSecurityConfigService.updateSessionManagementConfig(param);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@PermCode(code = "security:view", nameCn = "安全配置查看", nameEn = "Security View")
|
||||
@Operation(summary = "获取异常登录检测配置")
|
||||
@GetMapping("/security/anomaly-detection/get")
|
||||
@GetMapping("/anomaly-detection/get")
|
||||
public Result<PlatformAnomalyDetectionConfigResult> getAnomalyDetectionConfig() {
|
||||
return Res.ok(platformConfigService.findAnomalyDetectionConfig());
|
||||
return Res.ok(platformSecurityConfigService.findAnomalyDetectionConfig());
|
||||
}
|
||||
|
||||
@PermCode(code = "security:manage", nameCn = "安全配置管理", nameEn = "Security Manage")
|
||||
@Operation(summary = "更新异常登录检测配置")
|
||||
@PostMapping("/security/anomaly-detection/update")
|
||||
@PostMapping("/anomaly-detection/update")
|
||||
public Result<Void> updateAnomalyDetectionConfig(@RequestBody @Validated PlatformAnomalyDetectionConfigParam param) {
|
||||
platformConfigService.updateAnomalyDetectionConfig(param);
|
||||
platformSecurityConfigService.updateAnomalyDetectionConfig(param);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@PermCode(code = "security:view", nameCn = "安全配置查看", nameEn = "Security View")
|
||||
@Operation(summary = "获取双因素认证配置")
|
||||
@GetMapping("/security/two-factor-auth/get")
|
||||
@GetMapping("/two-factor-auth/get")
|
||||
public Result<PlatformTwoFactorAuthConfigResult> getTwoFactorAuthConfig() {
|
||||
return Res.ok(platformConfigService.findTwoFactorAuthConfig());
|
||||
return Res.ok(platformSecurityConfigService.findTwoFactorAuthConfig());
|
||||
}
|
||||
|
||||
@PermCode(code = "security:manage", nameCn = "安全配置管理", nameEn = "Security Manage")
|
||||
@Operation(summary = "更新双因素认证配置")
|
||||
@PostMapping("/security/two-factor-auth/update")
|
||||
@PostMapping("/two-factor-auth/update")
|
||||
public Result<Void> updateTwoFactorAuthConfig(@RequestBody @Validated PlatformTwoFactorAuthConfigParam param) {
|
||||
platformConfigService.updateTwoFactorAuthConfig(param);
|
||||
platformSecurityConfigService.updateTwoFactorAuthConfig(param);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@PermCode(code = "security:view", nameCn = "安全配置查看", nameEn = "Security View")
|
||||
@Operation(summary = "获取OSS配置")
|
||||
@GetMapping("/oss/get")
|
||||
public Result<PlatformOssConfigResult> getOssConfig() {
|
||||
return Res.ok(platformConfigService.findOssConfig());
|
||||
}
|
||||
|
||||
@PermCode(code = "security:manage", nameCn = "安全配置管理", nameEn = "Security Manage")
|
||||
@Operation(summary = "更新OSS配置")
|
||||
@PostMapping("/oss/update")
|
||||
public Result<Void> updateOssConfig(@RequestBody @Validated PlatformOssConfigParam param) {
|
||||
platformConfigService.updateOssConfig(param);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package cn.daxpay.open.platform.system.controller.config;
|
||||
|
||||
import cn.daxpay.open.platform.core.annotation.PermCode;
|
||||
import cn.daxpay.open.platform.core.rest.Res;
|
||||
import cn.daxpay.open.platform.core.rest.result.Result;
|
||||
import cn.daxpay.open.platform.system.param.config.PlatformUrlConfigParam;
|
||||
import cn.daxpay.open.platform.system.result.config.platform.PlatformUrlConfigResult;
|
||||
import cn.daxpay.open.platform.system.service.config.PlatformUrlConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/// # 平台端点配置
|
||||
///
|
||||
/// 管理系统访问地址等端点配置
|
||||
@PermCode(menuCode = "system:platform:config")
|
||||
@Validated
|
||||
@Tag(name = "平台端点配置")
|
||||
@RestController
|
||||
@RequestMapping("/platform/config/url")
|
||||
@RequiredArgsConstructor
|
||||
public class PlatformUrlConfigController {
|
||||
private final PlatformUrlConfigService platformUrlConfigService;
|
||||
|
||||
@PermCode(code = "platformConfig:view", nameCn = "平台配置查看", nameEn = "Platform Config View")
|
||||
@Operation(summary = "获取端点配置")
|
||||
@GetMapping("/get")
|
||||
public Result<PlatformUrlConfigResult> getUrlConfig() {
|
||||
return Res.ok(platformUrlConfigService.findUrlConfig());
|
||||
}
|
||||
|
||||
@PermCode(code = "platformConfig:manage", nameCn = "平台配置管理", nameEn = "Platform Config Manage")
|
||||
@Operation(summary = "更新端点配置")
|
||||
@PostMapping("/update")
|
||||
public Result<Void> updateUrlConfig(@RequestBody @Validated PlatformUrlConfigParam param) {
|
||||
platformUrlConfigService.updateUrlConfig(param);
|
||||
return Res.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.daxpay.open.platform.system.convert;
|
||||
|
||||
import cn.daxpay.open.platform.system.entity.config.platform.PlatformOssConfig;
|
||||
import cn.daxpay.open.platform.system.param.config.PlatformOssConfigParam;
|
||||
import cn.daxpay.open.platform.system.result.config.platform.PlatformOssConfigResult;
|
||||
import org.mapstruct.BeanMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.NullValuePropertyMappingStrategy;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 平台OSS配置转换
|
||||
///
|
||||
@Mapper
|
||||
public interface PlatformOssConfigConvert {
|
||||
PlatformOssConfigConvert CONVERT = Mappers.getMapper(PlatformOssConfigConvert.class);
|
||||
|
||||
PlatformOssConfigResult toOssResult(PlatformOssConfig data);
|
||||
|
||||
PlatformOssConfig convert(PlatformOssConfigParam param);
|
||||
|
||||
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
|
||||
void copy(PlatformOssConfigParam param, @MappingTarget PlatformOssConfig data);
|
||||
}
|
||||
@@ -1,49 +1,39 @@
|
||||
package cn.daxpay.open.platform.system.convert;
|
||||
|
||||
import cn.daxpay.open.platform.system.entity.config.platform.PlatformOssConfig;
|
||||
import cn.daxpay.open.platform.system.entity.config.platform.security.*;
|
||||
import cn.daxpay.open.platform.system.param.config.*;
|
||||
import cn.daxpay.open.platform.system.param.config.security.*;
|
||||
import cn.daxpay.open.platform.system.result.config.platform.*;
|
||||
import org.mapstruct.BeanMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.NullValuePropertyMappingStrategy;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 平台配置转换
|
||||
/// # 平台安全配置转换
|
||||
///
|
||||
/// 转换密码策略、登录安全、会话管理、异常登录检测、双因素认证等安全类配置
|
||||
@Mapper
|
||||
public interface PlatformConfigConvert {
|
||||
PlatformConfigConvert CONVERT = Mappers.getMapper(PlatformConfigConvert.class);
|
||||
|
||||
// ========== OSS配置转换(单配置模式) ==========
|
||||
PlatformOssConfigResult toOssResult(PlatformOssConfig data);
|
||||
|
||||
PlatformOssConfig convert(PlatformOssConfigParam param);
|
||||
|
||||
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
|
||||
void copy(PlatformOssConfigParam param, @MappingTarget PlatformOssConfig data);
|
||||
|
||||
// ========== 异常登录检测配置转换 ==========
|
||||
PlatformAnomalyDetectionConfigResult toAnomalyDetectionResult(PlatformAnomalyDetectionConfig data);
|
||||
|
||||
void copy(PlatformAnomalyDetectionConfigParam param, @MappingTarget PlatformAnomalyDetectionConfig data);
|
||||
|
||||
// ========== 登录安全配置转换 ==========
|
||||
PlatformLoginSecurityConfigResult toLoginSecurityResult(PlatformLoginSecurityConfig data);
|
||||
|
||||
void copy(PlatformLoginSecurityConfigParam param, @MappingTarget PlatformLoginSecurityConfig data);
|
||||
public interface PlatformSecurityConfigConvert {
|
||||
PlatformSecurityConfigConvert CONVERT = Mappers.getMapper(PlatformSecurityConfigConvert.class);
|
||||
|
||||
// ========== 密码策略配置转换 ==========
|
||||
PlatformPasswordPolicyConfigResult toPasswordPolicyResult(PlatformPasswordPolicyConfig data);
|
||||
|
||||
void copy(PlatformPasswordPolicyConfigParam param, @MappingTarget PlatformPasswordPolicyConfig data);
|
||||
|
||||
// ========== 登录安全配置转换 ==========
|
||||
PlatformLoginSecurityConfigResult toLoginSecurityResult(PlatformLoginSecurityConfig data);
|
||||
|
||||
void copy(PlatformLoginSecurityConfigParam param, @MappingTarget PlatformLoginSecurityConfig data);
|
||||
|
||||
// ========== 会话管理配置转换 ==========
|
||||
PlatformSessionManagementConfigResult toSessionManagementResult(PlatformSessionManagementConfig data);
|
||||
|
||||
void copy(PlatformSessionManagementConfigParam param, @MappingTarget PlatformSessionManagementConfig data);
|
||||
|
||||
// ========== 异常登录检测配置转换 ==========
|
||||
PlatformAnomalyDetectionConfigResult toAnomalyDetectionResult(PlatformAnomalyDetectionConfig data);
|
||||
|
||||
void copy(PlatformAnomalyDetectionConfigParam param, @MappingTarget PlatformAnomalyDetectionConfig data);
|
||||
|
||||
// ========== 双因素认证配置转换 ==========
|
||||
PlatformTwoFactorAuthConfigResult toTwoFactorAuthResult(PlatformTwoFactorAuthConfig data);
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package cn.daxpay.open.platform.system.convert;
|
||||
|
||||
import cn.daxpay.open.platform.system.entity.config.platform.PlatformUrlConfig;
|
||||
import cn.daxpay.open.platform.system.param.config.PlatformUrlConfigParam;
|
||||
import cn.daxpay.open.platform.system.result.config.platform.PlatformUrlConfigResult;
|
||||
import org.mapstruct.BeanMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.NullValuePropertyMappingStrategy;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/// # 平台端点配置转换
|
||||
///
|
||||
@Mapper
|
||||
public interface PlatformUrlConfigConvert {
|
||||
PlatformUrlConfigConvert CONVERT = Mappers.getMapper(PlatformUrlConfigConvert.class);
|
||||
|
||||
PlatformUrlConfigResult toUrlResult(PlatformUrlConfig data);
|
||||
|
||||
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
|
||||
void copy(PlatformUrlConfigParam param, @MappingTarget PlatformUrlConfig data);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package cn.daxpay.open.platform.system.entity.config.platform;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 平台端点配置
|
||||
///
|
||||
/// 各端(管理端/商户端/支付网关/后端 API)的访问地址, 用于第三方登录回调 URL 自动生成、
|
||||
/// 支付回调地址拼接等场景. 全局唯一, 通过 [PlatformConfigTypeEnum.URL] 存储于系统配置表.
|
||||
///
|
||||
/// getter 会自动去除 URL 尾部斜杠, 方便后续拼接路径.
|
||||
///
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class PlatformUrlConfig {
|
||||
|
||||
/// 管理端访问地址(如 https://admin.daxpay.com)
|
||||
/// 第三方登录回调等场景依赖此配置, client=admin 时使用
|
||||
private String adminBaseUrl;
|
||||
|
||||
/// 商户端访问地址(如 https://merchant.daxpay.com)
|
||||
/// client=merchant 时使用
|
||||
private String merchantBaseUrl;
|
||||
|
||||
/// 支付网关前端地址(如 https://pay.daxpay.com)
|
||||
/// 收银台/支付网关页面的访问地址
|
||||
private String paymentGatewayBaseUrl;
|
||||
|
||||
/// 后端 API 地址(如 https://api.daxpay.com)
|
||||
/// 用于支付回调通知等后端发起的场景
|
||||
private String backendBaseUrl;
|
||||
|
||||
/// 去除尾部斜杠, 方便后续拼接路径
|
||||
public String getAdminBaseUrl() {
|
||||
return StrUtil.removeSuffix(adminBaseUrl, "/");
|
||||
}
|
||||
|
||||
/// 去除尾部斜杠, 方便后续拼接路径
|
||||
public String getMerchantBaseUrl() {
|
||||
return StrUtil.removeSuffix(merchantBaseUrl, "/");
|
||||
}
|
||||
|
||||
/// 去除尾部斜杠, 方便后续拼接路径
|
||||
public String getPaymentGatewayBaseUrl() {
|
||||
return StrUtil.removeSuffix(paymentGatewayBaseUrl, "/");
|
||||
}
|
||||
|
||||
/// 去除尾部斜杠, 方便后续拼接路径
|
||||
public String getBackendBaseUrl() {
|
||||
return StrUtil.removeSuffix(backendBaseUrl, "/");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package cn.daxpay.open.platform.system.param.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 平台端点配置参数
|
||||
///
|
||||
/// 全部选填: 未配置的端对应功能不可用, 但不强制要求一次性配置完整.
|
||||
/// 如未启用支付网关的环境 paymentGatewayBaseUrl 可留空.
|
||||
///
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "平台端点配置参数")
|
||||
public class PlatformUrlConfigParam {
|
||||
|
||||
/// 管理端访问地址
|
||||
@Schema(description = "管理端访问地址")
|
||||
private String adminBaseUrl;
|
||||
|
||||
/// 商户端访问地址
|
||||
@Schema(description = "商户端访问地址")
|
||||
private String merchantBaseUrl;
|
||||
|
||||
/// 支付网关前端地址
|
||||
@Schema(description = "支付网关前端地址")
|
||||
private String paymentGatewayBaseUrl;
|
||||
|
||||
/// 后端 API 地址
|
||||
@Schema(description = "后端 API 地址")
|
||||
private String backendBaseUrl;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.daxpay.open.platform.system.param.config;
|
||||
package cn.daxpay.open.platform.system.param.config.security;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.daxpay.open.platform.system.param.config;
|
||||
package cn.daxpay.open.platform.system.param.config.security;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.daxpay.open.platform.system.param.config;
|
||||
package cn.daxpay.open.platform.system.param.config.security;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.daxpay.open.platform.system.param.config;
|
||||
package cn.daxpay.open.platform.system.param.config.security;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.daxpay.open.platform.system.param.config;
|
||||
package cn.daxpay.open.platform.system.param.config.security;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@@ -3,7 +3,7 @@ package cn.daxpay.open.platform.system.provider;
|
||||
import cn.daxpay.open.platform.capability.file.entity.FileStorageConfig;
|
||||
import cn.daxpay.open.platform.capability.file.provider.OssConfigProvider;
|
||||
import cn.daxpay.open.platform.system.entity.config.platform.PlatformOssConfig;
|
||||
import cn.daxpay.open.platform.system.service.config.PlatformConfigService;
|
||||
import cn.daxpay.open.platform.system.service.config.PlatformOssConfigService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -17,11 +17,11 @@ import java.util.Optional;
|
||||
@RequiredArgsConstructor
|
||||
public class OssConfigProviderImpl implements OssConfigProvider {
|
||||
|
||||
private final PlatformConfigService platformConfigService;
|
||||
private final PlatformOssConfigService platformOssConfigService;
|
||||
|
||||
@Override
|
||||
public Optional<FileStorageConfig> getDefaultConfig() {
|
||||
PlatformOssConfig config = platformConfigService.getOssConfig();
|
||||
PlatformOssConfig config = platformOssConfigService.getOssConfig();
|
||||
if (config == null || config.getEndpoint() == null) {
|
||||
log.warn("OSS配置不存在或未配置");
|
||||
return Optional.empty();
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.daxpay.open.platform.system.result.config.platform;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/// # 平台端点配置返回结果
|
||||
///
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "平台端点配置")
|
||||
public class PlatformUrlConfigResult {
|
||||
|
||||
/// 管理端访问地址
|
||||
@Schema(description = "管理端访问地址")
|
||||
private String adminBaseUrl;
|
||||
|
||||
/// 商户端访问地址
|
||||
@Schema(description = "商户端访问地址")
|
||||
private String merchantBaseUrl;
|
||||
|
||||
/// 支付网关前端地址
|
||||
@Schema(description = "支付网关前端地址")
|
||||
private String paymentGatewayBaseUrl;
|
||||
|
||||
/// 后端 API 地址
|
||||
@Schema(description = "后端 API 地址")
|
||||
private String backendBaseUrl;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package cn.daxpay.open.platform.system.service.config;
|
||||
|
||||
import cn.daxpay.open.platform.system.convert.PlatformOssConfigConvert;
|
||||
import cn.daxpay.open.platform.system.entity.config.platform.PlatformOssConfig;
|
||||
import cn.daxpay.open.platform.system.enums.EncryptPlatformConfigTypeEnum;
|
||||
import cn.daxpay.open.platform.system.param.config.PlatformOssConfigParam;
|
||||
import cn.daxpay.open.platform.system.result.config.platform.PlatformOssConfigResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/// # 平台OSS配置服务
|
||||
///
|
||||
/// 管理对象存储配置,数据通过加密配置服务进行加密存储
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PlatformOssConfigService {
|
||||
|
||||
private final SystemPlatformEncryptConfigService encryptConfigService;
|
||||
|
||||
/// 获取OSS配置
|
||||
public PlatformOssConfig getOssConfig() {
|
||||
return encryptConfigService.getOrCreateConfig(EncryptPlatformConfigTypeEnum.OSS,
|
||||
PlatformOssConfig.class,
|
||||
new PlatformOssConfig());
|
||||
}
|
||||
|
||||
/// 获取OSS配置
|
||||
public PlatformOssConfigResult findOssConfig() {
|
||||
return PlatformOssConfigConvert.CONVERT.toOssResult(this.getOssConfig());
|
||||
}
|
||||
|
||||
/// 更新OSS配置
|
||||
public void updateOssConfig(PlatformOssConfigParam param) {
|
||||
PlatformOssConfig data = this.getOssConfig();
|
||||
PlatformOssConfigConvert.CONVERT.copy(param, data);
|
||||
encryptConfigService.updateConfig(EncryptPlatformConfigTypeEnum.OSS, data);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,24 @@
|
||||
package cn.daxpay.open.platform.system.service.config;
|
||||
|
||||
import cn.daxpay.open.platform.system.convert.PlatformConfigConvert;
|
||||
import cn.daxpay.open.platform.system.entity.config.platform.PlatformOssConfig;
|
||||
import cn.daxpay.open.platform.system.convert.PlatformSecurityConfigConvert;
|
||||
import cn.daxpay.open.platform.system.entity.config.platform.security.*;
|
||||
import cn.daxpay.open.platform.system.enums.EncryptPlatformConfigTypeEnum;
|
||||
import cn.daxpay.open.platform.system.enums.PlatformConfigTypeEnum;
|
||||
import cn.daxpay.open.platform.system.param.config.*;
|
||||
import cn.daxpay.open.platform.system.param.config.security.*;
|
||||
import cn.daxpay.open.platform.system.result.config.platform.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/// # 平台配置
|
||||
/// # 平台安全配置服务
|
||||
///
|
||||
/// 统一管理密码策略、登录安全、会话管理、异常登录检测、双因素认证等安全类配置
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PlatformConfigService {
|
||||
public class PlatformSecurityConfigService {
|
||||
|
||||
private final SystemPlatformConfigService systemConfigService;
|
||||
|
||||
private final SystemPlatformEncryptConfigService encryptConfigService;
|
||||
|
||||
/// 获取密码策略配置
|
||||
public PlatformPasswordPolicyConfig getPasswordPolicyConfig() {
|
||||
return systemConfigService.getOrCreateConfig(PlatformConfigTypeEnum.SECURITY_PASSWORD_POLICY,
|
||||
@@ -31,13 +28,13 @@ public class PlatformConfigService {
|
||||
|
||||
/// 获取密码策略配置
|
||||
public PlatformPasswordPolicyConfigResult findPasswordPolicyConfig() {
|
||||
return PlatformConfigConvert.CONVERT.toPasswordPolicyResult(this.getPasswordPolicyConfig());
|
||||
return PlatformSecurityConfigConvert.CONVERT.toPasswordPolicyResult(this.getPasswordPolicyConfig());
|
||||
}
|
||||
|
||||
/// 更新密码策略配置
|
||||
public void updatePasswordPolicyConfig(PlatformPasswordPolicyConfigParam param) {
|
||||
PlatformPasswordPolicyConfig data = this.getPasswordPolicyConfig();
|
||||
PlatformConfigConvert.CONVERT.copy(param, data);
|
||||
PlatformSecurityConfigConvert.CONVERT.copy(param, data);
|
||||
systemConfigService.updateConfig(PlatformConfigTypeEnum.SECURITY_PASSWORD_POLICY, data);
|
||||
}
|
||||
|
||||
@@ -50,13 +47,13 @@ public class PlatformConfigService {
|
||||
|
||||
/// 获取登录安全配置
|
||||
public PlatformLoginSecurityConfigResult findLoginSecurityConfig() {
|
||||
return PlatformConfigConvert.CONVERT.toLoginSecurityResult(this.getLoginSecurityConfig());
|
||||
return PlatformSecurityConfigConvert.CONVERT.toLoginSecurityResult(this.getLoginSecurityConfig());
|
||||
}
|
||||
|
||||
/// 更新登录安全配置
|
||||
public void updateLoginSecurityConfig(PlatformLoginSecurityConfigParam param) {
|
||||
PlatformLoginSecurityConfig data = this.getLoginSecurityConfig();
|
||||
PlatformConfigConvert.CONVERT.copy(param, data);
|
||||
PlatformSecurityConfigConvert.CONVERT.copy(param, data);
|
||||
systemConfigService.updateConfig(PlatformConfigTypeEnum.SECURITY_LOGIN, data);
|
||||
}
|
||||
|
||||
@@ -69,13 +66,13 @@ public class PlatformConfigService {
|
||||
|
||||
/// 获取会话管理配置
|
||||
public PlatformSessionManagementConfigResult findSessionManagementConfig() {
|
||||
return PlatformConfigConvert.CONVERT.toSessionManagementResult(this.getSessionManagementConfig());
|
||||
return PlatformSecurityConfigConvert.CONVERT.toSessionManagementResult(this.getSessionManagementConfig());
|
||||
}
|
||||
|
||||
/// 更新会话管理配置
|
||||
public void updateSessionManagementConfig(PlatformSessionManagementConfigParam param) {
|
||||
PlatformSessionManagementConfig data = this.getSessionManagementConfig();
|
||||
PlatformConfigConvert.CONVERT.copy(param, data);
|
||||
PlatformSecurityConfigConvert.CONVERT.copy(param, data);
|
||||
systemConfigService.updateConfig(PlatformConfigTypeEnum.SECURITY_SESSION, data);
|
||||
}
|
||||
|
||||
@@ -88,13 +85,13 @@ public class PlatformConfigService {
|
||||
|
||||
/// 获取异常登录检测配置
|
||||
public PlatformAnomalyDetectionConfigResult findAnomalyDetectionConfig() {
|
||||
return PlatformConfigConvert.CONVERT.toAnomalyDetectionResult(this.getAnomalyDetectionConfig());
|
||||
return PlatformSecurityConfigConvert.CONVERT.toAnomalyDetectionResult(this.getAnomalyDetectionConfig());
|
||||
}
|
||||
|
||||
/// 更新异常登录检测配置
|
||||
public void updateAnomalyDetectionConfig(PlatformAnomalyDetectionConfigParam param) {
|
||||
PlatformAnomalyDetectionConfig data = this.getAnomalyDetectionConfig();
|
||||
PlatformConfigConvert.CONVERT.copy(param, data);
|
||||
PlatformSecurityConfigConvert.CONVERT.copy(param, data);
|
||||
systemConfigService.updateConfig(PlatformConfigTypeEnum.ANOMALY_DETECTION, data);
|
||||
}
|
||||
|
||||
@@ -107,33 +104,13 @@ public class PlatformConfigService {
|
||||
|
||||
/// 获取双因素认证配置
|
||||
public PlatformTwoFactorAuthConfigResult findTwoFactorAuthConfig() {
|
||||
return PlatformConfigConvert.CONVERT.toTwoFactorAuthResult(this.getTwoFactorAuthConfig());
|
||||
return PlatformSecurityConfigConvert.CONVERT.toTwoFactorAuthResult(this.getTwoFactorAuthConfig());
|
||||
}
|
||||
|
||||
/// 更新双因素认证配置
|
||||
public void updateTwoFactorAuthConfig(PlatformTwoFactorAuthConfigParam param) {
|
||||
PlatformTwoFactorAuthConfig data = this.getTwoFactorAuthConfig();
|
||||
PlatformConfigConvert.CONVERT.copy(param, data);
|
||||
PlatformSecurityConfigConvert.CONVERT.copy(param, data);
|
||||
systemConfigService.updateConfig(PlatformConfigTypeEnum.SECURITY_TWO_FACTOR_AUTH, data);
|
||||
}
|
||||
|
||||
/// 获取OSS配置
|
||||
public PlatformOssConfig getOssConfig() {
|
||||
return encryptConfigService.getOrCreateConfig(EncryptPlatformConfigTypeEnum.OSS,
|
||||
PlatformOssConfig.class,
|
||||
new PlatformOssConfig());
|
||||
}
|
||||
|
||||
/// 获取OSS配置
|
||||
public PlatformOssConfigResult findOssConfig() {
|
||||
return PlatformConfigConvert.CONVERT.toOssResult(this.getOssConfig());
|
||||
}
|
||||
|
||||
/// 更新OSS配置
|
||||
public void updateOssConfig(PlatformOssConfigParam param) {
|
||||
PlatformOssConfig data = this.getOssConfig();
|
||||
PlatformConfigConvert.CONVERT.copy(param, data);
|
||||
encryptConfigService.updateConfig(EncryptPlatformConfigTypeEnum.OSS, data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package cn.daxpay.open.platform.system.service.config;
|
||||
|
||||
import cn.daxpay.open.platform.system.convert.PlatformUrlConfigConvert;
|
||||
import cn.daxpay.open.platform.system.entity.config.platform.PlatformUrlConfig;
|
||||
import cn.daxpay.open.platform.system.enums.PlatformConfigTypeEnum;
|
||||
import cn.daxpay.open.platform.system.param.config.PlatformUrlConfigParam;
|
||||
import cn.daxpay.open.platform.system.result.config.platform.PlatformUrlConfigResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/// # 平台端点配置服务
|
||||
///
|
||||
/// 管理系统访问地址等端点配置
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PlatformUrlConfigService {
|
||||
|
||||
private final SystemPlatformConfigService systemConfigService;
|
||||
|
||||
/// 获取端点配置
|
||||
public PlatformUrlConfig getUrlConfig() {
|
||||
return systemConfigService.getOrCreateConfig(PlatformConfigTypeEnum.URL,
|
||||
PlatformUrlConfig.class,
|
||||
new PlatformUrlConfig());
|
||||
}
|
||||
|
||||
/// 获取端点配置
|
||||
public PlatformUrlConfigResult findUrlConfig() {
|
||||
return PlatformUrlConfigConvert.CONVERT.toUrlResult(this.getUrlConfig());
|
||||
}
|
||||
|
||||
/// 更新端点配置
|
||||
public void updateUrlConfig(PlatformUrlConfigParam param) {
|
||||
PlatformUrlConfig data = this.getUrlConfig();
|
||||
PlatformUrlConfigConvert.CONVERT.copy(param, data);
|
||||
systemConfigService.updateConfig(PlatformConfigTypeEnum.URL, data);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user