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

🥚 正式启用飞书登录

上级 2ff55703
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
### 2021/1/1 ### 2021/1/1
- 发布 v1.15.9 - 发布 v1.15.9
- 新增
- 修复并正式启用 飞书 平台的第三方登录
- AuthToken 类中新增 `refreshTokenExpireIn` 记录 refresh token 的有效期
- PR - PR
- 合并 [Github #101](https://gitee.com/yadong.zhang/JustAuth/pulls/101):支持喜马拉雅登录 - 合并 [Github #101](https://gitee.com/yadong.zhang/JustAuth/pulls/101):支持喜马拉雅登录
- 合并 [Github #105](https://gitee.com/yadong.zhang/JustAuth/pulls/105):支持企业微信网页授权登录 - 合并 [Github #105](https://gitee.com/yadong.zhang/JustAuth/pulls/105):支持企业微信网页授权登录
......
...@@ -141,7 +141,7 @@ public enum AuthDefaultSource implements AuthSource { ...@@ -141,7 +141,7 @@ public enum AuthDefaultSource implements AuthSource {
}, },
/** /**
* Coding, * Coding,
* * <p>
* 参考 https://help.coding.net/docs/project/open/oauth.html#%E7%94%A8%E6%88%B7%E6%8E%88%E6%9D%83 中的说明, * 参考 https://help.coding.net/docs/project/open/oauth.html#%E7%94%A8%E6%88%B7%E6%8E%88%E6%9D%83 中的说明,
* 新版的 coding API 地址需要传入用户团队名,这儿使用动态参数,方便在 request 中使用 * 新版的 coding API 地址需要传入用户团队名,这儿使用动态参数,方便在 request 中使用
*/ */
...@@ -730,30 +730,31 @@ public enum AuthDefaultSource implements AuthSource { ...@@ -730,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 { FEISHU {
@Override @Override
public String authorize() { public String authorize() {
return "https://open.feishu.cn/connect/qrconnect/page/sso/"; return "https://open.feishu.cn/open-apis/authen/v1/index";
} }
@Override @Override
public String accessToken() { 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 @Override
public String userInfo() { 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 @Override
public String refresh() { 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";
} }
}, },
/** /**
......
...@@ -19,6 +19,7 @@ public class AuthToken implements Serializable { ...@@ -19,6 +19,7 @@ public class AuthToken implements Serializable {
private String accessToken; private String accessToken;
private int expireIn; private int expireIn;
private String refreshToken; private String refreshToken;
private int refreshTokenExpireIn;
private String uid; private String uid;
private String openId; private String openId;
private String accessCode; private String accessCode;
......
...@@ -3,9 +3,11 @@ package me.zhyd.oauth.request; ...@@ -3,9 +3,11 @@ package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.xkcoding.http.support.HttpHeader; import com.xkcoding.http.support.HttpHeader;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig; import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource; import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthResponseStatus; import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException; import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback; import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse; import me.zhyd.oauth.model.AuthResponse;
...@@ -13,40 +15,63 @@ import me.zhyd.oauth.model.AuthToken; ...@@ -13,40 +15,63 @@ import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser; import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.GlobalAuthUtils; import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.HttpUtils; import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder; import me.zhyd.oauth.utils.UrlBuilder;
/** /**
* 注意:该平台暂时存在问题,请不要使用。待修复完成后会重新发版by yadong.zhang * 飞书平台,企业自建应用授权登录,原逻辑由 beacon 集成于 1.14.0 版,但最新的飞书 api 已修改,并且飞书平台一直为 {@code Deprecated} 状态
* <p>
* 所以,最终修改该平台的实际发布版本为 1.15.9
* *
* @author beacon * @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 class AuthFeishuRequest extends AuthDefaultRequest {
public AuthFeishuRequest(AuthConfig config) { public AuthFeishuRequest(AuthConfig config) {
super(config, AuthDefaultSource.FEISHU); super(config, AuthDefaultSource.FEISHU);
throw new AuthException(AuthResponseStatus.FAILURE);
} }
@Override public AuthFeishuRequest(AuthConfig config, AuthStateCache authStateCache) {
protected AuthToken getAccessToken(AuthCallback authCallback) { 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(); JSONObject requestObject = new JSONObject();
requestObject.put("app_id", config.getClientId()); requestObject.put("app_id", config.getClientId());
requestObject.put("app_secret", config.getClientSecret()); requestObject.put("app_secret", config.getClientSecret());
requestObject.put("grant_type", "authorization_code"); String response = new HttpUtils(config.getHttpConfig()).post(url, requestObject.toJSONString(), new HttpHeader()
requestObject.put("code", authCallback.getCode());
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), requestObject.toJSONString(), new HttpHeader()
.add("Content-Type", "application/json")); .add("Content-Type", "application/json"));
JSONObject jsonObject = JSON.parseObject(response); JSONObject jsonObject = JSON.parseObject(response);
this.checkResponse(jsonObject); this.checkResponse(jsonObject);
return AuthToken.builder() String appAccessToken = jsonObject.getString("app_access_token");
.accessToken(jsonObject.getString("access_token")) // 缓存 app access token
.refreshToken(jsonObject.getString("refresh_token")) this.authStateCache.cache(cacheKey, appAccessToken, jsonObject.getLongValue("expire") * 1000);
.expireIn(jsonObject.getIntValue("expires_in")) return appAccessToken;
.tokenType(jsonObject.getString("token_type")) }
.openId(jsonObject.getString("open_id"))
.build(); @Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
JSONObject requestObject = new JSONObject();
requestObject.put("app_access_token", this.getAppAccessToken());
requestObject.put("grant_type", "authorization_code");
requestObject.put("code", authCallback.getCode());
return getToken(requestObject, this.source.accessToken());
} }
...@@ -57,39 +82,49 @@ public class AuthFeishuRequest extends AuthDefaultRequest { ...@@ -57,39 +82,49 @@ public class AuthFeishuRequest extends AuthDefaultRequest {
.add("Content-Type", "application/json") .add("Content-Type", "application/json")
.add("Authorization", "Bearer " + accessToken), false); .add("Authorization", "Bearer " + accessToken), false);
JSONObject object = JSON.parseObject(response); JSONObject object = JSON.parseObject(response);
this.checkResponse(object);
JSONObject data = object.getJSONObject("data");
return AuthUser.builder() return AuthUser.builder()
.rawUserInfo(object) .rawUserInfo(object)
.avatar(object.getString("AvatarUrl")) .uuid(data.getString("union_id"))
.username(object.getString("Mobile")) .username(data.getString("name"))
.email(object.getString("Email")) .nickname(data.getString("name"))
.nickname("Name") .avatar(data.getString("avatar_url"))
.email(data.getString("email"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build(); .build();
} }
@Override @Override
public AuthResponse refresh(AuthToken authToken) { public AuthResponse refresh(AuthToken authToken) {
JSONObject requestObject = new JSONObject(); JSONObject requestObject = new JSONObject();
requestObject.put("app_id", config.getClientId()); requestObject.put("app_access_token", this.getAppAccessToken());
requestObject.put("app_secret", config.getClientSecret());
requestObject.put("grant_type", "refresh_token"); requestObject.put("grant_type", "refresh_token");
requestObject.put("refresh_token", authToken.getRefreshToken()); requestObject.put("refresh_token", authToken.getRefreshToken());
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), requestObject.toJSONString(), new HttpHeader()
.add("Content-Type", "application/json"));
JSONObject jsonObject = JSON.parseObject(response);
this.checkResponse(jsonObject);
return AuthResponse.builder() return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode()) .code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder() .data(getToken(requestObject, this.source.refresh()))
.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())
.build(); .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);
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 @Override
public String authorize(String state) { public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize()) return UrlBuilder.fromBaseUrl(source.authorize())
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册