1
0
mirror of synced 2025-12-25 08:58:06 +08:00

Compare commits

..

18 Commits

Author SHA1 Message Date
yadong.zhang
d6cbcecaab 💡 修改注释、文档 2021-01-04 15:19:19 +08:00
yadong.zhang
050459e616 📝 Writing docs. 2021-01-01 18:28:43 +08:00
yadong.zhang
9fd2b9b919 🥚 正式启用飞书登录 2021-01-01 17:48:49 +08:00
yadong.zhang
2ff5570399 💡 更新注释 2021-01-01 15:22:30 +08:00
yadong.zhang
1bec384525 👽 重命名企业微信扫码登录 request 类名,补充文档说明 2021-01-01 15:20:57 +08:00
yadong.zhang
824c68356d 📌 升级 FaceBook API 版本到 v9.0 2021-01-01 10:40:18 +08:00
yadong.zhang
f5e3c225f6 📝 Writing docs. 2021-01-01 10:27:52 +08:00
yadong.zhang
0259669288 Merge branch 'dev' of github.com:justauth/JustAuth into dev 2021-01-01 10:10:20 +08:00
yadong.zhang
edc9d1a3c3 Merge pull request #107 from 937624219/dev
添加AuthAlipayRequest网络代理构造器
2021-01-01 10:03:30 +08:00
yadong.zhang
d886bc95a2 Merge pull request #105 from jianghuzai/WeChatEnterpriseWeb
支持企业微信网页授权登录
2021-01-01 09:56:40 +08:00
冬瓜
7aaf52e953 添加AuthAlipayRequest网络代理构造器 2020-12-27 11:08:57 +08:00
yadong.zhang
e84bd7cdb5 🔖 merge #101 2020-12-20 17:14:06 +08:00
guanhua.li
28e19960f2 支持企业微信网页授权登录 2020-12-16 10:02:37 +08:00
yadong.zhang
dce2bd1e1e Merge pull request #101 from zwzch/dev
支持喜马拉雅登录
2020-12-09 13:18:52 +08:00
zwzch
bc30971482 优化代码
Signed-off-by: zwzch <zwzch4j@gmail.com>
2020-11-06 20:14:52 +08:00
zwzch
9bd6d88049 喜马拉雅登录
Signed-off-by: zwzch <zwzch4j@gmail.com>
2020-11-06 17:41:28 +08:00
yadong.zhang
442332be57 📝 Writing docs. 2020-11-02 21:30:23 +08:00
yadong.zhang
d4bfa8e75f 🔖 sponsors 2020-11-02 21:22:35 +08:00
17 changed files with 468 additions and 88 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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 用于学习、研究、生产环境和商业产品中,包括(但不限于):
![](docs/users/4ca0177c.png)
@@ -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;">

View File

@@ -1 +1 @@
1.15.8
1.15.9

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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";
}
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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();

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View 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"));
}
}
}

View File

@@ -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;
}

View File

@@ -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>