diff --git a/README.md b/README.md index 51985d1145c234c3886dedbe3ecc8c0c76db44ac..736f86465a85ce5f58edb3c4d3187993d7857de3 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ QQ 群:230017570 微信群:justauth (备注`justauth`或者`ja`) -帮助文档:[https://www.justauth.cn](https://www.justauth.cn) +帮助文档:[justauth.wiki](https://www.justauth.cn) ## 什么是 JustAuth? @@ -74,7 +74,7 @@ JustAuth 集成了诸如:Github、Gitee、支付宝、新浪微博、微信、 ``` > **latest-version** 可选: -> - 稳定版:![](https://img.shields.io/github/v/release/justauth/JustAuth?style=flat-square) +> - 稳定版:![](https://img.shields.io/github/v/release/justauth/JustAuth?style=flat-square) > - 快照版:![](https://img.shields.io/nexus/s/https/oss.sonatype.org/me.zhyd.oauth/JustAuth.svg?style=flat-square) > > 注意:快照版本是功能的尝鲜,并不保证稳定性。请勿在生产环境中使用。 > @@ -82,7 +82,7 @@ JustAuth 集成了诸如:Github、Gitee、支付宝、新浪微博、微信、 > 如何引入快照版本 > > JustAuth 的快照版本托管在 ossrh 上,所以要指定下载地址。 -> +> > ```xml > > @@ -94,9 +94,9 @@ JustAuth 集成了诸如:Github、Gitee、支付宝、新浪微博、微信、 > > > ``` -> +> > 如果你想第一时间获取 JustAuth 的最新快照,可以添加下列代码,每次构建时都检查是否有最新的快照(默认每天检查)。 -> +> > ```diff > https://oss.sonatype.org/content/repositories/snapshots > @@ -104,7 +104,7 @@ JustAuth 集成了诸如:Github、Gitee、支付宝、新浪微博、微信、 > true > > ``` -> +> > 如下**任选一种** HTTP 工具 依赖,_项目内如果已有,请忽略。另外需要特别注意,如果项目中已经引入了低版本的依赖,请先排除低版本依赖后,再引入高版本或者最新版本的依赖_ @@ -138,7 +138,7 @@ JustAuth 集成了诸如:Github、Gitee、支付宝、新浪微博、微信、 4.9.1 ``` - + ### 调用api #### 普通方式 @@ -233,7 +233,7 @@ AuthRequest authRequest = AuthRequestBuilder.builder() - `spring-boot-demo` 深度学习并实战 spring boot 的项目: [https://github.com/xkcoding/spring-boot-demo](https://github.com/xkcoding/spring-boot-demo) - `mica` SpringBoot 微服务高效开发工具集: [https://github.com/lets-mica/mica](https://github.com/lets-mica/mica) - `sureness` 面向restful api的高性能认证鉴权框架:[sureness](https://github.com/usthe/sureness) - + 更多推荐,请参考:[JustAuth - 开源推荐](https://www.justauth.cn) ## 鸣谢 diff --git a/example.md b/example.md index 65342b1872d8a905ac088b0d86b6ef67afe3ad44..6e16abf76aeeddd9cdb0874c4587cd18b9f78570 100644 --- a/example.md +++ b/example.md @@ -96,6 +96,10 @@ _注:非全部平台,部分平台可能不存在图例_ ![授权Stack Overflow](https://images.gitee.com/uploads/images/2019/0718/192639_cc301ba7_784199.png "授权Stack Overflow") +#### 授权afdian + +![授权afdian](https://img.fastmirror.net/s/2023/03/24/641d63a8c19b5.png "授权afDian") + #### 授权Twitter ![授权Twitter]( "授权Twitter") diff --git a/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java b/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java index 70e784f17f8e2e08793bf471ba403220ddc0a132..e115bfc3dd1066c7cc56373d1ecab4cddda14511 100644 --- a/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java +++ b/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java @@ -1268,6 +1268,30 @@ public enum AuthDefaultSource implements AuthSource { return "https://www.proginn.com/openapi/user/basic_info"; } + @Override + public Class getTargetClass() { + return AuthProginnRequest.class; + } + }, + /** + * 爱发电 爱发电 + */ + AFDIAN { + @Override + public String authorize() { + return "https://afdian.net/oauth2/authorize"; + } + + @Override + public String accessToken() { + return "https://afdian.net/api/oauth2/access_token"; + } + + @Override + public String userInfo() { + return ""; + } + @Override public Class getTargetClass() { return AuthProginnRequest.class; diff --git a/src/main/java/me/zhyd/oauth/request/AbstractAuthWeChatEnterpriseRequest.java b/src/main/java/me/zhyd/oauth/request/AbstractAuthWeChatEnterpriseRequest.java index 16ff639abcb9adddeab94f2432c0a06a6e27d100..a2105cf286f7b95d582d6dde416d1607dba7ecec 100644 --- a/src/main/java/me/zhyd/oauth/request/AbstractAuthWeChatEnterpriseRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AbstractAuthWeChatEnterpriseRequest.java @@ -3,7 +3,6 @@ package me.zhyd.oauth.request; import com.alibaba.fastjson.JSONObject; import me.zhyd.oauth.cache.AuthStateCache; import me.zhyd.oauth.config.AuthConfig; -import me.zhyd.oauth.config.AuthDefaultSource; import me.zhyd.oauth.config.AuthSource; import me.zhyd.oauth.enums.AuthResponseStatus; import me.zhyd.oauth.enums.AuthUserGender; @@ -12,6 +11,7 @@ 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.StringUtils; import me.zhyd.oauth.utils.UrlBuilder; /** @@ -56,8 +56,8 @@ public abstract class AbstractAuthWeChatEnterpriseRequest extends AuthDefaultReq throw new AuthException(AuthResponseStatus.UNIDENTIFIED_PLATFORM, source); } String userId = object.getString("UserId"); - String userDetailResponse = getUserDetail(authToken.getAccessToken(), userId); - JSONObject userDetail = this.checkResponse(userDetailResponse); + String userTicket = object.getString("user_ticket"); + JSONObject userDetail = getUserDetail(authToken.getAccessToken(), userId, userTicket); return AuthUser.builder() .rawUserInfo(userDetail) @@ -123,14 +123,31 @@ public abstract class AbstractAuthWeChatEnterpriseRequest extends AuthDefaultReq * * @param accessToken accessToken * @param userId 企业内用户id + * @param userTicket 成员票据,用于获取用户信息或敏感信息 * @return 用户详情 */ - private String getUserDetail(String accessToken, String userId) { - String userDetailUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/user/get") + private JSONObject getUserDetail(String accessToken, String userId, String userTicket) { + // 用户基础信息 + String userInfoUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/user/get") .queryParam("access_token", accessToken) .queryParam("userid", userId) .build(); - return new HttpUtils(config.getHttpConfig()).get(userDetailUrl).getBody(); + String userInfoResponse = new HttpUtils(config.getHttpConfig()).get(userInfoUrl).getBody(); + JSONObject userInfo = checkResponse(userInfoResponse); + + // 用户敏感信息 + if (StringUtils.isNotEmpty(userTicket)) { + String userDetailUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/auth/getuserdetail") + .queryParam("access_token", accessToken) + .build(); + JSONObject param = new JSONObject(); + param.put("user_ticket", userTicket); + String userDetailResponse = new HttpUtils(config.getHttpConfig()).post(userDetailUrl, param.toJSONString()).getBody(); + JSONObject userDetail = checkResponse(userDetailResponse); + + userInfo.putAll(userDetail); + } + return userInfo; } } diff --git a/src/main/java/me/zhyd/oauth/request/AuthAfDianRequest.java b/src/main/java/me/zhyd/oauth/request/AuthAfDianRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..b30385e2ebdbfe8d1c75339a8b3d9ea9157e6f3c --- /dev/null +++ b/src/main/java/me/zhyd/oauth/request/AuthAfDianRequest.java @@ -0,0 +1,73 @@ +package me.zhyd.oauth.request; + +import com.alibaba.fastjson.JSONObject; +import me.zhyd.oauth.cache.AuthStateCache; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.config.AuthDefaultSource; +import me.zhyd.oauth.enums.AuthUserGender; +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; + +import java.util.HashMap; +import java.util.Map; + +/** + * 爱发电 + * + * @author handy + */ +public class AuthAfDianRequest extends AuthDefaultRequest { + + public AuthAfDianRequest(AuthConfig config) { + super(config, AuthDefaultSource.AFDIAN); + } + + public AuthAfDianRequest(AuthConfig config, AuthStateCache authStateCache) { + super(config, AuthDefaultSource.AFDIAN, authStateCache); + } + + @Override + protected AuthToken getAccessToken(AuthCallback authCallback) { + Map params = new HashMap<>(); + params.put("grant_type", "authorization_code"); + params.put("client_id", config.getClientId()); + params.put("client_secret", config.getClientSecret()); + params.put("code", authCallback.getCode()); + params.put("redirect_uri", config.getRedirectUri()); + String response = new HttpUtils(config.getHttpConfig()).post(AuthDefaultSource.AFDIAN.accessToken(), params, false).getBody(); + JSONObject accessTokenObject = JSONObject.parseObject(response); + String userId = accessTokenObject.getJSONObject("data").getString("user_id"); + return AuthToken.builder().userId(userId).build(); + } + + @Override + protected AuthUser getUserInfo(AuthToken authToken) { + return AuthUser.builder() + .uuid(authToken.getUserId()) + .gender(AuthUserGender.UNKNOWN) + .token(authToken) + .source(source.toString()) + .build(); + } + + /** + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} + * + * @param state state 验证授权流程的参数,可以防止csrf + * @return 返回授权地址 + */ + @Override + public String authorize(String state) { + return UrlBuilder.fromBaseUrl(source.authorize()) + .queryParam("response_type", "code") + .queryParam("scope", "basic") + .queryParam("client_id", config.getClientId()) + .queryParam("redirect_uri", config.getRedirectUri()) + .queryParam("state", getRealState(state)) + .build(); + } + +} diff --git a/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequest.java b/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequest.java index 3a9747fc4725aaa8cbf6f1c53a8983da5f8e0e85..1a139cf8d203d6925ce708d6675fd1b65ea31fe6 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequest.java @@ -5,6 +5,7 @@ import me.zhyd.oauth.config.AuthConfig; import me.zhyd.oauth.config.AuthDefaultSource; import me.zhyd.oauth.enums.scope.AuthWeChatEnterpriseWebScope; import me.zhyd.oauth.utils.AuthScopeUtils; +import me.zhyd.oauth.utils.GlobalAuthUtils; import me.zhyd.oauth.utils.UrlBuilder; /** @@ -28,7 +29,8 @@ public class AuthWeChatEnterpriseWebRequest extends AbstractAuthWeChatEnterprise public String authorize(String state) { return UrlBuilder.fromBaseUrl(source.authorize()) .queryParam("appid", config.getClientId()) - .queryParam("redirect_uri", config.getRedirectUri()) + .queryParam("agentid", config.getAgentId()) + .queryParam("redirect_uri", GlobalAuthUtils.urlEncode(config.getRedirectUri())) .queryParam("response_type", "code") .queryParam("scope", this.getScopes(",", false, AuthScopeUtils.getDefaultScopes(AuthWeChatEnterpriseWebScope.values()))) .queryParam("state", getRealState(state).concat("#wechat_redirect")) diff --git a/src/test/java/me/zhyd/oauth/request/AuthFeiShuRequestTest.java b/src/test/java/me/zhyd/oauth/request/AuthFeiShuRequestTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f6e653b81f2cd305b594e7f7b3dc64340cdf4960 --- /dev/null +++ b/src/test/java/me/zhyd/oauth/request/AuthFeiShuRequestTest.java @@ -0,0 +1,84 @@ +package me.zhyd.oauth.request; + +import com.alibaba.fastjson.JSON; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthResponse; +import me.zhyd.oauth.model.AuthToken; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.utils.AuthStateUtils; +import org.junit.Assert; +import org.junit.Test; + +/** + * @ClassName AuthFeiShuRequestTest + * @Author jackcheng(chen781142032@gamil.com) + * @version 1.0 + * @since 1.16.5 + * @Date 2022/10/1 11:23 + * @Description 飞书第三方登录测试类 先执行authorize()方法获取state以及authorizeUrl, + * 然后在浏览器中打开authorizeUrl,登录成功后会跳转到redirectUri,并且会携带code和state参数 + **/ +public class AuthFeiShuRequestTest { + + @Test + public void authorize() { + AuthRequest request = new AuthFeishuRequest(AuthConfig.builder() + .clientId("your App ID") + .clientSecret("your App Secret") + .redirectUri("you set redirect uri") + .build()); + String state = AuthStateUtils.createState(); + System.out.println("state==" + state); + String authorize = request.authorize(state); + System.out.println("authorize==" + authorize); + Assert.assertNotNull(authorize); + } + + @Test + public void getAccessTokenAndUserInfo() { + AuthRequest request = new AuthFeishuRequest(AuthConfig.builder() + .clientId("your App ID") + .clientSecret("your App Secret") + .redirectUri("you set redirect uri") + .build()); + + String state = "your state"; + + AuthCallback callback = AuthCallback.builder() + .code("your code") + .state(state) + .build(); + AuthToken accessToken = ((AuthFeishuRequest) request).getAccessToken(callback); + Assert.assertNotNull(accessToken); + System.out.println("token==" + accessToken.getAccessToken()); + + AuthUser userInfo = ((AuthFeishuRequest) request).getUserInfo(accessToken); + Assert.assertNotNull(userInfo); + System.out.println("userInfo==" + JSON.toJSONString(userInfo)); + + } + + @Test + public void login() { + AuthRequest request = new AuthFeishuRequest(AuthConfig.builder() + .clientId("your App ID") + .clientSecret("your App Secret") + .redirectUri("you set redirect uri") + .build()); + + String state = "your state"; + request.authorize(state); + AuthCallback callback = AuthCallback.builder() + .code("your code") + .state(state) + .build(); + AuthResponse response = request.login(callback); + Assert.assertNotNull(response); + AuthUser user = (AuthUser) response.getData(); + Assert.assertNotNull(user); + System.out.println(JSON.toJSONString(user)); + } + +} + diff --git a/src/test/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequestTest.java b/src/test/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequestTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b0d1c9318acdb24af5c511f75c0c612fc9c92b92 --- /dev/null +++ b/src/test/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequestTest.java @@ -0,0 +1,20 @@ +package me.zhyd.oauth.request; + +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.utils.AuthStateUtils; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class AuthWeChatEnterpriseWebRequestTest { + + @Test + public void authorize() { + AuthRequest request = new AuthWeChatEnterpriseWebRequest(AuthConfig.builder() + .clientId("a") + .clientSecret("a") + .redirectUri("https://www.justauth.cn") + .build()); + System.out.println(request.authorize(AuthStateUtils.createState())); + } +}