提交 e29df531 编写于 作者: 智布道's avatar 智布道 👁

Merge branch 'dev'

# Conflicts:
#	example.md
#	src/main/java/me/zhyd/oauth/request/AuthMiRequest.java
#	src/main/java/me/zhyd/oauth/request/AuthWeChatRequest.java
#	src/main/java/me/zhyd/oauth/utils/UrlBuilder.java
# JustAuth 开发组IDE 编辑器标准
root = true
# 空格替代Tab缩进在各种编辑工具下效果一致
[*]
indent_style = space
indent_size = 2
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.java]
indent_size = 4
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
......@@ -37,10 +37,18 @@
<td align="center" width="200"><a href="#授权google"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/google.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权facebook"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/facebook.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权抖音"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/douyin.png" width="20"></a></td>
</tr>
</table>
<table>
<tr>
<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="#授权Teambition"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/teambition.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权人人"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/renren.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权Pinterest"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/pinterest.png" width="20"></a></td>
<td align="center" width="200"><a href="#授权Stack Overflow"><img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/stackoverflow.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>
......@@ -88,7 +96,9 @@ authRequest.login(callback);
注:`1.8.0`版本后,增加了`state`参数校验,用于防止[CSRF](https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%AB%99%E8%AF%B7%E6%B1%82%E4%BC%AA%E9%80%A0)。强烈建议,保证单次流程内`state`的唯一性,且每个`state`只可用一次。
**配套Demo**[JustAuth-demo](https://gitee.com/yadong.zhang/JustAuth-demo)
**配套Demo**
- [Springboot版](https://gitee.com/yadong.zhang/JustAuth-demo)
- [jFinal版](https://github.com/zhangyd-c/jfinal-justauth-demo)
具体的例子可以参考:
......@@ -118,6 +128,10 @@ authRequest.login(callback);
| <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/teambition.png" width="20"> | [AuthTeambitionRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthTeambitionRequest.java) | <a href="https://docs.teambition.com/" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/renren.png" width="20"> | [AuthRenrenRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthRenrenRequest.java) | <a href="http://open.renren.com/wiki/OAuth2.0" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/pinterest.png" width="20"> | [AuthPinterestRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthPinterestRequest.java) | <a href="https://developers.pinterest.com/docs/api/overview/?" target="_blank">参考文档</a> |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/JustAuth/stackoverflow.png" width="20"> | [AuthStackOverflowRequest](https://gitee.com/yadong.zhang/JustAuth/blob/master/src/main/java/me/zhyd/oauth/request/AuthStackOverflowRequest.java) | <a href="https://api.stackexchange.com/docs/authentication" 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登录只能针对少部分用户使用了_
......@@ -166,4 +180,4 @@ _请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经
| 支付宝 | 微信 |
| :------------: | :------------: |
| <img src="https://gitee.com/yadong.zhang/static/raw/master/qrcode/zfb_code.png" width="200"/> | <img src="https://gitee.com/yadong.zhang/static/raw/master/qrcode/wx_code.png" width="200" /> |
\ No newline at end of file
| <img src="https://gitee.com/yadong.zhang/static/raw/master/qrcode/zfb_code.png" width="200"/> | <img src="https://gitee.com/yadong.zhang/static/raw/master/qrcode/wx_code.png" width="200" /> |
......@@ -3,4 +3,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>
- <img src="https://avatar.gitee.com/uploads/51/1651_dolphinboy.png!avatar100?1479346570" width="20"> · skqing : <a href="https://gitee.com/skqing" target="_blank">[Gitee]</a> | <a href="https://my.oschina.net/dolphinboy" target="_blank">[个人网站]</a>
- <img src="https://avatars2.githubusercontent.com/u/2988765?s=115&v=4" width="20"> · pengisgood : <a href="https://github.com/pengisgood" target="_blank">[Github]</a> | <a href="https://pengisgood.github.io" target="_blank">[个人网站]</a>
- 千年等一回,我只为等你...
......@@ -70,7 +70,7 @@ _注:非全部平台,部分平台可能不存在图例_
#### 授权微软
暂无
![授权微软](https://images.gitee.com/uploads/images/2019/0718/224146_681aa535_784199.png "授权微软")
#### 授权小米
......@@ -80,16 +80,28 @@ _注:非全部平台,部分平台可能不存在图例_
暂无
#### 授权csdn
#### 授权Teambition
暂无
![授权Teambition](https://images.gitee.com/uploads/images/2019/0718/224119_3da514ab_784199.png "授权Teambition")
#### 授权Pinterest
![授权Pinterest](https://images.gitee.com/uploads/images/2019/0718/155012_6290f500_784199.jpeg "在这里输入图片标题")
![授权Pinterest](https://images.gitee.com/uploads/images/2019/0718/155012_6290f500_784199.jpeg "授权Pinterest")
#### 授权Renren
![授权Renre](https://images.gitee.com/uploads/images/2019/0718/155035_8e26c10a_784199.jpeg "在这里输入图片标题")
![授权Renre](https://images.gitee.com/uploads/images/2019/0718/155035_8e26c10a_784199.jpeg "授权Renren")
#### 授权Stack Overflow
![授权Stack Overflow](https://images.gitee.com/uploads/images/2019/0718/192639_cc301ba7_784199.png "授权Stack Overflow")
#### 授权Twitter
暂无
#### 授权csdn
暂无
_请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经下线。如果以前申请过的应用,可以继续使用,但是不再支持申请新的应用。so, 本项目中的CSDN登录只能针对少部分用户使用了_
\ No newline at end of file
_请知悉:经咨询CSDN官方客服得知,CSDN的授权开放平台已经下线。如果以前申请过的应用,可以继续使用,但是不再支持申请新的应用。so, 本项目中的CSDN登录只能针对少部分用户使用了_
......@@ -6,7 +6,7 @@
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>1.8.1</version>
<version>1.9.0</version>
<name>JustAuth</name>
<url>https://gitee.com/yadong.zhang/JustAuth</url>
......@@ -30,7 +30,7 @@
<developers>
<developer>
<name>yadong.zhang</name>
<name>Yadong.Zhang</name>
<email>yadong.zhang0415@gmail.com</email>
<url>https://www.zhyd.me</url>
</developer>
......@@ -39,6 +39,11 @@
<email>shenyangkai1994@gmail.com</email>
<url>https://xkcoding.com</url>
</developer>
<developer>
<name>Hongwei.Peng</name>
<email>pengisgood@gmail.com</email>
<url>https://github.com/pengisgood</url>
</developer>
</developers>
<properties>
......
package me.zhyd.oauth.config;
import lombok.*;
import lombok.Builder;
import lombok.Getter;
/**
* JustAuth配置类
......@@ -9,11 +10,8 @@ import lombok.*;
* @version 1.0
* @since 1.8
*/
@Setter
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AuthConfig {
/**
......@@ -51,4 +49,11 @@ public class AuthConfig {
* 1.8.0版本新增参数
*/
private String state;
/**
* Stack Overflow Key
* <p>
* 1.9.0版本新增参数
*/
private String stackOverflowKey;
}
package me.zhyd.oauth.config;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.request.ResponseStatus;
import me.zhyd.oauth.model.AuthResponseStatus;
/**
* 各api需要的url, 用枚举类分平台类型管理
......@@ -79,7 +79,7 @@ public enum AuthSource {
@Override
public String accessToken() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
@Override
......@@ -110,6 +110,11 @@ public enum AuthSource {
public String revoke() {
return "https://openapi.baidu.com/rest/2.0/passport/auth/revokeAuthorization";
}
@Override
public String refresh() {
return "https://openapi.baidu.com/oauth/2.0/token";
}
},
/**
* csdn
......@@ -224,6 +229,11 @@ public enum AuthSource {
public String userInfo() {
return "https://graph.qq.com/user/get_user_info";
}
@Override
public String refresh() {
return "https://graph.qq.com/oauth2.0/token";
}
},
/**
* 微信
......@@ -265,7 +275,7 @@ public enum AuthSource {
@Override
public String userInfo() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
},
/**
......@@ -420,6 +430,95 @@ public enum AuthSource {
public String userInfo() {
return "https://open.snssdk.com/data/user_profile";
}
},
/**
* Teambition
*/
TEAMBITION {
@Override
public String authorize() {
return "https://account.teambition.com/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://account.teambition.com/oauth2/access_token";
}
@Override
public String refresh() {
return "https://account.teambition.com/oauth2/refresh_token";
}
@Override
public String userInfo() {
return "https://api.teambition.com/users/me";
}
},
/**
* 人人网
*/
RENREN {
@Override
public String authorize() {
return "https://graph.renren.com/oauth/authorize";
}
@Override
public String accessToken() {
return "https://graph.renren.com/oauth/token";
}
@Override
public String refresh() {
return "https://graph.renren.com/oauth/token";
}
@Override
public String userInfo() {
return "https://api.renren.com/v2/user/get";
}
},
/**
* Pinterest
*/
PINTEREST {
@Override
public String authorize() {
return "https://api.pinterest.com/oauth";
}
@Override
public String accessToken() {
return "https://api.pinterest.com/v1/oauth/token";
}
@Override
public String userInfo() {
return "https://api.pinterest.com/v1/me";
}
},
/**
* Stack Overflow
*/
STACK_OVERFLOW {
@Override
public String authorize() {
return "https://stackoverflow.com/oauth";
}
@Override
public String accessToken() {
return "https://stackoverflow.com/oauth/access_token/json";
}
@Override
public String userInfo() {
return "https://api.stackexchange.com/2.2/me";
}
};
/**
......@@ -449,7 +548,7 @@ public enum AuthSource {
* @return url
*/
public String revoke() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
/**
......@@ -458,7 +557,7 @@ public enum AuthSource {
* @return url
*/
public String refresh() {
throw new AuthException(ResponseStatus.UNSUPPORTED);
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
}
\ No newline at end of file
}
package me.zhyd.oauth.enums;
import me.zhyd.oauth.utils.StringUtils;
/**
* 百度授权登录时的异常状态码
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public enum AuthBaiduErrorCode {
OK("ok", "ok", "ok"),
INVALID_REQUEST("invalid_request", "invalid refresh token", "请求缺少某个必需参数,包含一个不支持的参数或参数值,或者格式不正确。"),
INVALID_CLIENT("invalid_client", "unknown client id", "client_id”、“client_secret”参数无效。"),
INVALID_GRANT("invalid_grant", "The provided authorization grant is revoked", "提供的Access Grant是无效的、过期的或已撤销的,例如,Authorization Code无效(一个授权码只能使用一次)、Refresh Token无效、redirect_uri与获取Authorization Code时提供的不一致、Devie Code无效(一个设备授权码只能使用一次)等。"),
UNAUTHORIZED_CLIENT("unauthorized_client", "The client is not authorized to use this authorization grant type", "应用没有被授权,无法使用所指定的grant_type。"),
UNSUPPORTED_GRANT_TYPE("unsupported_grant_type", "The authorization grant type is not supported", "“grant_type”百度OAuth2.0服务不支持该参数。"),
INVALID_SCOPE("invalid_scope", "The requested scope is exceeds the scope granted by the resource owner", "请求的“scope”参数是无效的、未知的、格式不正确的、或所请求的权限范围超过了数据拥有者所授予的权限范围。"),
EXPIRED_TOKEN("expired_token", "refresh token has been used", "提供的Refresh Token已过期"),
REDIRECT_URI_MISMATCH("redirect_uri_mismatch", "Invalid redirect uri", "“redirect_uri”所在的根域与开发者注册应用时所填写的根域名不匹配。"),
UNSUPPORTED_RESPONSE_TYPE("unsupported_response_type", "The response type is not supported", "“response_type”参数值不为百度OAuth2.0服务所支持,或者应用已经主动禁用了对应的授权模式"),
SLOW_DOWN("slow_down", "The device is polling too frequently", "Device Flow中,设备通过Device Code换取Access Token的接口过于频繁,两次尝试的间隔应大于5秒。"),
AUTHORIZATION_PENDING("authorization_pending", "User has not yet completed the authorization", "Device Flow中,用户还没有对Device Code完成授权操作。"),
AUTHORIZATION_DECLINED("authorization_declined", "User has declined the authorization", "Device Flow中,用户拒绝了对Device Code的授权操作。"),
INVALID_REFERER("invalid_referer", "Invalid Referer", "Implicit Grant模式中,浏览器请求的Referer与根域名绑定不匹配");
private String code;
private String msg;
private String desc;
AuthBaiduErrorCode(String code, String msg, String desc) {
this.code = code;
this.msg = msg;
this.desc = desc;
}
public static AuthBaiduErrorCode getErrorCode(String code) {
if (StringUtils.isEmpty(code)) {
return OK;
}
AuthBaiduErrorCode[] errorCodes = AuthBaiduErrorCode.values();
for (AuthBaiduErrorCode errorCode : errorCodes) {
if (code.equalsIgnoreCase(errorCode.getCode())) {
return errorCode;
}
}
return OK;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
public String getDesc() {
return desc;
}
}
package me.zhyd.oauth.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 今日头条授权登录时的异常状态码
*
......@@ -7,6 +10,8 @@ package me.zhyd.oauth.enums;
* @version 1.0
* @since 1.8
*/
@Getter
@AllArgsConstructor
public enum AuthToutiaoErrorCode {
EC0(0, "接口调用成功"),
EC1(1, "API配置错误,未传入Client Key"),
......@@ -29,11 +34,6 @@ public enum AuthToutiaoErrorCode {
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) {
......@@ -43,12 +43,4 @@ public enum AuthToutiaoErrorCode {
}
return EC999;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
package me.zhyd.oauth.model;
package me.zhyd.oauth.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
......@@ -9,36 +12,25 @@ import java.util.Arrays;
* @version 1.0
* @since 1.8
*/
@Getter
@AllArgsConstructor
public enum AuthUserGender {
MALE(1, "男"), FEMALE(0, "女"), UNKNOW(-1, "未知");
MALE(1, "男"), FEMALE(0, "女"), UNKNOWN(-1, "未知");
private int code;
private String desc;
AuthUserGender(int code, String desc) {
this.code = code;
this.desc = desc;
}
public static AuthUserGender getRealGender(String code) {
if (code == null) {
return UNKNOW;
return UNKNOWN;
}
String[] males = {"m", "男", "1", "male", "F"};
if (Arrays.asList(males).contains(code)) {
String[] males = {"m", "男", "1", "male"};
if (Arrays.asList(males).contains(code.toLowerCase())) {
return MALE;
}
String[] females = {"f", "女", "0", "female"};
if (Arrays.asList(females).contains(code)) {
if (Arrays.asList(females).contains(code.toLowerCase())) {
return FEMALE;
}
return UNKNOW;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
return UNKNOWN;
}
}
package me.zhyd.oauth.exception;
import me.zhyd.oauth.request.ResponseStatus;
import me.zhyd.oauth.model.AuthResponseStatus;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
......@@ -13,16 +13,16 @@ public class AuthException extends RuntimeException {
private String errorMsg;
public AuthException(String errorMsg) {
this(ResponseStatus.FAILURE.getCode(), errorMsg);
this(AuthResponseStatus.FAILURE.getCode(), errorMsg);
}
public AuthException(int errorCode, String errorMsg) {
super(errorCode + ":" + errorMsg);
super(errorMsg);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public AuthException(ResponseStatus status) {
public AuthException(AuthResponseStatus status) {
super(status.getMsg());
}
......@@ -30,6 +30,10 @@ public class AuthException extends RuntimeException {
super(message, cause);
}
public AuthException(Throwable cause) {
super(cause);
}
public int getErrorCode() {
return errorCode;
}
......
......@@ -3,7 +3,6 @@ package me.zhyd.oauth.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import me.zhyd.oauth.request.ResponseStatus;
/**
* JustAuth统一授权响应类
......@@ -13,7 +12,6 @@ import me.zhyd.oauth.request.ResponseStatus;
* @since 1.8
*/
@Getter
@Setter
@Builder
public class AuthResponse<T> {
/**
......@@ -37,6 +35,6 @@ public class AuthResponse<T> {
* @return true or false
*/
public boolean ok() {
return this.code == ResponseStatus.SUCCESS.getCode();
return this.code == AuthResponseStatus.SUCCESS.getCode();
}
}
package me.zhyd.oauth.request;
package me.zhyd.oauth.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public enum ResponseStatus {
@Getter
@AllArgsConstructor
public enum AuthResponseStatus {
SUCCESS(2000, "Success"),
FAILURE(5000, "Failure"),
NOT_IMPLEMENTED(5001, "Not Implemented"),
......@@ -20,18 +25,5 @@ public enum ResponseStatus {
private int code;
private String msg;
ResponseStatus(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
package me.zhyd.oauth.model;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
/**
* 授权成功后的用户信息,根据授权平台的不同,获取的数据完整性也不同
......
......@@ -9,11 +9,11 @@ import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.alipay.api.response.AlipayUserInfoShareResponse;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
......@@ -24,14 +24,14 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthAlipayRequest extends BaseAuthRequest {
public class AuthAlipayRequest extends AuthDefaultRequest {
private AlipayClient alipayClient;
public AuthAlipayRequest(AuthConfig config) {
super(config, AuthSource.ALIPAY);
this.alipayClient = new DefaultAlipayClient(AuthSource.ALIPAY.accessToken(), config.getClientId(), config.getClientSecret(), "json", "UTF-8", config
.getAlipayPublicKey(), "RSA2");
.getAlipayPublicKey(), "RSA2");
}
@Override
......@@ -43,17 +43,17 @@ public class AuthAlipayRequest extends BaseAuthRequest {
try {
response = this.alipayClient.execute(request);
} catch (Exception e) {
throw new AuthException("Unable to get token from alipay using code [" + authCallback.getAuth_code() + "]", e);
throw new AuthException(e);
}
if (!response.isSuccess()) {
throw new AuthException(response.getSubMsg());
}
return AuthToken.builder()
.accessToken(response.getAccessToken())
.uid(response.getUserId())
.expireIn(Integer.parseInt(response.getExpiresIn()))
.refreshToken(response.getRefreshToken())
.build();
.accessToken(response.getAccessToken())
.uid(response.getUserId())
.expireIn(Integer.parseInt(response.getExpiresIn()))
.refreshToken(response.getRefreshToken())
.build();
}
@Override
......@@ -70,20 +70,19 @@ public class AuthAlipayRequest extends BaseAuthRequest {
throw new AuthException(response.getSubMsg());
}
String province = response.getProvince(),
city = response.getCity();
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(location)
.gender(AuthUserGender.getRealGender(response.getGender()))
.token(authToken)
.source(AuthSource.ALIPAY)
.build();
.uuid(response.getUserId())
.username(StringUtils.isEmpty(response.getUserName()) ? response.getNickName() : response.getUserName())
.nickname(response.getNickName())
.avatar(response.getAvatar())
.location(location)
.gender(AuthUserGender.getRealGender(response.getGender()))
.token(authToken)
.source(AuthSource.ALIPAY)
.build();
}
/**
......@@ -93,6 +92,11 @@ public class AuthAlipayRequest extends BaseAuthRequest {
*/
@Override
public String authorize() {
return UrlBuilder.getAlipayAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("app_id", config.getClientId())
.queryParam("scope", "auth_user")
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.build();
}
}
......@@ -5,7 +5,7 @@ import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthBaiduErrorCode;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.utils.UrlBuilder;
......@@ -17,7 +17,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthBaiduRequest extends BaseAuthRequest {
public class AuthBaiduRequest extends AuthDefaultRequest {
public AuthBaiduRequest(AuthConfig config) {
super(config, AuthSource.BAIDU);
......@@ -25,40 +25,51 @@ public class AuthBaiduRequest extends BaseAuthRequest {
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getBaiduAccessTokenUrl(config.getClientId(), config.getClientSecret(), authCallback.getCode(), config
.getRedirectUri());
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
AuthBaiduErrorCode errorCode = AuthBaiduErrorCode.getErrorCode(accessTokenObject.getString("error"));
if (AuthBaiduErrorCode.OK != errorCode) {
throw new AuthException(errorCode.getDesc());
}
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.scope(accessTokenObject.getString("scope"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.build();
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
return getAuthToken(response);
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getBaiduUserInfoUrl(accessToken)).execute();
HttpResponse response = doGetUserInfo(authToken);
String userInfo = response.body();
JSONObject object = JSONObject.parseObject(userInfo);
AuthBaiduErrorCode errorCode = AuthBaiduErrorCode.getErrorCode(object.getString("error"));
if (AuthBaiduErrorCode.OK != errorCode) {
throw new AuthException(errorCode.getDesc());
}
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("userid"))
.username(object.getString("username"))
.nickname(object.getString("username"))
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.token(authToken)
.source(AuthSource.BAIDU)
.build();
.uuid(object.getString("userid"))
.username(object.getString("username"))
.nickname(object.getString("username"))
.avatar(object.getString("portrait"))
.remark(object.getString("userdetail"))
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.token(authToken)
.source(AuthSource.BAIDU)
.build();
}
@Override
public AuthResponse revoke(AuthToken authToken) {
HttpResponse response = doGetRevoke(authToken);
JSONObject object = JSONObject.parseObject(response.body());
this.checkResponse(object);
// 返回1表示取消授权成功,否则失败
AuthResponseStatus status = object.getIntValue("result") == 1 ? AuthResponseStatus.SUCCESS : AuthResponseStatus.FAILURE;
return AuthResponse.builder().code(status.getCode()).msg(status.getMsg()).build();
}
@Override
public AuthResponse refresh(AuthToken authToken) {
String refreshUrl = UrlBuilder.fromBaseUrl(this.source.refresh())
.queryParam("grant_type", "refresh_token")
.queryParam("refresh_token", authToken.getRefreshToken())
.queryParam("client_id", this.config.getClientId())
.queryParam("client_secret", this.config.getClientSecret())
.build();
HttpResponse response = HttpRequest.get(refreshUrl).execute();
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(this.getAuthToken(response))
.build();
}
/**
......@@ -68,23 +79,35 @@ public class AuthBaiduRequest extends BaseAuthRequest {
*/
@Override
public String authorize() {
return UrlBuilder.getBaiduAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("display", "popup")
.queryParam("state", getRealState(config.getState()))
.build();
}
@Override
public AuthResponse revoke(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getBaiduRevokeUrl(accessToken)).execute();
String userInfo = response.body();
JSONObject object = JSONObject.parseObject(userInfo);
if (object.containsKey("error_code")) {
return AuthResponse.builder()
.code(ResponseStatus.FAILURE.getCode())
.msg(object.getString("error_msg"))
.build();
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error") || object.containsKey("error_code")) {
String msg = object.containsKey("error_description") ? object.getString("error_description") : object.getString("error_msg");
throw new AuthException(msg);
}
ResponseStatus status = object.getIntValue("result") == 1 ? ResponseStatus.SUCCESS : ResponseStatus.FAILURE;
return AuthResponse.builder().code(status.getCode()).msg(status.getMsg()).build();
}
private AuthToken getAuthToken(HttpResponse response) {
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.scope(accessTokenObject.getString("scope"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.build();
}
}
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;
......@@ -9,7 +8,7 @@ import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.utils.UrlBuilder;
/**
......@@ -19,7 +18,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthCodingRequest extends BaseAuthRequest {
public class AuthCodingRequest extends AuthDefaultRequest {
public AuthCodingRequest(AuthConfig config) {
super(config, AuthSource.CODING);
......@@ -27,43 +26,48 @@ public class AuthCodingRequest extends BaseAuthRequest {
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getCodingAccessTokenUrl(config.getClientId(), config.getClientSecret(), authCallback.getCode());
HttpResponse response = HttpRequest.get(accessTokenUrl).execute();
HttpResponse response = doGetAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
if (accessTokenObject.getIntValue("code") != 0) {
throw new AuthException("Unable to get token from coding using code [" + authCallback.getCode() + "]");
}
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getCodingUserInfoUrl(accessToken)).execute();
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
if (object.getIntValue("code") != 0) {
throw new AuthException(object.getString("msg"));
}
this.checkResponse(object);
object = object.getJSONObject("data");
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("name"))
.avatar("https://coding.net/" + object.getString("avatar"))
.blog("https://coding.net/" + object.getString("path"))
.nickname(object.getString("name"))
.company(object.getString("company"))
.location(object.getString("location"))
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.email(object.getString("email"))
.remark(object.getString("slogan"))
.token(authToken)
.source(AuthSource.CODING)
.build();
.uuid(object.getString("id"))
.username(object.getString("name"))
.avatar("https://coding.net/" + object.getString("avatar"))
.blog("https://coding.net/" + object.getString("path"))
.nickname(object.getString("name"))
.company(object.getString("company"))
.location(object.getString("location"))
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.email(object.getString("email"))
.remark(object.getString("slogan"))
.token(authToken)
.source(AuthSource.CODING)
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.getIntValue("code") != 0) {
throw new AuthException(object.getString("msg"));
}
}
/**
......@@ -73,6 +77,12 @@ public class AuthCodingRequest extends BaseAuthRequest {
*/
@Override
public String authorize() {
return UrlBuilder.getCodingAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("scope", "user")
.queryParam("state", getRealState(config.getState()))
.build();
}
}
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.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* CSDN登录
......@@ -19,7 +17,8 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthCsdnRequest extends BaseAuthRequest {
@Deprecated
public class AuthCsdnRequest extends AuthDefaultRequest {
public AuthCsdnRequest(AuthConfig config) {
super(config, AuthSource.CSDN);
......@@ -27,42 +26,36 @@ public class AuthCsdnRequest extends BaseAuthRequest {
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getCsdnAccessTokenUrl(config.getClientId(), config.getClientSecret(), authCallback.getCode(), config
.getRedirectUri());
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
if (accessTokenObject.containsKey("error_code")) {
throw new AuthException("Unable to get token from csdn using code [" + authCallback.getCode() + "]");
}
this.checkResponse(accessTokenObject);
return AuthToken.builder().accessToken(accessTokenObject.getString("access_token")).build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getCsdnUserInfoUrl(accessToken)).execute();
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
if (object.containsKey("error_code")) {
throw new AuthException(object.getString("error"));
}
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("username"))
.username(object.getString("username"))
.remark(object.getString("description"))
.blog(object.getString("website"))
.gender(AuthUserGender.UNKNOW)
.token(authToken)
.source(AuthSource.CSDN)
.build();
.uuid(object.getString("username"))
.username(object.getString("username"))
.remark(object.getString("description"))
.blog(object.getString("website"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(AuthSource.CSDN)
.build();
}
/**
* 返回认证url,可自行跳转页面
* 检查响应内容是否正确
*
* @return 返回授权地址
* @param object 请求响应内容
*/
@Override
public String authorize() {
return UrlBuilder.getCsdnAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
private void checkResponse(JSONObject object) {
if (object.containsKey("error_code")) {
throw new AuthException(object.getString("error"));
}
}
}
package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.utils.AuthChecker;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 默认的request处理类
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @author yangkai.shen (https://xkcoding.com)
* @version 1.0
* @since 1.8
*/
@Data
@Slf4j
public abstract class AuthDefaultRequest implements AuthRequest {
protected AuthConfig config;
protected AuthSource source;
public AuthDefaultRequest(AuthConfig config, AuthSource source) {
this.config = config;
this.source = source;
if (!AuthChecker.isSupportedAuth(config, source)) {
throw new AuthException(AuthResponseStatus.PARAMETER_INCOMPLETE);
}
// 校验配置合法性
AuthChecker.checkConfig(config, source);
}
protected abstract AuthToken getAccessToken(AuthCallback authCallback);
protected abstract AuthUser getUserInfo(AuthToken authToken);
@Override
public AuthResponse login(AuthCallback authCallback) {
try {
AuthChecker.checkCode(source == AuthSource.ALIPAY ? authCallback.getAuth_code() : authCallback.getCode());
AuthChecker.checkState(authCallback.getState(), config.getState());
AuthToken authToken = this.getAccessToken(authCallback);
AuthUser user = this.getUserInfo(authToken);
return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).data(user).build();
} catch (Exception e) {
log.error("Failed to login with oauth authorization.", e);
return this.responseError(e);
}
}
private AuthResponse responseError(Exception e) {
int errorCode = AuthResponseStatus.FAILURE.getCode();
if (e instanceof AuthException) {
errorCode = ((AuthException) e).getErrorCode();
}
return AuthResponse.builder().code(errorCode).msg(e.getMessage()).build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.build();
}
/**
* 返回获取accessToken的url
*
* @return 返回获取accessToken的url
*/
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("grant_type", "authorization_code")
.queryParam("redirect_uri", config.getRedirectUri())
.build();
}
/**
* 返回获取accessToken的url
*
* @return 返回获取accessToken的url
*/
protected String refreshTokenUrl(String refreshToken) {
return UrlBuilder.fromBaseUrl(source.refresh())
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("refresh_token", refreshToken)
.queryParam("grant_type", "refresh_token")
.queryParam("redirect_uri", config.getRedirectUri())
.build();
}
/**
* 返回获取userInfo的url
*
* @return 返回获取userInfo的url
*/
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo()).queryParam("access_token", authToken.getAccessToken()).build();
}
/**
* 返回获取revoke authorization的url
*
* @return 返回获取revoke authorization的url
*/
protected String revokeUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.revoke()).queryParam("access_token", authToken.getAccessToken()).build();
}
/**
* 获取state,如果为空, 则默认去当前日期的时间戳
*
* @param state 原始的state
* @return 返回不为null的state
*/
protected String getRealState(String state) {
return StringUtils.isEmpty(state) ? String.valueOf(System.currentTimeMillis()) : state;
}
/**
* 通用的 authorizationCode 协议
*
* @param code code码
* @return HttpResponse
*/
protected HttpResponse doPostAuthorizationCode(String code) {
return HttpRequest.post(accessTokenUrl(code)).execute();
}
/**
* 通用的 authorizationCode 协议
*
* @param code code码
* @return HttpResponse
*/
protected HttpResponse doGetAuthorizationCode(String code) {
return HttpRequest.get(accessTokenUrl(code)).execute();
}
/**
* 通用的 用户信息
*
* @param authToken token封装
* @return HttpResponse
*/
protected HttpResponse doPostUserInfo(AuthToken authToken) {
return HttpRequest.post(userInfoUrl(authToken)).execute();
}
/**
* 通用的 用户信息
*
* @param authToken token封装
* @return HttpResponse
*/
protected HttpResponse doGetUserInfo(AuthToken authToken) {
return HttpRequest.get(userInfoUrl(authToken)).execute();
}
/**
* 通用的post形式的取消授权方法
*
* @param authToken token封装
* @return HttpResponse
*/
protected HttpResponse doPostRevoke(AuthToken authToken) {
return HttpRequest.post(revokeUrl(authToken)).execute();
}
/**
* 通用的post形式的取消授权方法
*
* @param authToken token封装
* @return HttpResponse
*/
protected HttpResponse doGetRevoke(AuthToken authToken) {
return HttpRequest.get(revokeUrl(authToken)).execute();
}
}
......@@ -6,9 +6,11 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthDingTalkErrorCode;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.GlobalAuthUtil;
import me.zhyd.oauth.utils.UrlBuilder;
......@@ -19,7 +21,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthDingTalkRequest extends BaseAuthRequest {
public class AuthDingTalkRequest extends AuthDefaultRequest {
public AuthDingTalkRequest(AuthConfig config) {
super(config, AuthSource.DINGTALK);
......@@ -33,32 +35,26 @@ public class AuthDingTalkRequest extends BaseAuthRequest {
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String code = authToken.getAccessCode();
// 根据timestamp, appSecret计算签名值
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 = JSON.parseObject(userInfo);
AuthDingTalkErrorCode errorCode = AuthDingTalkErrorCode.getErrorCode(object.getIntValue("errcode"));
if (AuthDingTalkErrorCode.EC0 != errorCode) {
throw new AuthException(errorCode.getDesc());
HttpResponse response = HttpRequest.post(userInfoUrl(authToken)).body(param.toJSONString()).execute();
JSONObject object = JSON.parseObject(response.body());
if (object.getIntValue("errcode") != 0) {
throw new AuthException(object.getString("errmsg"));
}
object = object.getJSONObject("user_info");
AuthToken token = AuthToken.builder()
.openId(object.getString("openid"))
.unionId(object.getString("unionid"))
.build();
.openId(object.getString("openid"))
.unionId(object.getString("unionid"))
.build();
return AuthUser.builder()
.uuid(object.getString("unionid"))
.nickname(object.getString("nick"))
.username(object.getString("nick"))
.gender(AuthUserGender.UNKNOW)
.source(AuthSource.DINGTALK)
.token(token)
.build();
.uuid(object.getString("unionid"))
.nickname(object.getString("nick"))
.username(object.getString("nick"))
.gender(AuthUserGender.UNKNOWN)
.source(AuthSource.DINGTALK)
.token(token)
.build();
}
/**
......@@ -68,6 +64,31 @@ public class AuthDingTalkRequest extends BaseAuthRequest {
*/
@Override
public String authorize() {
return UrlBuilder.getDingTalkQrConnectUrl(config.getClientId(), config.getRedirectUri(), config.getState());
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("appid", config.getClientId())
.queryParam("scope", "snsapi_login")
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
// 根据timestamp, appSecret计算签名值
String timestamp = System.currentTimeMillis() + "";
String urlEncodeSignature = GlobalAuthUtil.generateDingTalkSignature(config.getClientSecret(), timestamp);
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("signature", urlEncodeSignature)
.queryParam("timestamp", timestamp)
.queryParam("accessKey", config.getClientId())
.build();
}
}
......@@ -5,6 +5,7 @@ import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.utils.UrlBuilder;
......@@ -17,7 +18,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthDouyinRequest extends BaseAuthRequest {
public class AuthDouyinRequest extends AuthDefaultRequest {
public AuthDouyinRequest(AuthConfig config) {
super(config, AuthSource.DOUYIN);
......@@ -25,65 +26,46 @@ public class AuthDouyinRequest extends BaseAuthRequest {
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getDouyinAccessTokenUrl(config.getClientId(), config.getClientSecret(), authCallback.getCode());
return this.getToken(accessTokenUrl);
return this.getToken(accessTokenUrl(authCallback.getCode()));
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
String openId = authToken.getOpenId();
String url = UrlBuilder.getDouyinUserInfoUrl(accessToken, openId);
HttpResponse response = HttpRequest.get(url).execute();
JSONObject object = JSONObject.parseObject(response.body());
JSONObject userInfoObject = this.checkResponse(object);
HttpResponse response = doGetUserInfo(authToken);
JSONObject userInfoObject = JSONObject.parseObject(response.body());
this.checkResponse(userInfoObject);
return AuthUser.builder()
.uuid(userInfoObject.getString("union_id"))
.username(userInfoObject.getString("nickname"))
.nickname(userInfoObject.getString("nickname"))
.avatar(userInfoObject.getString("avatar"))
.remark(userInfoObject.getString("description"))
.gender(AuthUserGender.UNKNOW)
.token(authToken)
.source(AuthSource.DOUYIN)
.build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.getDouyinAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
.uuid(userInfoObject.getString("union_id"))
.username(userInfoObject.getString("nickname"))
.nickname(userInfoObject.getString("nickname"))
.avatar(userInfoObject.getString("avatar"))
.remark(userInfoObject.getString("description"))
.gender(AuthUserGender.UNKNOWN)
.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();
.code(AuthResponseStatus.SUCCESS.getCode())
.data(getToken(refreshTokenUrl(oldToken.getRefreshToken())))
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
* @return 实际请求数据的json对象
*/
private JSONObject checkResponse(JSONObject object) {
private void 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;
}
/**
......@@ -96,14 +78,74 @@ public class AuthDouyinRequest extends BaseAuthRequest {
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
String accessTokenStr = response.body();
JSONObject object = JSONObject.parseObject(accessTokenStr);
JSONObject accessTokenObject = this.checkResponse(object);
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();
.accessToken(object.getString("access_token"))
.openId(object.getString("open_id"))
.expireIn(object.getIntValue("expires_in"))
.refreshToken(object.getString("refresh_token"))
.scope(object.getString("scope"))
.build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_key", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.queryParam("scope", "user_info")
.build();
}
/**
* 返回获取accessToken的url
*
* @param code oauth的授权码
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("client_key", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("grant_type", "authorization_code")
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken oauth返回的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("open_id", authToken.getOpenId())
.build();
}
/**
* 返回获取accessToken的url
*
* @param refreshToken oauth返回的refreshtoken
* @return 返回获取accessToken的url
*/
@Override
protected String refreshTokenUrl(String refreshToken) {
return UrlBuilder.fromBaseUrl(source.refresh())
.queryParam("client_key", config.getClientId())
.queryParam("refresh_token", refreshToken)
.queryParam("grant_type", "refresh_token")
.build();
}
}
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.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.UrlBuilder;
/**
......@@ -19,7 +18,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthFacebookRequest extends BaseAuthRequest {
public class AuthFacebookRequest extends AuthDefaultRequest {
public AuthFacebookRequest(AuthConfig config) {
super(config, AuthSource.FACEBOOK);
......@@ -27,31 +26,36 @@ public class AuthFacebookRequest extends BaseAuthRequest {
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getFacebookAccessTokenUrl(config.getClientId(), config.getClientSecret(),
authCallback.getCode(), config.getRedirectUri());
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
if (accessTokenObject.containsKey("error")) {
throw new AuthException(accessTokenObject.getJSONObject("error").getString("message"));
}
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.tokenType(accessTokenObject.getString("token_type"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.tokenType(accessTokenObject.getString("token_type"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getFacebookUserInfoUrl(accessToken)).execute();
HttpResponse response = doGetUserInfo(authToken);
String userInfo = response.body();
JSONObject object = JSONObject.parseObject(userInfo);
if (object.containsKey("error")) {
throw new AuthException(object.getJSONObject("error").getString("message"));
}
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("name"))
.nickname(object.getString("name"))
.avatar(getUserPicture(object))
.location(object.getString("locale"))
.email(object.getString("email"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(AuthSource.FACEBOOK)
.build();
}
private String getUserPicture(JSONObject object) {
String picture = null;
if (object.containsKey("picture")) {
JSONObject pictureObj = object.getJSONObject("picture");
......@@ -60,26 +64,31 @@ public class AuthFacebookRequest extends BaseAuthRequest {
picture = pictureObj.getString("url");
}
}
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("name"))
.nickname(object.getString("name"))
.avatar(picture)
.location(object.getString("locale"))
.email(object.getString("email"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(AuthSource.FACEBOOK)
.build();
return picture;
}
/**
* 返回认证url,可自行跳转页面
* 返回获取userInfo的url
*
* @return 返回授权地址
* @param authToken 用户token
* @return 返回获取userInfo的url
*/
@Override
public String authorize() {
return UrlBuilder.getFacebookAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("fields", "id,name,birthday,gender,hometown,email,devices,picture.width(400)")
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getJSONObject("error").getString("message"));
}
}
}
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.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* Gitee登录
......@@ -19,7 +17,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthGiteeRequest extends BaseAuthRequest {
public class AuthGiteeRequest extends AuthDefaultRequest {
public AuthGiteeRequest(AuthConfig config) {
super(config, AuthSource.GITEE);
......@@ -27,45 +25,48 @@ public class AuthGiteeRequest extends BaseAuthRequest {
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getGiteeAccessTokenUrl(config.getClientId(), config.getClientSecret(),
authCallback.getCode(), config.getRedirectUri());
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
if (accessTokenObject.containsKey("error")) {
throw new AuthException("Unable to get token from gitee using code [" + authCallback.getCode() + "]");
}
return AuthToken.builder().accessToken(accessTokenObject.getString("access_token")).build();
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.scope(accessTokenObject.getString("scope"))
.tokenType(accessTokenObject.getString("token_type"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getGiteeUserInfoUrl(accessToken)).execute();
HttpResponse response = doGetUserInfo(authToken);
String userInfo = response.body();
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("login"))
.avatar(object.getString("avatar_url"))
.blog(object.getString("blog"))
.nickname(object.getString("name"))
.company(object.getString("company"))
.location(object.getString("address"))
.email(object.getString("email"))
.remark(object.getString("bio"))
.gender(AuthUserGender.UNKNOW)
.token(authToken)
.source(AuthSource.GITEE)
.build();
.uuid(object.getString("id"))
.username(object.getString("login"))
.avatar(object.getString("avatar_url"))
.blog(object.getString("blog"))
.nickname(object.getString("name"))
.company(object.getString("company"))
.location(object.getString("address"))
.email(object.getString("email"))
.remark(object.getString("bio"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(AuthSource.GITEE)
.build();
}
/**
* 返回认证url,可自行跳转页面
* 检查响应内容是否正确
*
* @return 返回授权地址
* @param object 请求响应内容
*/
@Override
public String authorize() {
return UrlBuilder.getGiteeAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
}
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.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.GlobalAuthUtil;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.Map;
/**
* Github登录
......@@ -22,7 +17,7 @@ import java.util.Map;
* @version 1.0
* @since 1.8
*/
public class AuthGithubRequest extends BaseAuthRequest {
public class AuthGithubRequest extends AuthDefaultRequest {
public AuthGithubRequest(AuthConfig config) {
super(config, AuthSource.GITHUB);
......@@ -30,44 +25,45 @@ public class AuthGithubRequest extends BaseAuthRequest {
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getGithubAccessTokenUrl(config.getClientId(), config.getClientSecret(), authCallback.getCode(), config.getRedirectUri());
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
Map<String, String> res = GlobalAuthUtil.parseStringToMap(response.body());
if (res.containsKey("error")) {
throw new AuthException(res.get("error") + ":" + res.get("error_description"));
}
return AuthToken.builder().accessToken(res.get("access_token")).build();
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.scope(accessTokenObject.getString("scope"))
.tokenType(accessTokenObject.getString("token_type"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getGithubUserInfoUrl(accessToken)).execute();
String userInfo = response.body();
JSONObject object = JSONObject.parseObject(userInfo);
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("login"))
.avatar(object.getString("avatar_url"))
.blog(object.getString("blog"))
.nickname(object.getString("name"))
.company(object.getString("company"))
.location(object.getString("location"))
.email(object.getString("email"))
.remark(object.getString("bio"))
.gender(AuthUserGender.UNKNOW)
.token(authToken)
.source(AuthSource.GITHUB)
.build();
.uuid(object.getString("id"))
.username(object.getString("login"))
.avatar(object.getString("avatar_url"))
.blog(object.getString("blog"))
.nickname(object.getString("name"))
.company(object.getString("company"))
.location(object.getString("location"))
.email(object.getString("email"))
.remark(object.getString("bio"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(AuthSource.GITHUB)
.build();
}
/**
* 返回认证url,可自行跳转页面
* 检查响应内容是否正确
*
* @return 返回授权地址
* @param object 请求响应内容
*/
@Override
public String authorize() {
return UrlBuilder.getGithubAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
}
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.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.UrlBuilder;
/**
......@@ -19,7 +18,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.3
* @since 1.3
*/
public class AuthGoogleRequest extends BaseAuthRequest {
public class AuthGoogleRequest extends AuthDefaultRequest {
public AuthGoogleRequest(AuthConfig config) {
super(config, AuthSource.GOOGLE);
......@@ -27,42 +26,35 @@ public class AuthGoogleRequest extends BaseAuthRequest {
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getGoogleAccessTokenUrl(config.getClientId(), config.getClientSecret(), authCallback.getCode(), config
.getRedirectUri());
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
if (accessTokenObject.containsKey("error") || accessTokenObject.containsKey("error_description")) {
throw new AuthException("get google access_token has error:[" + accessTokenObject.getString("error") + "], error_description:[" + accessTokenObject
.getString("error_description") + "]");
}
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.scope(accessTokenObject.getString("scope"))
.tokenType(accessTokenObject.getString("token_type"))
.idToken(accessTokenObject.getString("id_token"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.scope(accessTokenObject.getString("scope"))
.tokenType(accessTokenObject.getString("token_type"))
.idToken(accessTokenObject.getString("id_token"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getIdToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getGoogleUserInfoUrl(accessToken)).execute();
HttpResponse response = doGetUserInfo(authToken);
String userInfo = response.body();
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("sub"))
.username(object.getString("name"))
.avatar(object.getString("picture"))
.nickname(object.getString("name"))
.location(object.getString("locale"))
.email(object.getString("email"))
.gender(AuthUserGender.UNKNOW)
.token(authToken)
.source(AuthSource.GOOGLE)
.build();
.uuid(object.getString("sub"))
.username(object.getString("name"))
.avatar(object.getString("picture"))
.nickname(object.getString("name"))
.location(object.getString("locale"))
.email(object.getString("email"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(AuthSource.GOOGLE)
.build();
}
/**
......@@ -72,6 +64,34 @@ public class AuthGoogleRequest extends BaseAuthRequest {
*/
@Override
public String authorize() {
return UrlBuilder.getGoogleAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("scope", "openid%20email%20profile")
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo()).queryParam("id_token", authToken.getAccessToken()).build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error") || object.containsKey("error_description")) {
throw new AuthException(object.getString("error_description"));
}
}
}
......@@ -6,6 +6,7 @@ import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.utils.StringUtils;
......@@ -19,7 +20,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthLinkedinRequest extends BaseAuthRequest {
public class AuthLinkedinRequest extends AuthDefaultRequest {
public AuthLinkedinRequest(AuthConfig config) {
super(config, AuthSource.LINKEDIN);
......@@ -27,24 +28,47 @@ public class AuthLinkedinRequest extends BaseAuthRequest {
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getLinkedinAccessTokenUrl(config.getClientId(), config.getClientSecret(), authCallback.getCode(), config
.getRedirectUri());
return this.getToken(accessTokenUrl);
return this.getToken(accessTokenUrl(authCallback.getCode()));
}
@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();
HttpResponse response = HttpRequest.get(userInfoUrl(authToken))
.header("Host", "api.linkedin.com")
.header("Connection", "Keep-Alive")
.header("Authorization", "Bearer " + accessToken)
.execute();
JSONObject userInfoObject = JSONObject.parseObject(response.body());
this.checkResponse(userInfoObject);
// 组装用户名
String userName = getUserName(userInfoObject);
// 获取用户头像
String avatar = this.getAvatar(userInfoObject);
// 获取用户邮箱地址
String email = this.getUserEmail(accessToken);
return AuthUser.builder()
.uuid(userInfoObject.getString("id"))
.username(userName)
.nickname(userName)
.avatar(avatar)
.email(email)
.token(authToken)
.gender(AuthUserGender.UNKNOWN)
.source(AuthSource.LINKEDIN)
.build();
}
/**
* 获取用户的真实名
*
* @param userInfoObject 用户json对象
* @return 用户名
*/
private String getUserName(JSONObject userInfoObject) {
String firstName, lastName;
// 获取firstName
if (userInfoObject.containsKey("localizedFirstName")) {
......@@ -58,58 +82,49 @@ public class AuthLinkedinRequest extends BaseAuthRequest {
} else {
lastName = getUserName(userInfoObject, "lastName");
}
String userName = firstName + " " + lastName;
return firstName + " " + lastName;
}
// 获取用户头像
/**
* 获取用户的头像
*
* @param userInfoObject 用户json对象
* @return 用户的头像地址
*/
private String getAvatar(JSONObject userInfoObject) {
String avatar = null;
JSONObject profilePictureObject = userInfoObject.getJSONObject("profilePicture");
if (profilePictureObject.containsKey("displayImage~")) {
JSONArray displayImageElements = profilePictureObject.getJSONObject("displayImage~")
.getJSONArray("elements");
.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)
.gender(AuthUserGender.UNKNOW)
.source(AuthSource.LINKEDIN)
.build();
return avatar;
}
/**
* 返回认证url,可自行跳转页面
* 获取用户的email
*
* @return 返回授权地址
* @param accessToken 用户授权后返回的token
* @return 用户的邮箱地址
*/
@Override
public String authorize() {
return UrlBuilder.getLinkedinAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
}
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());
.header("Host", "api.linkedin.com")
.header("Connection", "Keep-Alive")
.header("Authorization", "Bearer " + accessToken)
.execute();
JSONObject emailObj = JSONObject.parseObject(emailResponse.body());
this.checkResponse(emailObj);
if (emailObj.containsKey("elements")) {
email = emailObj.getJSONArray("elements")
.getJSONObject(0)
.getJSONObject("handle~")
.getString("emailAddress");
.getJSONObject(0)
.getJSONObject("handle~")
.getString("emailAddress");
}
return email;
}
......@@ -125,20 +140,25 @@ public class AuthLinkedinRequest extends BaseAuthRequest {
@Override
public AuthResponse refresh(AuthToken oldToken) {
if (StringUtils.isEmpty(oldToken.getRefreshToken())) {
throw new AuthException(ResponseStatus.UNSUPPORTED);
String refreshToken = oldToken.getRefreshToken();
if (StringUtils.isEmpty(refreshToken)) {
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
String refreshTokenUrl = UrlBuilder.getLinkedinRefreshUrl(config.getClientId(), config.getClientSecret(), oldToken
.getRefreshToken());
String refreshTokenUrl = refreshTokenUrl(refreshToken);
return AuthResponse.builder()
.code(ResponseStatus.SUCCESS.getCode())
.data(this.getToken(refreshTokenUrl))
.build();
.code(AuthResponseStatus.SUCCESS.getCode())
.data(this.getToken(refreshTokenUrl))
.build();
}
private void checkResponse(JSONObject userInfoObject) {
if (userInfoObject.containsKey("error")) {
throw new AuthException(userInfoObject.getString("error_description"));
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
......@@ -150,18 +170,47 @@ public class AuthLinkedinRequest extends BaseAuthRequest {
*/
private AuthToken getToken(String accessTokenUrl) {
HttpResponse response = HttpRequest.post(accessTokenUrl)
.header("Host", "www.linkedin.com")
.header("Content-Type", "application/x-www-form-urlencoded")
.execute();
.header("Host", "www.linkedin.com")
.contentType("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();
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.queryParam("scope", "r_liteprofile%20r_emailaddress%20w_member_social")
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("projection", "(id,firstName,lastName,profilePicture(displayImage~:playableStreams))")
.build();
}
}
......@@ -7,6 +7,7 @@ import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.utils.UrlBuilder;
......@@ -21,7 +22,7 @@ import java.text.MessageFormat;
* @since 1.5
*/
@Slf4j
public class AuthMiRequest extends BaseAuthRequest {
public class AuthMiRequest extends AuthDefaultRequest {
private static final String PREFIX = "&&&START&&&";
public AuthMiRequest(AuthConfig config) {
......@@ -30,8 +31,7 @@ public class AuthMiRequest extends BaseAuthRequest {
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getMiAccessTokenUrl(config.getClientId(), config.getClientSecret(), config.getRedirectUri(), authCallback.getCode());
return getToken(accessTokenUrl);
return getToken(accessTokenUrl(authCallback.getCode()));
}
private AuthToken getToken(String accessTokenUrl) {
......@@ -44,22 +44,21 @@ public class AuthMiRequest extends BaseAuthRequest {
}
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.scope(accessTokenObject.getString("scope"))
.tokenType(accessTokenObject.getString("token_type"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.openId(accessTokenObject.getString("openId"))
.macAlgorithm(accessTokenObject.getString("mac_algorithm"))
.macKey(accessTokenObject.getString("mac_key"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.scope(accessTokenObject.getString("scope"))
.tokenType(accessTokenObject.getString("token_type"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.openId(accessTokenObject.getString("openId"))
.macAlgorithm(accessTokenObject.getString("mac_algorithm"))
.macKey(accessTokenObject.getString("mac_key"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
// 获取用户信息
HttpResponse userResponse = HttpRequest.get(UrlBuilder.getMiUserInfoUrl(config.getClientId(), authToken.getAccessToken()))
.execute();
HttpResponse userResponse = doGetUserInfo(authToken);
JSONObject userProfile = JSONObject.parseObject(userResponse.body());
if ("error".equalsIgnoreCase(userProfile.getString("result"))) {
......@@ -69,32 +68,46 @@ public class AuthMiRequest extends BaseAuthRequest {
JSONObject user = userProfile.getJSONObject("data");
AuthUser authUser = AuthUser.builder()
.uuid(authToken.getOpenId())
.username(user.getString("miliaoNick"))
.nickname(user.getString("miliaoNick"))
.avatar(user.getString("miliaoIcon"))
.email(user.getString("mail"))
.gender(AuthUserGender.UNKNOW)
.token(authToken)
.source(AuthSource.MI)
.build();
.uuid(authToken.getOpenId())
.username(user.getString("miliaoNick"))
.nickname(user.getString("miliaoNick"))
.avatar(user.getString("miliaoIcon"))
.email(user.getString("mail"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(AuthSource.MI)
.build();
// 获取用户邮箱手机号等信息
String emailPhoneUrl = MessageFormat.format("{0}?clientId={1}&token={2}", "https://open.account.xiaomi.com/user/phoneAndEmail", config
.getClientId(), authToken.getAccessToken());
.getClientId(), authToken.getAccessToken());
HttpResponse emailResponse = HttpRequest.get(emailPhoneUrl).execute();
JSONObject userEmailPhone = JSONObject.parseObject(emailResponse.body());
if (!"error".equalsIgnoreCase(userEmailPhone.getString("result"))) {
JSONObject emailPhone = userEmailPhone.getJSONObject("data");
authUser.setEmail(emailPhone.getString("email"));
}else {
} else {
log.warn("小米开发平台暂时不对外开放用户手机及邮箱信息的获取");
}
return authUser;
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
@Override
public AuthResponse refresh(AuthToken authToken) {
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(getToken(refreshTokenUrl(authToken.getRefreshToken())))
.build();
}
/**
* 返回认证url,可自行跳转页面
*
......@@ -102,20 +115,27 @@ public class AuthMiRequest extends BaseAuthRequest {
*/
@Override
public String authorize() {
return UrlBuilder.getMiAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.queryParam("scope", "user/profile%20user/openIdV2%20user/phoneAndEmail")
.queryParam("skip_confirm", "false")
.build();
}
/**
* 刷新access token (续期)
* 返回获取userInfo的url
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
* @param authToken
* @return 返回获取userInfo的url
*/
@Override
public AuthResponse refresh(AuthToken authToken) {
String miRefreshUrl = UrlBuilder.getMiRefreshUrl(config.getClientId(), config.getClientSecret(), config.getRedirectUri(), authToken
.getRefreshToken());
return AuthResponse.builder().code(ResponseStatus.SUCCESS.getCode()).data(getToken(miRefreshUrl)).build();
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("clientId", config.getClientId())
.queryParam("token", authToken.getAccessToken())
.build();
}
}
......@@ -2,16 +2,15 @@ package me.zhyd.oauth.request;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.HashMap;
import java.util.Map;
import static me.zhyd.oauth.utils.GlobalAuthUtil.parseQueryToMap;
/**
* 微软登录
......@@ -20,17 +19,14 @@ import java.util.Map;
* @version 1.5
* @since 1.5
*/
public class AuthMicrosoftRequest extends BaseAuthRequest {
public class AuthMicrosoftRequest extends AuthDefaultRequest {
public AuthMicrosoftRequest(AuthConfig config) {
super(config, AuthSource.MICROSOFT);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getMicrosoftAccessTokenUrl(config.getClientId(), config.getClientSecret(), config
.getRedirectUri(), authCallback.getCode());
return getToken(accessTokenUrl);
return getToken(accessTokenUrl(authCallback.getCode()));
}
/**
......@@ -40,30 +36,33 @@ public class AuthMicrosoftRequest extends BaseAuthRequest {
* @return token对象
*/
private AuthToken getToken(String accessTokenUrl) {
Map<String, Object> paramMap = new HashMap<>(6);
HttpUtil.decodeParamMap(accessTokenUrl, "UTF-8").forEach(paramMap::put);
HttpResponse response = HttpRequest.post(accessTokenUrl)
.header("Host", "https://login.microsoftonline.com")
.header("Content-Type", "application/x-www-form-urlencoded")
.form(paramMap)
.execute();
.header("Host", "https://login.microsoftonline.com")
.contentType("application/x-www-form-urlencoded")
.form(parseQueryToMap(accessTokenUrl))
.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"))
.scope(accessTokenObject.getString("scope"))
.tokenType(accessTokenObject.getString("token_type"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.scope(accessTokenObject.getString("scope"))
.tokenType(accessTokenObject.getString("token_type"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
private void checkResponse(JSONObject response) {
if (response.containsKey("error")) {
throw new AuthException(response.getString("error_description"));
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
......@@ -72,21 +71,34 @@ public class AuthMicrosoftRequest extends BaseAuthRequest {
String token = authToken.getAccessToken();
String tokenType = authToken.getTokenType();
String jwt = tokenType + " " + token;
HttpResponse response = HttpRequest.get(UrlBuilder.getMicrosoftUserInfoUrl())
.header("Authorization", jwt)
.execute();
HttpResponse response = HttpRequest.get(userInfoUrl(authToken)).header("Authorization", jwt).execute();
String userInfo = response.body();
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("userPrincipalName"))
.nickname(object.getString("displayName"))
.location(object.getString("officeLocation"))
.email(object.getString("mail"))
.gender(AuthUserGender.UNKNOW)
.token(authToken)
.source(AuthSource.MICROSOFT)
.build();
.uuid(object.getString("id"))
.username(object.getString("userPrincipalName"))
.nickname(object.getString("displayName"))
.location(object.getString("officeLocation"))
.email(object.getString("mail"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(AuthSource.MICROSOFT)
.build();
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
@Override
public AuthResponse refresh(AuthToken authToken) {
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(getToken(refreshTokenUrl(authToken.getRefreshToken())))
.build();
}
/**
......@@ -96,20 +108,60 @@ public class AuthMicrosoftRequest extends BaseAuthRequest {
*/
@Override
public String authorize() {
return UrlBuilder.getMicrosoftAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("response_mode", "query")
.queryParam("scope", "offline_access%20user.read%20mail.read")
.queryParam("state", getRealState(config.getState()))
.build();
}
/**
* 刷新access token (续期)
* 返回获取accessToken的url
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
* @param code
* @return 返回获取accessToken的url
*/
@Override
public AuthResponse refresh(AuthToken authToken) {
String refreshTokenUrl = UrlBuilder.getMicrosoftRefreshUrl(config.getClientId(), config.getClientSecret(), config
.getRedirectUri(), authToken.getRefreshToken());
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("grant_type", "authorization_code")
.queryParam("scope", "user.read%20mail.read")
.queryParam("redirect_uri", config.getRedirectUri())
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo()).build();
}
return AuthResponse.builder().code(ResponseStatus.SUCCESS.getCode()).data(getToken(refreshTokenUrl)).build();
/**
* 返回获取accessToken的url
*
* @param refreshToken
* @return 返回获取accessToken的url
*/
@Override
protected String refreshTokenUrl(String refreshToken) {
return UrlBuilder.fromBaseUrl(source.refresh())
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("refresh_token", refreshToken)
.queryParam("grant_type", "refresh_token")
.queryParam("scope", "user.read%20mail.read")
.queryParam("redirect_uri", config.getRedirectUri())
.build();
}
}
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.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.UrlBuilder;
/**
......@@ -19,7 +18,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthOschinaRequest extends BaseAuthRequest {
public class AuthOschinaRequest extends AuthDefaultRequest {
public AuthOschinaRequest(AuthConfig config) {
super(config, AuthSource.OSCHINA);
......@@ -27,50 +26,76 @@ public class AuthOschinaRequest extends BaseAuthRequest {
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getOschinaAccessTokenUrl(config.getClientId(), config.getClientSecret(),
authCallback.getCode(), config.getRedirectUri());
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
if (accessTokenObject.containsKey("error")) {
throw new AuthException("Unable to get token from oschina using code [" + authCallback.getCode() + "]");
}
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.uid(accessTokenObject.getString("uid"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.uid(accessTokenObject.getString("uid"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getOschinaUserInfoUrl(accessToken)).execute();
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("name"))
.nickname(object.getString("name"))
.avatar(object.getString("avatar"))
.blog(object.getString("url"))
.location(object.getString("location"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.email(object.getString("email"))
.token(authToken)
.source(AuthSource.OSCHINA)
.build();
.uuid(object.getString("id"))
.username(object.getString("name"))
.nickname(object.getString("name"))
.avatar(object.getString("avatar"))
.blog(object.getString("url"))
.location(object.getString("location"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.email(object.getString("email"))
.token(authToken)
.source(AuthSource.OSCHINA)
.build();
}
/**
* 返回获取accessToken的url
*
* @param code
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("grant_type", "authorization_code")
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("dataType", "json")
.build();
}
/**
* 返回认证url,可自行跳转页面
* 返回获取userInfo的url
*
* @return 返回授权地址
* @param authToken
* @return 返回获取userInfo的url
*/
@Override
public String authorize() {
return UrlBuilder.getOschinaAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("dataType", "json")
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
}
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.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.Objects;
import static me.zhyd.oauth.config.AuthSource.PINTEREST;
/**
* Pinterest登录
*
* @author hongwei.peng (pengisgood(at)gmail(dot)com)
* @version 1.9.0
* @since 1.9.0
*/
public class AuthPinterestRequest extends AuthDefaultRequest {
private static final String FAILURE = "failure";
public AuthPinterestRequest(AuthConfig config) {
super(config, PINTEREST);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.tokenType(accessTokenObject.getString("token_type"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String userinfoUrl = UrlBuilder.fromBaseUrl(userInfoUrl(authToken))
.queryParam("fields", "id,username,first_name,last_name,bio,image")
.build();
HttpResponse response = HttpRequest.post(userinfoUrl).execute();
JSONObject object = JSONObject.parseObject(response.body());
this.checkResponse(object);
JSONObject userObj = object.getJSONObject("data");
return AuthUser.builder()
.uuid(userObj.getString("id"))
.avatar(getAvatarUrl(userObj))
.username(userObj.getString("username"))
.nickname(userObj.getString("first_name") + " " + userObj.getString("last_name"))
.gender(AuthUserGender.UNKNOWN)
.remark(userObj.getString("bio"))
.token(authToken)
.source(PINTEREST)
.build();
}
private String getAvatarUrl(JSONObject userObj) {
// image is a map data structure
JSONObject jsonObject = userObj.getJSONObject("image");
if (Objects.isNull(jsonObject)) {
return null;
}
return jsonObject.getJSONObject("60x60").getString("url");
}
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.queryParam("scope", "read_public")
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (!object.containsKey("status") && FAILURE.equals(object.getString("status"))) {
throw new AuthException(object.getString("message"));
}
}
}
......@@ -6,11 +6,9 @@ import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.utils.GlobalAuthUtil;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
......@@ -25,33 +23,30 @@ import java.util.Map;
* @version 1.0
* @since 1.8
*/
public class AuthQqRequest extends BaseAuthRequest {
public class AuthQqRequest extends AuthDefaultRequest {
public AuthQqRequest(AuthConfig config) {
super(config, AuthSource.QQ);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getQqAccessTokenUrl(config.getClientId(), config.getClientSecret(),
authCallback.getCode(), config.getRedirectUri());
HttpResponse response = HttpRequest.get(accessTokenUrl).execute();
Map<String, String> accessTokenObject = GlobalAuthUtil.parseStringToMap(response.body());
if (!accessTokenObject.containsKey("access_token")) {
throw new AuthException("Unable to get token from qq using code [" + authCallback.getCode() + "]");
}
return AuthToken.builder()
.accessToken(accessTokenObject.get("access_token"))
.expireIn(Integer.valueOf(accessTokenObject.get("expires_in")))
.refreshToken(accessTokenObject.get("refresh_token"))
.build();
HttpResponse response = doGetAuthorizationCode(authCallback.getCode());
return getAuthToken(response);
}
@Override
public AuthResponse refresh(AuthToken authToken) {
HttpResponse response = HttpRequest.get(refreshTokenUrl(authToken.getRefreshToken())).execute();
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(getAuthToken(response))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
String openId = this.getOpenId(authToken);
HttpResponse response = HttpRequest.get(UrlBuilder.getQqUserInfoUrl(config.getClientId(), accessToken, openId))
.execute();
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
if (object.getIntValue("ret") != 0) {
throw new AuthException(object.getString("msg"));
......@@ -63,31 +58,22 @@ public class AuthQqRequest extends BaseAuthRequest {
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(location)
.uuid(openId)
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(AuthSource.QQ)
.build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.getQqAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(avatar)
.location(location)
.uuid(openId)
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(AuthSource.QQ)
.build();
}
private String getOpenId(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getQqOpenidUrl("https://graph.qq.com/oauth2.0/me", accessToken, config.isUnionId()))
.execute();
HttpResponse response = HttpRequest.get(UrlBuilder.fromBaseUrl("https://graph.qq.com/oauth2.0/me")
.queryParam("access_token", authToken.getAccessToken())
.queryParam("unionid", config.isUnionId() ? 1 : 0)
.build()).execute();
if (response.isOk()) {
String body = response.body();
String removePrefix = StrUtil.replace(body, "callback(", "");
......@@ -106,4 +92,31 @@ public class AuthQqRequest extends BaseAuthRequest {
throw new AuthException("request error");
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("oauth_consumer_key", config.getClientId())
.queryParam("openid", authToken.getOpenId())
.build();
}
private AuthToken getAuthToken(HttpResponse response) {
Map<String, String> accessTokenObject = GlobalAuthUtil.parseStringToMap(response.body());
if (!accessTokenObject.containsKey("access_token") || accessTokenObject.containsKey("code")) {
throw new AuthException(accessTokenObject.get("msg"));
}
return AuthToken.builder()
.accessToken(accessTokenObject.get("access_token"))
.expireIn(Integer.valueOf(accessTokenObject.get("expires_in")))
.refreshToken(accessTokenObject.get("refresh_token"))
.build();
}
}
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.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.Objects;
import static me.zhyd.oauth.config.AuthSource.RENREN;
import static me.zhyd.oauth.model.AuthResponseStatus.SUCCESS;
/**
* 人人登录
*
* @author hongwei.peng (pengisgood(at)gmail(dot)com)
* @version 1.8.1
* @since 1.8.1
*/
public class AuthRenrenRequest extends AuthDefaultRequest {
public AuthRenrenRequest(AuthConfig config) {
super(config, RENREN);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
return this.getToken(accessTokenUrl(authCallback.getCode()));
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse response = doGetUserInfo(authToken);
JSONObject userObj = JSONObject.parseObject(response.body()).getJSONObject("response");
return AuthUser.builder()
.uuid(userObj.getString("id"))
.avatar(getAvatarUrl(userObj))
.nickname(userObj.getString("name"))
.company(getCompany(userObj))
.gender(getGender(userObj))
.token(authToken)
.source(RENREN)
.build();
}
@Override
public AuthResponse refresh(AuthToken authToken) {
return AuthResponse.builder()
.code(SUCCESS.getCode())
.data(getToken(this.refreshTokenUrl(authToken.getRefreshToken())))
.build();
}
private AuthToken getToken(String url) {
HttpResponse response = HttpRequest.post(url).execute();
JSONObject jsonObject = JSONObject.parseObject(response.body());
if (jsonObject.containsKey("error")) {
throw new AuthException("Failed to get token from Renren: " + jsonObject);
}
return AuthToken.builder()
.tokenType(jsonObject.getString("token_type"))
.expireIn(jsonObject.getIntValue("expires_in"))
.accessToken(jsonObject.getString("access_token"))
.refreshToken(jsonObject.getString("refresh_token"))
.openId(jsonObject.getJSONObject("user").getString("id"))
.build();
}
private String getAvatarUrl(JSONObject userObj) {
JSONArray jsonArray = userObj.getJSONArray("avatar");
if (Objects.isNull(jsonArray) || jsonArray.isEmpty()) {
return null;
}
return jsonArray.getJSONObject(0).getString("url");
}
private AuthUserGender getGender(JSONObject userObj) {
JSONObject basicInformation = userObj.getJSONObject("basicInformation");
if (Objects.isNull(basicInformation)) {
return AuthUserGender.UNKNOWN;
}
return AuthUserGender.getRealGender(basicInformation.getString("sex"));
}
private String getCompany(JSONObject userObj) {
JSONArray jsonArray = userObj.getJSONArray("work");
if (Objects.isNull(jsonArray) || jsonArray.isEmpty()) {
return null;
}
return jsonArray.getJSONObject(0).getString("name");
}
/**
* 返回获取userInfo的url
*
* @param authToken
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("userId", authToken.getOpenId())
.build();
}
}
......@@ -3,6 +3,7 @@ package me.zhyd.oauth.request;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthResponseStatus;
import me.zhyd.oauth.model.AuthToken;
/**
......@@ -18,7 +19,7 @@ public interface AuthRequest {
* @return 返回授权地址
*/
default String authorize() {
throw new AuthException(ResponseStatus.NOT_IMPLEMENTED);
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
}
/**
......@@ -28,7 +29,7 @@ public interface AuthRequest {
* @return 返回登录成功后的用户信息
*/
default AuthResponse login(AuthCallback authCallback) {
throw new AuthException(ResponseStatus.NOT_IMPLEMENTED);
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
}
/**
......@@ -38,7 +39,7 @@ public interface AuthRequest {
* @return AuthResponse
*/
default AuthResponse revoke(AuthToken authToken) {
throw new AuthException(ResponseStatus.NOT_IMPLEMENTED);
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
}
/**
......@@ -48,6 +49,6 @@ public interface AuthRequest {
* @return AuthResponse
*/
default AuthResponse refresh(AuthToken authToken) {
throw new AuthException(ResponseStatus.NOT_IMPLEMENTED);
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
}
}
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.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.UrlBuilder;
import static me.zhyd.oauth.config.AuthSource.STACK_OVERFLOW;
import static me.zhyd.oauth.utils.GlobalAuthUtil.parseQueryToMap;
/**
* Stack Overflow登录
*
* @author hongwei.peng (pengisgood(at)gmail(dot)com)
* @version 1.9.0
* @since 1.9.0
*/
public class AuthStackOverflowRequest extends AuthDefaultRequest {
public AuthStackOverflowRequest(AuthConfig config) {
super(config, STACK_OVERFLOW);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = accessTokenUrl(authCallback.getCode());
HttpResponse response = HttpRequest.post(accessTokenUrl)
.contentType("application/x-www-form-urlencoded")
.form(parseQueryToMap(accessTokenUrl))
.execute();
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String userInfoUrl = UrlBuilder.fromBaseUrl(this.source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("site", "stackoverflow")
.queryParam("key", this.config.getStackOverflowKey())
.build();
HttpResponse response = HttpRequest.get(userInfoUrl).execute();
JSONObject object = JSONObject.parseObject(response.body());
this.checkResponse(object);
JSONObject userObj = object.getJSONArray("items").getJSONObject(0);
return AuthUser.builder()
.uuid(userObj.getString("user_id"))
.avatar(userObj.getString("profile_image"))
.location(userObj.getString("location"))
.nickname(userObj.getString("display_name"))
.blog(userObj.getString("website_url"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(STACK_OVERFLOW)
.build();
}
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.queryParam("scope", "read_inbox")
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
}
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;
......@@ -9,7 +8,7 @@ import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.utils.GlobalAuthUtil;
import me.zhyd.oauth.utils.UrlBuilder;
......@@ -20,7 +19,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthTaobaoRequest extends BaseAuthRequest {
public class AuthTaobaoRequest extends AuthDefaultRequest {
public AuthTaobaoRequest(AuthConfig config) {
super(config, AuthSource.TAOBAO);
......@@ -33,12 +32,10 @@ public class AuthTaobaoRequest extends BaseAuthRequest {
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessCode = authToken.getAccessCode();
HttpResponse response = HttpRequest.post(UrlBuilder.getTaobaoAccessTokenUrl(this.config.getClientId(), this.config
.getClientSecret(), accessCode, this.config.getRedirectUri())).execute();
HttpResponse response = doPostAuthorizationCode(authToken.getAccessCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
if (accessTokenObject.containsKey("error")) {
throw new AuthException(ResponseStatus.FAILURE + ":" + accessTokenObject.getString("error_description"));
throw new AuthException(accessTokenObject.getString("error_description"));
}
authToken.setAccessToken(accessTokenObject.getString("access_token"));
authToken.setRefreshToken(accessTokenObject.getString("refresh_token"));
......@@ -48,13 +45,13 @@ public class AuthTaobaoRequest extends BaseAuthRequest {
String nick = GlobalAuthUtil.urlDecode(accessTokenObject.getString("taobao_user_nick"));
return AuthUser.builder()
.uuid(accessTokenObject.getString("taobao_user_id"))
.username(nick)
.nickname(nick)
.gender(AuthUserGender.UNKNOW)
.token(authToken)
.source(AuthSource.TAOBAO)
.build();
.uuid(accessTokenObject.getString("taobao_user_id"))
.username(nick)
.nickname(nick)
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(AuthSource.TAOBAO)
.build();
}
/**
......@@ -64,6 +61,12 @@ public class AuthTaobaoRequest extends BaseAuthRequest {
*/
@Override
public String authorize() {
return UrlBuilder.getTaobaoAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.queryParam("view", "web")
.build();
}
}
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.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
/**
* Teambition授权登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
public class AuthTeambitionRequest extends AuthDefaultRequest {
public AuthTeambitionRequest(AuthConfig config) {
super(config, AuthSource.TEAMBITION);
}
/**
* @param authCallback 回调返回的参数
* @return 所有信息
*/
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
HttpResponse response = HttpRequest.post(source.accessToken())
.form("client_id", config.getClientId())
.form("client_secret", config.getClientSecret())
.form("code", authCallback.getCode())
.form("grant_type", "code")
.execute();
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(source.userInfo())
.header("Authorization", "OAuth2 " + accessToken)
.execute();
JSONObject object = JSONObject.parseObject(response.body());
this.checkResponse(object);
authToken.setUid(object.getString("_id"));
return AuthUser.builder()
.uuid(object.getString("_id"))
.username(object.getString("name"))
.nickname(object.getString("name"))
.avatar(object.getString("avatarUrl"))
.blog(object.getString("website"))
.location(object.getString("location"))
.email(object.getString("email"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(AuthSource.TEAMBITION)
.build();
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
String uid = oldToken.getUid();
String refreshToken = oldToken.getRefreshToken();
HttpResponse response = HttpRequest.post(source.refresh())
.form("_userId", uid)
.form("refresh_token", refreshToken)
.execute();
JSONObject refreshTokenObject = JSONObject.parseObject(response.body());
this.checkResponse(refreshTokenObject);
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(refreshTokenObject.getString("access_token"))
.refreshToken(refreshTokenObject.getString("refresh_token"))
.build())
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if ((object.containsKey("message") && object.containsKey("name"))) {
throw new AuthException(object.getString("name") + ", " + object.getString("message"));
}
}
}
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.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.UrlBuilder;
/**
......@@ -19,7 +18,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthTencentCloudRequest extends BaseAuthRequest {
public class AuthTencentCloudRequest extends AuthDefaultRequest {
public AuthTencentCloudRequest(AuthConfig config) {
super(config, AuthSource.TENCENT_CLOUD);
......@@ -27,42 +26,48 @@ public class AuthTencentCloudRequest extends BaseAuthRequest {
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getTencentCloudAccessTokenUrl(config.getClientId(), config.getClientSecret(), authCallback.getCode());
HttpResponse response = HttpRequest.get(accessTokenUrl).execute();
HttpResponse response = doGetAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
if (accessTokenObject.getIntValue("code") != 0) {
throw new AuthException("Unable to get token from tencent cloud using code [" + authCallback.getCode() + "]: " + accessTokenObject.get("msg"));
}
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpResponse response = HttpRequest.get(UrlBuilder.getTencentCloudUserInfoUrl(accessToken)).execute();
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
this.checkResponse(object);
object = object.getJSONObject("data");
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("name"))
.avatar("https://dev.tencent.com/" + object.getString("avatar"))
.blog("https://dev.tencent.com/" + object.getString("path"))
.nickname(object.getString("name"))
.company(object.getString("company"))
.location(object.getString("location"))
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.email(object.getString("email"))
.remark(object.getString("slogan"))
.token(authToken)
.source(AuthSource.TENCENT_CLOUD)
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.getIntValue("code") != 0) {
throw new AuthException(object.getString("msg"));
}
object = object.getJSONObject("data");
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("name"))
.avatar("https://dev.tencent.com/" + object.getString("avatar"))
.blog("https://dev.tencent.com/" + object.getString("path"))
.nickname(object.getString("name"))
.company(object.getString("company"))
.location(object.getString("location"))
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.email(object.getString("email"))
.remark(object.getString("slogan"))
.token(authToken)
.source(AuthSource.TENCENT_CLOUD)
.build();
}
/**
......@@ -72,6 +77,12 @@ public class AuthTencentCloudRequest extends BaseAuthRequest {
*/
@Override
public String authorize() {
return UrlBuilder.getTencentCloudAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("scope", "user")
.queryParam("state", getRealState(config.getState()))
.build();
}
}
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.config.AuthSource;
import me.zhyd.oauth.enums.AuthToutiaoErrorCode;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.UrlBuilder;
/**
......@@ -17,7 +19,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.5
* @since 1.5
*/
public class AuthToutiaoRequest extends BaseAuthRequest {
public class AuthToutiaoRequest extends AuthDefaultRequest {
public AuthToutiaoRequest(AuthConfig config) {
super(config, AuthSource.TOUTIAO);
......@@ -25,30 +27,25 @@ public class AuthToutiaoRequest extends BaseAuthRequest {
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getToutiaoAccessTokenUrl(config.getClientId(), config.getClientSecret(), authCallback.getCode());
HttpResponse response = HttpRequest.get(accessTokenUrl).execute();
HttpResponse response = doGetAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response.body());
if (accessTokenObject.containsKey("error_code")) {
throw new AuthException(AuthToutiaoErrorCode.getErrorCode(accessTokenObject.getIntValue("error_code")).getDesc());
}
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.openId(accessTokenObject.getString("open_id"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.openId(accessTokenObject.getString("open_id"))
.build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
HttpResponse userResponse = HttpRequest.get(UrlBuilder.getToutiaoUserInfoUrl(config.getClientId(), authToken.getAccessToken())).execute();
HttpResponse userResponse = doGetUserInfo(authToken);
JSONObject userProfile = JSONObject.parseObject(userResponse.body());
if (userProfile.containsKey("error_code")) {
throw new AuthException(AuthToutiaoErrorCode.getErrorCode(userProfile.getIntValue("error_code")).getDesc());
}
this.checkResponse(userProfile);
JSONObject user = userProfile.getJSONObject("data");
......@@ -56,15 +53,15 @@ public class AuthToutiaoRequest extends BaseAuthRequest {
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();
.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();
}
/**
......@@ -74,6 +71,55 @@ public class AuthToutiaoRequest extends BaseAuthRequest {
*/
@Override
public String authorize() {
return UrlBuilder.getToutiaoAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_key", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(config.getState()))
.queryParam("auth_only", 1)
.queryParam("display", 0)
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("client_key", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("grant_type", "authorization_code")
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("client_key", config.getClientId())
.queryParam("access_token", authToken.getAccessToken())
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error_code")) {
throw new AuthException(AuthToutiaoErrorCode.getErrorCode(object.getIntValue("error_code"))
.getDesc());
}
}
}
......@@ -5,6 +5,7 @@ import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.*;
import me.zhyd.oauth.utils.UrlBuilder;
......@@ -16,7 +17,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthWeChatRequest extends BaseAuthRequest {
public class AuthWeChatRequest extends AuthDefaultRequest {
public AuthWeChatRequest(AuthConfig config) {
super(config, AuthSource.WECHAT);
}
......@@ -29,55 +30,42 @@ public class AuthWeChatRequest extends BaseAuthRequest {
*/
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getWeChatAccessTokenUrl(config.getClientId(), config.getClientSecret(), authCallback.getCode());
return this.getToken(accessTokenUrl);
return this.getToken(accessTokenUrl(authCallback.getCode()));
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
String openId = authToken.getOpenId();
HttpResponse response = HttpRequest.get(UrlBuilder.getWeChatUserInfoUrl(accessToken, openId)).execute();
HttpResponse response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response.body());
this.checkResponse(object);
String location = String.format("%s-%s-%s", object.getString("country"), object.getString("province"), object.getString("city"));
if (object.containsKey("unionid")){
if (object.containsKey("unionid")) {
authToken.setUnionId(object.getString("unionid"));
}
return AuthUser.builder()
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(object.getString("headimgurl"))
.location(location)
.uuid(openId)
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.token(authToken)
.source(AuthSource.WECHAT)
.build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.getWeChatAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(object.getString("headimgurl"))
.location(location)
.uuid(openId)
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.token(authToken)
.source(AuthSource.WECHAT)
.build();
}
@Override
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();
.code(AuthResponseStatus.SUCCESS.getCode())
.data(this.getToken(refreshTokenUrl(oldToken.getRefreshToken())))
.build();
}
/**
......@@ -104,10 +92,72 @@ public class AuthWeChatRequest extends BaseAuthRequest {
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.openId(accessTokenObject.getString("openid"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.openId(accessTokenObject.getString("openid"))
.build();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public String authorize() {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("appid", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("scope", "snsapi_login")
.queryParam("state", getRealState(config.getState()).concat("#wechat_redirect"))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("appid", config.getClientId())
.queryParam("secret", config.getClientSecret())
.queryParam("grant_type", "authorization_code")
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("openid", authToken.getOpenId())
.queryParam("lang", "zh_CN")
.build();
}
/**
* 返回获取userInfo的url
*
* @param refreshToken getAccessToken方法返回的refreshToken
* @return 返回获取userInfo的url
*/
@Override
protected String refreshTokenUrl(String refreshToken) {
return UrlBuilder.fromBaseUrl(source.refresh())
.queryParam("appid", config.getClientId())
.queryParam("refresh_token", refreshToken)
.queryParam("grant_type", "refresh_token")
.build();
}
}
......@@ -5,11 +5,11 @@ import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import me.zhyd.oauth.utils.IpUtils;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
......@@ -22,7 +22,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
* @version 1.0
* @since 1.8
*/
public class AuthWeiboRequest extends BaseAuthRequest {
public class AuthWeiboRequest extends AuthDefaultRequest {
public AuthWeiboRequest(AuthConfig config) {
super(config, AuthSource.WEIBO);
......@@ -30,20 +30,18 @@ public class AuthWeiboRequest extends BaseAuthRequest {
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = UrlBuilder.getWeiboAccessTokenUrl(config.getClientId(), config.getClientSecret(), authCallback.getCode(), config
.getRedirectUri());
HttpResponse response = HttpRequest.post(accessTokenUrl).execute();
HttpResponse response = doPostAuthorizationCode(authCallback.getCode());
String accessTokenStr = response.body();
JSONObject accessTokenObject = JSONObject.parseObject(accessTokenStr);
if (accessTokenObject.containsKey("error")) {
throw new AuthException("Unable to get token from weibo using code [" + authCallback.getCode() + "]:" + accessTokenObject.getString("error_description"));
throw new AuthException(accessTokenObject.getString("error_description"));
}
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.uid(accessTokenObject.getString("uid"))
.openId(accessTokenObject.getString("uid"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.build();
.accessToken(accessTokenObject.getString("access_token"))
.uid(accessTokenObject.getString("uid"))
.openId(accessTokenObject.getString("uid"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.build();
}
@Override
......@@ -51,37 +49,41 @@ public class AuthWeiboRequest extends BaseAuthRequest {
String accessToken = authToken.getAccessToken();
String uid = authToken.getUid();
String oauthParam = String.format("uid=%s&access_token=%s", uid, accessToken);
HttpResponse response = HttpRequest.get(UrlBuilder.getWeiboUserInfoUrl(oauthParam))
.header("Authorization", "OAuth2 " + oauthParam)
.header("API-RemoteIP", IpUtils.getIp())
.execute();
HttpResponse response = HttpRequest.get(userInfoUrl(authToken))
.header("Authorization", "OAuth2 " + oauthParam)
.header("API-RemoteIP", IpUtils.getIp())
.execute();
String userInfo = response.body();
JSONObject object = JSONObject.parseObject(userInfo);
if (object.containsKey("error")) {
throw new AuthException(object.getString("error"));
}
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("name"))
.avatar(object.getString("profile_image_url"))
.blog(StringUtils.isEmpty(object.getString("url")) ? "https://weibo.com/" + object.getString("profile_url") : object
.getString("url"))
.nickname(object.getString("screen_name"))
.location(object.getString("location"))
.remark(object.getString("description"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(AuthSource.WEIBO)
.build();
.uuid(object.getString("id"))
.username(object.getString("name"))
.avatar(object.getString("profile_image_url"))
.blog(StringUtils.isEmpty(object.getString("url")) ? "https://weibo.com/" + object.getString("profile_url") : object
.getString("url"))
.nickname(object.getString("screen_name"))
.location(object.getString("location"))
.remark(object.getString("description"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(AuthSource.WEIBO)
.build();
}
/**
* 返回认证url,可自行跳转页面
* 返回获取userInfo的url
*
* @return 返回授权地址
* @param authToken
* @return 返回获取userInfo的url
*/
@Override
public String authorize() {
return UrlBuilder.getWeiboAuthorizeUrl(config.getClientId(), config.getRedirectUri(), config.getState());
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("uid", authToken.getUid())
.build();
}
}
package me.zhyd.oauth.request;
import lombok.Data;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.AuthChecker;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.8
*/
@Data
public abstract class BaseAuthRequest implements AuthRequest {
protected AuthConfig config;
protected AuthSource source;
public BaseAuthRequest(AuthConfig config, AuthSource source) {
this.config = config;
this.source = source;
if (!AuthChecker.isSupportedAuth(config, source)) {
throw new AuthException(ResponseStatus.PARAMETER_INCOMPLETE);
}
// 校验配置合法性
AuthChecker.checkConfig(config, source);
}
protected abstract AuthToken getAccessToken(AuthCallback authCallback);
protected abstract AuthUser getUserInfo(AuthToken authToken);
@Override
public AuthResponse login(AuthCallback authCallback) {
try {
AuthChecker.checkCode(source == AuthSource.ALIPAY ? authCallback.getAuth_code() : authCallback.getCode());
AuthChecker.checkState(authCallback.getState(), config.getState());
AuthToken authToken = this.getAccessToken(authCallback);
AuthUser user = this.getUserInfo(authToken);
return AuthResponse.builder().code(ResponseStatus.SUCCESS.getCode()).data(user).build();
} catch (Exception e) {
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();
}
/**
* 返回认证url,可自行跳转页面
*
* @return 返回授权地址
*/
@Override
public abstract String authorize();
}
......@@ -3,7 +3,7 @@ package me.zhyd.oauth.utils;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.request.ResponseStatus;
import me.zhyd.oauth.model.AuthResponseStatus;
/**
* 授权配置类的校验器
......@@ -38,15 +38,15 @@ public class AuthChecker {
public static void checkConfig(AuthConfig config, AuthSource source) {
String redirectUri = config.getRedirectUri();
if (!GlobalAuthUtil.isHttpProtocol(redirectUri) && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) {
throw new AuthException(ResponseStatus.ILLEGAL_REDIRECT_URI);
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI);
}
// facebook的回调地址必须为https的链接
if (AuthSource.FACEBOOK == source && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) {
throw new AuthException(ResponseStatus.ILLEGAL_REDIRECT_URI);
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI);
}
// 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1
if (AuthSource.ALIPAY == source && GlobalAuthUtil.isLocalHost(redirectUri)) {
throw new AuthException(ResponseStatus.ILLEGAL_REDIRECT_URI);
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI);
}
}
......@@ -57,7 +57,7 @@ public class AuthChecker {
*/
public static void checkCode(String code) {
if (StringUtils.isEmpty(code)) {
throw new AuthException(ResponseStatus.ILLEGAL_CODE);
throw new AuthException(AuthResponseStatus.ILLEGAL_CODE);
}
}
......@@ -74,11 +74,11 @@ public class AuthChecker {
}
// 如果授权之前使用了state,但是回调时未返回state,则表示当前请求为非法的请求,可能正在被CSRF攻击
if (StringUtils.isEmpty(newState)) {
throw new AuthException(ResponseStatus.ILLEGAL_REQUEST);
throw new AuthException(AuthResponseStatus.ILLEGAL_REQUEST);
}
// 如果授权前后的state不一致,则表示当前请求为非法的请求,新的state可能为伪造
if (!newState.equals(originalState)) {
throw new AuthException(ResponseStatus.ILLEGAL_REQUEST);
throw new AuthException(AuthResponseStatus.ILLEGAL_REQUEST);
}
}
}
......@@ -4,8 +4,9 @@ import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.request.ResponseStatus;
import me.zhyd.oauth.model.AuthResponseStatus;
import java.nio.charset.Charset;
import java.util.concurrent.ConcurrentHashMap;
......@@ -30,6 +31,16 @@ public class AuthState {
*/
private static ConcurrentHashMap<String, String> stateBucket = new ConcurrentHashMap<>();
/**
* 生成随机的state
*
* @param source oauth平台
* @return state
*/
public static String create(AuthSource source) {
return create(source.name());
}
/**
* 生成随机的state
*
......@@ -113,7 +124,7 @@ public class AuthState {
String noneSourceState = decodedState.substring(source.length() + 1);
if (!noneSourceState.startsWith(currentIp)) {
// ip不相同,可能为非法的请求
throw new AuthException(ResponseStatus.ILLEGAL_REQUEST);
throw new AuthException(AuthResponseStatus.ILLEGAL_REQUEST);
}
String body = noneSourceState.substring(currentIp.length() + 1);
log.debug("body is [{}]", body);
......@@ -158,6 +169,15 @@ public class AuthState {
stateBucket.remove(key);
}
/**
* 登录成功后,清除state
*
* @param source oauth平台
*/
public static void delete(AuthSource source) {
delete(source.name());
}
private static String getCurrentIp() {
String currentIp = IpUtils.getIp();
return StringUtils.isEmpty(currentIp) ? EMPTY_STR : currentIp;
......
package me.zhyd.oauth.utils;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import me.zhyd.oauth.exception.AuthException;
import javax.crypto.Mac;
......@@ -12,9 +15,7 @@ import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
/**
* 全局的工具类
......@@ -82,6 +83,26 @@ public class GlobalAuthUtil {
return res;
}
public static String parseMapToString(Map<String, Object> params, boolean encode) {
List<String> paramList = new ArrayList<>();
params.forEach((k, v) -> {
if (ObjectUtil.isNull(v)) {
paramList.add(k + "=");
} else {
String valueString = v.toString();
paramList.add(k + "=" + (encode ? urlEncode(valueString) : valueString));
}
});
return CollUtil.join(paramList, "&");
}
public static Map<String, Object> parseQueryToMap(String url) {
Map<String, Object> paramMap = new HashMap<>();
HttpUtil.decodeParamMap(url, "UTF-8").forEach(paramMap::put);
return paramMap;
}
public static boolean isHttpProtocol(String url) {
if (StringUtils.isEmpty(url)) {
return false;
......
......@@ -26,7 +26,7 @@ public class AuthStateTest {
* null
*/
@Test
public void test() {
public void usage() {
String source = "github";
System.out.println("\nstep1 生成state: 预期创建一个新的state...");
String state = AuthState.create(source);
......
package me.zhyd.oauth.utils;
import org.junit.Test;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @website https://www.zhyd.me
* @date 2019/7/19 15:52
* @since 1.8
*/
public class CustomTest {
/**
* 1000000: 23135ms
* 100000: 3016ms
* 10000: 328ms
* 1000: 26ms
*/
@Test
public void test() {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
callMethod();
}
long end = System.currentTimeMillis();
System.out.println((end - start) + "ms");
}
/**
* 1000000: 19058ms
* 100000: 2772ms
* 10000: 323ms
* 1000: 29ms
*/
@Test
public void test2() {
long end = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
callMethod2();
}
long end2 = System.currentTimeMillis();
System.out.println((end2 - end) + "ms");
}
public String callMethod() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
// for (StackTraceElement stackTraceElement : stackTrace) {
// System.out.println(stackTraceElement.getMethodName());
// }
return stackTrace[2].getMethodName();
}
public String callMethod2() {
StackTraceElement[] stackTrace = (new Throwable()).getStackTrace();
// for (StackTraceElement stackTraceElement : stackTrace) {
// System.out.println(stackTraceElement.getMethodName());
// }
return stackTrace[2].getMethodName();
}
}
package me.zhyd.oauth.utils;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.request.AuthWeChatRequest;
import org.junit.Assert;
import org.junit.Test;
/**
* <p>
* UrlBuilder测试类
* </p>
*
* @author yangkai.shen (https://xkcoding.com)
* @date Created in 2019-07-18 16:36
*/
public class UrlBuilderTest {
@Test
public void testUrlBuilder() {
AuthConfig config = AuthConfig.builder()
.clientId("appid-110110110")
.clientSecret("secret-110110110")
.redirectUri("https://xkcoding.com")
.state(AuthState.create(AuthSource.WECHAT))
.build();
String build = UrlBuilder.fromBaseUrl(AuthSource.WECHAT.authorize())
.queryParam("appid", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("response_type", "code")
.queryParam("scope", "snsapi_login")
.queryParam("state", config.getState().concat("#wechat_redirect"))
.build(false);
AuthWeChatRequest request = new AuthWeChatRequest(config);
String authorize = request.authorize();
Assert.assertEquals(build, authorize);
AuthState.delete(AuthSource.WECHAT);
}
}
### 2019/07/19
1. 合并github上[@dyc12ii](https://github.com/dyc12ii)[pr#25](https://github.com/zhangyd-c/JustAuth/pull/25),升级fastjson版本至1.2.58,避免安全漏洞
2. `AuthUserGender`枚举类挪到`enums`包下
3. 删除`AuthBaiduErrorCode``AuthDingTalkErrorCode`枚举类
4. 优化百度授权流程,增加refresh token的方法
5. 优化`AuthConfig``AuthResponse`类,去掉不必要的lombonk注解,减少编译后的代码量
6. 使用lombok注解优化枚举类
7. `AuthQqRequest`增加refresh方法
8. 优化代码
### 2019/07/18
1. 合并github上[@pengisgood](https://github.com/pengisgood)[pr#19](https://github.com/zhangyd-c/JustAuth/pull/19),集成人人
2. 合并github上[@pengisgood](https://github.com/pengisgood)[pr#20](https://github.com/zhangyd-c/JustAuth/pull/20),集成Pinterest
3. 合并github上[@pengisgood](https://github.com/pengisgood)[pr#21](https://github.com/zhangyd-c/JustAuth/pull/21),集成StackOverflow
4. 合并github上[@xkcoding](https://github.com/xkcoding)[pr#23](https://github.com/zhangyd-c/JustAuth/pull/23),重构代码、新增编辑器规范,规范PR代码风格
### 2019/07/17
1. 优化代码
2. 集成Teambition登录
### 2019/07/16
1. 重构UrlBuilder类
2. 将CSDN相关的类置为`Deprecated`,后续可能会删除,也可能一直保留。毕竟CSDN的openAPI已经不对外开放了。
3. `BaseAuthRequest` 改名为 `AuthDefaultRequest`
4. `ResponseStatus` 改名为 `AuthResponseStatus` 并且移动到 `me.zhyd.oauth.model`
5. 合并github上[@xkcoding](https://github.com/xkcoding)[pr#18](https://github.com/zhangyd-c/JustAuth/pull/18),修复小米回调错误问题 同时 支持微信获取
### 2019/07/15
1. 新增 `AuthState` 类,内置默认的state生成规则和校验规则
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册