diff --git a/README.md b/README.md
index 6b8e40323ff8a52dba88ca3b3382d7d2667e3ace..7170fd3e201319be9d578736851af836a582b787 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 a74df6147301513dd3ea6fc203fd98adac6658d5..0125652f2b5f76d90d17ca9a2ba71036853a0d91 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 355d4cc4fcb358faca914d03cfb42d2dda89f419..af23e13dae1892508b046db1861efda062aa6a93 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 0000000000000000000000000000000000000000..a374399f13ee1ea591878d7f309847d2e445c076
--- /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 0000000000000000000000000000000000000000..0cb60be90b59b02e352ae83093213435089f7f29
--- /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 c81c034f67f723eca95357bdd3bc0238f075c0ae..77b617feb380c82daa2b900b50028a1892273954 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 97c45a17668c32f29eccbdd378d7e596d6de435c..b98573659142df3a66b9cda8b0189ed202e6d6db 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 f53704aa1bff9b8c5fb2a0fd0a3cb723da6c4c44..ef941261095e110b58f43be0812f33ff6cf672b9 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 0000000000000000000000000000000000000000..06ddaa569a703bda429a9e347f8f0b70491cca14
--- /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 0000000000000000000000000000000000000000..d8849572f752381e696e75437b1972c51bc3d9d7
--- /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 1183e325063a69d127f305ca7b52f0e7f48ca1ff..5e856743652c6984ad9dbfa66f39444248cbc37f 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 f21e2a72a383be1727b518b56d4548cbe0c7cab0..6a418f15307cc656b584f8544ee8a0cfab3a83b6 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 989f3a97a8d289f50416151fc6d8191ba70aedff..7bc97049fef43f12277e982a269e373dc4bc0da7 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 64165cef3730cfb2e58d0ddd4564980f0f896e7b..00c7467de65ecc7d285d4541f12c327ef779ab3c 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`唯一确定一个用户,此举解决了用户身份归属的问题。