diff --git a/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java b/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java index 97bca550..a4f5a856 100644 --- a/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java +++ b/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java @@ -1464,6 +1464,35 @@ public String userInfo() { public Class extends AuthDefaultRequest> getTargetClass() { return AuthWechatMiniProgramRequest.class; } + }, + + /** + * QQ小程序授权登录 + */ + QQ_MINI_PROGRAM { + @Override + public String authorize() { + // 参见 https://q.qq.com/wiki/develop/miniprogram/frame/open_ability/open_userinfo.html 文档 + throw new UnsupportedOperationException("不支持获取授权 url,请使用小程序内置函数 qq.login() 登录获取 code"); + } + + @Override + public String accessToken() { + // 参见 https://q.qq.com/wiki/develop/miniprogram/server/open_port/port_login.html 文档 + // 获取 openid, unionId , session_key 等字段 + return "https://api.q.qq.com/sns/jscode2session"; + } + + @Override + public String userInfo() { + // 参见 https://q.qq.com/wiki/develop/miniprogram/API/open_port/port_userinfo.html 文档 + throw new UnsupportedOperationException("不支持获取用户信息 url,请使用小程序内置函数 qq.getUserInfo() 获取用户信息"); + } + + @Override + public Class extends AuthDefaultRequest> getTargetClass() { + return null; + } } } diff --git a/src/main/java/me/zhyd/oauth/request/AuthQQMiniProgramRequest.java b/src/main/java/me/zhyd/oauth/request/AuthQQMiniProgramRequest.java new file mode 100644 index 00000000..97971316 --- /dev/null +++ b/src/main/java/me/zhyd/oauth/request/AuthQQMiniProgramRequest.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; + +/** + * QQ小程序登陆 Request 请求 + *
+ * 参照微信小程序实现 + * + * @author hurentian + * @since 2024-10-08 + */ +public class AuthQQMiniProgramRequest extends AuthDefaultRequest { + public AuthQQMiniProgramRequest(AuthConfig config) { + super(config, AuthDefaultSource.QQ_MINI_PROGRAM); + } + + public AuthQQMiniProgramRequest(AuthConfig config, AuthStateCache authStateCache) { + super(config, AuthDefaultSource.QQ_MINI_PROGRAM, authStateCache); + } + + @Override + public AuthToken getAccessToken(AuthCallback authCallback) { + // 参见 https://q.qq.com/wiki/develop/miniprogram/server/open_port/port_login.html#code2session 文档 + // 使用 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://q.qq.com/wiki/develop/game/API/open-port/user-info.html#qq-getuserinfo 文档 + // 如果需要用户信息,需要在小程序调用函数后传给后端 + 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; + + } + +} diff --git a/src/test/java/me/zhyd/oauth/AuthRequestBuilderTest.java b/src/test/java/me/zhyd/oauth/AuthRequestBuilderTest.java index 5de9dcc3..e66c5d01 100644 --- a/src/test/java/me/zhyd/oauth/AuthRequestBuilderTest.java +++ b/src/test/java/me/zhyd/oauth/AuthRequestBuilderTest.java @@ -107,6 +107,12 @@ public void build4() { System.out.println(value.getTargetClass()); continue; } + case QQ_MINI_PROGRAM: { + // 小程序不支持获取调用 authorize + AuthRequest authRequest = new AuthQQMiniProgramRequest(config); + System.out.println(value.getTargetClass()); + continue; + } default: AuthRequest authRequest = AuthRequestBuilder.builder() .source(value.getName())