Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cf74f811fa | |||
| 65daa0592a | |||
| 25424023c4 | |||
| 67579bfb07 | |||
| 458de3840d | |||
| 1c1d2dc9db | |||
| f5de7f93b5 | |||
| dcf5f30e61 | |||
| e534a4b62e | |||
| 42ede32fc5 | |||
| c0dd700b0a | |||
| f32c341b63 | |||
| 82358cbddb | |||
| 56df9bc1b0 | |||
| 438660621e | |||
| 937fba37f5 | |||
| fde490a205 | |||
| da23fcd54c |
@@ -6,7 +6,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a target="_blank" href="https://search.maven.org/search?q=JustAuth">
|
||||
<img src="https://img.shields.io/badge/Maven Central-1.5.1-blue.svg" ></img>
|
||||
<img src="https://img.shields.io/badge/Maven Central-1.6.1_beta-blue.svg" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://gitee.com/yadong.zhang/JustAuth/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/apm/l/vim-mode.svg?color=yellow" ></img>
|
||||
@@ -37,6 +37,7 @@
|
||||
<td align="center" width="200"><a href="#授权领英"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/linkedin.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权微软"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/microsoft.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权小米"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/mi.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权今日头条"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/toutiao.png" width="20"></a></td>
|
||||
<td align="center" width="200"><a href="#授权csdn"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/csdn.png" width="20"></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -63,7 +64,7 @@ JustAuth,如你所见,它仅仅是一个**第三方授权登录**的**工具
|
||||
<dependency>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>1.5.1</version>
|
||||
<version>1.6.1-beta</version>
|
||||
</dependency>
|
||||
```
|
||||
- 调用api
|
||||
@@ -108,6 +109,7 @@ authRequest.login("code");
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/linkedin.png" width="20"> | [AuthLinkedinRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java) | <a href="https://docs.microsoft.com/zh-cn/linkedin/shared/authentication/authorization-code-flow?context=linkedin/context" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/microsoft.png" width="20"> | [AuthMicrosoftRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthMicrosoftRequest.java) | <a href="https://docs.microsoft.com/zh-cn/graph/auth/" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/mi.png" width="20"> | [AuthMiRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthMiRequest.java) | <a href="https://dev.mi.com/console/doc/detail?pId=711" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/toutiao.png" width="20"> | [AuthToutiaoRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthToutiaoRequest.java) | <a href="https://open.mp.toutiao.com/#/resource?_k=y7mfgk" target="_blank">参考文档</a> |
|
||||
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/csdn.png" width="20"> | [AuthCsdnRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthCsdnRequest.java) | 无 |
|
||||
|
||||
_请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经下线。如果以前申请过的应用,可以继续使用,但是不再支持申请新的应用。so, 本项目中的CSDN登录只能针对少部分用户使用了_
|
||||
@@ -135,82 +137,6 @@ _请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经
|
||||
|
||||
[阿里妈妈MUX倾力打造的矢量图标库-iconfont](https://www.iconfont.cn/search/index): 本文档中的图标大部分取自该平台
|
||||
|
||||
|
||||
## 参考授权图例
|
||||
|
||||
#### 授权gitee
|
||||
|
||||

|
||||
|
||||
#### 授权github
|
||||
|
||||

|
||||
|
||||
#### 授权weibo
|
||||
|
||||

|
||||
|
||||
#### 授权钉钉
|
||||
|
||||

|
||||
|
||||
#### 授权百度
|
||||
|
||||

|
||||
|
||||
#### 授权coding
|
||||
|
||||

|
||||
|
||||
#### 授权腾讯云开发者平台
|
||||
|
||||

|
||||
|
||||
#### 授权oschina
|
||||
|
||||

|
||||
|
||||
#### 授权支付宝
|
||||
|
||||

|
||||
|
||||
#### 授权qq
|
||||
|
||||
待续
|
||||
|
||||
#### 授权微信
|
||||
|
||||

|
||||
|
||||
#### 授权淘宝
|
||||
|
||||

|
||||
|
||||
|
||||
#### 授权Google
|
||||
|
||||

|
||||
|
||||
#### 授权Facebook
|
||||
|
||||

|
||||
|
||||
#### 授权抖音
|
||||
|
||||
|
||||
#### 授权领英
|
||||
|
||||

|
||||
|
||||
|
||||
#### 授权微软
|
||||
|
||||
#### 授权小米
|
||||
|
||||
#### 授权csdn
|
||||
|
||||
_请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经下线。如果以前申请过的应用,可以继续使用,但是不再支持申请新的应用。so, 本项目中的CSDN登录只能针对少部分用户使用了_
|
||||
|
||||
## 关注&交流
|
||||
|
||||
| 公众号 | 微信(备注:加群) |
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# 项目贡献者名单
|
||||
|
||||
- <img src="https://avatar.gitee.com/uploads/99/784199_yadong.zhang.png!avatar100?1462325358" width="20"> · yadong.zhang : <a href="https://github.com/zhangyd-c" target="_blank">[Github]</a> | <a href="https://gitee.com/yadong.zhang" target="_blank">[Gitee]</a> | <a href="https://www.zhyd.me" target="_blank">[个人网站]</a>
|
||||
- <img src="https://avatars0.githubusercontent.com/u/10429917?s=460&v=4" width="20"> · yangkai.shen : <a href="https://github.com/xkcoding" target="_blank">[Github]</a> | <a href="https://xkcoding.com" target="_blank">[个人网站]</a>
|
||||
- 千年等一回,我只为等你...
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
## 各平台授权页面示例
|
||||
|
||||
_注:非全部平台,部分平台可能不存在图例_
|
||||
|
||||
#### 授权gitee
|
||||
|
||||

|
||||
|
||||
#### 授权github
|
||||
|
||||

|
||||
|
||||
#### 授权weibo
|
||||
|
||||

|
||||
|
||||
#### 授权钉钉
|
||||
|
||||

|
||||
|
||||
#### 授权百度
|
||||
|
||||

|
||||
|
||||
#### 授权coding
|
||||
|
||||

|
||||
|
||||
#### 授权腾讯云开发者平台
|
||||
|
||||

|
||||
|
||||
#### 授权oschina
|
||||
|
||||

|
||||
|
||||
#### 授权支付宝
|
||||
|
||||

|
||||
|
||||
#### 授权qq
|
||||
|
||||
待续
|
||||
|
||||
#### 授权微信
|
||||
|
||||

|
||||
|
||||
#### 授权淘宝
|
||||
|
||||

|
||||
|
||||
|
||||
#### 授权Google
|
||||
|
||||

|
||||
|
||||
#### 授权Facebook
|
||||
|
||||

|
||||
|
||||
#### 授权抖音
|
||||
|
||||
|
||||
#### 授权领英
|
||||
|
||||

|
||||
|
||||
|
||||
#### 授权微软
|
||||
|
||||
#### 授权小米
|
||||
|
||||
#### 授权今日头条
|
||||
|
||||
#### 授权csdn
|
||||
|
||||
_请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经下线。如果以前申请过的应用,可以继续使用,但是不再支持申请新的应用。so, 本项目中的CSDN登录只能针对少部分用户使用了_
|
||||
@@ -6,12 +6,12 @@
|
||||
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>1.5.1</version>
|
||||
<version>1.6.1-beta</version>
|
||||
|
||||
<name>JustAuth</name>
|
||||
<url>https://gitee.com/yadong.zhang/JustAuth</url>
|
||||
<description>
|
||||
史上最全的整合第三方登录的工具,目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米和微软等第三方平台的授权登录。
|
||||
史上最全的整合第三方登录的工具,目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软和今日头条等第三方平台的授权登录。
|
||||
Login, so easy!
|
||||
</description>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.Map;
|
||||
/**
|
||||
* 授权工厂类,负责创建指定平台的授权类获取授权地址
|
||||
* <p>
|
||||
* 使用策略模式 + 工厂模式 避免大量的if else(swatch)操作
|
||||
* 使用策略模式 + 工厂模式 避免大量的if else(switch)操作
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
@@ -71,6 +71,7 @@ public class AuthorizationFactory {
|
||||
AuthorizationFactory.register(AuthSource.LINKEDIN, new LinkedinAuthorization());
|
||||
AuthorizationFactory.register(AuthSource.MICROSOFT, new MicrosoftAuthorization());
|
||||
AuthorizationFactory.register(AuthSource.MI, new MiAuthorization());
|
||||
AuthorizationFactory.register(AuthSource.TOUTIAO, new ToutiaoAuthorization());
|
||||
loader = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ToutiaoAuthorization implements Authorization {
|
||||
|
||||
@Override
|
||||
public String getAuthorizeUrl(AuthConfig config) {
|
||||
return UrlBuilder.getToutiaoAuthorizeUrl(config.getClientId(), config.getRedirectUri());
|
||||
}
|
||||
}
|
||||
@@ -561,6 +561,35 @@ public enum ApiUrl {
|
||||
public String refresh() {
|
||||
return "https://account.xiaomi.com/oauth2/token";
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 今日头条
|
||||
*/
|
||||
TOUTIAO {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://open.snssdk.com/auth/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://open.snssdk.com/auth/token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://open.snssdk.com/data/user_profile";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String revoke() {
|
||||
throw new AuthException(ResponseStatus.UNSUPPORTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
throw new AuthException(ResponseStatus.UNSUPPORTED);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,5 +26,6 @@ public enum AuthSource {
|
||||
DOUYIN,
|
||||
LINKEDIN,
|
||||
MICROSOFT,
|
||||
MI
|
||||
MI,
|
||||
TOUTIAO
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ public class AuthToken {
|
||||
private String uid;
|
||||
private String openId;
|
||||
private String accessCode;
|
||||
private String unionId;
|
||||
|
||||
/**
|
||||
* Google附带属性
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package me.zhyd.oauth.model;
|
||||
|
||||
/**
|
||||
* 今日头条授权登录时的异常状态码
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.0
|
||||
* @since 1.8
|
||||
*/
|
||||
public enum AuthToutiaoErrorCode {
|
||||
EC0(0, "接口调用成功"),
|
||||
EC1(1, "API配置错误,未传入Client Key"),
|
||||
EC2(2, "API配置错误,Client Key错误,请检查是否和开放平台的ClientKey一致"),
|
||||
EC3(3, "没有授权信息"),
|
||||
EC4(4, "响应类型错误"),
|
||||
EC5(5, "授权类型错误"),
|
||||
EC6(6, "client_secret错误"),
|
||||
EC7(7, "authorize_code过期"),
|
||||
EC8(8, "指定url的scheme不是https"),
|
||||
EC9(9, "接口内部错误,请联系头条技术"),
|
||||
EC10(10, "access_token过期"),
|
||||
EC11(11, "缺少access_token"),
|
||||
EC12(12, "参数缺失"),
|
||||
EC13(13, "url错误"),
|
||||
EC21(21, "域名与登记域名不匹配"),
|
||||
EC999(999, "未知错误,请联系头条技术"),
|
||||
;
|
||||
|
||||
private int code;
|
||||
private String desc;
|
||||
|
||||
AuthToutiaoErrorCode(int code, String desc) {
|
||||
this.code = code;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public static AuthToutiaoErrorCode getErrorCode(int errorCode) {
|
||||
AuthToutiaoErrorCode[] errorCodes = AuthToutiaoErrorCode.values();
|
||||
for (AuthToutiaoErrorCode code : errorCodes) {
|
||||
if (code.getCode() == errorCode) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
return EC999;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import java.util.Arrays;
|
||||
* @since 1.8
|
||||
*/
|
||||
public enum AuthUserGender {
|
||||
MALE(1, "男"), FEMALE(0, "女"), UNKNOW(-1, "");
|
||||
MALE(1, "男"), FEMALE(0, "女"), UNKNOW(-1, "未知");
|
||||
private int code;
|
||||
private String desc;
|
||||
|
||||
|
||||
@@ -67,14 +67,17 @@ public class AuthAlipayRequest extends BaseAuthRequest {
|
||||
if (!response.isSuccess()) {
|
||||
throw new AuthException(response.getSubMsg());
|
||||
}
|
||||
|
||||
String province = response.getProvince(),
|
||||
city = response.getCity();
|
||||
String location = String.format("%s %s", StringUtils.isEmpty(province) ? "" : province, StringUtils.isEmpty(city) ? "" : city);
|
||||
|
||||
return AuthUser.builder()
|
||||
.uuid(response.getUserId())
|
||||
.username(StringUtils.isEmpty(response.getUserName()) ? response.getNickName() : response.getUserName())
|
||||
.nickname(response.getNickName())
|
||||
.avatar(response.getAvatar())
|
||||
.location(String.format("%s %s", StringUtils.isEmpty(province) ? "" : province, StringUtils.isEmpty(city) ? "" : city))
|
||||
.location(location)
|
||||
.gender(AuthUserGender.getRealGender(response.getGender()))
|
||||
.token(authToken)
|
||||
.source(AuthSource.ALIPAY)
|
||||
|
||||
@@ -45,6 +45,7 @@ public class AuthCodingRequest extends BaseAuthRequest {
|
||||
if (object.getIntValue("code") != 0) {
|
||||
throw new AuthException(object.getString("msg"));
|
||||
}
|
||||
|
||||
object = object.getJSONObject("data");
|
||||
return AuthUser.builder()
|
||||
.uuid(object.getString("id"))
|
||||
|
||||
@@ -8,6 +8,7 @@ import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthSource;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.model.AuthUserGender;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
@@ -49,6 +50,7 @@ public class AuthCsdnRequest extends BaseAuthRequest {
|
||||
.username(object.getString("username"))
|
||||
.remark(object.getString("description"))
|
||||
.blog(object.getString("website"))
|
||||
.gender(AuthUserGender.UNKNOW)
|
||||
.token(authToken)
|
||||
.source(AuthSource.CSDN)
|
||||
.build();
|
||||
|
||||
@@ -2,18 +2,14 @@ package me.zhyd.oauth.request;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthDingTalkErrorCode;
|
||||
import me.zhyd.oauth.model.AuthSource;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.model.*;
|
||||
import me.zhyd.oauth.utils.GlobalAuthUtil;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 钉钉登录
|
||||
*
|
||||
@@ -38,23 +34,31 @@ public class AuthDingTalkRequest extends BaseAuthRequest {
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
String code = authToken.getAccessCode();
|
||||
// 根据timestamp, appSecret计算签名值
|
||||
String stringToSign = System.currentTimeMillis() + "";
|
||||
String urlEncodeSignature = GlobalAuthUtil.generateDingTalkSignature(config.getClientSecret(), stringToSign);
|
||||
HttpResponse response = HttpRequest.post(UrlBuilder.getDingTalkUserInfoUrl(urlEncodeSignature, stringToSign, config.getClientId()))
|
||||
.body(Objects.requireNonNull(new JSONObject().put("tmp_auth_code", code)))
|
||||
String timestamp = System.currentTimeMillis() + "";
|
||||
String urlEncodeSignature = GlobalAuthUtil.generateDingTalkSignature(config.getClientSecret(), timestamp);
|
||||
JSONObject param = new JSONObject();
|
||||
param.put("tmp_auth_code", code);
|
||||
HttpResponse response = HttpRequest.post(UrlBuilder.getDingTalkUserInfoUrl(urlEncodeSignature, timestamp, config.getClientId()))
|
||||
.body(param.toJSONString())
|
||||
.execute();
|
||||
String userInfo = response.body();
|
||||
JSONObject object = new JSONObject(userInfo);
|
||||
AuthDingTalkErrorCode errorCode = AuthDingTalkErrorCode.getErrorCode(object.getInt("errcode"));
|
||||
JSONObject object = JSON.parseObject(userInfo);
|
||||
AuthDingTalkErrorCode errorCode = AuthDingTalkErrorCode.getErrorCode(object.getIntValue("errcode"));
|
||||
if (!AuthDingTalkErrorCode.EC0.equals(errorCode)) {
|
||||
throw new AuthException(errorCode.getDesc());
|
||||
}
|
||||
object = object.getJSONObject("user_info");
|
||||
AuthToken token = AuthToken.builder()
|
||||
.openId(object.getString("openid"))
|
||||
.unionId(object.getString("unionid"))
|
||||
.build();
|
||||
return AuthUser.builder()
|
||||
.uuid(object.getStr("openid"))
|
||||
.nickname(object.getStr("nick"))
|
||||
.username(object.getStr("nick"))
|
||||
.uuid(object.getString("unionid"))
|
||||
.nickname(object.getString("nick"))
|
||||
.username(object.getString("nick"))
|
||||
.gender(AuthUserGender.UNKNOW)
|
||||
.source(AuthSource.DINGTALK)
|
||||
.token(token)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,7 @@ 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.model.*;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
|
||||
@@ -45,6 +42,7 @@ public class AuthDouyinRequest extends BaseAuthRequest {
|
||||
.username(userInfoObject.getString("nickname"))
|
||||
.nickname(userInfoObject.getString("nickname"))
|
||||
.avatar(userInfoObject.getString("avatar"))
|
||||
.gender(AuthUserGender.UNKNOW)
|
||||
.token(authToken)
|
||||
.source(AuthSource.DOUYIN)
|
||||
.build();
|
||||
|
||||
@@ -8,6 +8,7 @@ import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthSource;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.model.AuthUserGender;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
@@ -52,6 +53,7 @@ public class AuthGiteeRequest extends BaseAuthRequest {
|
||||
.location(object.getString("address"))
|
||||
.email(object.getString("email"))
|
||||
.remark(object.getString("bio"))
|
||||
.gender(AuthUserGender.UNKNOW)
|
||||
.token(authToken)
|
||||
.source(AuthSource.GITEE)
|
||||
.build();
|
||||
|
||||
@@ -8,6 +8,7 @@ import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthSource;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.model.AuthUserGender;
|
||||
import me.zhyd.oauth.utils.GlobalAuthUtil;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
@@ -55,6 +56,7 @@ public class AuthGithubRequest extends BaseAuthRequest {
|
||||
.location(object.getString("location"))
|
||||
.email(object.getString("email"))
|
||||
.remark(object.getString("bio"))
|
||||
.gender(AuthUserGender.UNKNOW)
|
||||
.token(authToken)
|
||||
.source(AuthSource.GITHUB)
|
||||
.build();
|
||||
|
||||
@@ -8,6 +8,7 @@ import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthSource;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.model.AuthUserGender;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
@@ -57,6 +58,7 @@ public class AuthGoogleRequest extends BaseAuthRequest {
|
||||
.nickname(object.getString("name"))
|
||||
.location(object.getString("locale"))
|
||||
.email(object.getString("email"))
|
||||
.gender(AuthUserGender.UNKNOW)
|
||||
.token(authToken)
|
||||
.source(AuthSource.GOOGLE)
|
||||
.build();
|
||||
|
||||
@@ -6,10 +6,7 @@ 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.model.*;
|
||||
import me.zhyd.oauth.utils.StringUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
@@ -81,6 +78,7 @@ public class AuthLinkedinRequest extends BaseAuthRequest {
|
||||
.avatar(avatar)
|
||||
.email(email)
|
||||
.token(authToken)
|
||||
.gender(AuthUserGender.UNKNOW)
|
||||
.source(AuthSource.LINKEDIN)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -6,10 +6,7 @@ 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.model.*;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
@@ -74,6 +71,7 @@ public class AuthMiRequest extends BaseAuthRequest {
|
||||
.nickname(user.getString("miliaoNick"))
|
||||
.avatar(user.getString("miliaoIcon"))
|
||||
.email(user.getString("mail"))
|
||||
.gender(AuthUserGender.UNKNOW)
|
||||
.token(authToken)
|
||||
.source(AuthSource.MI)
|
||||
.build();
|
||||
|
||||
@@ -6,10 +6,7 @@ import cn.hutool.http.HttpUtil;
|
||||
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.model.*;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -85,6 +82,7 @@ public class AuthMicrosoftRequest extends BaseAuthRequest {
|
||||
.nickname(object.getString("displayName"))
|
||||
.location(object.getString("officeLocation"))
|
||||
.email(object.getString("mail"))
|
||||
.gender(AuthUserGender.UNKNOW)
|
||||
.token(authToken)
|
||||
.source(AuthSource.MICROSOFT)
|
||||
.build();
|
||||
|
||||
@@ -48,7 +48,7 @@ public class AuthQqRequest extends BaseAuthRequest {
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
String accessToken = authToken.getAccessToken();
|
||||
String openId = this.getOpenId(accessToken);
|
||||
String openId = this.getOpenId(authToken);
|
||||
HttpResponse response = HttpRequest.get(UrlBuilder.getQqUserInfoUrl(config.getClientId(), accessToken, openId))
|
||||
.execute();
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
@@ -59,11 +59,13 @@ public class AuthQqRequest extends BaseAuthRequest {
|
||||
if (StringUtils.isEmpty(avatar)) {
|
||||
avatar = object.getString("figureurl_qq_1");
|
||||
}
|
||||
|
||||
String location = String.format("%s-%s", object.getString("province"), object.getString("city"));
|
||||
return AuthUser.builder()
|
||||
.username(object.getString("nickname"))
|
||||
.nickname(object.getString("nickname"))
|
||||
.avatar(avatar)
|
||||
.location(object.getString("province") + "-" + object.getString("city"))
|
||||
.location(location)
|
||||
.uuid(openId)
|
||||
.gender(AuthUserGender.getRealGender(object.getString("gender")))
|
||||
.token(authToken)
|
||||
@@ -71,7 +73,8 @@ public class AuthQqRequest extends BaseAuthRequest {
|
||||
.build();
|
||||
}
|
||||
|
||||
private String getOpenId(String accessToken) {
|
||||
private String getOpenId(AuthToken authToken) {
|
||||
String accessToken = authToken.getAccessToken();
|
||||
HttpResponse response = HttpRequest.get(UrlBuilder.getQqOpenidUrl("https://graph.qq.com/oauth2.0/me", accessToken))
|
||||
.execute();
|
||||
if (response.isOk()) {
|
||||
@@ -80,11 +83,14 @@ public class AuthQqRequest extends BaseAuthRequest {
|
||||
String removeSuffix = StrUtil.replace(removePrefix, ");", "");
|
||||
String openId = StrUtil.trim(removeSuffix);
|
||||
JSONObject object = JSONObject.parseObject(openId);
|
||||
if (object.containsKey("openid")) {
|
||||
return object.getString("openid");
|
||||
if (object.containsKey("error")) {
|
||||
throw new AuthException(object.get("error") + ":" + object.get("error_description"));
|
||||
}
|
||||
throw new AuthException("Invalid openId");
|
||||
authToken.setOpenId(object.getString("openid"));
|
||||
authToken.setUnionId(object.getString("unionid"));
|
||||
return StringUtils.isEmpty(authToken.getUnionId()) ? authToken.getOpenId() : authToken.getUnionId();
|
||||
}
|
||||
throw new AuthException("Invalid openId");
|
||||
|
||||
throw new AuthException("request error");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
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.*;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
* 今日头条登录
|
||||
*
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @version 1.5
|
||||
* @since 1.5
|
||||
*/
|
||||
public class AuthToutiaoRequest extends BaseAuthRequest {
|
||||
|
||||
public AuthToutiaoRequest(AuthConfig config) {
|
||||
super(config, AuthSource.TOUTIAO);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(String code) {
|
||||
String accessTokenUrl = UrlBuilder.getToutiaoAccessTokenUrl(config.getClientId(), config.getClientSecret(), code);
|
||||
HttpResponse response = HttpRequest.get(accessTokenUrl).execute();
|
||||
JSONObject object = JSONObject.parseObject(response.body());
|
||||
|
||||
if (object.containsKey("error_code")) {
|
||||
throw new AuthException(AuthToutiaoErrorCode.getErrorCode(object.getIntValue("error_code")).getDesc());
|
||||
}
|
||||
|
||||
return AuthToken.builder()
|
||||
.accessToken(object.getString("access_token"))
|
||||
.expireIn(object.getIntValue("expires_in"))
|
||||
.openId(object.getString("open_id"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthUser getUserInfo(AuthToken authToken) {
|
||||
HttpResponse userResponse = HttpRequest.get(UrlBuilder.getToutiaoUserInfoUrl(config.getClientId(), authToken.getAccessToken())).execute();
|
||||
|
||||
JSONObject userProfile = JSONObject.parseObject(userResponse.body());
|
||||
|
||||
if (userProfile.containsKey("error_code")) {
|
||||
throw new AuthException(AuthToutiaoErrorCode.getErrorCode(userProfile.getIntValue("error_code")).getDesc());
|
||||
}
|
||||
|
||||
JSONObject user = userProfile.getJSONObject("data");
|
||||
|
||||
boolean isAnonymousUser = user.getIntValue("uid_type") == 14;
|
||||
String anonymousUserName = "匿名用户";
|
||||
|
||||
return AuthUser.builder()
|
||||
.uuid(user.getString("uid"))
|
||||
.username(isAnonymousUser ? anonymousUserName : user.getString("screen_name"))
|
||||
.nickname(isAnonymousUser ? anonymousUserName : user.getString("screen_name"))
|
||||
.avatar(user.getString("avatar_url"))
|
||||
.remark(user.getString("description"))
|
||||
.gender(AuthUserGender.getRealGender(user.getString("gender")))
|
||||
.token(authToken)
|
||||
.source(AuthSource.TOUTIAO)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -42,11 +42,12 @@ public class AuthWeChatRequest extends BaseAuthRequest {
|
||||
|
||||
this.checkResponse(object);
|
||||
|
||||
String location = String.format("%s-%s-%s", object.getString("country"), object.getString("province"), object.getString("city"));
|
||||
return AuthUser.builder()
|
||||
.username(object.getString("nickname"))
|
||||
.nickname(object.getString("nickname"))
|
||||
.avatar(object.getString("headimgurl"))
|
||||
.location(object.getString("country") + "-" + object.getString("province") + "-" + object.getString("city"))
|
||||
.location(location)
|
||||
.uuid(openId)
|
||||
.gender(AuthUserGender.getRealGender(object.getString("sex")))
|
||||
.token(authToken)
|
||||
@@ -73,6 +74,7 @@ public class AuthWeChatRequest extends BaseAuthRequest {
|
||||
throw new AuthException(object.getIntValue("errcode"), object.getString("errmsg"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取token,适用于获取access_token和刷新token
|
||||
*
|
||||
|
||||
@@ -23,9 +23,11 @@ public abstract class BaseAuthRequest implements AuthRequest {
|
||||
public BaseAuthRequest(AuthConfig config, AuthSource source) {
|
||||
this.config = config;
|
||||
this.source = source;
|
||||
if (!AuthConfigChecker.isSupportedAuth(config)) {
|
||||
if (!AuthConfigChecker.isSupportedAuth(config, source)) {
|
||||
throw new AuthException(ResponseStatus.PARAMETER_INCOMPLETE);
|
||||
}
|
||||
// 校验配置合法性
|
||||
AuthConfigChecker.check(config, source);
|
||||
}
|
||||
|
||||
protected abstract AuthToken getAccessToken(String code);
|
||||
|
||||
@@ -13,6 +13,7 @@ public enum ResponseStatus {
|
||||
UNSUPPORTED(5003, "Unsupported operation"),
|
||||
NO_AUTH_SOURCE(5004, "AuthSource cannot be null"),
|
||||
UNIDENTIFIED_PLATFORM(5005, "Unidentified platform"),
|
||||
ILLEGAL_REDIRECT_URI(5006, "Illegal redirect uri"),
|
||||
;
|
||||
|
||||
private int code;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthSource;
|
||||
import me.zhyd.oauth.request.ResponseStatus;
|
||||
|
||||
/**
|
||||
* 授权配置类的校验器
|
||||
@@ -15,9 +18,35 @@ public class AuthConfigChecker {
|
||||
* 是否支持第三方登录
|
||||
*
|
||||
* @param config config
|
||||
* @param source source
|
||||
* @return true or false
|
||||
*/
|
||||
public static boolean isSupportedAuth(AuthConfig config) {
|
||||
return StringUtils.isNotEmpty(config.getClientId()) && StringUtils.isNotEmpty(config.getClientSecret()) && StringUtils.isNotEmpty(config.getRedirectUri());
|
||||
public static boolean isSupportedAuth(AuthConfig config, AuthSource source) {
|
||||
boolean isSupported = StringUtils.isNotEmpty(config.getClientId()) && StringUtils.isNotEmpty(config.getClientSecret()) && StringUtils.isNotEmpty(config.getRedirectUri());
|
||||
if (isSupported && AuthSource.ALIPAY == source) {
|
||||
isSupported = StringUtils.isNotEmpty(config.getAlipayPublicKey());
|
||||
}
|
||||
return isSupported;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查配置合法性。针对部分平台, 对redirect uri有特定要求。一般来说redirect uri都是http://,而对于facebook平台, redirect uri 必须是https的链接
|
||||
*
|
||||
* @param config config
|
||||
* @param source source
|
||||
*/
|
||||
public static void check(AuthConfig config, AuthSource source) {
|
||||
String redirectUri = config.getRedirectUri();
|
||||
if (!GlobalAuthUtil.isHttpProtocol(redirectUri) && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) {
|
||||
throw new AuthException(ResponseStatus.ILLEGAL_REDIRECT_URI);
|
||||
}
|
||||
// facebook的回调地址必须为https的链接
|
||||
if (AuthSource.FACEBOOK == source && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) {
|
||||
throw new AuthException(ResponseStatus.ILLEGAL_REDIRECT_URI);
|
||||
}
|
||||
// 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1
|
||||
if (AuthSource.ALIPAY == source && GlobalAuthUtil.isLocalHost(redirectUri)) {
|
||||
throw new AuthException(ResponseStatus.ILLEGAL_REDIRECT_URI);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ public class GlobalAuthUtil {
|
||||
private static final String DEFAULT_ENCODING = "UTF-8";
|
||||
private static final String ALGORITHM = "HmacSHA256";
|
||||
|
||||
public static String generateDingTalkSignature(String canonicalString, String secret) {
|
||||
public static String generateDingTalkSignature(String secretKey, String timestamp) {
|
||||
try {
|
||||
byte[] signData = sign(canonicalString.getBytes(DEFAULT_ENCODING), secret.getBytes(DEFAULT_ENCODING));
|
||||
byte[] signData = sign(secretKey.getBytes(DEFAULT_ENCODING), timestamp.getBytes(DEFAULT_ENCODING));
|
||||
return urlEncode(new String(Base64.encode(signData, false)));
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
throw new AuthException("Unsupported algorithm: " + DEFAULT_ENCODING, ex);
|
||||
@@ -84,4 +84,23 @@ public class GlobalAuthUtil {
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static boolean isHttpProtocol(String url) {
|
||||
if (StringUtils.isEmpty(url)) {
|
||||
return false;
|
||||
}
|
||||
return url.startsWith("http://");
|
||||
}
|
||||
|
||||
public static boolean isHttpsProtocol(String url) {
|
||||
if (StringUtils.isEmpty(url)) {
|
||||
return false;
|
||||
}
|
||||
return url.startsWith("https://");
|
||||
}
|
||||
|
||||
public static boolean isLocalHost(String url) {
|
||||
return StringUtils.isEmpty(url) || url.contains("127.0.0.1") || url.contains("localhost");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public class UrlBuilder {
|
||||
private static final String QQ_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&grant_type=authorization_code&code={3}&redirect_uri={4}";
|
||||
private static final String QQ_USER_INFO_PATTERN = "{0}?oauth_consumer_key={1}&access_token={2}&openid={3}";
|
||||
private static final String QQ_AUTHORIZE_PATTERN = "{0}?client_id={1}&response_type=code&redirect_uri={2}&state={3}";
|
||||
private static final String QQ_OPENID_PATTERN = "{0}?access_token={1}";
|
||||
private static final String QQ_OPENID_PATTERN = "{0}?access_token={1}&unionid=1";
|
||||
|
||||
private static final String WECHAT_AUTHORIZE_PATTERN = "{0}?appid={1}&redirect_uri={2}&response_type=code&scope=snsapi_login&state={3}#wechat_redirect";
|
||||
private static final String WECHAT_ACCESS_TOKEN_PATTERN = "{0}?appid={1}&secret={2}&code={3}&grant_type=authorization_code";
|
||||
@@ -92,6 +92,10 @@ public class UrlBuilder {
|
||||
private static final String MI_USER_INFO_PATTERN = "{0}?clientId={1}&token={2}";
|
||||
private static final String MI_REFRESH_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&redirect_uri={3}&refresh_token={4}&grant_type=refresh_token";
|
||||
|
||||
private static final String TOUTIAO_ACCESS_TOKEN_PATTERN = "{0}?client_key={1}&client_secret={2}&code={3}&grant_type=authorize_code";
|
||||
private static final String TOUTIAO_USER_INFO_PATTERN = "{0}?client_key={1}&access_token={2}";
|
||||
private static final String TOUTIAO_AUTHORIZE_PATTERN = "{0}?client_key={1}&redirect_uri={2}&state={3}&response_type=code&auth_only=1&display=0";
|
||||
|
||||
/**
|
||||
* 获取githubtoken的接口地址
|
||||
*
|
||||
@@ -776,4 +780,38 @@ public class UrlBuilder {
|
||||
public static String getMiRefreshUrl(String clientId, String clientSecret, String redirectUrl, String refreshToken) {
|
||||
return MessageFormat.format(MI_REFRESH_TOKEN_PATTERN, ApiUrl.MI.refresh(), clientId, clientSecret, redirectUrl, refreshToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取今日头条授权地址
|
||||
*
|
||||
* @param clientId 今日头条 应用的Client ID
|
||||
* @param redirectUrl 今日头条 应用授权成功后的回调地址
|
||||
* @return full url
|
||||
*/
|
||||
public static String getToutiaoAuthorizeUrl(String clientId, String redirectUrl) {
|
||||
return MessageFormat.format(TOUTIAO_AUTHORIZE_PATTERN, ApiUrl.TOUTIAO.authorize(), clientId, redirectUrl, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取今日头条 token的接口地址
|
||||
*
|
||||
* @param clientId 今日头条 应用的Client ID
|
||||
* @param clientSecret 今日头条 应用的Client Secret
|
||||
* @param code 今日头条 授权前的code,用来换token
|
||||
* @return full url
|
||||
*/
|
||||
public static String getToutiaoAccessTokenUrl(String clientId, String clientSecret, String code) {
|
||||
return MessageFormat.format(TOUTIAO_ACCESS_TOKEN_PATTERN, ApiUrl.TOUTIAO.accessToken(), clientId, clientSecret, code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取今日头条用户详情的接口地址
|
||||
*
|
||||
* @param clientId 今日头条 应用的client_key
|
||||
* @param token token
|
||||
* @return full url
|
||||
*/
|
||||
public static String getToutiaoUserInfoUrl(String clientId, String token) {
|
||||
return MessageFormat.format(TOUTIAO_USER_INFO_PATTERN, ApiUrl.TOUTIAO.userInfo(), clientId, token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
### 2019/06/18
|
||||
1. 解决Issue [#IY2HW](https://gitee.com/yadong.zhang/JustAuth/issues/IY2HW)
|
||||
2. 解决Issue [#IY2OH](https://gitee.com/yadong.zhang/JustAuth/issues/IY2OH)
|
||||
3. 解决Issue [#IY2FV](https://gitee.com/yadong.zhang/JustAuth/issues/IY2FV)
|
||||
4. 修复部分注释、拼写错误
|
||||
5. 解决Issue [#IY1QR](https://gitee.com/yadong.zhang/JustAuth/issues/IY1QR) 增加对Config属性的校验功能,主要校验redirect uri的合法性
|
||||
6. 合并[skqing](https://gitee.com/skqing)提交的[PR](https://gitee.com/yadong.zhang/JustAuth/pulls/2)
|
||||
|
||||
### 2019/06/06
|
||||
1. 增加今日头条的授权登陆
|
||||
2. 发布1.6.0-beta版本,今日头条开发者暂时不能认证, 所以无法做测试,等测试通过后,正式发布release版本
|
||||
|
||||
### 2019/05/28
|
||||
1. 增加小米账号和微软的授权登陆
|
||||
2. 发布1.5.0版本
|
||||
|
||||
Reference in New Issue
Block a user