diff --git a/CHANGELOGS.md b/CHANGELOGS.md index 4287a48f..6e399cbb 100644 --- a/CHANGELOGS.md +++ b/CHANGELOGS.md @@ -1,11 +1,20 @@ -## 1.16.6 +## 1.16.7 ### 2024/08/03 - +: - 新增 - - 添加 appleid 社交登录能力。 [Github #192](https://github.com/justauth/JustAuth/pull/192) -- Fixed + - 添加`appleid`社交登录能力。 [Github#192](https://github.com/justauth/JustAuth/pull/192) + - 添加`figma`社交登录能力。 [Gitee#41](https://gitee.com/yadong.zhang/JustAuth/pulls/41) + - 添加新版`企业微信扫码`登录能力。 [Github Issue#165](https://github.com/justauth/JustAuth/issues/165) + - 新增微信小程序授权登录 - 优化 + - 更新 Google 端点地址。[Github #198](https://github.com/justauth/JustAuth/pull/198) + - Amazon PKCE 中的 `code_verifier` 基于 `state` 缓存 + - `AuthRequest`响应时携带泛型,避免二次解析。[Gitee#38](https://gitee.com/yadong.zhang/JustAuth/pulls/38) + - 优化业务调用方式:`getAccessToken`和`getUserInfo`两个方法从`AuthDefaultRequest`提升至`AuthRequest`中,部分场景下可以减少一次网络请求。[Github Issue#194](https://github.com/justauth/JustAuth/issues/194) + - ***注意:如果有基于 JustAuth 规范自定义实现的三方平台 Request([自定义第三方平台的OAuth](https://justauth.cn/features/customize-the-oauth/)),需要注意`getAccessToken`和`getUserInfo`接口的访问级别是否正确!!!*** +- 其他 + - 补充单侧,[Gitee#39](https://gitee.com/yadong.zhang/JustAuth/pulls/39) ## 1.16.6 diff --git a/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java b/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java index 5e67ceed..dd704c3c 100644 --- a/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java +++ b/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java @@ -1375,6 +1375,36 @@ public String refresh() { public Class getTargetClass() { return AuthFigmaRequest.class; } + }, + /** + * 微信小程序授权登录 + * @since yudaocode + */ + WECHAT_MINI_PROGRAM { + + @Override + public String authorize() { + // 参见 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html 文档 + throw new UnsupportedOperationException("不支持获取授权 url,请使用小程序内置函数 wx.login() 登录获取 code"); + } + + @Override + public String accessToken() { + // 参见 https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html 文档 + // 获取 openid, unionId , session_key 等字段 + return "https://api.weixin.qq.com/sns/jscode2session"; + } + + @Override + public String userInfo() { + // 参见 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html 文档 + throw new UnsupportedOperationException("不支持获取用户信息 url,请使用小程序内置函数 wx.getUserProfile() 获取用户信息"); + } + + @Override + public Class getTargetClass() { + return AuthWechatMiniProgramRequest.class; + } } } diff --git a/src/main/java/me/zhyd/oauth/request/AuthWechatMiniProgramRequest.java b/src/main/java/me/zhyd/oauth/request/AuthWechatMiniProgramRequest.java new file mode 100644 index 00000000..7d630d66 --- /dev/null +++ b/src/main/java/me/zhyd/oauth/request/AuthWechatMiniProgramRequest.java @@ -0,0 +1,100 @@ +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 yadong.zhang (yadong.zhang0415(a)gmail.com) + * @author yudaocode + * @version 1.0.0 + * @since 1.0.0 + */ +public class AuthWechatMiniProgramRequest extends AuthDefaultRequest { + public AuthWechatMiniProgramRequest(AuthConfig config) { + super(config, AuthDefaultSource.WECHAT_MINI_PROGRAM); + } + + public AuthWechatMiniProgramRequest(AuthConfig config, AuthStateCache authStateCache) { + super(config, AuthDefaultSource.WECHAT_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())).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()) + .build(); + } + + @Override + public AuthUser getUserInfo(AuthToken authToken) { + // 参见 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html 文档 + // 如果需要用户信息,需要在小程序调用函数后传给后端 + return AuthUser.builder() + .username("") + .nickname("") + .avatar("") + .uuid(authToken.getOpenId()) + .token(authToken) + .source(source.toString()) + .build(); + } + + /** + * 检查响应内容是否正确 + * + * @param response 请求响应内容 + */ + private void checkResponse(JSCode2SessionResponse response) { + if (response.getErrorCode() != 0) { + throw new AuthException(response.getErrorCode(), response.getErrorMsg()); + } + } + + @Override + protected String accessTokenUrl(String code) { + return UrlBuilder.fromBaseUrl(source.accessToken()) + .queryParam("appid", config.getClientId()) + .queryParam("secret", config.getClientSecret()) + .queryParam("js_code", code) + .queryParam("grant_type", "authorization_code") + .build(); + } + + @Data + @SuppressWarnings("SpellCheckingInspection") + private static class JSCode2SessionResponse { + + @JSONField(name = "errcode") + private int errorCode; + @JSONField(name = "errmsg") + private String errorMsg; + @JSONField(name = "session_key") + private String sessionKey; + private String openid; + @JSONField(name = "unionid") + private String unionId; + + } + +}