From 3f67757a3ab09658ba24a396c36065ba4018da4e Mon Sep 17 00:00:00 2001 From: "yadong.zhang" Date: Mon, 27 May 2019 11:43:56 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E6=94=AF=E6=8C=81=E6=8A=96?= =?UTF-8?q?=E9=9F=B3=E5=92=8C=E9=A2=86=E8=8B=B1=EF=BC=8C=E5=85=B6=E4=BB=96?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=8F=82=E8=80=83update.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 38 +++-- pom.xml | 2 +- .../authorization/AuthorizationFactory.java | 2 + .../authorization/DouyinAuthorization.java | 19 +++ .../authorization/LinkedinAuthorization.java | 19 +++ .../java/me/zhyd/oauth/consts/ApiUrl.java | 58 +++++++ .../me/zhyd/oauth/model/AuthResponse.java | 9 ++ .../java/me/zhyd/oauth/model/AuthSource.java | 2 + .../zhyd/oauth/request/AuthDouyinRequest.java | 98 +++++++++++ .../oauth/request/AuthLinkedinRequest.java | 152 ++++++++++++++++++ .../zhyd/oauth/request/AuthWeChatRequest.java | 55 ++++--- .../zhyd/oauth/request/BaseAuthRequest.java | 13 +- .../java/me/zhyd/oauth/utils/UrlBuilder.java | 102 ++++++++++++ update.md | 6 + 14 files changed, 542 insertions(+), 33 deletions(-) create mode 100644 src/main/java/me/zhyd/oauth/authorization/DouyinAuthorization.java create mode 100644 src/main/java/me/zhyd/oauth/authorization/LinkedinAuthorization.java create mode 100644 src/main/java/me/zhyd/oauth/request/AuthDouyinRequest.java create mode 100644 src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java diff --git a/README.md b/README.md index 6b8e403..7170fd3 100644 --- a/README.md +++ b/README.md @@ -19,20 +19,22 @@
- + - + - - + + - + + +
@@ -46,7 +48,15 @@ JustAuth,如你所见,它仅仅是一个**第三方授权登录**的**工具 项目开源地址:[gitee](https://gitee.com/yadong.zhang/JustAuth) | [github](https://github.com/zhangyd-c/JustAuth) +## 特点 + +废话不多说,就俩字: + +1. **全**:已集成十多家第三方平台(国内外常用的基本都已包含),后续依然还有扩展计划! +2. **简**:API就是奔着最简单去设计的(见后面`快速开始`),尽量让您用起来没有障碍感! + ## 快速开始 + - 引入依赖 ```xml @@ -79,20 +89,22 @@ authRequest.login("code"); #### API列表 | :computer: 平台 | :coffee: API类 | :page_facing_up: SDK | |:------:|:-------:|:-------:| -| | [AuthGiteeRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGiteeRequest.java) | 参考文档 | +| | [AuthGiteeRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGiteeRequest.java) | 参考文档 | | | [AuthGithubRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGiteeRequest.java) | 参考文档 | | | [AuthWeiboRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGiteeRequest.java) | 参考文档 | -| | [AuthDingTalkRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthDingTalkRequest.java) | 参考文档 | +| | [AuthDingTalkRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthDingTalkRequest.java) | 参考文档 | | | [AuthBaiduRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthBaiduRequest.java) | 参考文档 | | | [AuthCodingRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthCodingRequest.java) | 参考文档 | -| | [AuthTencentCloudRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthTencentCloudRequest.java) | 参考文档 | -| | [AuthOschinaRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthOschinaRequest.java) | 参考文档 | +| | [AuthTencentCloudRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthTencentCloudRequest.java) | 参考文档 | +| | [AuthOschinaRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthOschinaRequest.java) | 参考文档 | | | [AuthAlipayRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthAlipayRequest.java) | 参考文档 | | | [AuthQqRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthQqRequest.java) | 参考文档 | -| | [AuthWeChatRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthWeChatRequest.java) | 参考文档 | +| | [AuthWeChatRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthWeChatRequest.java) | 参考文档 | | | [AuthTaobaoRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthTaobaoRequest.java) | 参考文档 | | | [AuthGoogleRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthGoogleRequest.java) | 参考文档 | | | [AuthFacebookRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthFacebookRequest.java) | 参考文档 | +| | [AuthDouyinRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthDouyinRequest.java) | 参考文档 | +| | [AuthLinkedinRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java) | 参考文档 | | | [AuthCsdnRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthCsdnRequest.java) | 无 | _请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经下线。如果以前申请过的应用,可以继续使用,但是不再支持申请新的应用。so, 本项目中的CSDN登录只能针对少部分用户使用了_ @@ -178,6 +190,12 @@ _请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经 ![授权facebook登录](https://images.gitee.com/uploads/images/2019/0521/233647_6a89fb45_784199.png "授权facebook登录") +#### 授权抖音 + + +#### 授权领英 + + #### 授权csdn _请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经下线。如果以前申请过的应用,可以继续使用,但是不再支持申请新的应用。so, 本项目中的CSDN登录只能针对少部分用户使用了_ diff --git a/pom.xml b/pom.xml index a74df61..0125652 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ me.zhyd.oauth JustAuth - 1.3.3 + 1.4.0 JustAuth https://gitee.com/yadong.zhang/JustAuth diff --git a/src/main/java/me/zhyd/oauth/authorization/AuthorizationFactory.java b/src/main/java/me/zhyd/oauth/authorization/AuthorizationFactory.java index 355d4cc..af23e13 100644 --- a/src/main/java/me/zhyd/oauth/authorization/AuthorizationFactory.java +++ b/src/main/java/me/zhyd/oauth/authorization/AuthorizationFactory.java @@ -67,6 +67,8 @@ public class AuthorizationFactory { AuthorizationFactory.register(AuthSource.WECHAT, new WeChatAuthorization()); AuthorizationFactory.register(AuthSource.WEIBO, new WeiboAuthorization()); AuthorizationFactory.register(AuthSource.FACEBOOK, new FacebookAuthorization()); + AuthorizationFactory.register(AuthSource.DOUYIN, new DouyinAuthorization()); + AuthorizationFactory.register(AuthSource.LINKEDIN, new LinkedinAuthorization()); loader = true; } diff --git a/src/main/java/me/zhyd/oauth/authorization/DouyinAuthorization.java b/src/main/java/me/zhyd/oauth/authorization/DouyinAuthorization.java new file mode 100644 index 0000000..a374399 --- /dev/null +++ b/src/main/java/me/zhyd/oauth/authorization/DouyinAuthorization.java @@ -0,0 +1,19 @@ +package me.zhyd.oauth.authorization; + +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.utils.UrlBuilder; + +/** + * 抖音授权 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @version 1.0 + * @since 1.8 + */ +public class DouyinAuthorization implements Authorization { + + @Override + public String getAuthorizeUrl(AuthConfig config) { + return UrlBuilder.getDouyinAuthorizeUrl(config.getClientId(), config.getRedirectUri()); + } +} diff --git a/src/main/java/me/zhyd/oauth/authorization/LinkedinAuthorization.java b/src/main/java/me/zhyd/oauth/authorization/LinkedinAuthorization.java new file mode 100644 index 0000000..0cb60be --- /dev/null +++ b/src/main/java/me/zhyd/oauth/authorization/LinkedinAuthorization.java @@ -0,0 +1,19 @@ +package me.zhyd.oauth.authorization; + +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.utils.UrlBuilder; + +/** + * 领英授权 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @version 1.0 + * @since 1.8 + */ +public class LinkedinAuthorization implements Authorization { + + @Override + public String getAuthorizeUrl(AuthConfig config) { + return UrlBuilder.getLinkedinAuthorizeUrl(config.getClientId(), config.getRedirectUri()); + } +} diff --git a/src/main/java/me/zhyd/oauth/consts/ApiUrl.java b/src/main/java/me/zhyd/oauth/consts/ApiUrl.java index c81c034..77b617f 100644 --- a/src/main/java/me/zhyd/oauth/consts/ApiUrl.java +++ b/src/main/java/me/zhyd/oauth/consts/ApiUrl.java @@ -445,6 +445,64 @@ public enum ApiUrl { public String refresh() { throw new AuthException(ResponseStatus.UNSUPPORTED); } + }, + /** + * 抖音 + */ + DOUYIN { + @Override + public String authorize() { + return "https://open.douyin.com/platform/oauth/connect"; + } + + @Override + public String accessToken() { + return "https://open.douyin.com/oauth/access_token"; + } + + @Override + public String userInfo() { + return "https://open.douyin.com/oauth/userinfo"; + } + + @Override + public String revoke() { + throw new AuthException(ResponseStatus.UNSUPPORTED); + } + + @Override + public String refresh() { + return "https://open.douyin.com/oauth/refresh_token"; + } + }, + /** + * 领英 + */ + LINKEDIN { + @Override + public String authorize() { + return "https://www.linkedin.com/oauth/v2/authorization"; + } + + @Override + public String accessToken() { + return "https://www.linkedin.com/oauth/v2/accessToken"; + } + + @Override + public String userInfo() { + return "https://api.linkedin.com/v2/me"; + } + + @Override + public String revoke() { + throw new AuthException(ResponseStatus.UNSUPPORTED); + } + + @Override + public String refresh() { + return "https://www.linkedin.com/oauth/v2/accessToken"; + } }; /** diff --git a/src/main/java/me/zhyd/oauth/model/AuthResponse.java b/src/main/java/me/zhyd/oauth/model/AuthResponse.java index 97c45a1..b985736 100644 --- a/src/main/java/me/zhyd/oauth/model/AuthResponse.java +++ b/src/main/java/me/zhyd/oauth/model/AuthResponse.java @@ -28,4 +28,13 @@ public class AuthResponse { * 授权响应数据,当且仅当 code = 2000 时返回 */ private T data; + + /** + * 是否请求成功 + * + * @return true or false + */ + public boolean ok() { + return this.code == ResponseStatus.SUCCESS.getCode(); + } } diff --git a/src/main/java/me/zhyd/oauth/model/AuthSource.java b/src/main/java/me/zhyd/oauth/model/AuthSource.java index f53704a..ef94126 100644 --- a/src/main/java/me/zhyd/oauth/model/AuthSource.java +++ b/src/main/java/me/zhyd/oauth/model/AuthSource.java @@ -23,4 +23,6 @@ public enum AuthSource { WECHAT, GOOGLE, FACEBOOK, + DOUYIN, + LINKEDIN, } diff --git a/src/main/java/me/zhyd/oauth/request/AuthDouyinRequest.java b/src/main/java/me/zhyd/oauth/request/AuthDouyinRequest.java new file mode 100644 index 0000000..06ddaa5 --- /dev/null +++ b/src/main/java/me/zhyd/oauth/request/AuthDouyinRequest.java @@ -0,0 +1,98 @@ +package me.zhyd.oauth.request; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import com.alibaba.fastjson.JSONObject; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.exception.AuthException; +import me.zhyd.oauth.model.AuthResponse; +import me.zhyd.oauth.model.AuthSource; +import me.zhyd.oauth.model.AuthToken; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.utils.UrlBuilder; + + +/** + * 抖音登录 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @version 1.0 + * @since 1.8 + */ +public class AuthDouyinRequest extends BaseAuthRequest { + + public AuthDouyinRequest(AuthConfig config) { + super(config, AuthSource.DOUYIN); + } + + @Override + protected AuthToken getAccessToken(String code) { + String accessTokenUrl = UrlBuilder.getDouyinAccessTokenUrl(config.getClientId(), config.getClientSecret(), code); + return this.getToken(accessTokenUrl); + } + + @Override + protected AuthUser getUserInfo(AuthToken authToken) { + String accessToken = authToken.getAccessToken(); + String openId = authToken.getOpenId(); + HttpResponse response = HttpRequest.get(UrlBuilder.getDouyinUserInfoUrl(accessToken, openId)).execute(); + JSONObject object = JSONObject.parseObject(response.body()); + + JSONObject userInfoObject = this.checkResponse(object); + + return AuthUser.builder() + .uuid(userInfoObject.getString("open_id")) + .username(userInfoObject.getString("nickname")) + .nickname(userInfoObject.getString("nickname")) + .avatar(userInfoObject.getString("avatar")) + .token(authToken) + .source(AuthSource.DOUYIN) + .build(); + } + + @Override + public AuthResponse refresh(AuthToken oldToken) { + String refreshTokenUrl = UrlBuilder.getDouyinRefreshUrl(config.getClientId(), oldToken.getRefreshToken()); + return AuthResponse.builder() + .code(ResponseStatus.SUCCESS.getCode()) + .data(this.getToken(refreshTokenUrl)) + .build(); + } + + /** + * 检查响应内容是否正确 + * + * @param object 请求响应内容 + * @return 实际请求数据的json对象 + */ + private JSONObject checkResponse(JSONObject object) { + String message = object.getString("message"); + JSONObject data = object.getJSONObject("data"); + int errorCode = data.getIntValue("error_code"); + if ("error".equals(message) || errorCode != 0) { + throw new AuthException(errorCode, data.getString("description")); + } + return data; + } + + /** + * 获取token,适用于获取access_token和刷新token + * + * @param accessTokenUrl 实际请求token的地址 + * @return token对象 + */ + private AuthToken getToken(String accessTokenUrl) { + HttpResponse response = HttpRequest.post(accessTokenUrl).execute(); + String accessTokenStr = response.body(); + JSONObject object = JSONObject.parseObject(accessTokenStr); + + JSONObject accessTokenObject = this.checkResponse(object); + return AuthToken.builder() + .accessToken(accessTokenObject.getString("access_token")) + .openId(accessTokenObject.getString("open_id")) + .expireIn(accessTokenObject.getIntValue("expires_in")) + .refreshToken(accessTokenObject.getString("refresh_token")) + .scope(accessTokenObject.getString("scope")) + .build(); + } +} diff --git a/src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java b/src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java new file mode 100644 index 0000000..d884957 --- /dev/null +++ b/src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java @@ -0,0 +1,152 @@ +package me.zhyd.oauth.request; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.exception.AuthException; +import me.zhyd.oauth.model.AuthResponse; +import me.zhyd.oauth.model.AuthSource; +import me.zhyd.oauth.model.AuthToken; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.utils.StringUtils; +import me.zhyd.oauth.utils.UrlBuilder; + + +/** + * 领英登录 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @version 1.0 + * @since 1.8 + */ +public class AuthLinkedinRequest extends BaseAuthRequest { + + public AuthLinkedinRequest(AuthConfig config) { + super(config, AuthSource.LINKEDIN); + } + + @Override + protected AuthToken getAccessToken(String code) { + String accessTokenUrl = UrlBuilder.getLinkedinAccessTokenUrl(config.getClientId(), config.getClientSecret(), code, config.getRedirectUri()); + return this.getToken(accessTokenUrl); + } + + @Override + protected AuthUser getUserInfo(AuthToken authToken) { + String accessToken = authToken.getAccessToken(); + HttpResponse response = HttpRequest.get(UrlBuilder.getLinkedinUserInfoUrl()) + .header("Host", "api.linkedin.com") + .header("Connection", "Keep-Alive") + .header("Authorization", "Bearer " + accessToken) + .execute(); + JSONObject userInfoObject = JSONObject.parseObject(response.body()); + + this.checkResponse(userInfoObject); + + // 组装用户名 + String firstName, lastName; + // 获取firstName + if (userInfoObject.containsKey("localizedFirstName")) { + firstName = userInfoObject.getString("localizedFirstName"); + } else { + firstName = getUserName(userInfoObject, "firstName"); + } + // 获取lastName + if (userInfoObject.containsKey("localizedLastName")) { + lastName = userInfoObject.getString("localizedLastName"); + } else { + lastName = getUserName(userInfoObject, "lastName"); + } + String userName = firstName + " " + lastName; + + // 获取用户头像 + String avatar = null; + JSONObject profilePictureObject = userInfoObject.getJSONObject("profilePicture"); + if (profilePictureObject.containsKey("displayImage~")) { + JSONArray displayImageElements = profilePictureObject.getJSONObject("displayImage~").getJSONArray("elements"); + if (null != displayImageElements && displayImageElements.size() > 0) { + JSONObject largestImageObj = displayImageElements.getJSONObject(displayImageElements.size() - 1); + avatar = largestImageObj.getJSONArray("identifiers").getJSONObject(0).getString("identifier"); + } + } + + // 获取用户邮箱地址 + String email = this.getUserEmail(accessToken); + return AuthUser.builder() + .uuid(userInfoObject.getString("id")) + .username(userName) + .nickname(userName) + .avatar(avatar) + .email(email) + .token(authToken) + .source(AuthSource.LINKEDIN) + .build(); + } + + private String getUserEmail(String accessToken) { + String email = null; + HttpResponse emailResponse = HttpRequest.get("https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))") + .header("Host", "api.linkedin.com") + .header("Connection", "Keep-Alive") + .header("Authorization", "Bearer " + accessToken) + .execute(); + System.out.println(emailResponse.body()); + JSONObject emailObj = JSONObject.parseObject(emailResponse.body()); + if (emailObj.containsKey("elements")) { + email = emailObj.getJSONArray("elements").getJSONObject(0).getJSONObject("handle~").getString("emailAddress"); + } + return email; + } + + private String getUserName(JSONObject userInfoObject, String nameKey) { + String firstName; + JSONObject firstNameObj = userInfoObject.getJSONObject(nameKey); + JSONObject localizedObj = firstNameObj.getJSONObject("localized"); + JSONObject preferredLocaleObj = firstNameObj.getJSONObject("preferredLocale"); + firstName = localizedObj.getString(preferredLocaleObj.getString("language") + "_" + preferredLocaleObj.getString("country")); + return firstName; + } + + @Override + public AuthResponse refresh(AuthToken oldToken) { + if (StringUtils.isEmpty(oldToken.getRefreshToken())) { + throw new AuthException(ResponseStatus.UNSUPPORTED); + } + String refreshTokenUrl = UrlBuilder.getLinkedinRefreshUrl(config.getClientId(), config.getClientSecret(), oldToken.getRefreshToken()); + return AuthResponse.builder() + .code(ResponseStatus.SUCCESS.getCode()) + .data(this.getToken(refreshTokenUrl)) + .build(); + } + + private void checkResponse(JSONObject userInfoObject) { + if (userInfoObject.containsKey("error")) { + throw new AuthException(userInfoObject.getString("error_description")); + } + } + + /** + * 获取token,适用于获取access_token和刷新token + * + * @param accessTokenUrl 实际请求token的地址 + * @return token对象 + */ + private AuthToken getToken(String accessTokenUrl) { + HttpResponse response = HttpRequest.post(accessTokenUrl) + .header("Host", "www.linkedin.com") + .header("Content-Type", "application/x-www-form-urlencoded") + .execute(); + String accessTokenStr = response.body(); + JSONObject accessTokenObject = JSONObject.parseObject(accessTokenStr); + + this.checkResponse(accessTokenObject); + + return AuthToken.builder() + .accessToken(accessTokenObject.getString("access_token")) + .expireIn(accessTokenObject.getIntValue("expires_in")) + .refreshToken(accessTokenObject.getString("refresh_token")) + .build(); + } +} diff --git a/src/main/java/me/zhyd/oauth/request/AuthWeChatRequest.java b/src/main/java/me/zhyd/oauth/request/AuthWeChatRequest.java index 1183e32..5e85674 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthWeChatRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthWeChatRequest.java @@ -29,16 +29,7 @@ public class AuthWeChatRequest extends BaseAuthRequest { @Override protected AuthToken getAccessToken(String code) { String accessTokenUrl = UrlBuilder.getWeChatAccessTokenUrl(config.getClientId(), config.getClientSecret(), code); - HttpResponse response = HttpRequest.get(accessTokenUrl).execute(); - JSONObject object = JSONObject.parseObject(response.body()); - if (!object.containsKey("access_token") || !object.containsKey("openid") || !object.containsKey("refresh_token")) { - throw new AuthException("Unable to get access_token or openid or refresh_token from wechat using code [" + code + "]"); - } - return AuthToken.builder() - .accessToken(object.getString("access_token")) - .refreshToken(object.getString("refresh_token")) - .openId(object.getString("openid")) - .build(); + return this.getToken(accessTokenUrl); } @Override @@ -48,9 +39,8 @@ public class AuthWeChatRequest extends BaseAuthRequest { HttpResponse response = HttpRequest.get(UrlBuilder.getWeChatUserInfoUrl(accessToken, openId)).execute(); JSONObject object = JSONObject.parseObject(response.body()); - if (object.containsKey("errcode")) { - throw new AuthException(object.getString("errmsg")); - } + + this.checkResponse(object); return AuthUser.builder() .username(object.getString("nickname")) @@ -65,16 +55,41 @@ public class AuthWeChatRequest extends BaseAuthRequest { } @Override - public AuthResponse refresh(AuthToken authToken) { - String refreshToken = authToken.getRefreshToken(); - HttpResponse response = HttpRequest.get(UrlBuilder.getWeChatRefreshUrl(config.getClientId(), refreshToken)) - .execute(); + public AuthResponse refresh(AuthToken oldToken) { + String refreshTokenUrl = UrlBuilder.getWeChatRefreshUrl(config.getClientId(), oldToken.getRefreshToken()); + return AuthResponse.builder() + .code(ResponseStatus.SUCCESS.getCode()) + .data(this.getToken(refreshTokenUrl)) + .build(); + } - JSONObject object = JSONObject.parseObject(response.body()); + /** + * 检查响应内容是否正确 + * + * @param object 请求响应内容 + */ + private void checkResponse(JSONObject object) { if (object.containsKey("errcode")) { - throw new AuthException(object.getString("errmsg")); + throw new AuthException(object.getIntValue("errcode"), object.getString("errmsg")); } + } + /** + * 获取token,适用于获取access_token和刷新token + * + * @param accessTokenUrl 实际请求token的地址 + * @return token对象 + */ + private AuthToken getToken(String accessTokenUrl) { + HttpResponse response = HttpRequest.get(accessTokenUrl).execute(); + JSONObject object = JSONObject.parseObject(response.body()); + + this.checkResponse(object); - return AuthResponse.builder().data(object).build(); + return AuthToken.builder() + .accessToken(object.getString("access_token")) + .refreshToken(object.getString("refresh_token")) + .expireIn(object.getIntValue("expires_in")) + .openId(object.getString("openid")) + .build(); } } diff --git a/src/main/java/me/zhyd/oauth/request/BaseAuthRequest.java b/src/main/java/me/zhyd/oauth/request/BaseAuthRequest.java index f21e2a7..6a418f1 100644 --- a/src/main/java/me/zhyd/oauth/request/BaseAuthRequest.java +++ b/src/main/java/me/zhyd/oauth/request/BaseAuthRequest.java @@ -35,13 +35,22 @@ public abstract class BaseAuthRequest implements AuthRequest { @Override public AuthResponse login(String code) { try { - AuthUser user = this.getUserInfo(this.getAccessToken(code)); + AuthToken authToken = this.getAccessToken(code); + AuthUser user = this.getUserInfo(authToken); return AuthResponse.builder().code(ResponseStatus.SUCCESS.getCode()).data(user).build(); } catch (Exception e) { - return AuthResponse.builder().code(ResponseStatus.FAILURE.getCode()).msg(e.getMessage()).build(); + return this.responseError(e); } } + private AuthResponse responseError(Exception e) { + int errorCode = ResponseStatus.FAILURE.getCode(); + if (e instanceof AuthException) { + errorCode = ((AuthException) e).getErrorCode(); + } + return AuthResponse.builder().code(errorCode).msg(e.getMessage()).build(); + } + @Override public String authorize() { return AuthorizationFactory.getAuthorize(source).getAuthorizeUrl(config); diff --git a/src/main/java/me/zhyd/oauth/utils/UrlBuilder.java b/src/main/java/me/zhyd/oauth/utils/UrlBuilder.java index 989f3a9..7bc9704 100644 --- a/src/main/java/me/zhyd/oauth/utils/UrlBuilder.java +++ b/src/main/java/me/zhyd/oauth/utils/UrlBuilder.java @@ -72,6 +72,11 @@ public class UrlBuilder { private static final String FACEBOOK_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&code={3}&redirect_uri={4}&grant_type=authorization_code"; private static final String FACEBOOK_USER_INFO_PATTERN = "{0}?access_token={1}&fields=id,name,birthday,gender,hometown,email,devices,picture.width(400)"; + private static final String DOUYIN_AUTHORIZE_PATTERN = "{0}?client_key={1}&redirect_uri={2}&state={3}&response_type=code&scope=user_info"; + private static final String DOUYIN_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&code={3}&grant_type=authorization_code"; + private static final String DOUYIN_USER_INFO_PATTERN = "{0}?access_token={1}&open_id={2}"; + private static final String DOUYIN_REFRESH_TOKEN_PATTERN = "{0}?client_key={1}&refresh_token={2}&grant_type=refresh_token"; + /** * 获取githubtoken的接口地址 * @@ -569,4 +574,101 @@ public class UrlBuilder { public static String getFacebookUserInfoUrl(String token) { return MessageFormat.format(FACEBOOK_USER_INFO_PATTERN, ApiUrl.FACEBOOK.userInfo(), token); } + + /** + * 获取Douyin授权地址 + * + * @param clientId Douyin 应用的Client ID + * @param redirectUrl Douyin 应用授权成功后的回调地址 + * @return full url + */ + public static String getDouyinAuthorizeUrl(String clientId, String redirectUrl) { + return MessageFormat.format(DOUYIN_AUTHORIZE_PATTERN, ApiUrl.DOUYIN.authorize(), clientId, redirectUrl, System.currentTimeMillis()); + } + + /** + * 获取Douyin token的接口地址 + * + * @param clientId Douyin 应用的Client ID + * @param clientSecret Douyin 应用的Client Secret + * @param code Douyin 授权前的code,用来换token + * @return full url + */ + public static String getDouyinAccessTokenUrl(String clientId, String clientSecret, String code) { + return MessageFormat.format(DOUYIN_ACCESS_TOKEN_PATTERN, ApiUrl.DOUYIN.accessToken(), clientId, clientSecret, code); + } + + /** + * 获取Douyin用户详情的接口地址 + * + * @param token Douyin 应用的token + * @param openId 用户在当前应用的唯一标识 通过token接口获取 + * @return full url + */ + public static String getDouyinUserInfoUrl(String token, String openId) { + return MessageFormat.format(DOUYIN_USER_INFO_PATTERN, ApiUrl.DOUYIN.userInfo(), token, openId); + } + + /** + * 获取Douyin 刷新令牌 地址 + * + * @param clientId Douyin应用的client_key + * @param refreshToken Douyin应用返回的refresh_token + * @return full url + */ + public static String getDouyinRefreshUrl(String clientId, String refreshToken) { + return MessageFormat.format(DOUYIN_REFRESH_TOKEN_PATTERN, ApiUrl.DOUYIN.refresh(), clientId, refreshToken); + } + + + + private static final String LINKEDIN_AUTHORIZE_PATTERN = "{0}?client_id={1}&redirect_uri={2}&state={3}&response_type=code&scope=r_liteprofile%20r_emailaddress%20w_member_social"; + private static final String LINKEDIN_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&code={3}&redirect_uri={4}&grant_type=authorization_code"; + private static final String LINKEDIN_USER_INFO_PATTERN = "{0}?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))"; + private static final String LINKEDIN_REFRESH_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&refresh_token={3}&grant_type=refresh_token"; + + /** + * 获取Linkedin授权地址 + * + * @param clientId Linkedin 应用的Client ID + * @param redirectUrl Linkedin 应用授权成功后的回调地址 + * @return full url + */ + public static String getLinkedinAuthorizeUrl(String clientId, String redirectUrl) { + return MessageFormat.format(LINKEDIN_AUTHORIZE_PATTERN, ApiUrl.LINKEDIN.authorize(), clientId, redirectUrl, System.currentTimeMillis()); + } + + /** + * 获取Linkedin token的接口地址 + * + * @param clientId Linkedin 应用的Client ID + * @param clientSecret Linkedin 应用的Client Secret + * @param code Linkedin 授权前的code,用来换token + * @param redirectUrl google 应用授权成功后的回调地址 + * @return full url + */ + public static String getLinkedinAccessTokenUrl(String clientId, String clientSecret, String code, String redirectUrl) { + return MessageFormat.format(LINKEDIN_ACCESS_TOKEN_PATTERN, ApiUrl.LINKEDIN.accessToken(), clientId, clientSecret, code, redirectUrl); + } + + /** + * 获取Linkedin用户详情的接口地址 + * + * @return full url + */ + public static String getLinkedinUserInfoUrl() { + return MessageFormat.format(LINKEDIN_USER_INFO_PATTERN, ApiUrl.LINKEDIN.userInfo()); + } + + /** + * 获取Linkedin 刷新令牌 地址 + * + * @param clientId Linkedin应用的client_key + * @param clientSecret Linkedin 应用的Client Secret + * @param refreshToken Linkedin应用返回的refresh_token + * @return full url + */ + public static String getLinkedinRefreshUrl(String clientId, String clientSecret, String refreshToken) { + return MessageFormat.format(LINKEDIN_REFRESH_TOKEN_PATTERN, ApiUrl.LINKEDIN.refresh(), clientId, clientSecret, refreshToken); + } } diff --git a/update.md b/update.md index 64165ce..00c7467 100644 --- a/update.md +++ b/update.md @@ -1,3 +1,9 @@ +### 2019/05/26 +1. 增加抖音和Linkedin的授权登陆 +2. 修改部分图片命名 +3. 优化部分代码 + + ### 2019/05/24 1. 修复一些问题 2. 升级api,在AuthUser中增加`uuid`属性,可以通过`uuid` + `source`唯一确定一个用户,此举解决了用户身份归属的问题。 -- GitLab