diff --git a/CHANGELOGS.md b/CHANGELOGS.md index e8b9403..6f92850 100644 --- a/CHANGELOGS.md +++ b/CHANGELOGS.md @@ -3,14 +3,14 @@ ### 2024/12/14 - 新增 - - 添加`微信小程序`登录能力。 - - 添加`支付宝证书模式`登录能力(原支持的公钥登录模式依然可用)。 - - 添加`appleid`社交登录能力。 [Github#192](https://github.com/justauth/JustAuth/pull/192) + - 添加`微信小程序`登录能力,对接文档:[点击查看](https://justauth.cn/guide/oauth/wechat_mini_program/)。 + - 添加`支付宝证书模式`登录能力(原支持的公钥登录模式依然可用),对接文档:[点击查看](https://justauth.cn/guide/oauth/alipay_cert)。 + - 添加`appleid`社交登录能力,对接文档:[点击查看](https://justauth.cn/guide/oauth/appleid/)。 [Github#192](https://github.com/justauth/JustAuth/pull/192) - 添加`QQ小程序`社交登录能力。 [Github#223](https://github.com/justauth/JustAuth/pull/223) - 添加`figma`社交登录能力。 [Gitee#41](https://gitee.com/yadong.zhang/JustAuth/pulls/41) - - 添加新版`企业微信扫码`登录能力。 [Github Issue#165](https://github.com/justauth/JustAuth/issues/165) - - 添加新版`钉钉扫码`登录能力。 [Gitee Issue#I73FZL](https://gitee.com/yadong.zhang/JustAuth/issues/I73FZL) - - 添加新版`华为`登录能力,原`AuthHuaweiRequest`会在后面版本被弃用,如有使用,请切换到`AuthHuaweiV3Request` + - 添加新版`企业微信扫码`登录能力,对接文档:[点击查看](https://justauth.cn/guide/oauth/wechat_enterprise_qrcode_v2/)。 [Github Issue#165](https://github.com/justauth/JustAuth/issues/165) + - 添加新版`钉钉扫码`登录能力,对接文档:[点击查看](https://justauth.cn/guide/oauth/dingtalk_v2/)。 [Gitee Issue#I73FZL](https://gitee.com/yadong.zhang/JustAuth/issues/I73FZL) + - 添加新版`华为`登录能力,对接文档:[点击查看](https://justauth.cn/guide/oauth/huawei_v3/),原`AuthHuaweiRequest`会在后面版本被弃用,如有使用,请切换到`AuthHuaweiV3Request` - 优化 - 修复文档错误。[Github #222](https://github.com/justauth/JustAuth/pull/222) - 更新 Google 端点地址。[Github #198](https://github.com/justauth/JustAuth/pull/198) diff --git a/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java b/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java index a4f5a85..e089fed 100644 --- a/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java +++ b/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java @@ -2,7 +2,58 @@ import me.zhyd.oauth.enums.AuthResponseStatus; import me.zhyd.oauth.exception.AuthException; -import me.zhyd.oauth.request.*; +import me.zhyd.oauth.request.AuthAlipayRequest; +import me.zhyd.oauth.request.AuthAliyunRequest; +import me.zhyd.oauth.request.AuthAmazonRequest; +import me.zhyd.oauth.request.AuthAppleRequest; +import me.zhyd.oauth.request.AuthBaiduRequest; +import me.zhyd.oauth.request.AuthCodingRequest; +import me.zhyd.oauth.request.AuthCsdnRequest; +import me.zhyd.oauth.request.AuthDefaultRequest; +import me.zhyd.oauth.request.AuthDingTalkAccountRequest; +import me.zhyd.oauth.request.AuthDingTalkRequest; +import me.zhyd.oauth.request.AuthDingTalkV2Request; +import me.zhyd.oauth.request.AuthDouyinMiniProgramRequest; +import me.zhyd.oauth.request.AuthDouyinRequest; +import me.zhyd.oauth.request.AuthElemeRequest; +import me.zhyd.oauth.request.AuthFacebookRequest; +import me.zhyd.oauth.request.AuthFeishuRequest; +import me.zhyd.oauth.request.AuthFigmaRequest; +import me.zhyd.oauth.request.AuthGiteeRequest; +import me.zhyd.oauth.request.AuthGithubRequest; +import me.zhyd.oauth.request.AuthGitlabRequest; +import me.zhyd.oauth.request.AuthGoogleRequest; +import me.zhyd.oauth.request.AuthHuaweiRequest; +import me.zhyd.oauth.request.AuthHuaweiV3Request; +import me.zhyd.oauth.request.AuthJdRequest; +import me.zhyd.oauth.request.AuthKujialeRequest; +import me.zhyd.oauth.request.AuthLineRequest; +import me.zhyd.oauth.request.AuthLinkedinRequest; +import me.zhyd.oauth.request.AuthMeituanRequest; +import me.zhyd.oauth.request.AuthMiRequest; +import me.zhyd.oauth.request.AuthMicrosoftCnRequest; +import me.zhyd.oauth.request.AuthMicrosoftRequest; +import me.zhyd.oauth.request.AuthOktaRequest; +import me.zhyd.oauth.request.AuthOschinaRequest; +import me.zhyd.oauth.request.AuthPinterestRequest; +import me.zhyd.oauth.request.AuthProginnRequest; +import me.zhyd.oauth.request.AuthQqRequest; +import me.zhyd.oauth.request.AuthRenrenRequest; +import me.zhyd.oauth.request.AuthSlackRequest; +import me.zhyd.oauth.request.AuthStackOverflowRequest; +import me.zhyd.oauth.request.AuthTaobaoRequest; +import me.zhyd.oauth.request.AuthTeambitionRequest; +import me.zhyd.oauth.request.AuthToutiaoRequest; +import me.zhyd.oauth.request.AuthTwitterRequest; +import me.zhyd.oauth.request.AuthWeChatEnterpriseQrcodeRequest; +import me.zhyd.oauth.request.AuthWeChatEnterpriseQrcodeV2Request; +import me.zhyd.oauth.request.AuthWeChatEnterpriseThirdQrcodeRequest; +import me.zhyd.oauth.request.AuthWeChatEnterpriseWebRequest; +import me.zhyd.oauth.request.AuthWeChatMpRequest; +import me.zhyd.oauth.request.AuthWeChatOpenRequest; +import me.zhyd.oauth.request.AuthWechatMiniProgramRequest; +import me.zhyd.oauth.request.AuthWeiboRequest; +import me.zhyd.oauth.request.AuthXmlyRequest; /** * JustAuth内置的各api需要的url, 用枚举类分平台类型管理 @@ -565,7 +616,9 @@ public String refresh() { } @Override - public Class extends AuthDefaultRequest> getTargetClass() { return AuthMicrosoftCnRequest.class; } + public Class extends AuthDefaultRequest> getTargetClass() { + return AuthMicrosoftCnRequest.class; + } }, /** * 小米 @@ -732,7 +785,7 @@ public Class extends AuthDefaultRequest> getTargetClass() { /** * 华为 - * + *
* 当前方式未来可能被废弃,建议使用 {@link this#HUAWEI_V3} * * @since 1.10.0 @@ -1409,7 +1462,7 @@ public Class extends AuthDefaultRequest> getTargetClass() { } }, - FIGMA{ + FIGMA { @Override public String authorize() { return "https://www.figma.com/oauth"; @@ -1437,10 +1490,10 @@ public Class extends AuthDefaultRequest> getTargetClass() { }, /** * 微信小程序授权登录 + * * @since yudaocode */ WECHAT_MINI_PROGRAM { - @Override public String authorize() { // 参见 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html 文档 @@ -1493,6 +1546,55 @@ public String userInfo() { public Class extends AuthDefaultRequest> getTargetClass() { return null; } + }, + + /** + * 抖音小程序授权 + */ + DOUYIN_MINI_PROGRAM { + /** + * 授权的api + * + * @return url + */ + @Override + public String authorize() { + // 参见 https://developer.open-douyin.com/docs/resource/zh-CN/mini-game/develop/api/open-capacity/log-in/tt-login 文档 + throw new UnsupportedOperationException("不支持获取授权 url,请使用小程序内置函数 tt.login() 登录获取 code"); + } + + /** + * 获取accessToken的api + * + * @return url + */ + @Override + public String accessToken() { + // 参见 https://developer.open-douyin.com/docs/resource/zh-CN/mini-game/develop/server/log-in/code-2-session 文档 + // 获取 openid, unionId , session_key 等字段 + return "https://minigame.zijieapi.com/mgplatform/api/apps/jscode2session"; + } + + /** + * 获取用户信息的api + * + * @return url + */ + @Override + public String userInfo() { + // 参见 https://developer.open-douyin.com/docs/resource/zh-CN/mini-game/develop/guide/open-api/info/tt-get-user-info 文档 + throw new UnsupportedOperationException("不支持获取用户信息 url,请使用小程序内置函数 tt.getUserInfo() 获取用户信息"); + } + + /** + * 平台对应的 AuthRequest 实现类,必须继承自 {@link AuthDefaultRequest} + * + * @return class + */ + @Override + public Class extends AuthDefaultRequest> getTargetClass() { + return AuthDouyinMiniProgramRequest.class; + } } } diff --git a/src/main/java/me/zhyd/oauth/model/AuthCallback.java b/src/main/java/me/zhyd/oauth/model/AuthCallback.java index b6558e2..1bc12f9 100644 --- a/src/main/java/me/zhyd/oauth/model/AuthCallback.java +++ b/src/main/java/me/zhyd/oauth/model/AuthCallback.java @@ -27,6 +27,11 @@ public class AuthCallback implements Serializable { */ private String code; + /** + * 该参数目前只使用于抖音小程序匿名登录 + */ + private String anonymous_code; + /** * 访问AuthorizeUrl后回调时带的参数auth_code,该参数目前只使用于支付宝登录 */ @@ -60,12 +65,14 @@ public class AuthCallback implements Serializable { /** * 苹果仅在用户首次授权应用程序时返回此值。如果您的应用程序已经获得了用户的授权,那么苹果将不会再次返回此值 + * * @see user info */ private String user; /** * 苹果错误信息,仅在用户取消授权时返回此值 + * * @see error response */ private String error; diff --git a/src/main/java/me/zhyd/oauth/model/AuthToken.java b/src/main/java/me/zhyd/oauth/model/AuthToken.java index 706f95c..c7abaaf 100644 --- a/src/main/java/me/zhyd/oauth/model/AuthToken.java +++ b/src/main/java/me/zhyd/oauth/model/AuthToken.java @@ -1,6 +1,10 @@ package me.zhyd.oauth.model; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import java.io.Serializable; @@ -46,7 +50,7 @@ public class AuthToken implements Serializable { private String code; /** * 微信公众号 - 网页授权的登录时可用 - * + *
* 微信针对网页授权登录,增加了一个快照页的逻辑,快照页获取到的微信用户的 uid oid 和头像昵称都是虚拟的信息 */ private boolean snapshotUser; @@ -73,4 +77,9 @@ public class AuthToken implements Serializable { * @since 1.16.7 */ private String corpId; + + /** + * 抖音小程序附带属性 + */ + private String anonymousOpenid; } diff --git a/src/main/java/me/zhyd/oauth/request/AuthDouyinMiniProgramRequest.java b/src/main/java/me/zhyd/oauth/request/AuthDouyinMiniProgramRequest.java new file mode 100644 index 0000000..4d40402 --- /dev/null +++ b/src/main/java/me/zhyd/oauth/request/AuthDouyinMiniProgramRequest.java @@ -0,0 +1,99 @@ +package me.zhyd.oauth.request; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import lombok.Data; +import me.zhyd.oauth.cache.AuthStateCache; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.config.AuthDefaultSource; +import me.zhyd.oauth.exception.AuthException; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthToken; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.utils.HttpUtils; +import me.zhyd.oauth.utils.UrlBuilder; + +/** + * @author JackyTang (jackytang01(a)gmail.com) + * @version 1.0 + * @website + * @date 2024/04/22 00:06 + * @since 17 + */ +public class AuthDouyinMiniProgramRequest extends AuthDefaultRequest { + public AuthDouyinMiniProgramRequest(AuthConfig config) { + super(config, AuthDefaultSource.DOUYIN_MINI_PROGRAM); + } + + public AuthDouyinMiniProgramRequest(AuthConfig config, AuthStateCache authStateCache) { + super(config, AuthDefaultSource.DOUYIN_MINI_PROGRAM, authStateCache); + } + + @Override + public AuthToken getAccessToken(AuthCallback authCallback) { + // 参见 https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html 文档 + // 使用 code 获取对应的 openId、unionId 等字段 + String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl(authCallback.getCode(), authCallback.getAnonymous_code())).getBody(); + JSCode2SessionResponse accessTokenObject = JSONObject.parseObject(response, JSCode2SessionResponse.class); + assert accessTokenObject != null; + checkResponse(accessTokenObject); + // 拼装结果 + return AuthToken.builder() + .openId(accessTokenObject.getOpenid()) + .unionId(accessTokenObject.getUnionId()) + .accessToken(accessTokenObject.getSessionKey()) + .anonymousOpenid(accessTokenObject.anonymousOpenid) + .build(); + } + + @Override + public AuthUser getUserInfo(AuthToken authToken) { + // 参见 https://developer.open-douyin.com/docs/resource/zh-CN/mini-game/develop/guide/open-api/info/tt-get-user-info 文档 + // 如果需要用户信息,需要在小程序调用函数后传给后端 + return AuthUser.builder() + .username("") + .nickname("") + .avatar("") + .uuid(authToken.getOpenId()) + .token(authToken) + .source(source.toString()) + .build(); + } + + private void checkResponse(JSCode2SessionResponse response) { + if (response.getError() != 0) { + throw new AuthException(response.getErrorCode(), response.getErrorMsg()); + } + } + + private String accessTokenUrl(String code, String anonymousCode) { + return UrlBuilder.fromBaseUrl(source.accessToken()) + .queryParam("appid", config.getClientId()) + .queryParam("secret", config.getClientSecret()) + .queryParam("code", code) + .queryParam("anonymous_code", anonymousCode) + .build(); + } + + @Data + @SuppressWarnings("SpellCheckingInspection") + private static class JSCode2SessionResponse { + + @JSONField(name = "error") + private int error; + @JSONField(name = "errcode") + private int errorCode; + @JSONField(name = "errmsg") + private String errorMsg; + @JSONField(name = "session_key") + private String sessionKey; + @JSONField(name = "openid") + private String openid; + @JSONField(name = "anonymous_openid") + private String anonymousOpenid; + @JSONField(name = "unionid") + private String unionId; + + } + +}