diff --git a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/auth/SocialAuthRequestFactory.java b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/auth/SocialAuthRequestFactory.java index e7fff1f69..c407255ce 100644 --- a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/auth/SocialAuthRequestFactory.java +++ b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/auth/SocialAuthRequestFactory.java @@ -10,6 +10,7 @@ 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.justauth.request.WechatOpenRequest; 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; @@ -31,6 +32,7 @@ public class SocialAuthRequestFactory { case GOOGLE -> new GoogleRequest(config); case QQ -> new QqRequest(config); case WECHAT_MP -> new WechatMpRequest(config); + case WECHAT_OPEN -> new WechatOpenRequest(config); case WE_COM -> new WeComRequest(config); case FEISHU -> new FeishuRequest(config); case DINGTALK -> new DingTalkRequest(config); diff --git a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/SocialAuthConfig.java b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/SocialAuthConfig.java index c1896f1ab..623c6aa8a 100644 --- a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/SocialAuthConfig.java +++ b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/SocialAuthConfig.java @@ -25,4 +25,9 @@ public class SocialAuthConfig { /// QQ: 是否申请 unionId private boolean unionId; + + /// 静默/应用内授权模式 + /// 企业微信: true 走网页授权(oauth2/authorize), false 走扫码(qrConnect) + /// 微信公众号: true 使用 snsapi_base, false 使用 snsapi_userinfo + private boolean silent; } diff --git a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/SocialSourceEnum.java b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/SocialSourceEnum.java index c6ab49678..7ae0110de 100644 --- a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/SocialSourceEnum.java +++ b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/SocialSourceEnum.java @@ -15,7 +15,7 @@ import java.util.Arrays; @AllArgsConstructor public enum SocialSourceEnum implements I18nSupport { - /// 微信开放平台 + /// 微信公众号(网页授权, 凭据在平台级 wechat_mp_auth, 本枚举仅作登录启停占位) WECHAT_MP( "weChat", "https://open.weixin.qq.com/connect/oauth2/authorize", @@ -23,7 +23,15 @@ public enum SocialSourceEnum implements I18nSupport { "https://api.weixin.qq.com/sns/userinfo" ), - /// 企业微信(企业自建应用扫码) + /// 微信开放平台(网站应用扫码登录, 凭据在 iam_social_login_config) + WECHAT_OPEN( + "weChatOpen", + "https://open.weixin.qq.com/connect/qrconnect", + "https://api.weixin.qq.com/sns/oauth2/access_token", + "https://api.weixin.qq.com/sns/userinfo" + ), + + /// 企业微信(企业自建应用: 扫码 + 网页授权) WE_COM( "weCom", "https://open.work.weixin.qq.com/wwopen/sso/qrConnect", @@ -142,13 +150,13 @@ public enum SocialSourceEnum implements I18nSupport { /// 是否"平台级跳转型"配置 /// - /// 此类平台(支付宝)非标准 OAuth2 或配置较重, + /// 此类平台非标准 OAuth2 或配置较重, /// 不在 iam_social_login_config 表内存 clientId/clientSecret, /// 而是使用独立的平台级配置(EncryptPlatformConfigTypeEnum / 独立表)。 /// 前端登录配置抽屉据此隐藏 clientId/clientSecret, 仅保留启用开关与「前往配置凭据」入口。 /// /// 新增此类平台时在此方法追加判断。 public boolean isPlatformRedirect() { - return this == ALIPAY; + return this == ALIPAY || this == WECHAT_MP; } } diff --git a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/request/WeComRequest.java b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/request/WeComRequest.java index 4fb01e80f..91efbb1cf 100644 --- a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/request/WeComRequest.java +++ b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/request/WeComRequest.java @@ -9,17 +9,36 @@ import cn.daxpay.open.platform.capability.social.justauth.model.AuthUser; import cn.daxpay.open.platform.capability.social.justauth.util.SocialUrlBuilder; import cn.hutool.json.JSONObject; -/// # 企业微信(企业自建应用扫码)授权登录 +/// # 企业微信(企业自建应用)授权登录 +/// +/// 默认扫码登录(qrConnect); 当 [SocialAuthConfig#isSilent] 为 true 时改走网页授权, +/// 供企业微信内置浏览器应用内自动登录使用. 两种模式换票与用户标识(UserId)一致. /// public class WeComRequest extends AbstractSocialAuthRequest { + /// 企业微信网页授权地址(应用内) + private static final String WEB_OAUTH_AUTHORIZE = + "https://open.weixin.qq.com/connect/oauth2/authorize"; + public WeComRequest(SocialAuthConfig config) { super(config, SocialSourceEnum.WE_COM); } - /// 企业微信授权地址使用 appid(corpid) 和 agentid + /// 企业微信授权地址: silent=网页授权, 否则扫码 @Override public String authorize(String state) { + if (this.getConfig().isSilent()) { + // 应用内网页授权(snsapi_base 静默拿 UserId) + return SocialUrlBuilder.ofBaseUrl(WEB_OAUTH_AUTHORIZE) + .queryParam("appid", this.getConfig().getClientId()) + .queryParam("redirect_uri", this.encode(this.buildRedirectUri())) + .queryParam("response_type", "code") + .queryParam("scope", "snsapi_base") + .queryParam("agentid", this.getConfig().getAgentId()) + .queryParam("state", state.concat("#wechat_redirect")) + .build(); + } + // PC 扫码登录 return SocialUrlBuilder.ofBaseUrl(this.getSource().authorize()) .queryParam("appid", this.getConfig().getClientId()) .queryParam("agentid", this.getConfig().getAgentId()) diff --git a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/request/WechatMpRequest.java b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/request/WechatMpRequest.java index 673d038b4..7b95d2410 100644 --- a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/request/WechatMpRequest.java +++ b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/request/WechatMpRequest.java @@ -19,13 +19,15 @@ public class WechatMpRequest extends AbstractSocialAuthRequest { } /// 微信授权地址使用 appid 参数, 且 state 末尾需追加 #wechat_redirect + /// silent 时使用 snsapi_base(静默仅 openId), 否则 snsapi_userinfo @Override public String authorize(String state) { + String scope = this.getConfig().isSilent() ? "snsapi_base" : "snsapi_userinfo"; return SocialUrlBuilder.ofBaseUrl(this.getSource().authorize()) .queryParam("appid", this.getConfig().getClientId()) .queryParam("redirect_uri", this.encode(this.buildRedirectUri())) .queryParam("response_type", "code") - .queryParam("scope", "snsapi_userinfo") + .queryParam("scope", scope) .queryParam("state", state.concat("#wechat_redirect")) .build(); } diff --git a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/request/WechatOpenRequest.java b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/request/WechatOpenRequest.java new file mode 100644 index 000000000..99d9f349c --- /dev/null +++ b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/java/cn/daxpay/open/platform/capability/social/justauth/request/WechatOpenRequest.java @@ -0,0 +1,81 @@ +package cn.daxpay.open.platform.capability.social.justauth.request; + +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.exception.SocialException; +import cn.daxpay.open.platform.capability.social.justauth.model.AuthCallback; +import cn.daxpay.open.platform.capability.social.justauth.model.AuthToken; +import cn.daxpay.open.platform.capability.social.justauth.model.AuthUser; +import cn.daxpay.open.platform.capability.social.justauth.util.SocialUrlBuilder; +import cn.hutool.json.JSONObject; + +/// # 微信开放平台(网站应用)扫码登录 +/// +/// 使用 qrconnect + snsapi_login, 与公众号网页授权([WechatMpRequest])分离。 +/// +public class WechatOpenRequest extends AbstractSocialAuthRequest { + + public WechatOpenRequest(SocialAuthConfig config) { + super(config, SocialSourceEnum.WECHAT_OPEN); + } + + /// 开放平台网站应用扫码授权 + @Override + public String authorize(String state) { + return SocialUrlBuilder.ofBaseUrl(this.getSource().authorize()) + .queryParam("appid", this.getConfig().getClientId()) + .queryParam("redirect_uri", this.encode(this.buildRedirectUri())) + .queryParam("response_type", "code") + .queryParam("scope", "snsapi_login") + .queryParam("state", state) + .build() + "#wechat_redirect"; + } + + /// 用 code 换 accessToken, 同时拿到 openid + @Override + public AuthToken getAccessToken(AuthCallback callback) { + String url = SocialUrlBuilder.ofBaseUrl(this.getSource().accessToken()) + .queryParam("appid", this.getConfig().getClientId()) + .queryParam("secret", this.getConfig().getClientSecret()) + .queryParam("code", callback.getCode()) + .queryParam("grant_type", "authorization_code") + .build(); + JSONObject object = this.parseObj(this.doGet(url)); + this.checkResponse(object); + return new AuthToken() + .setAccessToken(object.getStr("access_token")) + .setRefreshToken(object.getStr("refresh_token")) + .setExpireIn(object.getInt("expires_in", 0)) + .setOpenId(object.getStr("openid")) + .setUnionId(object.getStr("unionid")) + .setScope(object.getStr("scope")); + } + + @Override + public AuthUser getUserInfo(AuthToken token) { + String url = SocialUrlBuilder.ofBaseUrl(this.getSource().userInfo()) + .queryParam("access_token", token.getAccessToken()) + .queryParam("openid", token.getOpenId()) + .queryParam("lang", "zh_CN") + .build(); + JSONObject object = this.parseObj(this.doGet(url)); + this.checkResponse(object); + if (object.containsKey("unionid")) { + token.setUnionId(object.getStr("unionid")); + } + return new AuthUser() + .setUuid(token.getOpenId()) + .setUsername(object.getStr("nickname")) + .setNickname(object.getStr("nickname")) + .setAvatar(object.getStr("headimgurl")) + .setSource(this.getSourceName()) + .setToken(token); + } + + /// 校验微信响应(errcode 不为 0 表示错误) + private void checkResponse(JSONObject object) { + if (object.containsKey("errcode") && object.getInt("errcode", 0) != 0) { + throw new SocialException(object.getStr("errmsg")); + } + } +} diff --git a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/en-US/error/social.json b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/en-US/error/social.json index 8df2583d5..6db5c3e5b 100644 --- a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/en-US/error/social.json +++ b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/en-US/error/social.json @@ -19,5 +19,10 @@ "alreadyBoundPlatform": "You have already bound this platform, please unbind first", "clientMismatch": "Current user does not belong to this client, binding is not allowed" }, - "oauthFailed": "Third-party authorization failed" + "oauthFailed": "Third-party authorization failed", + "autoLogin": { + "sourceRequired": "Select at least one platform when auto-login is enabled", + "sourceNotEnabled": "Auto-login platform is not configured or disabled: {0}", + "sourceNotAllowed": "Platform does not support in-app auto-login: {0}" + } } diff --git a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/id-ID/error/social.json b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/id-ID/error/social.json index 2655157d8..014ebd95a 100644 --- a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/id-ID/error/social.json +++ b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/id-ID/error/social.json @@ -19,5 +19,10 @@ "alreadyBoundPlatform": "Anda sudah mengikat platform ini, silakan unbind terlebih dahulu", "clientMismatch": "Pengguna saat ini bukan milik klien ini, pengikatan tidak diperbolehkan" }, - "oauthFailed": "Otorisasi pihak ketiga gagal" + "oauthFailed": "Otorisasi pihak ketiga gagal", + "autoLogin": { + "sourceRequired": "Pilih setidaknya satu platform saat auto-login diaktifkan", + "sourceNotEnabled": "Platform auto-login belum dikonfigurasi atau dinonaktifkan: {0}", + "sourceNotAllowed": "Platform tidak mendukung auto-login dalam aplikasi: {0}" + } } diff --git a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/ja-JP/error/social.json b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/ja-JP/error/social.json new file mode 100644 index 000000000..90b239566 --- /dev/null +++ b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/ja-JP/error/social.json @@ -0,0 +1,28 @@ +{ + "configNotExist": "プラットフォーム設定が存在しないか無効です", + "unsupportedSource": "未対応のプラットフォームです", + "unsupportedClient": "ソーシャルログインは管理端(admin)と加盟店端(merchant)のみ対応しています", + "configDuplicated": "このプラットフォーム設定は既に存在します", + "notConfigured": "プラットフォーム未設定のため有効/無効を切り替えできません", + "authCodeBlank": "認可コードは空にできません", + "endpointNotConfigured": "エンドポイント設定にフロントのアクセスURLがなく、コールバックURLを生成できません", + "adminEndpointNotConfigured": "先にエンドポイント設定で「管理端アクセスURL」(adminBaseUrl)を設定してください", + "merchantEndpointNotConfigured": "先にエンドポイント設定で「加盟店端アクセスURL」(merchantBaseUrl)を設定してください", + "alipayNotConfigured": "プラットフォーム級のAlipay設定が不完全です。「三方平台管理」で設定してください", + "wechatMpNotConfigured": "プラットフォーム級のWeChat公式アカウント設定が不完全です。「三方平台管理」で設定してください", + "douyinH5NotConfigured": "プラットフォーム級のDouyin H5アプリ設定が不完全です。「三方平台管理」で設定してください", + "weCom": { + "notCorpMember": "本企業のメンバーではないためログインできません" + }, + "bind": { + "alreadyBoundByOther": "この外部アカウントは他のユーザーに紐付け済みです", + "alreadyBoundPlatform": "既にこのプラットフォームを紐付けています。先に解除してください", + "clientMismatch": "現在のユーザーはこの端末に属していないため紐付けできません" + }, + "oauthFailed": "外部認可に失敗しました", + "autoLogin": { + "sourceRequired": "自動ログインを有効にする場合はプラットフォームを1つ以上選択してください", + "sourceNotEnabled": "選択した自動ログインプラットフォームが未設定または無効です: {0}", + "sourceNotAllowed": "このプラットフォームはアプリ内自動ログインに対応していません: {0}" + } +} diff --git a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/ko-KR/error/social.json b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/ko-KR/error/social.json new file mode 100644 index 000000000..6354faf60 --- /dev/null +++ b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/ko-KR/error/social.json @@ -0,0 +1,28 @@ +{ + "configNotExist": "플랫폼 구성이 없거나 비활성화되어 있습니다", + "unsupportedSource": "지원하지 않는 플랫폼입니다", + "unsupportedClient": "소셜 로그인은 운영단(admin)과 가맹점단(merchant)만 지원합니다", + "configDuplicated": "해당 플랫폼 구성이 이미 존재합니다", + "notConfigured": "플랫폼이 구성되지 않아 활성/비활성할 수 없습니다", + "authCodeBlank": "인가 코드는 비울 수 없습니다", + "endpointNotConfigured": "엔드포인트 구성에 프론트 접속 URL이 없어 콜백 URL을 생성할 수 없습니다", + "adminEndpointNotConfigured": "먼저 엔드포인트 구성에서 「운영단 접속 주소」(adminBaseUrl)를 설정하세요", + "merchantEndpointNotConfigured": "먼저 엔드포인트 구성에서 「가맹점단 접속 주소」(merchantBaseUrl)를 설정하세요", + "alipayNotConfigured": "플랫폼급 Alipay 구성이 불완전합니다. 「삼자 플랫폼 관리」에서 설정하세요", + "wechatMpNotConfigured": "플랫폼급 WeChat 공식계정 구성이 불완전합니다. 「삼자 플랫폼 관리」에서 설정하세요", + "douyinH5NotConfigured": "플랫폼급 Douyin H5 앱 구성이 불완전합니다. 「삼자 플랫폼 관리」에서 설정하세요", + "weCom": { + "notCorpMember": "본 기업 구성원이 아니어서 로그인할 수 없습니다" + }, + "bind": { + "alreadyBoundByOther": "해당 소셜 계정이 다른 사용자에게 연결되어 있습니다", + "alreadyBoundPlatform": "이미 이 플랫폼을 연결했습니다. 먼저 연결을 해제하세요", + "clientMismatch": "현재 사용자는 해당 단말에 속하지 않아 연결할 수 없습니다" + }, + "oauthFailed": "소셜 인가에 실패했습니다", + "autoLogin": { + "sourceRequired": "자동 로그인 사용 시 플랫폼을 하나 이상 선택하세요", + "sourceNotEnabled": "선택한 자동 로그인 플랫폼이 미구성이거나 비활성입니다: {0}", + "sourceNotAllowed": "해당 플랫폼은 인앱 자동 로그인을 지원하지 않습니다: {0}" + } +} diff --git a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/ms-MY/error/social.json b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/ms-MY/error/social.json index cdf8314f9..0313e431f 100644 --- a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/ms-MY/error/social.json +++ b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/ms-MY/error/social.json @@ -19,5 +19,10 @@ "alreadyBoundPlatform": "Anda telah mengikat platform ini, sila buka ikatan dahulu", "clientMismatch": "Pengguna semasa bukan milik klien ini, pengikatan tidak dibenarkan" }, - "oauthFailed": "Kebenaran pihak ketiga gagal" + "oauthFailed": "Kebenaran pihak ketiga gagal", + "autoLogin": { + "sourceRequired": "Pilih sekurang-kurangnya satu platform apabila log masuk auto diaktifkan", + "sourceNotEnabled": "Platform log masuk auto belum dikonfigurasi atau dilumpuhkan: {0}", + "sourceNotAllowed": "Platform tidak menyokong log masuk auto dalam aplikasi: {0}" + } } diff --git a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/th-TH/error/social.json b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/th-TH/error/social.json index b2c2f17b2..975b498f3 100644 --- a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/th-TH/error/social.json +++ b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/th-TH/error/social.json @@ -19,5 +19,10 @@ "alreadyBoundPlatform": "คุณได้ผูกมัดแพลตฟอร์มนี้แล้ว โปรดยกเลิกการผูกก่อน", "clientMismatch": "ผู้ใช้ปัจจุบันไม่ได้เป็นของลูกค้ารายนี้ ไม่อนุญาตให้มีการผูกข้อมูล" }, - "oauthFailed": "การอนุญาตของบุคคลที่สามล้มเหลว" + "oauthFailed": "การอนุญาตของบุคคลที่สามล้มเหลว", + "autoLogin": { + "sourceRequired": "เมื่อเปิดใช้งานเข้าสู่ระบบอัตโนมัติต้องเลือกอย่างน้อยหนึ่งแพลตฟอร์ม", + "sourceNotEnabled": "แพลตฟอร์มเข้าสู่ระบบอัตโนมัติยังไม่ได้ตั้งค่าหรือปิดอยู่: {0}", + "sourceNotAllowed": "แพลตฟอร์มนี้ไม่รองรับการเข้าสู่ระบบอัตโนมัติในแอป: {0}" + } } diff --git a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/vi-VN/error/social.json b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/vi-VN/error/social.json index 616e75047..7fe184e4c 100644 --- a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/vi-VN/error/social.json +++ b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/vi-VN/error/social.json @@ -19,5 +19,10 @@ "alreadyBoundPlatform": "Bạn đã ràng buộc nền tảng này, vui lòng hủy liên kết trước", "clientMismatch": "Người dùng hiện tại không thuộc về khách hàng này, không được phép ràng buộc" }, - "oauthFailed": "Ủy quyền bên thứ ba thất bại" + "oauthFailed": "Ủy quyền bên thứ ba thất bại", + "autoLogin": { + "sourceRequired": "Khi bật tự động đăng nhập phải chọn ít nhất một nền tảng", + "sourceNotEnabled": "Nền tảng tự động đăng nhập chưa cấu hình hoặc đã tắt: {0}", + "sourceNotAllowed": "Nền tảng không hỗ trợ tự động đăng nhập trong ứng dụng: {0}" + } } diff --git a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/zh-CN/error/social.json b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/zh-CN/error/social.json index 2ec48dcbe..8fc283e81 100644 --- a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/zh-CN/error/social.json +++ b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/zh-CN/error/social.json @@ -19,5 +19,10 @@ "alreadyBoundPlatform": "您已绑定该平台,请先解绑后再绑定新账号", "clientMismatch": "当前登录用户不属于该终端,无法完成绑定" }, - "oauthFailed": "三方授权失败" + "oauthFailed": "三方授权失败", + "autoLogin": { + "sourceRequired": "开启自动登录时必须至少选择一个平台", + "sourceNotEnabled": "所选自动登录平台未配置或未启用: {0}", + "sourceNotAllowed": "该平台不支持应用内自动登录: {0}" + } } diff --git a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/zh-HK/error/social.json b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/zh-HK/error/social.json index d269e627c..c8a5b523c 100644 --- a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/zh-HK/error/social.json +++ b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/zh-HK/error/social.json @@ -19,5 +19,10 @@ "alreadyBoundPlatform": "您已綁定該平台,請先解綁後再綁定新賬號", "clientMismatch": "當前登錄用户不屬於該終端,無法完成綁定" }, - "oauthFailed": "三方授權失敗" + "oauthFailed": "三方授權失敗", + "autoLogin": { + "sourceRequired": "開啟自動登入時必須至少選擇一個平台", + "sourceNotEnabled": "所選自動登入平台未配置或未啟用: {0}", + "sourceNotAllowed": "該平台不支援應用內自動登入: {0}" + } } diff --git a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/zh-TW/error/social.json b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/zh-TW/error/social.json index 0b602a9f3..4a8d97402 100644 --- a/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/zh-TW/error/social.json +++ b/daxpay-platform/daxpay-platform-capability/capability-social/src/main/resources/i18n/zh-TW/error/social.json @@ -19,5 +19,10 @@ "alreadyBoundPlatform": "您已繫結該平臺,請先解綁後再繫結新賬號", "clientMismatch": "當前登入使用者不屬於該終端,無法完成繫結" }, - "oauthFailed": "三方授權失敗" + "oauthFailed": "三方授權失敗", + "autoLogin": { + "sourceRequired": "開啟自動登入時必須至少選擇一個平台", + "sourceNotEnabled": "所選自動登入平台未配置或未啟用: {0}", + "sourceNotAllowed": "該平台不支援應用內自動登入: {0}" + } } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/platform_config_type.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/platform_config_type.json index 03131c1da..0947acca6 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/platform_config_type.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/platform_config_type.json @@ -9,5 +9,6 @@ "sensitive_word": "Sensitive word policy", "api_security": "API Security Config", "pay_security": "Pay Security Config", - "iam_replay_protect": "IAM Anti-Replay Config" + "iam_replay_protect": "IAM Anti-Replay Config", + "security_social_auto": "In-app social auto login" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/social_source.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/social_source.json index 16d091c80..12802a6ae 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/social_source.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/social_source.json @@ -8,5 +8,6 @@ "feishu": "Feishu", "dingTalk": "DingTalk", "douyin": "Douyin", - "alipay": "Alipay" + "alipay": "Alipay", + "weChatOpen": "WeChat Open Platform" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/id-ID/enum/platform_config_type.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/id-ID/enum/platform_config_type.json index 604bf6755..170b71697 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/id-ID/enum/platform_config_type.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/id-ID/enum/platform_config_type.json @@ -9,5 +9,6 @@ "api_security": "Konfigurasi Keamanan API", "iam_replay_protect": "Konfigurasi Anti-Replay IAM", "sensitive_word": "Kebijakan kata sensitif", - "pay_security": "Konfigurasi keamanan pembayaran" + "pay_security": "Konfigurasi keamanan pembayaran", + "security_social_auto": "Login otomatis sosial in-app" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/id-ID/enum/social_source.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/id-ID/enum/social_source.json index d7ff18594..3fcbfb974 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/id-ID/enum/social_source.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/id-ID/enum/social_source.json @@ -8,5 +8,6 @@ "feishu": "Feishu", "dingTalk": "DingTalk", "douyin": "Douyin", - "alipay": "Alipay" + "alipay": "Alipay", + "weChatOpen": "Platform Terbuka WeChat" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ja-JP/enum/platform_config_type.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ja-JP/enum/platform_config_type.json index 994bb366c..32578cc83 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ja-JP/enum/platform_config_type.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ja-JP/enum/platform_config_type.json @@ -9,5 +9,6 @@ "sensitive_word": "センシティブワード設定", "api_security": "APIセキュリティ設定", "pay_security": "決済セキュリティ設定", - "iam_replay_protect": "IAMドメインリプレイ防止設定" + "iam_replay_protect": "IAMドメインリプレイ防止設定", + "security_social_auto": "アプリ内ソーシャル自動ログイン設定" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ja-JP/enum/social_source.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ja-JP/enum/social_source.json index e9cd2ca1e..0cfdc652d 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ja-JP/enum/social_source.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ja-JP/enum/social_source.json @@ -8,5 +8,6 @@ "feishu": "フェイシュ", "dingTalk": "ディントーク", "douyin": "抖音", - "alipay": "アリペイ" + "alipay": "アリペイ", + "weChatOpen": "WeChatオープンプラットフォーム" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ko-KR/enum/platform_config_type.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ko-KR/enum/platform_config_type.json index c803a1e94..764c23ae5 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ko-KR/enum/platform_config_type.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ko-KR/enum/platform_config_type.json @@ -9,5 +9,6 @@ "sensitive_word": "민감어 정책 설정", "api_security": "API 보안 설정", "pay_security": "결제 보안 설정", - "iam_replay_protect": "IAM 도메인 재생 방지 설정" + "iam_replay_protect": "IAM 도메인 재생 방지 설정", + "security_social_auto": "인앱 소셜 자동 로그인 설정" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ko-KR/enum/social_source.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ko-KR/enum/social_source.json index 95e6b85a1..bc0f80c3a 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ko-KR/enum/social_source.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ko-KR/enum/social_source.json @@ -8,5 +8,6 @@ "feishu": "페이슈", "dingTalk": "딩톡", "douyin": "두음", - "alipay": "알리페이" + "alipay": "알리페이", + "weChatOpen": "위챗 오픈 플랫폼" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ms-MY/enum/platform_config_type.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ms-MY/enum/platform_config_type.json index 4385e3621..1cd4cef1a 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ms-MY/enum/platform_config_type.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ms-MY/enum/platform_config_type.json @@ -9,5 +9,6 @@ "api_security": "Konfigurasi Keselamatan API", "iam_replay_protect": "Konfigurasi Anti-Main Semula IAM", "sensitive_word": "Konfigurasi perkataan sensitif", - "pay_security": "Konfigurasi keselamatan pembayaran" + "pay_security": "Konfigurasi keselamatan pembayaran", + "security_social_auto": "Konfigurasi log masuk sosial auto dalam app" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ms-MY/enum/social_source.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ms-MY/enum/social_source.json index bd1ace0ac..17f038385 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ms-MY/enum/social_source.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/ms-MY/enum/social_source.json @@ -8,5 +8,6 @@ "feishu": "Feishu", "dingTalk": "DingTalk", "douyin": "Douyin", - "alipay": "Alipay" + "alipay": "Alipay", + "weChatOpen": "Platform Terbuka WeChat" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/th-TH/enum/platform_config_type.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/th-TH/enum/platform_config_type.json index c9b867b7c..f619ef292 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/th-TH/enum/platform_config_type.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/th-TH/enum/platform_config_type.json @@ -9,5 +9,6 @@ "api_security": "การกำหนดค่าความปลอดภัย API", "iam_replay_protect": "การกำหนดค่าการป้องกันการเล่นซ้ำ IAM", "sensitive_word": "การกำหนดค่าคำที่ละเอียดอ่อน", - "pay_security": "การกำหนดค่าความปลอดภัยการชำระเงิน" + "pay_security": "การกำหนดค่าความปลอดภัยการชำระเงิน", + "security_social_auto": "ตั้งค่าเข้าสู่ระบบโซเชียลอัตโนมัติในแอป" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/th-TH/enum/social_source.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/th-TH/enum/social_source.json index 346ab7db0..42c547369 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/th-TH/enum/social_source.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/th-TH/enum/social_source.json @@ -1,5 +1,5 @@ { - "weChat": "บัญชีอย่างเป็นทางการของ WeChat", + "weChat": "บัญชีทางการ WeChat", "weCom": "วีคอม", "qq": "คิวคิว", "github": "GitHub", @@ -8,5 +8,6 @@ "feishu": "เฟยซู", "dingTalk": "ติงทอล์ค", "douyin": "โต่วอิน", - "alipay": "อาลีเพย์" + "alipay": "อาลีเพย์", + "weChatOpen": "แพลตฟอร์มเปิด WeChat" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/vi-VN/enum/platform_config_type.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/vi-VN/enum/platform_config_type.json index 87205d237..a4e0ad94d 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/vi-VN/enum/platform_config_type.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/vi-VN/enum/platform_config_type.json @@ -9,5 +9,6 @@ "api_security": "Cấu hình bảo mật API", "iam_replay_protect": "Cấu hình chống phát lại IAM", "sensitive_word": "Cấu hình từ nhạy cảm", - "pay_security": "Cấu hình bảo mật thanh toán" + "pay_security": "Cấu hình bảo mật thanh toán", + "security_social_auto": "Cấu hình đăng nhập xã hội tự động trong app" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/vi-VN/enum/social_source.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/vi-VN/enum/social_source.json index 27bd9c8bf..def89e517 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/vi-VN/enum/social_source.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/vi-VN/enum/social_source.json @@ -1,5 +1,5 @@ { - "weChat": "Tài khoản chính thức của WeChat", + "weChat": "Tài khoản chính thức WeChat", "weCom": "WeCom", "qq": "QQ", "github": "GitHub", @@ -8,5 +8,6 @@ "feishu": "Feishu", "dingTalk": "DingTalk", "douyin": "Douyin", - "alipay": "Alipay" + "alipay": "Alipay", + "weChatOpen": "Nền tảng mở WeChat" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/platform_config_type.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/platform_config_type.json index 976ae240b..826b00d8c 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/platform_config_type.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/platform_config_type.json @@ -9,5 +9,6 @@ "sensitive_word": "敏感词策略配置", "api_security": "API安全配置", "pay_security": "支付安全配置", - "iam_replay_protect": "IAM域防重放配置" + "iam_replay_protect": "IAM域防重放配置", + "security_social_auto": "应用内社交自动登录配置" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/social_source.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/social_source.json index cbafb6be1..6b9d40a31 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/social_source.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/social_source.json @@ -1,5 +1,5 @@ { - "weChat": "微信开放平台", + "weChat": "微信公众号", "weCom": "企业微信", "qq": "QQ", "github": "GitHub", @@ -8,5 +8,6 @@ "feishu": "飞书", "dingTalk": "钉钉", "douyin": "抖音", - "alipay": "支付宝" + "alipay": "支付宝", + "weChatOpen": "微信开放平台" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-HK/enum/platform_config_type.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-HK/enum/platform_config_type.json index 75bc517a1..c7ba441e1 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-HK/enum/platform_config_type.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-HK/enum/platform_config_type.json @@ -9,5 +9,6 @@ "sensitive_word": "敏感詞策略配置", "api_security": "API安全配置", "pay_security": "支付安全配置", - "iam_replay_protect": "IAM域防重放配置" + "iam_replay_protect": "IAM域防重放配置", + "security_social_auto": "應用內社交自動登入配置" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-HK/enum/social_source.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-HK/enum/social_source.json index d52fd9980..56ee7a62e 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-HK/enum/social_source.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-HK/enum/social_source.json @@ -1,5 +1,5 @@ { - "weChat": "微信開放平台", + "weChat": "微信公眾號", "weCom": "企業微信", "qq": "QQ", "github": "GitHub", @@ -8,5 +8,6 @@ "feishu": "飛書", "dingTalk": "釘釘", "douyin": "抖音", - "alipay": "支付寶" + "alipay": "支付寶", + "weChatOpen": "微信開放平台" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-TW/enum/platform_config_type.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-TW/enum/platform_config_type.json index 9baa8bd8d..1dc9f8384 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-TW/enum/platform_config_type.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-TW/enum/platform_config_type.json @@ -9,5 +9,6 @@ "sensitive_word": "敏感詞策略配置", "api_security": "API安全配置", "pay_security": "支付安全配置", - "iam_replay_protect": "IAM域防重放配置" + "iam_replay_protect": "IAM域防重放配置", + "security_social_auto": "應用內社交自動登入配置" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-TW/enum/social_source.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-TW/enum/social_source.json index 7e521c00b..281aeba4f 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-TW/enum/social_source.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-TW/enum/social_source.json @@ -1,5 +1,5 @@ { - "weChat": "微信開放平臺", + "weChat": "微信公眾號", "weCom": "企業微信", "qq": "QQ", "github": "GitHub", @@ -8,5 +8,6 @@ "feishu": "飛書", "dingTalk": "釘釘", "douyin": "抖音", - "alipay": "支付寶" + "alipay": "支付寶", + "weChatOpen": "微信開放平臺" } diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/auth/service/LoginContentService.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/auth/service/LoginContentService.java index 93cd25a3e..15819cae7 100644 --- a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/auth/service/LoginContentService.java +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/auth/service/LoginContentService.java @@ -5,7 +5,10 @@ import cn.daxpay.open.platform.core.enums.client.ClientEnum; import cn.daxpay.open.platform.iam.exception.auth.ApplicationNotFoundException; import cn.daxpay.open.platform.iam.param.auth.LoginContentParam; import cn.daxpay.open.platform.iam.result.auth.LoginContentResult; +import cn.daxpay.open.platform.iam.service.social.SocialAutoLoginConfigService; import cn.daxpay.open.platform.system.entity.config.platform.security.PlatformLoginSecurityConfig; +import cn.daxpay.open.platform.system.entity.config.platform.security.PlatformSocialAutoLoginConfig; +import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -21,14 +24,17 @@ public class LoginContentService { private final IamSecurityConfigService iamSecurityConfigService; + private final SocialAutoLoginConfigService socialAutoLoginConfigService; + /// 获取登录页上下文信息 public LoginContentResult getLoginContent(LoginContentParam param) { - if (param != null && param.getClientId() != null && !param.getClientId().isBlank()) { - ClientEnum.findByCode(param.getClientId()) + String clientCode = param != null ? param.getClientId() : null; + if (StrUtil.isNotBlank(clientCode)) { + ClientEnum.findByCode(clientCode) .orElseThrow(ApplicationNotFoundException::new); } PlatformLoginSecurityConfig loginSecurity = iamSecurityConfigService.getLoginSecurity(); - return new LoginContentResult() + LoginContentResult result = new LoginContentResult() .setLoginTypes(authenticators.stream() .map(Authenticator::getLoginType) .distinct() @@ -36,6 +42,17 @@ public class LoginContentService { // 是否启用验证码触发(登录失败达阈值后要求输入验证码) .setEnableCaptcha(Boolean.TRUE.equals(loginSecurity.getCaptchaEnabled())) .setPasswordEncrypted(false); + // 按本端下发自动登录片段(无 client 时不下发, 保持关闭) + if (StrUtil.isNotBlank(clientCode)) { + PlatformSocialAutoLoginConfig.ClientAutoLogin auto = + socialAutoLoginConfigService.resolveForClient(clientCode); + result.setAutoSocialLogin(new LoginContentResult.AutoSocialLogin() + .setEnabled(Boolean.TRUE.equals(auto.getEnabled())) + .setSources(auto.resolveSources())); + } else { + result.setAutoSocialLogin(new LoginContentResult.AutoSocialLogin().setEnabled(false)); + } + return result; } } diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/controller/social/SocialLoginConfigController.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/controller/social/SocialLoginConfigController.java index 46a7f652a..efeecb226 100644 --- a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/controller/social/SocialLoginConfigController.java +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/controller/social/SocialLoginConfigController.java @@ -3,12 +3,15 @@ package cn.daxpay.open.platform.iam.controller.social; import cn.daxpay.open.platform.iam.param.social.SocialLoginConfigParam; import cn.daxpay.open.platform.iam.result.social.SocialCallbackUrlResult; import cn.daxpay.open.platform.iam.result.social.SocialLoginConfigResult; +import cn.daxpay.open.platform.iam.service.social.SocialAutoLoginConfigService; import cn.daxpay.open.platform.iam.service.social.SocialLoginConfigService; import cn.daxpay.open.platform.iam.service.social.SocialLoginService; import cn.daxpay.open.platform.core.annotation.PermCode; import cn.daxpay.open.platform.core.code.PermCodes; 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.security.PlatformSocialAutoLoginConfigParam; +import cn.daxpay.open.platform.system.result.config.security.PlatformSocialAutoLoginConfigResult; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.NotBlank; @@ -37,6 +40,8 @@ public class SocialLoginConfigController { private final SocialLoginService socialLoginService; + private final SocialAutoLoginConfigService socialAutoLoginConfigService; + @PermCode(code = PermCodes.Action.VIEW) @Operation(summary = "全量查询平台配置(枚举驱动, 读时初始化缺失平台)") @GetMapping("/find-all") @@ -76,4 +81,20 @@ public class SocialLoginConfigController { socialLoginConfigService.updateEnabled(source, enabled); return Res.ok(); } + + /// 应用内自动登录策略(按 admin/merchant 分端) + @PermCode(code = PermCodes.Action.VIEW) + @Operation(summary = "获取应用内社交自动登录配置") + @GetMapping("/auto-login/get") + public Result getAutoLoginConfig() { + return Res.ok(socialAutoLoginConfigService.findConfig()); + } + + @PermCode(code = PermCodes.Action.MANAGE) + @Operation(summary = "更新应用内社交自动登录配置") + @PostMapping("/auto-login/update") + public Result updateAutoLoginConfig(@RequestBody @Validated PlatformSocialAutoLoginConfigParam param) { + socialAutoLoginConfigService.updateConfig(param); + return Res.ok(); + } } diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/endpoint/SocialEndpoint.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/endpoint/SocialEndpoint.java index 948b5337f..8abf0c033 100644 --- a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/endpoint/SocialEndpoint.java +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/endpoint/SocialEndpoint.java @@ -53,8 +53,9 @@ public class SocialEndpoint { @GetMapping("/render/{source}") public Result render(@PathVariable String source, @RequestParam String client, - @RequestParam(required = false) String mode) { - return Res.ok(socialLoginService.generateAuthorizeUrl(source, client, mode)); + @RequestParam(required = false) String mode, + @RequestParam(required = false) Boolean silent) { + return Res.ok(socialLoginService.generateAuthorizeUrl(source, client, mode, silent)); } /// OAuth 授权码兑换 - 登录(公开, 无需认证) diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/result/auth/LoginContentResult.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/result/auth/LoginContentResult.java index a673c2856..28a50e3f7 100644 --- a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/result/auth/LoginContentResult.java +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/result/auth/LoginContentResult.java @@ -23,4 +23,20 @@ public class LoginContentResult { @Schema(description = "密码是否加密传输") private boolean passwordEncrypted; + /// 本端应用内社交自动登录策略(已按 client 裁剪且交叉校验社交平台启用态) + @Schema(description = "应用内社交自动登录") + private AutoSocialLogin autoSocialLogin; + + /// 单端自动登录下发片段 + @Data + @Accessors(chain = true) + @Schema(title = "应用内社交自动登录") + public static class AutoSocialLogin { + + @Schema(description = "是否启用") + private boolean enabled; + + @Schema(description = "可自动跳转的社交平台编码列表(前端按 UA 匹配其一)") + private List sources; + } } diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialAutoLoginConfigService.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialAutoLoginConfigService.java new file mode 100644 index 000000000..acf587a43 --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialAutoLoginConfigService.java @@ -0,0 +1,132 @@ +package cn.daxpay.open.platform.iam.service.social; + +import cn.daxpay.open.platform.capability.social.justauth.SocialSourceEnum; +import cn.daxpay.open.platform.core.enums.client.ClientEnum; +import cn.daxpay.open.platform.core.exception.operation.OperationFailException; +import cn.daxpay.open.platform.iam.entity.social.SocialLoginConfig; +import cn.daxpay.open.platform.system.entity.config.platform.security.PlatformSocialAutoLoginConfig; +import cn.daxpay.open.platform.system.param.config.security.PlatformSocialAutoLoginConfigParam; +import cn.daxpay.open.platform.system.result.config.security.PlatformSocialAutoLoginConfigResult; +import cn.daxpay.open.platform.system.service.config.security.PlatformSocialAutoLoginConfigService; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/// # 应用内社交自动登录编排服务 +/// +/// 在平台配置读写之上叠加社交平台启用校验, 供运营配置页与登录上下文使用. +/// +@Service +@RequiredArgsConstructor +public class SocialAutoLoginConfigService { + + /// 支持应用内 UA 静默授权的平台(不含开放平台扫码 weChatOpen) + private static final Set AUTO_LOGIN_ALLOWED_SOURCES = Set.of( + SocialSourceEnum.FEISHU.getCode(), + SocialSourceEnum.WECHAT_MP.getCode(), + SocialSourceEnum.WE_COM.getCode() + ); + + private final PlatformSocialAutoLoginConfigService platformSocialAutoLoginConfigService; + + private final SocialLoginConfigService socialLoginConfigService; + + /// 查询完整配置(运营端管理) + public PlatformSocialAutoLoginConfigResult findConfig() { + return platformSocialAutoLoginConfigService.findConfig(); + } + + /// 更新配置: 开启时校验 sources 合法且社交登录平台已启用 + public void updateConfig(PlatformSocialAutoLoginConfigParam param) { + this.validateClientItem(ClientEnum.ADMIN.getCode(), param.getAdmin()); + this.validateClientItem(ClientEnum.MERCHANT.getCode(), param.getMerchant()); + platformSocialAutoLoginConfigService.updateConfig(param); + } + + /// 按终端解析对外下发的自动登录片段(登录页用) + /// 过滤未启用或不可应用内自动登录的平台; 过滤后为空则视为未开启. + public PlatformSocialAutoLoginConfig.ClientAutoLogin resolveForClient(String clientCode) { + PlatformSocialAutoLoginConfig config = platformSocialAutoLoginConfigService.getConfig(); + PlatformSocialAutoLoginConfig.ClientAutoLogin item = this.pickClient(config, clientCode); + if (item == null || !Boolean.TRUE.equals(item.getEnabled())) { + return new PlatformSocialAutoLoginConfig.ClientAutoLogin().setEnabled(false); + } + List configured = item.resolveSources(); + if (CollUtil.isEmpty(configured)) { + return new PlatformSocialAutoLoginConfig.ClientAutoLogin().setEnabled(false); + } + List effective = new ArrayList<>(); + for (String source : configured) { + if (!AUTO_LOGIN_ALLOWED_SOURCES.contains(source)) { + continue; + } + SocialLoginConfig enabled = socialLoginConfigService.findEnabledBySource(source); + if (enabled != null) { + effective.add(source); + } + } + if (effective.isEmpty()) { + return new PlatformSocialAutoLoginConfig.ClientAutoLogin().setEnabled(false); + } + return new PlatformSocialAutoLoginConfig.ClientAutoLogin() + .setEnabled(true) + .setSources(effective); + } + + /// 校验单端配置项 + private void validateClientItem(String clientCode, PlatformSocialAutoLoginConfigParam.ClientAutoLoginParam item) { + if (item == null || !Boolean.TRUE.equals(item.getEnabled())) { + return; + } + List sources = this.resolveParamSources(item); + if (CollUtil.isEmpty(sources)) { + // 开启自动登录时必须至少选择一个平台 + throw new OperationFailException("error.social.autoLogin.sourceRequired", clientCode); + } + for (String code : sources) { + if (!AUTO_LOGIN_ALLOWED_SOURCES.contains(code)) { + // 所选平台不支持应用内自动登录(如微信开放平台扫码) + throw new OperationFailException("error.social.autoLogin.sourceNotAllowed", code); + } + SocialSourceEnum source = SocialSourceEnum.of(code); + if (source == null) { + throw new OperationFailException("error.social.unsupportedSource"); + } + SocialLoginConfig enabled = socialLoginConfigService.findEnabledBySource(code); + if (enabled == null) { + // 所选平台未配置或未启用 + throw new OperationFailException("error.social.autoLogin.sourceNotEnabled", code); + } + } + } + + private List resolveParamSources(PlatformSocialAutoLoginConfigParam.ClientAutoLoginParam item) { + List resolved = new ArrayList<>(); + if (CollUtil.isNotEmpty(item.getSources())) { + for (String code : item.getSources()) { + if (StrUtil.isNotBlank(code) && !resolved.contains(code)) { + resolved.add(code); + } + } + } else if (StrUtil.isNotBlank(item.getSource())) { + resolved.add(item.getSource()); + } + return resolved; + } + + private PlatformSocialAutoLoginConfig.ClientAutoLogin pickClient(PlatformSocialAutoLoginConfig config, + String clientCode) { + if (ClientEnum.ADMIN.getCode().equals(clientCode)) { + return config.getAdmin(); + } + if (ClientEnum.MERCHANT.getCode().equals(clientCode)) { + return config.getMerchant(); + } + return null; + } +} diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialLoginService.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialLoginService.java index 997785b29..ff1eaec40 100644 --- a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialLoginService.java +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/social/SocialLoginService.java @@ -29,7 +29,9 @@ import cn.daxpay.open.platform.iam.result.user.UserInfoResult; import cn.daxpay.open.platform.iam.service.social.other.AlipaySocialAuthRequest; import cn.daxpay.open.platform.iam.service.social.other.AlipaySocialAuthRequestFactory; import cn.daxpay.open.platform.iam.service.user.UserQueryService; +import cn.daxpay.open.platform.system.entity.config.platform.auth.PlatformWechatMpAuthConfig; import cn.daxpay.open.platform.system.entity.config.platform.infra.PlatformUrlConfig; +import cn.daxpay.open.platform.system.service.config.auth.PlatformWechatMpAuthConfigService; import cn.daxpay.open.platform.system.service.config.infra.PlatformUrlConfigService; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; @@ -71,6 +73,8 @@ public class SocialLoginService { private final PlatformUrlConfigService platformUrlConfigService; + private final PlatformWechatMpAuthConfigService platformWechatMpAuthConfigService; + private final UserQueryService userQueryService; private final PlatformStarterProperties platformStarterProperties; @@ -119,6 +123,12 @@ public class SocialLoginService { /// @param client 终端编码(admin/merchant), 用于解析端点配置中的 baseUrl /// @param mode 授权场景(不传则按登录态判断: 已登录=绑定, 未登录=登录) public String generateAuthorizeUrl(String source, String client, String mode) { + return this.generateAuthorizeUrl(source, client, mode, null); + } + + /// 生成授权地址 + /// @param silent true=应用内静默/网页授权(企微 oauth / 微信 snsapi_base) + public String generateAuthorizeUrl(String source, String client, String mode, Boolean silent) { // 仅 admin/merchant SocialClientEnum socialClient = this.requireSocialClient(client); // 加载平台配置(全局唯一) @@ -138,6 +148,12 @@ public class SocialLoginService { String redirectUri = this.buildRedirectUri(baseUrl, authMode); // 构建授权请求 SocialAuthConfig authConfig = socialLoginConfigService.buildAuthConfig(config, redirectUri); + // 公众号: 凭据来自平台级配置, 不写在 iam_social_login_config + this.fillPlatformRedirectCredentials(socialSource, authConfig); + // 应用内自动登录: 企微走网页授权、公众号走 snsapi_base + if (Boolean.TRUE.equals(silent)) { + authConfig.setSilent(true); + } SocialAuthRequest request = this.createAuthRequest(socialSource, authConfig); // state 仅用于 OAuth2 合规, 不缓存业务上下文 String state = IdUtil.fastSimpleUUID(); @@ -256,10 +272,25 @@ public class SocialLoginService { if (socialSource == null) { throw new OperationFailException("error.social.unsupportedSource"); } + // 公众号: 凭据来自平台级配置 + this.fillPlatformRedirectCredentials(socialSource, authConfig); SocialAuthRequest authRequest = this.createAuthRequest(socialSource, authConfig); return authRequest.login(AuthCallback.of(code, state)); } + /// 平台级跳转型凭据回填(公众号 AppId/Secret 等) + private void fillPlatformRedirectCredentials(SocialSourceEnum socialSource, SocialAuthConfig authConfig) { + if (socialSource != SocialSourceEnum.WECHAT_MP) { + return; + } + PlatformWechatMpAuthConfig mp = platformWechatMpAuthConfigService.getWechatMpAuthConfig(); + if (mp == null || StrUtil.isBlank(mp.getAppId()) || StrUtil.isBlank(mp.getAppSecret())) { + throw new OperationFailException("error.social.wechatMpNotConfigured"); + } + authConfig.setClientId(mp.getAppId()); + authConfig.setClientSecret(mp.getAppSecret()); + } + /// 按平台创建授权请求: 支付宝走 iam 侧 [AlipaySocialAuthRequest], 其余走 JustAuth 工厂 private SocialAuthRequest createAuthRequest(SocialSourceEnum socialSource, SocialAuthConfig authConfig) { if (socialSource == SocialSourceEnum.ALIPAY) { diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/convert/config/security/PlatformSocialAutoLoginConfigConvert.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/convert/config/security/PlatformSocialAutoLoginConfigConvert.java new file mode 100644 index 000000000..ceba45070 --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/convert/config/security/PlatformSocialAutoLoginConfigConvert.java @@ -0,0 +1,20 @@ +package cn.daxpay.open.platform.system.convert.config.security; + +import cn.daxpay.open.platform.system.entity.config.platform.security.PlatformSocialAutoLoginConfig; +import cn.daxpay.open.platform.system.param.config.security.PlatformSocialAutoLoginConfigParam; +import cn.daxpay.open.platform.system.result.config.security.PlatformSocialAutoLoginConfigResult; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +/// # 应用内社交自动登录配置转换 +/// +@Mapper +public interface PlatformSocialAutoLoginConfigConvert { + + PlatformSocialAutoLoginConfigConvert CONVERT = Mappers.getMapper(PlatformSocialAutoLoginConfigConvert.class); + + PlatformSocialAutoLoginConfigResult toResult(PlatformSocialAutoLoginConfig data); + + void copy(PlatformSocialAutoLoginConfigParam param, @MappingTarget PlatformSocialAutoLoginConfig data); +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/entity/config/platform/security/PlatformSocialAutoLoginConfig.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/entity/config/platform/security/PlatformSocialAutoLoginConfig.java new file mode 100644 index 000000000..1f54fdde0 --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/entity/config/platform/security/PlatformSocialAutoLoginConfig.java @@ -0,0 +1,66 @@ +package cn.daxpay.open.platform.system.entity.config.platform.security; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.ArrayList; +import java.util.List; + +/// # 应用内社交自动登录配置 +/// +/// 按终端(admin/merchant)分别配置是否在飞书/微信/企微等内置浏览器中自动发起 OAuth 登录。 +/// 凭据仍存放于 iam_social_login_config / 平台级公众号配置, 本配置仅存策略开关与可选平台列表. +/// +@Data +@Accessors(chain = true) +public class PlatformSocialAutoLoginConfig { + + /// 运营端自动登录策略 + private ClientAutoLogin admin = new ClientAutoLogin(); + + /// 商户端自动登录策略 + private ClientAutoLogin merchant = new ClientAutoLogin(); + + /// 单端自动登录项 + @Data + @Accessors(chain = true) + public static class ClientAutoLogin { + + /// 是否启用应用内自动登录 + private Boolean enabled = Boolean.FALSE; + + /// 可自动跳转的社交平台编码列表(如 feishu / weChat / weCom), 登录时按 UA 匹配其一 + private List sources = new ArrayList<>(); + + /// 兼容旧版单字段 source; 反序列化后由 [normalize] 合并进 sources, 新写入不再持久化 + @Deprecated + private String source; + + /// 归一化: 旧 source 迁移为 sources[], 去空并去重 + public ClientAutoLogin normalize() { + List resolved = new ArrayList<>(); + if (CollUtil.isNotEmpty(sources)) { + for (String item : sources) { + if (StrUtil.isNotBlank(item) && !resolved.contains(item)) { + resolved.add(item); + } + } + } else if (StrUtil.isNotBlank(source)) { + resolved.add(source); + } + this.sources = resolved; + this.source = null; + return this; + } + + /// 对外读取有效平台列表(已 normalize 后可直接取 sources) + @JsonIgnore + public List resolveSources() { + this.normalize(); + return this.sources == null ? List.of() : this.sources; + } + } +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/enums/PlatformConfigTypeEnum.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/enums/PlatformConfigTypeEnum.java index 40cfd04a2..1d5744a6d 100644 --- a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/enums/PlatformConfigTypeEnum.java +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/enums/PlatformConfigTypeEnum.java @@ -17,6 +17,8 @@ public enum PlatformConfigTypeEnum implements I18nSupport { SECURITY_PASSWORD_POLICY("security_password_policy"), /// 登录安全配置 SECURITY_LOGIN("security_login"), + /// 应用内社交自动登录配置(按 admin/merchant 分端) + SECURITY_SOCIAL_AUTO("security_social_auto"), /// 会话管理配置 SECURITY_SESSION("security_session"), /// 双因素认证配置 diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/config/security/PlatformSocialAutoLoginConfigParam.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/config/security/PlatformSocialAutoLoginConfigParam.java new file mode 100644 index 000000000..204d7c932 --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/config/security/PlatformSocialAutoLoginConfigParam.java @@ -0,0 +1,39 @@ +package cn.daxpay.open.platform.system.param.config.security; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +/// # 应用内社交自动登录配置参数 +/// +@Data +@Accessors(chain = true) +@Schema(title = "应用内社交自动登录配置参数") +public class PlatformSocialAutoLoginConfigParam { + + @Schema(description = "运营端自动登录策略") + private ClientAutoLoginParam admin; + + @Schema(description = "商户端自动登录策略") + private ClientAutoLoginParam merchant; + + /// 单端自动登录参数 + @Data + @Accessors(chain = true) + @Schema(title = "单端自动登录参数") + public static class ClientAutoLoginParam { + + @Schema(description = "是否启用应用内自动登录") + private Boolean enabled; + + @Schema(description = "可自动跳转的社交平台编码列表") + private List sources; + + /// 兼容旧客户端单字段; 服务端优先用 sources, 空时回退 + @Schema(description = "兼容旧版单平台编码", deprecated = true) + @Deprecated + private String source; + } +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/config/security/PlatformSocialAutoLoginConfigResult.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/config/security/PlatformSocialAutoLoginConfigResult.java new file mode 100644 index 000000000..65c58d15d --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/config/security/PlatformSocialAutoLoginConfigResult.java @@ -0,0 +1,34 @@ +package cn.daxpay.open.platform.system.result.config.security; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +/// # 应用内社交自动登录配置结果 +/// +@Data +@Accessors(chain = true) +@Schema(title = "应用内社交自动登录配置结果") +public class PlatformSocialAutoLoginConfigResult { + + @Schema(description = "运营端自动登录策略") + private ClientAutoLoginResult admin; + + @Schema(description = "商户端自动登录策略") + private ClientAutoLoginResult merchant; + + /// 单端自动登录结果 + @Data + @Accessors(chain = true) + @Schema(title = "单端自动登录结果") + public static class ClientAutoLoginResult { + + @Schema(description = "是否启用应用内自动登录") + private Boolean enabled; + + @Schema(description = "可自动跳转的社交平台编码列表") + private List sources; + } +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/service/config/security/PlatformSocialAutoLoginConfigService.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/service/config/security/PlatformSocialAutoLoginConfigService.java new file mode 100644 index 000000000..686402c5f --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/service/config/security/PlatformSocialAutoLoginConfigService.java @@ -0,0 +1,96 @@ +package cn.daxpay.open.platform.system.service.config.security; + +import cn.daxpay.open.platform.system.convert.config.security.PlatformSocialAutoLoginConfigConvert; +import cn.daxpay.open.platform.system.entity.config.platform.security.PlatformSocialAutoLoginConfig; +import cn.daxpay.open.platform.system.enums.PlatformConfigTypeEnum; +import cn.daxpay.open.platform.system.param.config.security.PlatformSocialAutoLoginConfigParam; +import cn.daxpay.open.platform.system.result.config.security.PlatformSocialAutoLoginConfigResult; +import cn.daxpay.open.platform.system.service.config.SystemPlatformConfigService; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/// # 应用内社交自动登录配置服务 +/// +/// 仅负责读写 [PlatformConfigTypeEnum#SECURITY_SOCIAL_AUTO]; +/// 社交平台是否已启用等业务校验由 IAM 层在更新前完成. +/// +@Service +@RequiredArgsConstructor +public class PlatformSocialAutoLoginConfigService { + + private final SystemPlatformConfigService systemConfigService; + + /// 获取配置实体(不存在则创建默认关闭态), 并归一化旧 source 字段 + public PlatformSocialAutoLoginConfig getConfig() { + PlatformSocialAutoLoginConfig config = systemConfigService.getOrCreateConfig( + PlatformConfigTypeEnum.SECURITY_SOCIAL_AUTO, + PlatformSocialAutoLoginConfig.class, + defaultConfig()); + this.normalize(config); + return config; + } + + /// 查询配置结果 + public PlatformSocialAutoLoginConfigResult findConfig() { + return PlatformSocialAutoLoginConfigConvert.CONVERT.toResult(this.getConfig()); + } + + /// 更新配置(调用方已完成业务校验) + public void updateConfig(PlatformSocialAutoLoginConfigParam param) { + PlatformSocialAutoLoginConfig data = this.getConfig(); + PlatformSocialAutoLoginConfigConvert.CONVERT.copy(param, data); + // 嵌套对象 MapStruct 可能留下 null, 兜底为空对象避免 NPE + if (data.getAdmin() == null) { + data.setAdmin(new PlatformSocialAutoLoginConfig.ClientAutoLogin()); + } + if (data.getMerchant() == null) { + data.setMerchant(new PlatformSocialAutoLoginConfig.ClientAutoLogin()); + } + // 参数可能仍带旧 source, 统一合并后落盘只保留 sources + this.applyParamSources(data.getAdmin(), param.getAdmin()); + this.applyParamSources(data.getMerchant(), param.getMerchant()); + this.normalize(data); + systemConfigService.updateConfig(PlatformConfigTypeEnum.SECURITY_SOCIAL_AUTO, data); + } + + /// 默认: 两端均关闭 + private PlatformSocialAutoLoginConfig defaultConfig() { + return new PlatformSocialAutoLoginConfig() + .setAdmin(new PlatformSocialAutoLoginConfig.ClientAutoLogin().setEnabled(false)) + .setMerchant(new PlatformSocialAutoLoginConfig.ClientAutoLogin().setEnabled(false)); + } + + private void normalize(PlatformSocialAutoLoginConfig config) { + if (config.getAdmin() != null) { + config.getAdmin().normalize(); + } + if (config.getMerchant() != null) { + config.getMerchant().normalize(); + } + } + + /// 将 Param 的 sources/source 写回实体, 避免 MapStruct 只拷同名后遗留旧 source + private void applyParamSources(PlatformSocialAutoLoginConfig.ClientAutoLogin target, + PlatformSocialAutoLoginConfigParam.ClientAutoLoginParam param) { + if (target == null || param == null) { + return; + } + List resolved = new ArrayList<>(); + if (CollUtil.isNotEmpty(param.getSources())) { + for (String item : param.getSources()) { + if (StrUtil.isNotBlank(item) && !resolved.contains(item)) { + resolved.add(item); + } + } + } else if (StrUtil.isNotBlank(param.getSource())) { + resolved.add(param.getSource()); + } + target.setSources(resolved); + target.setSource(null); + } +}