diff --git a/README.md b/README.md index c4643e020ff57a823ac833712241762843196c04..0422f73436dbf34b6312c6b09d06d361e8fc2aa0 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@

- + @@ -15,7 +15,7 @@ - + @@ -89,7 +89,7 @@ JustAuth,如你所见,它仅仅是一个**第三方授权登录**的**工具 me.zhyd.oauth JustAuth - 1.9.6 + 1.10.0 ``` - 调用api diff --git a/docs/README.md b/docs/README.md index 75f0897fe18b1e7b5ac757bd8a7c8b8ed590865f..efccdb766ad623d9a1200fcb21d214ef0e9137cf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,7 +9,7 @@

- + @@ -18,7 +18,7 @@ - + @@ -91,7 +91,7 @@ JustAuth,如你所见,它仅仅是一个**第三方授权登录**的**工具 me.zhyd.oauth JustAuth - 1.9.6 + 1.10.0 ``` - 调用api diff --git a/docs/_coverpage.md b/docs/_coverpage.md index 693b73e2f9c6211cc18f67c053e86e1bf48759f5..771d0dfc202e088044bd38ca897eaf9c340b4311 100644 --- a/docs/_coverpage.md +++ b/docs/_coverpage.md @@ -1,6 +1,6 @@ ![](_media/logo.png) -# JustAuth 1.9.6 +# JustAuth 1.10.0 史上最全的整合第三方登录的开源库 diff --git a/docs/update.md b/docs/update.md index c823dc4d46fbbd4b069e6d8016b551bfb8f299a9..7e180a847e093c84c7b11e938d93be72d871e4fa 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,4 +1,9 @@ -## v1.9.6 +## v1.10.0 +### 2019/08/06 + +- 合并[PR-34](https://github.com/zhangyd-c/JustAuth/pull/34),添加StringUtil单元测试,修复bug +- 合并[PR-35](https://github.com/zhangyd-c/JustAuth/pull/35),集成企业微信 + ### 2019/08/05 - 集成华为登录 diff --git a/pom.xml b/pom.xml index 858ebe6421f979b7a0b982fa43ebbcf24cd52366..48fe67952ff8b3cde0497ee65af9984b69f1f423 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ me.zhyd.oauth JustAuth - 1.9.6 + 1.10.0 JustAuth https://gitee.com/yadong.zhang/JustAuth diff --git a/src/main/java/me/zhyd/oauth/cache/AuthCacheConfig.java b/src/main/java/me/zhyd/oauth/cache/AuthCacheConfig.java index a4a22deaeab307e70690f6c7b72f92e151948c97..73deb886103b85cc5625c850d435e118315543d4 100644 --- a/src/main/java/me/zhyd/oauth/cache/AuthCacheConfig.java +++ b/src/main/java/me/zhyd/oauth/cache/AuthCacheConfig.java @@ -4,7 +4,7 @@ package me.zhyd.oauth.cache; * AuthCache配置类 * * @author yadong.zhang (yadong.zhang0415(a)gmail.com) - * @since 1.9.6 + * @since 1.10.0 */ public class AuthCacheConfig { diff --git a/src/main/java/me/zhyd/oauth/cache/AuthDefaultStateCache.java b/src/main/java/me/zhyd/oauth/cache/AuthDefaultStateCache.java index 7dcf1eb05aeb145e04318bec0c32824f7c2586be..c48264ba1f7cda58e74e230dfc8a7da4dd680f35 100644 --- a/src/main/java/me/zhyd/oauth/cache/AuthDefaultStateCache.java +++ b/src/main/java/me/zhyd/oauth/cache/AuthDefaultStateCache.java @@ -4,7 +4,7 @@ package me.zhyd.oauth.cache; * 默认的state缓存实现 * * @author yadong.zhang (yadong.zhang0415(a)gmail.com) - * @since 1.9.6 + * @since 1.10.0 */ public enum AuthDefaultStateCache implements AuthStateCache { diff --git a/src/main/java/me/zhyd/oauth/cache/AuthStateCache.java b/src/main/java/me/zhyd/oauth/cache/AuthStateCache.java index 639202edc9193cd5ddc0ca89145afeb72f2c15d4..ec3cf521de0105153aab288eff4fde888e9cebef 100644 --- a/src/main/java/me/zhyd/oauth/cache/AuthStateCache.java +++ b/src/main/java/me/zhyd/oauth/cache/AuthStateCache.java @@ -6,7 +6,7 @@ package me.zhyd.oauth.cache; *

* * @author yangkai.shen - * @since 1.9.6 + * @since 1.10.0 */ public interface AuthStateCache { /** diff --git a/src/main/java/me/zhyd/oauth/config/AuthConfig.java b/src/main/java/me/zhyd/oauth/config/AuthConfig.java index deada8904ac748d5b980696eb5cfe3a229ab07b6..2fef943bab882deb5aaf80bd529de3eda4291750 100644 --- a/src/main/java/me/zhyd/oauth/config/AuthConfig.java +++ b/src/main/java/me/zhyd/oauth/config/AuthConfig.java @@ -47,7 +47,15 @@ public class AuthConfig { /** * Stack Overflow Key *

- * 1.9.0版本新增参数 + * + * @since 1.9.0 */ private String stackOverflowKey; + + /** + * 企业微信,授权方的网页应用ID + * + * @since 1.10.0 + */ + private String agentId; } diff --git a/src/main/java/me/zhyd/oauth/config/AuthSource.java b/src/main/java/me/zhyd/oauth/config/AuthSource.java index dd7a4368fc4453621a63c40ffad62427d822e978..3cd14a16b9b0d69688bf2a23de7feaade163ea01 100644 --- a/src/main/java/me/zhyd/oauth/config/AuthSource.java +++ b/src/main/java/me/zhyd/oauth/config/AuthSource.java @@ -1,7 +1,7 @@ package me.zhyd.oauth.config; -import me.zhyd.oauth.exception.AuthException; import me.zhyd.oauth.enums.AuthResponseStatus; +import me.zhyd.oauth.exception.AuthException; /** * 各api需要的url, 用枚举类分平台类型管理 @@ -522,7 +522,8 @@ public enum AuthSource { /** * 华为 - * @since 1.9.6 + * + * @since 1.10.0 */ HUAWEI { @Override @@ -544,6 +545,28 @@ public enum AuthSource { public String refresh() { return "https://oauth-login.cloud.huawei.com/oauth2/v2/token"; } + }, + + /** + * 企业微信 + * + * @since 1.10.0 + */ + WECHAT_ENTERPRISE { + @Override + public String authorize() { + return "https://open.work.weixin.qq.com/wwopen/sso/qrConnect"; + } + + @Override + public String accessToken() { + return "https://qyapi.weixin.qq.com/cgi-bin/gettoken"; + } + + @Override + public String userInfo() { + return "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo"; + } }; /** diff --git a/src/main/java/me/zhyd/oauth/log/Log.java b/src/main/java/me/zhyd/oauth/log/Log.java index a6f54f9f107e0702505afafdf71d7f3efcc32f88..7a783373bf83354be34a5bc68a06269abd4a5b5f 100644 --- a/src/main/java/me/zhyd/oauth/log/Log.java +++ b/src/main/java/me/zhyd/oauth/log/Log.java @@ -17,7 +17,7 @@ import java.time.format.DateTimeFormatter; * @see Log#warn(String, Throwable) * @see Log#error(String) * @see Log#error(String, Throwable) - * @since 1.9.6 + * @since 1.10.0 */ public class Log { @@ -109,7 +109,7 @@ public class Log { * 日志级别 * * @author yadong.zhang (yadong.zhang0415(a)gmail.com) - * @since 1.9.6 + * @since 1.10.0 */ @Getter @AllArgsConstructor @@ -134,7 +134,7 @@ public class Log { * 日志配置 * * @author yadong.zhang (yadong.zhang0415(a)gmail.com) - * @since 1.9.6 + * @since 1.10.0 */ static class Config { diff --git a/src/main/java/me/zhyd/oauth/model/AuthCallback.java b/src/main/java/me/zhyd/oauth/model/AuthCallback.java index 0ea02707d625c41177274def0e2be40e1358b9b0..4e268f0016f0e496b35b9bd64cf66b72299af974 100644 --- a/src/main/java/me/zhyd/oauth/model/AuthCallback.java +++ b/src/main/java/me/zhyd/oauth/model/AuthCallback.java @@ -31,7 +31,7 @@ public class AuthCallback { /** * 华为授权登录接受code的参数名 * - * @since 1.9.6 + * @since 1.10.0 */ private String authorization_code; } diff --git a/src/main/java/me/zhyd/oauth/model/AuthToken.java b/src/main/java/me/zhyd/oauth/model/AuthToken.java index 805a196c3c93b2149c07c5c44265e3d998fedea7..5ae5fbd6ea2682a3f3029838cb31e887b6645703 100644 --- a/src/main/java/me/zhyd/oauth/model/AuthToken.java +++ b/src/main/java/me/zhyd/oauth/model/AuthToken.java @@ -1,7 +1,6 @@ package me.zhyd.oauth.model; import lombok.Builder; -import lombok.Data; import lombok.Getter; import lombok.Setter; @@ -36,4 +35,11 @@ public class AuthToken { private String macAlgorithm; private String macKey; + /** + * 企业微信附带属性 + * + * @since 1.10.0 + */ + private String code; + } diff --git a/src/main/java/me/zhyd/oauth/request/AuthHuaweiRequest.java b/src/main/java/me/zhyd/oauth/request/AuthHuaweiRequest.java index be33552ac37ac480d0a261064e315fb2f5687834..aea00abc179b1e26fc3c76db7003af348d872a7a 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthHuaweiRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthHuaweiRequest.java @@ -21,7 +21,7 @@ import static me.zhyd.oauth.enums.AuthResponseStatus.SUCCESS; * * @author yadong.zhang (yadong.zhang0415(a)gmail.com) * @version 1.0 - * @since 1.9.6 + * @since 1.10.0 */ public class AuthHuaweiRequest extends AuthDefaultRequest { diff --git a/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseRequest.java b/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..3d9befdb43ba4481e08516a5c5170aa00d5f2b69 --- /dev/null +++ b/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseRequest.java @@ -0,0 +1,171 @@ +package me.zhyd.oauth.request; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import com.alibaba.fastjson.JSONObject; +import me.zhyd.oauth.cache.AuthStateCache; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.config.AuthSource; +import me.zhyd.oauth.enums.AuthResponseStatus; +import me.zhyd.oauth.enums.AuthUserGender; +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.UrlBuilder; + +/** + *

+ * 企业微信登录 + *

+ * + * @author yangkai.shen (https://xkcoding.com) + * @since 1.10.0 + */ +public class AuthWeChatEnterpriseRequest extends AuthDefaultRequest { + public AuthWeChatEnterpriseRequest(AuthConfig config) { + super(config, AuthSource.WECHAT_ENTERPRISE); + } + + public AuthWeChatEnterpriseRequest(AuthConfig config, AuthStateCache authStateCache) { + super(config, AuthSource.WECHAT_ENTERPRISE, authStateCache); + } + + /** + * 微信的特殊性,此时返回的信息同时包含 openid 和 access_token + * + * @param authCallback 回调返回的参数 + * @return 所有信息 + */ + @Override + protected AuthToken getAccessToken(AuthCallback authCallback) { + HttpResponse response = doGetAuthorizationCode(accessTokenUrl(authCallback.getCode())); + + JSONObject object = this.checkResponse(response); + + return AuthToken.builder() + .accessToken(object.getString("access_token")) + .expireIn(object.getIntValue("expires_in")) + .code(authCallback.getCode()) + .build(); + } + + @Override + protected AuthUser getUserInfo(AuthToken authToken) { + HttpResponse response = doGetUserInfo(authToken); + JSONObject object = this.checkResponse(response); + + // 返回 OpenId 或其他,均代表非当前企业用户,不支持 + if (!object.containsKey("UserId")) { + throw new AuthException(AuthResponseStatus.UNIDENTIFIED_PLATFORM); + } + String userId = object.getString("UserId"); + HttpResponse userDetailResponse = getUserDetail(authToken.getAccessToken(), userId); + JSONObject userDetail = this.checkResponse(userDetailResponse); + + String gender = getRealGender(userDetail); + + return AuthUser.builder() + .username(userDetail.getString("name")) + .nickname(userDetail.getString("alias")) + .avatar(userDetail.getString("avatar")) + .location(userDetail.getString("address")) + .email(userDetail.getString("email")) + .uuid(userId) + .gender(AuthUserGender.getRealGender(gender)) + .token(authToken) + .source(source) + .build(); + } + + /** + * 校验请求结果 + * + * @param response 请求结果 + * @return 如果请求结果正常,则返回JSONObject + */ + private JSONObject checkResponse(HttpResponse response) { + JSONObject object = JSONObject.parseObject(response.body()); + + if (object.containsKey("errcode") && object.getIntValue("errcode") != 0) { + throw new AuthException(object.getIntValue("errcode"), object.getString("errmsg")); + } + + return object; + } + + /** + * 获取用户的实际性别,0表示未定义,1表示男性,2表示女性 + * + * @param userDetail 用户详情 + * @return 用户性别 + */ + private String getRealGender(JSONObject userDetail) { + int gender = userDetail.getIntValue("gender"); + if (AuthUserGender.MALE.getCode() == gender) { + return "1"; + } + return 2 == gender ? "0" : null; + } + + /** + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} + * + * @param state state 验证授权流程的参数,可以防止csrf + * @return 返回授权地址 + * @since 1.9.3 + */ + @Override + public String authorize(String state) { + return UrlBuilder.fromBaseUrl(source.authorize()) + .queryParam("appid", config.getClientId()) + .queryParam("agentid", config.getAgentId()) + .queryParam("redirect_uri", config.getRedirectUri()) + .queryParam("state", getRealState(state)) + .build(); + } + + /** + * 返回获取accessToken的url + * + * @param code 授权码 + * @return 返回获取accessToken的url + */ + @Override + protected String accessTokenUrl(String code) { + return UrlBuilder.fromBaseUrl(source.accessToken()) + .queryParam("corpid", config.getClientId()) + .queryParam("corpsecret", config.getClientSecret()) + .build(); + } + + /** + * 返回获取userInfo的url + * + * @param authToken 用户授权后的token + * @return 返回获取userInfo的url + */ + @Override + protected String userInfoUrl(AuthToken authToken) { + return UrlBuilder.fromBaseUrl(source.userInfo()) + .queryParam("access_token", authToken.getAccessToken()) + .queryParam("code", authToken.getCode()) + .build(); + } + + /** + * 用户详情 + * + * @param accessToken accessToken + * @param userId 企业内用户id + * @return 用户详情 + */ + private HttpResponse getUserDetail(String accessToken, String userId) { + String userDetailUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/user/get") + .queryParam("access_token", accessToken) + .queryParam("userid", userId) + .build(); + return HttpRequest.get(userDetailUrl).execute(); + } + +} diff --git a/src/main/java/me/zhyd/oauth/utils/AuthChecker.java b/src/main/java/me/zhyd/oauth/utils/AuthChecker.java index 8eaee0ef7eaf2a3a9532b69a413d37f1296d2302..8e56e2120d1cedd9c1ec19f8fedf74549099ba5f 100644 --- a/src/main/java/me/zhyd/oauth/utils/AuthChecker.java +++ b/src/main/java/me/zhyd/oauth/utils/AuthChecker.java @@ -30,6 +30,9 @@ public class AuthChecker { if (isSupported && AuthSource.STACK_OVERFLOW == source) { isSupported = StringUtils.isNotEmpty(config.getStackOverflowKey()); } + if (isSupported && AuthSource.WECHAT_ENTERPRISE == source){ + isSupported = StringUtils.isNotEmpty(config.getAgentId()); + } return isSupported; } @@ -58,7 +61,7 @@ public class AuthChecker { /** * 校验回调传回的code *

- * {@code v1.9.6}版本中改为传入{@code source}和{@code callback},对于不同平台使用不同参数接受code的情况统一做处理 + * {@code v1.10.0}版本中改为传入{@code source}和{@code callback},对于不同平台使用不同参数接受code的情况统一做处理 * * @param source 当前授权平台 * @param callback 从第三方授权回调回来时传入的参数集合 diff --git a/src/test/java/me/zhyd/oauth/sdk/ThirdPartSdkTest.java b/src/test/java/me/zhyd/oauth/sdk/ThirdPartSdkTest.java index 9c89ed19942e38aceffe7c2ba59273942f6d28e9..6aa252c53c6a76641480e2a44e138c55a3697477 100644 --- a/src/test/java/me/zhyd/oauth/sdk/ThirdPartSdkTest.java +++ b/src/test/java/me/zhyd/oauth/sdk/ThirdPartSdkTest.java @@ -8,19 +8,19 @@ import org.junit.Test; /** * @author yadong.zhang (yadong.zhang0415(a)gmail.com) * @version 1.0 - * @since 1.9.6 + * @since 1.10.0 */ public class ThirdPartSdkTest { @Test public void huawei() { - String code = "CF1IvwdXw18r6LTfoRSgs+LrdP/DuO1VJJmAD0up2grQrSs3gcuyrt1O+jjWp7/TFiBy9IlPepNs/PUggcLe8cgjesqj1+DGXXojJsjEqsokFCCU0eJVt1F02zLDWH1+bq40HSlljXDaTvCBNrqWJJnIZhRetoV9pocrWPLZpYrx/h0iaC9T0GjMRVEXC//LnTAlTjg7"; + String code = "CF1IwmFc6uZABI9Y795BkhXfvHidIFFw04I4Zc4KML4n+vlXxwNUcQKS4xlopjFDpEk6LzQbjwdTNxvjZ9jqnd/1m5nswhx8X7e0/dL2kyGAMVZWFgVq9ClxNN18b+Z0xtfJjkm7bDnfC3W5h4COgTCoLSjiWKSHWp5hCunp6pQRo1FHovZXm13TLNlhF9mCVtJx3kTQ"; HttpResponse response = HttpRequest.post("https://oauth-login.cloud.huawei.com/oauth2/v2/token") .form("grant_type", "authorization_code") .form("code", code) - .form("client_id", "100xxxxx") - .form("client_secret", "22aea400bef603xxxxxbfb80d") - .form("redirect_uri", "http://localhost:8443/huawei/login") + .form("client_id", "100994535") + .form("client_secret", "22aea400bef603fef26d15a79c806eb477b35de0a529758f2a3b1bda32bfb80d") + .form("redirect_uri", "http://127.0.0.1:8443/oauth/callback/huawei") .execute(); System.out.println(response.body()); @@ -31,7 +31,7 @@ public class ThirdPartSdkTest { .form("nsp_ts", System.currentTimeMillis()) .form("access_token", JSONObject.parseObject(response.body()).getString("access_token")) .form("nsp_fmt", "JS") -// .form("nsp_cb", "") +// .form("nsp_cb", "_jqjsp") .form("nsp_svc", "OpenUP.User.getInfo") .execute(); System.out.println(response2.body());