Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6cbcecaab | ||
|
|
050459e616 | ||
|
|
9fd2b9b919 | ||
|
|
2ff5570399 | ||
|
|
1bec384525 | ||
|
|
824c68356d | ||
|
|
f5e3c225f6 | ||
|
|
0259669288 | ||
|
|
edc9d1a3c3 | ||
|
|
d886bc95a2 | ||
|
|
7aaf52e953 | ||
|
|
e84bd7cdb5 | ||
|
|
28e19960f2 | ||
|
|
dce2bd1e1e | ||
|
|
bc30971482 | ||
|
|
9bd6d88049 | ||
|
|
442332be57 | ||
|
|
d4bfa8e75f |
@@ -1,3 +1,33 @@
|
||||
## 1.15.9
|
||||
|
||||
### 2021/1/1
|
||||
|
||||
- 发布 v1.15.9
|
||||
- 新增
|
||||
- 修复并正式启用 飞书 平台的第三方登录
|
||||
- AuthToken 类中新增 `refreshTokenExpireIn` 记录 refresh token 的有效期
|
||||
- PR
|
||||
- 合并 [Github #101](https://gitee.com/yadong.zhang/JustAuth/pulls/101):支持喜马拉雅登录
|
||||
- 合并 [Github #105](https://gitee.com/yadong.zhang/JustAuth/pulls/105):支持企业微信网页授权登录
|
||||
- 合并 [Github #107](https://gitee.com/yadong.zhang/JustAuth/pulls/107):添加AuthAlipayRequest网络代理构造器,解决 Github Issue [#102](https://github.com/justauth/JustAuth/issues/102)
|
||||
- 修改
|
||||
- 修改喜马拉雅配置参数,将`ClientOsType`参数提到 AuthConfig 中
|
||||
- AuthChecker 中增加对喜马拉雅平台的校验
|
||||
- 升级 facebook api 版本到 v9.0,解决 Gitee Issue [#I2AR5S](https://gitee.com/yadong.zhang/JustAuth/issues/I2AR5S)
|
||||
- !!!**注意**!!!修改原来的企业微信 Request 类名为 `AuthWeChatEnterpriseQrcodeRequest`,升级后注意该点
|
||||
|
||||
注意:可能有些开发者对于 JA 集成的四个微信平台不太理解,这儿统一说明:
|
||||
- 按照类名
|
||||
- AuthWeChatEnterpriseQrcodeRequest:企业微信二维码登录
|
||||
- AuthWeChatEnterpriseWebRequest:企业微信网页登录
|
||||
- AuthWeChatOpenRequest:微信开放平台
|
||||
- AuthWeChatMpRequest:微信公众平台
|
||||
- 按照枚举
|
||||
- WECHAT_ENTERPRISE:企业微信二维码登录
|
||||
- WECHAT_ENTERPRISE_WEB:企业微信网页登录
|
||||
- WECHAT_OPEN:微信开放平台
|
||||
- WECHAT_MP:微信公众平台
|
||||
|
||||
## 1.15.8
|
||||
|
||||
### 2020/10/25
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a target="_blank" href="https://search.maven.org/search?q=JustAuth">
|
||||
<img src="https://img.shields.io/badge/Maven%20Central-1.15.8-blue" ></img>
|
||||
<img src="https://img.shields.io/badge/Maven%20Central-1.15.9-blue" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://gitee.com/yadong.zhang/JustAuth/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/apm/l/vim-mode.svg?color=yellow" ></img>
|
||||
@@ -15,7 +15,7 @@
|
||||
<img src="https://img.shields.io/badge/JDK-1.8+-green.svg" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://apidoc.gitee.com/yadong.zhang/JustAuth/" title="API文档">
|
||||
<img src="https://img.shields.io/badge/Api%20Docs-1.15.8-orange" ></img>
|
||||
<img src="https://img.shields.io/badge/Api%20Docs-1.15.9-orange" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://justauth.wiki" title="参考文档">
|
||||
<img src="https://img.shields.io/badge/Docs-latest-blueviolet.svg" ></img>
|
||||
@@ -97,7 +97,7 @@ These artifacts are available from Maven Central:
|
||||
<dependency>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>1.15.8-beta.2</version>
|
||||
<version>1.15.9</version>
|
||||
</dependency>
|
||||
```
|
||||
- Using JustAuth
|
||||
|
||||
14
README.md
14
README.md
@@ -6,7 +6,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a target="_blank" href="https://search.maven.org/search?q=JustAuth">
|
||||
<img src="https://img.shields.io/badge/Maven%20Central-1.15.8-blue" ></img>
|
||||
<img src="https://img.shields.io/badge/Maven%20Central-1.15.9-blue" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://gitee.com/yadong.zhang/JustAuth/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/apm/l/vim-mode.svg?color=yellow" ></img>
|
||||
@@ -15,7 +15,7 @@
|
||||
<img src="https://img.shields.io/badge/JDK-1.8+-green.svg" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://apidoc.gitee.com/yadong.zhang/JustAuth/" title="API文档">
|
||||
<img src="https://img.shields.io/badge/Api%20Docs-1.15.8-orange" ></img>
|
||||
<img src="https://img.shields.io/badge/Api%20Docs-1.15.9-orange" ></img>
|
||||
</a>
|
||||
<a target="_blank" href="https://justauth.wiki" title="参考文档">
|
||||
<img src="https://img.shields.io/badge/Docs-latest-blueviolet.svg" ></img>
|
||||
@@ -107,7 +107,7 @@ JustAuth 集成了诸如:Github、Gitee、支付宝、新浪微博、微信、
|
||||
<dependency>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>1.15.8</version>
|
||||
<version>1.15.9</version>
|
||||
</dependency>
|
||||
```
|
||||
- 调用api
|
||||
@@ -157,6 +157,12 @@ authRequest.login(callback);
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## 赞助和支持
|
||||
|
||||
感谢以下赞助商的支持:
|
||||
|
||||
<a href="https://www.duohui.cn?utm_source=justauth" target="_blank"><img src="https://docs.duohui.cn/brand_source/img/std.svg" alt="多会 - 专业活动管理系统" style="height: 54px;" height="54px" /></a>
|
||||
|
||||
## JustAuth 的用户
|
||||
有很多公司、组织和个人把 JustAuth 用于学习、研究、生产环境和商业产品中,包括(但不限于):
|
||||

|
||||
@@ -172,7 +178,7 @@ authRequest.login(callback);
|
||||
- `MaxKey` 马克思的钥匙,寓意是最大钥匙,是用户单点登录认证系统(Sigle Sign On System),OAuth 2.0/OpenID Connect、SAML 2.0、JWT、CAS等标准化的开放协议,使用JustAuth集成OAuth第三方认证。: [https://shimingxy.github.io/MaxKey/](https://shimingxy.github.io/MaxKey/)
|
||||
- `YurunOAuthLogin` PHP 第三方登录授权 SDK:[YurunOAuthLogin](https://gitee.com/yurunsoft/YurunOAuthLogin)
|
||||
|
||||
# 鸣谢
|
||||
## 鸣谢
|
||||
- 感谢 JetBrains 提供的免费开源 License:
|
||||
<img src="https://images.gitee.com/uploads/images/2020/0406/220236_f5275c90_5531506.png" alt="图片引用自lets-mica" style="float:left;">
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.15.8
|
||||
1.15.9
|
||||
|
||||
4
pom.xml
4
pom.xml
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>1.15.8</version>
|
||||
<version>1.15.9</version>
|
||||
|
||||
<name>JustAuth</name>
|
||||
<url>https://gitee.com/yadong.zhang/JustAuth</url>
|
||||
@@ -52,7 +52,7 @@
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven-source.version>2.2.1</maven-source.version>
|
||||
<maven-compiler.version>3.8.1</maven-compiler.version>
|
||||
<maven-javadoc.version>3.1.0</maven-javadoc.version>
|
||||
<maven-javadoc.version>2.9.1</maven-javadoc.version>
|
||||
<cobertura-version>2.7</cobertura-version>
|
||||
<maven-surefire-version>2.20</maven-surefire-version>
|
||||
<maven-gpg-version>1.6</maven-gpg-version>
|
||||
|
||||
@@ -110,4 +110,25 @@ public class AuthConfig {
|
||||
* @since 1.15.7
|
||||
*/
|
||||
private List<String> scopes;
|
||||
|
||||
/**
|
||||
* 设备ID, 设备唯一标识ID
|
||||
*
|
||||
* @since 1.15.8
|
||||
*/
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 喜马拉雅:客户端操作系统类型,1-iOS系统,2-Android系统,3-Web
|
||||
*
|
||||
* @since 1.15.9
|
||||
*/
|
||||
private Integer clientOsType;
|
||||
|
||||
/**
|
||||
* 喜马拉雅:客户端包名,如果 {@link AuthConfig#clientOsType} 为1或2时必填。对Android客户端是包名,对IOS客户端是Bundle ID
|
||||
*
|
||||
* @since 1.15.9
|
||||
*/
|
||||
private String packId;
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
},
|
||||
/**
|
||||
* Coding,
|
||||
*
|
||||
* <p>
|
||||
* 参考 https://help.coding.net/docs/project/open/oauth.html#%E7%94%A8%E6%88%B7%E6%8E%88%E6%9D%83 中的说明,
|
||||
* 新版的 coding API 地址需要传入用户团队名,这儿使用动态参数,方便在 request 中使用
|
||||
*/
|
||||
@@ -315,17 +315,17 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
FACEBOOK {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://www.facebook.com/v3.3/dialog/oauth";
|
||||
return "https://www.facebook.com/v9.0/dialog/oauth";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://graph.facebook.com/v3.3/oauth/access_token";
|
||||
return "https://graph.facebook.com/v9.0/oauth/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://graph.facebook.com/v3.3/me";
|
||||
return "https://graph.facebook.com/v9.0/me";
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -561,7 +561,7 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
},
|
||||
|
||||
/**
|
||||
* 企业微信
|
||||
* 企业微信二维码登录
|
||||
*
|
||||
* @since 1.10.0
|
||||
*/
|
||||
@@ -582,6 +582,26 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 企业微信网页登录
|
||||
*/
|
||||
WECHAT_ENTERPRISE_WEB {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://open.weixin.qq.com/connect/oauth2/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://qyapi.weixin.qq.com/cgi-bin/gettoken";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 酷家乐
|
||||
*
|
||||
@@ -710,30 +730,31 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
},
|
||||
|
||||
/**
|
||||
* 飞书
|
||||
* 注意:该平台暂时存在问题,请不要使用。待修复完成后会重新发版
|
||||
* 飞书平台,企业自建应用授权登录,原逻辑由 beacon 集成于 1.14.0 版,但最新的飞书 api 已修改,并且飞书平台一直为 {@code Deprecated} 状态
|
||||
* <p>
|
||||
* 所以,最终修改该平台的实际发布版本为 1.15.9
|
||||
*
|
||||
* @since 1.14.0
|
||||
* @since 1.15.9
|
||||
*/
|
||||
FEISHU {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://open.feishu.cn/connect/qrconnect/page/sso/";
|
||||
return "https://open.feishu.cn/open-apis/authen/v1/index";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://open.feishu.cn/connect/qrconnect/oauth2/access_token/";
|
||||
return "https://open.feishu.cn/open-apis/authen/v1/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://open.feishu.cn/connect/qrconnect/oauth2/user_info/";
|
||||
return "https://open.feishu.cn/open-apis/authen/v1/user_info";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://open.feishu.cn/connect/qrconnect/oauth2/access_token/";
|
||||
return "https://open.feishu.cn/open-apis/authen/v1/refresh_access_token";
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -786,6 +807,30 @@ public enum AuthDefaultSource implements AuthSource {
|
||||
public String refresh() {
|
||||
return "https://oauth.aliyun.com/v1/token";
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 喜马拉雅
|
||||
*/
|
||||
XMLY {
|
||||
@Override
|
||||
public String authorize() {
|
||||
return "https://api.ximalaya.com/oauth2/js/authorize";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String accessToken() {
|
||||
return "https://api.ximalaya.com/oauth2/v2/access_token";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userInfo() {
|
||||
return "https://api.ximalaya.com/profile/user_info";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refresh() {
|
||||
return "https://oauth.aliyun.com/v1/token";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package me.zhyd.oauth.enums.scope;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 企业自建应用授权范围
|
||||
*
|
||||
* @author liguanhua (347826496(a)qq.com)
|
||||
* @since 1.15.9
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum AuthWeChatEnterpriseWebScope implements AuthScope {
|
||||
/**
|
||||
* {@code scope} 含义,以{@code description} 为准
|
||||
*/
|
||||
SNSAPI_BASE("snsapi_base", "应用授权作用域。企业自建应用固定填写:snsapi_base", true);
|
||||
|
||||
private String scope;
|
||||
private String description;
|
||||
private boolean isDefault;
|
||||
|
||||
}
|
||||
@@ -19,6 +19,7 @@ public class AuthToken implements Serializable {
|
||||
private String accessToken;
|
||||
private int expireIn;
|
||||
private String refreshToken;
|
||||
private int refreshTokenExpireIn;
|
||||
private String uid;
|
||||
private String openId;
|
||||
private String accessCode;
|
||||
|
||||
@@ -1,41 +1,38 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.config.AuthSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 企业微信登录
|
||||
* 企业微信登录父类
|
||||
* </p>
|
||||
*
|
||||
* @author yangkai.shen (https://xkcoding.com)
|
||||
* @since 1.10.0
|
||||
* @author liguanhua (347826496(a)qq.com)
|
||||
* @since 1.15.9
|
||||
*/
|
||||
public class AuthWeChatEnterpriseRequest extends AuthDefaultRequest {
|
||||
public AuthWeChatEnterpriseRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.WECHAT_ENTERPRISE);
|
||||
public abstract class AbstractAuthWeChatEnterpriseRequest extends AuthDefaultRequest {
|
||||
|
||||
public AbstractAuthWeChatEnterpriseRequest(AuthConfig config, AuthSource source) {
|
||||
super(config,source);
|
||||
}
|
||||
|
||||
public AuthWeChatEnterpriseRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.WECHAT_ENTERPRISE, authStateCache);
|
||||
|
||||
public AbstractAuthWeChatEnterpriseRequest(AuthConfig config, AuthSource source, AuthStateCache authStateCache) {
|
||||
super(config, source, authStateCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信的特殊性,此时返回的信息同时包含 openid 和 access_token
|
||||
*
|
||||
* @param authCallback 回调返回的参数
|
||||
* @return 所有信息
|
||||
*/
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
String response = doGetAuthorizationCode(accessTokenUrl(authCallback.getCode()));
|
||||
@@ -92,22 +89,6 @@ public class AuthWeChatEnterpriseRequest extends AuthDefaultRequest {
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.9.3
|
||||
*/
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("appid", config.getClientId())
|
||||
.queryParam("agentid", config.getAgentId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回获取accessToken的url
|
||||
@@ -43,6 +43,12 @@ public class AuthAlipayRequest extends AuthDefaultRequest {
|
||||
.getAlipayPublicKey(), "RSA2");
|
||||
}
|
||||
|
||||
public AuthAlipayRequest(AuthConfig config, AuthStateCache authStateCache, String proxyHost, Integer proxyPort) {
|
||||
super(config, AuthDefaultSource.ALIPAY, authStateCache);
|
||||
this.alipayClient = new DefaultAlipayClient(AuthDefaultSource.ALIPAY.accessToken(), config.getClientId(), config.getClientSecret(),
|
||||
"json", "UTF-8", config.getAlipayPublicKey(), "RSA2", proxyHost, proxyPort);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
|
||||
|
||||
@@ -3,9 +3,11 @@ package me.zhyd.oauth.request;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.xkcoding.http.support.HttpHeader;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.enums.AuthUserGender;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
@@ -13,40 +15,63 @@ import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.utils.GlobalAuthUtils;
|
||||
import me.zhyd.oauth.utils.HttpUtils;
|
||||
import me.zhyd.oauth.utils.StringUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
* 注意:该平台暂时存在问题,请不要使用。待修复完成后会重新发版by yadong.zhang
|
||||
* 飞书平台,企业自建应用授权登录,原逻辑由 beacon 集成于 1.14.0 版,但最新的飞书 api 已修改,并且飞书平台一直为 {@code Deprecated} 状态
|
||||
* <p>
|
||||
* 所以,最终修改该平台的实际发布版本为 1.15.9
|
||||
*
|
||||
* @author beacon
|
||||
* @since 1.14.0
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com) 重构业务逻辑 20210101
|
||||
* @since 1.15.9
|
||||
*/
|
||||
@Deprecated
|
||||
public class AuthFeishuRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthFeishuRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.FEISHU);
|
||||
throw new AuthException(AuthResponseStatus.FAILURE);
|
||||
}
|
||||
|
||||
public AuthFeishuRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.FEISHU, authStateCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 app_access_token(企业自建应用)
|
||||
* <p>
|
||||
* Token 有效期为 2 小时,在此期间调用该接口 token 不会改变。当 token 有效期小于 30 分的时候,再次请求获取 token 的时候,
|
||||
* 会生成一个新的 token,与此同时老的 token 依然有效。
|
||||
*
|
||||
* @return appAccessToken
|
||||
*/
|
||||
private String getAppAccessToken() {
|
||||
String cacheKey = this.source.getName().concat(":app_access_token:").concat(config.getClientId());
|
||||
String cacheAppAccessToken = this.authStateCache.get(cacheKey);
|
||||
if (StringUtils.isNotEmpty(cacheAppAccessToken)) {
|
||||
return cacheAppAccessToken;
|
||||
}
|
||||
String url = "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal/";
|
||||
JSONObject requestObject = new JSONObject();
|
||||
requestObject.put("app_id", config.getClientId());
|
||||
requestObject.put("app_secret", config.getClientSecret());
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(url, requestObject.toJSONString(), new HttpHeader()
|
||||
.add("Content-Type", "application/json"));
|
||||
JSONObject jsonObject = JSON.parseObject(response);
|
||||
this.checkResponse(jsonObject);
|
||||
String appAccessToken = jsonObject.getString("app_access_token");
|
||||
// 缓存 app access token
|
||||
this.authStateCache.cache(cacheKey, appAccessToken, jsonObject.getLongValue("expire") * 1000);
|
||||
return appAccessToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
JSONObject requestObject = new JSONObject();
|
||||
requestObject.put("app_id", config.getClientId());
|
||||
requestObject.put("app_secret", config.getClientSecret());
|
||||
requestObject.put("app_access_token", this.getAppAccessToken());
|
||||
requestObject.put("grant_type", "authorization_code");
|
||||
requestObject.put("code", authCallback.getCode());
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), requestObject.toJSONString(), new HttpHeader()
|
||||
.add("Content-Type", "application/json"));
|
||||
JSONObject jsonObject = JSON.parseObject(response);
|
||||
this.checkResponse(jsonObject);
|
||||
return AuthToken.builder()
|
||||
.accessToken(jsonObject.getString("access_token"))
|
||||
.refreshToken(jsonObject.getString("refresh_token"))
|
||||
.expireIn(jsonObject.getIntValue("expires_in"))
|
||||
.tokenType(jsonObject.getString("token_type"))
|
||||
.openId(jsonObject.getString("open_id"))
|
||||
.build();
|
||||
return getToken(requestObject, this.source.accessToken());
|
||||
|
||||
}
|
||||
|
||||
@@ -57,37 +82,47 @@ public class AuthFeishuRequest extends AuthDefaultRequest {
|
||||
.add("Content-Type", "application/json")
|
||||
.add("Authorization", "Bearer " + accessToken), false);
|
||||
JSONObject object = JSON.parseObject(response);
|
||||
this.checkResponse(object);
|
||||
JSONObject data = object.getJSONObject("data");
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(object)
|
||||
.avatar(object.getString("AvatarUrl"))
|
||||
.username(object.getString("Mobile"))
|
||||
.email(object.getString("Email"))
|
||||
.nickname("Name")
|
||||
.uuid(data.getString("union_id"))
|
||||
.username(data.getString("name"))
|
||||
.nickname(data.getString("name"))
|
||||
.avatar(data.getString("avatar_url"))
|
||||
.email(data.getString("email"))
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.token(authToken)
|
||||
.source(source.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResponse refresh(AuthToken authToken) {
|
||||
JSONObject requestObject = new JSONObject();
|
||||
requestObject.put("app_id", config.getClientId());
|
||||
requestObject.put("app_secret", config.getClientSecret());
|
||||
requestObject.put("app_access_token", this.getAppAccessToken());
|
||||
requestObject.put("grant_type", "refresh_token");
|
||||
requestObject.put("refresh_token", authToken.getRefreshToken());
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), requestObject.toJSONString(), new HttpHeader()
|
||||
return AuthResponse.builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(getToken(requestObject, this.source.refresh()))
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
private AuthToken getToken(JSONObject param, String url) {
|
||||
String response = new HttpUtils(config.getHttpConfig()).post(url, param.toJSONString(), new HttpHeader()
|
||||
.add("Content-Type", "application/json"));
|
||||
JSONObject jsonObject = JSON.parseObject(response);
|
||||
this.checkResponse(jsonObject);
|
||||
return AuthResponse.builder()
|
||||
.code(AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(AuthToken.builder()
|
||||
.accessToken(jsonObject.getString("access_token"))
|
||||
.refreshToken(jsonObject.getString("refresh_token"))
|
||||
.expireIn(jsonObject.getIntValue("expires_in"))
|
||||
.tokenType(jsonObject.getString("token_type"))
|
||||
.openId(jsonObject.getString("open_id"))
|
||||
.build())
|
||||
JSONObject data = jsonObject.getJSONObject("data");
|
||||
return AuthToken.builder()
|
||||
.accessToken(data.getString("access_token"))
|
||||
.refreshToken(data.getString("refresh_token"))
|
||||
.expireIn(data.getIntValue("expires_in"))
|
||||
.tokenType(data.getString("token_type"))
|
||||
.openId(data.getString("open_id"))
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 企业微信二维码登录
|
||||
* </p>
|
||||
*
|
||||
* @author yangkai.shen (https://xkcoding.com)
|
||||
* @author liguanhua (347826496(a)qq.com) 重构该类,将通用方法提取
|
||||
* @author lyadong.zhang (yadong.zhang0415(a)gmail.com) 修改类名
|
||||
* @since 1.10.0
|
||||
*/
|
||||
public class AuthWeChatEnterpriseQrcodeRequest extends AbstractAuthWeChatEnterpriseRequest {
|
||||
public AuthWeChatEnterpriseQrcodeRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.WECHAT_ENTERPRISE);
|
||||
}
|
||||
|
||||
public AuthWeChatEnterpriseQrcodeRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.WECHAT_ENTERPRISE, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("appid", config.getClientId())
|
||||
.queryParam("agentid", config.getAgentId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(state))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.enums.scope.AuthWeChatEnterpriseWebScope;
|
||||
import me.zhyd.oauth.utils.AuthScopeUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 企业微信网页登录
|
||||
* </p>
|
||||
*
|
||||
* @author liguanhua (347826496(a)qq.com)
|
||||
* @since 1.15.9
|
||||
*/
|
||||
public class AuthWeChatEnterpriseWebRequest extends AbstractAuthWeChatEnterpriseRequest {
|
||||
public AuthWeChatEnterpriseWebRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.WECHAT_ENTERPRISE_WEB);
|
||||
}
|
||||
|
||||
public AuthWeChatEnterpriseWebRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.WECHAT_ENTERPRISE_WEB, authStateCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("appid", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("scope", this.getScopes(",", false, AuthScopeUtils.getDefaultScopes(AuthWeChatEnterpriseWebScope.values())))
|
||||
.queryParam("state", getRealState(state).concat("#wechat_redirect"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
124
src/main/java/me/zhyd/oauth/request/AuthXmlyRequest.java
Normal file
124
src/main/java/me/zhyd/oauth/request/AuthXmlyRequest.java
Normal file
@@ -0,0 +1,124 @@
|
||||
package me.zhyd.oauth.request;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.xkcoding.http.HttpUtil;
|
||||
import me.zhyd.oauth.cache.AuthStateCache;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
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.GlobalAuthUtils;
|
||||
import me.zhyd.oauth.utils.UrlBuilder;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* 喜马拉雅登录
|
||||
*
|
||||
* @author zwzch (zwzch4j@gmail.com)
|
||||
* @since 1.15.9
|
||||
*/
|
||||
public class AuthXmlyRequest extends AuthDefaultRequest {
|
||||
|
||||
public AuthXmlyRequest(AuthConfig config) {
|
||||
super(config, AuthDefaultSource.XMLY);
|
||||
}
|
||||
|
||||
public AuthXmlyRequest(AuthConfig config, AuthStateCache authStateCache) {
|
||||
super(config, AuthDefaultSource.XMLY, authStateCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取access token
|
||||
*
|
||||
* @param authCallback 授权成功后的回调参数
|
||||
* @return token
|
||||
* @see AuthDefaultRequest#authorize(String)
|
||||
*/
|
||||
@Override
|
||||
protected AuthToken getAccessToken(AuthCallback authCallback) {
|
||||
Map<String, String> map = new HashMap<>(6);
|
||||
map.put("code", authCallback.getCode());
|
||||
map.put("client_id", config.getClientId());
|
||||
map.put("client_secret", config.getClientSecret());
|
||||
map.put("device_id", config.getDeviceId());
|
||||
map.put("grant_type", "authorization_code");
|
||||
map.put("redirect_uri", config.getRedirectUri());
|
||||
String response = HttpUtil.post(source.accessToken(), map, true);
|
||||
JSONObject accessTokenObject = JSONObject.parseObject(response);
|
||||
this.checkResponse(accessTokenObject);
|
||||
|
||||
return AuthToken.builder()
|
||||
.accessToken(accessTokenObject.getString("access_token"))
|
||||
.refreshToken(accessTokenObject.getString("refresh_token"))
|
||||
.expireIn(accessTokenObject.getIntValue("expires_in"))
|
||||
.uid(accessTokenObject.getString("uid"))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
|
||||
*
|
||||
* @param state state 验证授权流程的参数,可以防止csrf
|
||||
* @return 返回授权地址
|
||||
* @since 1.15.8
|
||||
*/
|
||||
@Override
|
||||
public String authorize(String state) {
|
||||
return UrlBuilder.fromBaseUrl(source.authorize())
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("client_id", config.getClientId())
|
||||
.queryParam("redirect_uri", config.getRedirectUri())
|
||||
.queryParam("state", getRealState(state))
|
||||
.queryParam("client_os_type", "3")
|
||||
.queryParam("device_id", config.getDeviceId())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用token换取用户信息
|
||||
*
|
||||
* @param authToken token信息
|
||||
* @return 用户信息
|
||||
* @see AuthDefaultRequest#getAccessToken(AuthCallback)
|
||||
*/
|
||||
@Override
|
||||
public AuthUser getUserInfo(AuthToken authToken) {
|
||||
Map<String, String> map = new TreeMap<>();
|
||||
map.put("app_key", config.getClientId());
|
||||
map.put("client_os_type", Optional.ofNullable(config.getClientOsType()).orElse(3).toString());
|
||||
map.put("device_id", config.getDeviceId());
|
||||
map.put("pack_id", config.getPackId());
|
||||
map.put("access_token", authToken.getAccessToken());
|
||||
map.put("sig", GlobalAuthUtils.generateXmlySignature(map, config.getClientSecret()));
|
||||
String rawUserInfo = HttpUtil.get(source.userInfo(), map, false);
|
||||
JSONObject object = JSONObject.parseObject(rawUserInfo);
|
||||
checkResponse(object);
|
||||
return AuthUser.builder()
|
||||
.uuid(object.getString("id"))
|
||||
.nickname(object.getString("nickname"))
|
||||
.avatar(object.getString("avatar_url"))
|
||||
.rawUserInfo(object)
|
||||
.source(source.toString())
|
||||
.token(authToken)
|
||||
.gender(AuthUserGender.UNKNOWN)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验响应结果
|
||||
*
|
||||
* @param object 接口返回的结果
|
||||
*/
|
||||
private void checkResponse(JSONObject object) {
|
||||
if (object.containsKey("errcode")) {
|
||||
throw new AuthException(object.getIntValue("error_no"), object.getString("error_desc"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,12 @@ public class AuthChecker {
|
||||
if (isSupported && AuthDefaultSource.CODING == source) {
|
||||
isSupported = StringUtils.isNotEmpty(config.getCodingGroupName());
|
||||
}
|
||||
if (isSupported && AuthDefaultSource.XMLY == source) {
|
||||
isSupported = StringUtils.isNotEmpty(config.getDeviceId()) && null != config.getClientOsType();
|
||||
if (isSupported) {
|
||||
isSupported = config.getClientOsType() == 3 || StringUtils.isNotEmpty(config.getPackId());
|
||||
}
|
||||
}
|
||||
return isSupported;
|
||||
}
|
||||
|
||||
|
||||
@@ -220,6 +220,34 @@ public class GlobalAuthUtils {
|
||||
return new String(Base64Utils.encode(signature, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* 喜马拉雅签名算法
|
||||
* {@code https://open.ximalaya.com/doc/detailApi?categoryId=6&articleId=69}
|
||||
*
|
||||
* @param params 加密参数
|
||||
* @param clientSecret 平台应用的授权key
|
||||
* @return Signature
|
||||
* @since 1.15.9
|
||||
*/
|
||||
public static String generateXmlySignature(Map<String, String> params, String clientSecret) {
|
||||
TreeMap<String, String> map = new TreeMap<>(params);
|
||||
String baseStr = Base64Utils.encode(parseMapToString(map, false));
|
||||
byte[] sign = sign(clientSecret.getBytes(DEFAULT_ENCODING), baseStr.getBytes(DEFAULT_ENCODING), HMAC_SHA1);
|
||||
MessageDigest md5 = null;
|
||||
StringBuilder builder = null;
|
||||
try {
|
||||
builder = new StringBuilder();
|
||||
md5 = MessageDigest.getInstance("MD5");
|
||||
md5.update(sign);
|
||||
byte[] byteData = md5.digest();
|
||||
for (byte byteDatum : byteData) {
|
||||
builder.append(Integer.toString((byteDatum & 0xff) + 0x100, 16).substring(1));
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return null == builder ? "" : builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成饿了么请求的Signature
|
||||
* <p>
|
||||
|
||||
Reference in New Issue
Block a user