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;
/**
* 是否开启 PKCE 模式,该配置仅用于支持 PKCE 模式的平台,针对无服务应用,不推荐使用隐式授权,推荐使用 PKCE 模式
*
* @since 1.15.9
*/
private boolean pkce;
/**
* Okta 授权服务器的 ID, 默认为 default。如果要使用自定义授权服务,此处传实际的授权服务器 ID(一个随机串)
*
* 创建自定义授权服务器,请参考:
*
* ① https://developer.okta.com/docs/concepts/auth-servers
*
* ② https://developer.okta.com/docs/guides/customize-authz-server
*
* @since 1.16.0
*/
private String authServerId;
/**
* 忽略校验 {@code redirectUri} 参数,默认不开启。当 {@code ignoreCheckRedirectUri} 为 {@code true} 时,
* {@link me.zhyd.oauth.utils.AuthChecker#checkConfig(AuthConfig, AuthSource)} 将不会校验 {@code redirectUri} 的合法性。
*
* @since 1.16.1
*/
private boolean ignoreCheckRedirectUri;
/**
* 适配 builder 模式 set 值的情况
*
* @return authServerId
*/
public String getAuthServerId() {
return StringUtils.isEmpty(authServerId) ? "default" : authServerId;
}
/**
* Microsoft Entra ID(原微软 AAD)中的租户 ID
*/
private String tenantId;
/**
* 苹果开发者账号中的密钥标识符
* @see create-a-sign-in-with-apple-private-key
*/
private String kid;
/**
* 苹果开发者账号中的团队ID
* @see team id
*/
private String teamId;
/**
* 新版企业微信 Web 登录时的参数,
*
* 登录类型。ServiceApp:服务商登录;CorpApp:企业自建/代开发应用登录。
* @see https://developer.work.weixin.qq.com/document/path/98152
* @since 1.16.7
*/
private String loginType = "CorpApp";
/**
* 企业微信平台的语言编码
*
* @since 1.16.7
*/
private String lang = "zh";
/**
* 钉钉平台参数:控制输出特定类型的组织列表,org_type=management 表示只输出有管理权限的组织。
*
* scope包含corpid时该参数存在意义。
*
* @see https://open.dingtalk.com/document/orgapp/obtain-identity-credentials#title-4up-u8w-5ug
* @since 1.16.7
*/
private String dingTalkOrgType;
/**
* 钉钉平台参数:用于指定用户需要选择的组织。
*
* scope包含corpid时该参数存在意义。传入的corpId需要是当前用户所在的组织。
*
* @see https://open.dingtalk.com/document/orgapp/obtain-identity-credentials#title-4up-u8w-5ug
* @since 1.16.7
*/
private String dingTalkCorpId;
/**
* 钉钉平台参数:true表示专属帐号登录,展示组织代码输入页。
*
* @see https://open.dingtalk.com/document/orgapp/obtain-identity-credentials#title-4up-u8w-5ug
* @since 1.16.7
*/
private boolean dingTalkExclusiveLogin;
/**
* 钉钉平台参数:开启了专属帐号功能的组织corpId。
*
* scope包含corpid时该参数存在意义。传入的corpId需要是当前用户所在的组织。
*
* @see https://open.dingtalk.com/document/orgapp/obtain-identity-credentials#title-4up-u8w-5ug
* @since 1.16.7
*/
private String dingTalkExclusiveCorpId;
}
================================================
FILE: src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java
================================================
package me.zhyd.oauth.config;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.request.*;
/**
* JustAuth内置的各api需要的url, 用枚举类分平台类型管理
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0
*/
public enum AuthDefaultSource implements AuthSource {
/**
* Github
*/
GITHUB {
@Override
public String authorize() {
return "https://github.com/login/oauth/authorize";
}
@Override
public String accessToken() {
return "https://github.com/login/oauth/access_token";
}
@Override
public String userInfo() {
return "https://api.github.com/user";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthGithubRequest.class;
}
},
/**
* 新浪微博
*/
WEIBO {
@Override
public String authorize() {
return "https://api.weibo.com/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://api.weibo.com/oauth2/access_token";
}
@Override
public String userInfo() {
return "https://api.weibo.com/2/users/show.json";
}
@Override
public String revoke() {
return "https://api.weibo.com/oauth2/revokeoauth2";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthWeiboRequest.class;
}
},
/**
* gitee
*/
GITEE {
@Override
public String authorize() {
return "https://gitee.com/oauth/authorize";
}
@Override
public String accessToken() {
return "https://gitee.com/oauth/token";
}
@Override
public String userInfo() {
return "https://gitee.com/api/v5/user";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthGiteeRequest.class;
}
},
/**
* 钉钉扫码登录
*/
DINGTALK {
@Override
public String authorize() {
return "https://oapi.dingtalk.com/connect/qrconnect";
}
@Override
public String accessToken() {
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
@Override
public String userInfo() {
return "https://oapi.dingtalk.com/sns/getuserinfo_bycode";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthDingTalkRequest.class;
}
},
/**
* 新版钉钉扫码登录
*/
DINGTALK_V2 {
@Override
public String authorize() {
return "https://login.dingtalk.com/oauth2/challenge.htm";
}
@Override
public String accessToken() {
return "https://api.dingtalk.com/v1.0/oauth2/userAccessToken";
}
@Override
public String userInfo() {
return "https://api.dingtalk.com/v1.0/contact/users/me";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthDingTalkV2Request.class;
}
},
/**
* 钉钉账号登录
*/
DINGTALK_ACCOUNT {
@Override
public String authorize() {
return "https://oapi.dingtalk.com/connect/oauth2/sns_authorize";
}
@Override
public String accessToken() {
return DINGTALK.accessToken();
}
@Override
public String userInfo() {
return DINGTALK.userInfo();
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthDingTalkAccountRequest.class;
}
},
/**
* 百度
*/
BAIDU {
@Override
public String authorize() {
return "https://openapi.baidu.com/oauth/2.0/authorize";
}
@Override
public String accessToken() {
return "https://openapi.baidu.com/oauth/2.0/token";
}
@Override
public String userInfo() {
return "https://openapi.baidu.com/rest/2.0/passport/users/getInfo";
}
@Override
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";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthBaiduRequest.class;
}
},
/**
* csdn
*/
CSDN {
@Override
public String authorize() {
return "https://api.csdn.net/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://api.csdn.net/oauth2/access_token";
}
@Override
public String userInfo() {
return "https://api.csdn.net/user/getinfo";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthCsdnRequest.class;
}
},
/**
* Coding,
*
* 参考 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 {
@Override
public String authorize() {
return "https://%s.coding.net/oauth_authorize.html";
}
@Override
public String accessToken() {
return "https://%s.coding.net/api/oauth/access_token";
}
@Override
public String userInfo() {
return "https://%s.coding.net/api/account/current_user";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthCodingRequest.class;
}
},
/**
* oschina 开源中国
*/
OSCHINA {
@Override
public String authorize() {
return "https://www.oschina.net/action/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://www.oschina.net/action/openapi/token";
}
@Override
public String userInfo() {
return "https://www.oschina.net/action/openapi/user";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthOschinaRequest.class;
}
},
/**
* 支付宝
*/
ALIPAY {
@Override
public String authorize() {
return "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm";
}
@Override
public String accessToken() {
return "https://openapi.alipay.com/gateway.do";
}
@Override
public String userInfo() {
return "https://openapi.alipay.com/gateway.do";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthAlipayRequest.class;
}
},
/**
* QQ
*/
QQ {
@Override
public String authorize() {
return "https://graph.qq.com/oauth2.0/authorize";
}
@Override
public String accessToken() {
return "https://graph.qq.com/oauth2.0/token";
}
@Override
public String userInfo() {
return "https://graph.qq.com/user/get_user_info";
}
@Override
public String refresh() {
return "https://graph.qq.com/oauth2.0/token";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthQqRequest.class;
}
},
/**
* 微信开放平台
*/
WECHAT_OPEN {
@Override
public String authorize() {
return "https://open.weixin.qq.com/connect/qrconnect";
}
@Override
public String accessToken() {
return "https://api.weixin.qq.com/sns/oauth2/access_token";
}
@Override
public String userInfo() {
return "https://api.weixin.qq.com/sns/userinfo";
}
@Override
public String refresh() {
return "https://api.weixin.qq.com/sns/oauth2/refresh_token";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthWeChatOpenRequest.class;
}
},
/**
* 微信公众平台
*/
WECHAT_MP {
@Override
public String authorize() {
return "https://open.weixin.qq.com/connect/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://api.weixin.qq.com/sns/oauth2/access_token";
}
@Override
public String userInfo() {
return "https://api.weixin.qq.com/sns/userinfo";
}
@Override
public String refresh() {
return "https://api.weixin.qq.com/sns/oauth2/refresh_token";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthWeChatMpRequest.class;
}
},
/**
* 淘宝
*/
TAOBAO {
@Override
public String authorize() {
return "https://oauth.taobao.com/authorize";
}
@Override
public String accessToken() {
return "https://oauth.taobao.com/token";
}
@Override
public String userInfo() {
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthTaobaoRequest.class;
}
},
/**
* Google
* 端点地址:https://accounts.google.com/.well-known/openid-configuration
*/
GOOGLE {
@Override
public String authorize() {
return "https://accounts.google.com/o/oauth2/v2/auth";
}
@Override
public String accessToken() {
return "https://oauth2.googleapis.com/token";
}
@Override
public String userInfo() {
return "https://openidconnect.googleapis.com/v1/userinfo";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthGoogleRequest.class;
}
},
/**
* Facebook
*/
FACEBOOK {
@Override
public String authorize() {
return "https://www.facebook.com/v18.0/dialog/oauth";
}
@Override
public String accessToken() {
return "https://graph.facebook.com/v18.0/oauth/access_token";
}
@Override
public String userInfo() {
return "https://graph.facebook.com/v18.0/me";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthFacebookRequest.class;
}
},
/**
* 抖音
*/
DOUYIN {
@Override
public String authorize() {
return "https://open.douyin.com/platform/oauth/connect";
}
@Override
public String accessToken() {
return "https://open.douyin.com/oauth/access_token/";
}
@Override
public String userInfo() {
return "https://open.douyin.com/oauth/userinfo/";
}
@Override
public String refresh() {
return "https://open.douyin.com/oauth/refresh_token/";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthDouyinRequest.class;
}
},
/**
* 领英
*/
LINKEDIN {
@Override
public String authorize() {
return "https://www.linkedin.com/oauth/v2/authorization";
}
@Override
public String accessToken() {
return "https://www.linkedin.com/oauth/v2/accessToken";
}
@Override
public String userInfo() {
return "https://api.linkedin.com/v2/me";
}
@Override
public String refresh() {
return "https://www.linkedin.com/oauth/v2/accessToken";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthLinkedinRequest.class;
}
},
/**
* 微软
*/
MICROSOFT {
@Override
public String authorize() {
return "https://login.microsoftonline.com/%s/oauth2/v2.0/authorize";
}
@Override
public String accessToken() {
return "https://login.microsoftonline.com/%s/oauth2/v2.0/token";
}
@Override
public String userInfo() {
return "https://graph.microsoft.com/v1.0/me";
}
@Override
public String refresh() {
return "https://login.microsoftonline.com/%s/oauth2/v2.0/token";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthMicrosoftRequest.class;
}
},
/**
* 微软中国(世纪互联)
*/
MICROSOFT_CN {
@Override
public String authorize() {
return "https://login.partner.microsoftonline.cn/%s/oauth2/v2.0/authorize";
}
@Override
public String accessToken() {
return "https://login.partner.microsoftonline.cn/%s/oauth2/v2.0/token";
}
@Override
public String userInfo() {
return "https://microsoftgraph.chinacloudapi.cn/v1.0/me";
}
@Override
public String refresh() {
return "https://login.partner.microsoftonline.cn/%s/oauth2/v2.0/token";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() { return AuthMicrosoftCnRequest.class; }
},
/**
* 小米
*/
MI {
@Override
public String authorize() {
return "https://account.xiaomi.com/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://account.xiaomi.com/oauth2/token";
}
@Override
public String userInfo() {
return "https://open.account.xiaomi.com/user/profile";
}
@Override
public String refresh() {
return "https://account.xiaomi.com/oauth2/token";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthMiRequest.class;
}
},
/**
* 今日头条
*/
TOUTIAO {
@Override
public String authorize() {
return "https://open.snssdk.com/auth/authorize";
}
@Override
public String accessToken() {
return "https://open.snssdk.com/auth/token";
}
@Override
public String userInfo() {
return "https://open.snssdk.com/data/user_profile";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthToutiaoRequest.class;
}
},
/**
* 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";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthTeambitionRequest.class;
}
},
/**
* 人人网
*/
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";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthRenrenRequest.class;
}
},
/**
* 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";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthPinterestRequest.class;
}
},
/**
* 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";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthStackOverflowRequest.class;
}
},
/**
* 华为
*
* 当前方式未来可能被废弃,建议使用 {@link this#HUAWEI_V3}
*
* @since 1.10.0
*/
@Deprecated
HUAWEI {
@Override
public String authorize() {
return "https://oauth-login.cloud.huawei.com/oauth2/v2/authorize";
}
@Override
public String accessToken() {
return "https://oauth-login.cloud.huawei.com/oauth2/v2/token";
}
@Override
public String userInfo() {
return "https://api.vmall.com/rest.php";
}
@Override
public String refresh() {
return "https://oauth-login.cloud.huawei.com/oauth2/v2/token";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthHuaweiRequest.class;
}
},
/**
* 华为最新版本的 API
*
* @since 1.16.7
*/
HUAWEI_V3 {
@Override
public String authorize() {
return "https://oauth-login.cloud.huawei.com/oauth2/v3/authorize";
}
@Override
public String accessToken() {
return "https://oauth-login.cloud.huawei.com/oauth2/v3/token";
}
@Override
public String userInfo() {
return "https://account.cloud.huawei.com/rest.php";
}
@Override
public String refresh() {
return "https://oauth-login.cloud.huawei.com/oauth2/v3/token";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthHuaweiV3Request.class;
}
},
/**
* 企业微信二维码登录
*
* @since 1.10.0
*/
WECHAT_ENTERPRISE {
@Override
public String authorize() {
return "https://open.work.weixin.qq.com/wwopen/sso/qrConnect";
}
@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";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthWeChatEnterpriseQrcodeRequest.class;
}
},
/**
* 新版企业微信 Web 登录(扫码),参考 https://developer.work.weixin.qq.com/document/path/98152
*
* @since 1.16.7
*/
WECHAT_ENTERPRISE_V2 {
@Override
public String authorize() {
return "https://login.work.weixin.qq.com/wwlogin/sso/login";
}
@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/auth/getuserinfo";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthWeChatEnterpriseQrcodeV2Request.class;
}
},
/**
* 企业微信二维码第三方登录
*/
WECHAT_ENTERPRISE_QRCODE_THIRD {
/**
* 授权的api
*
* @return url
*/
@Override
public String authorize() {
return "https://open.work.weixin.qq.com/wwopen/sso/3rd_qrConnect";
}
/**
* 获取accessToken的api
*
* @return url
*/
@Override
public String accessToken() {
return "https://qyapi.weixin.qq.com/cgi-bin/service/get_provider_token";
}
/**
* 获取用户信息的api
*
* @return url
*/
@Override
public String userInfo() {
return "https://qyapi.weixin.qq.com/cgi-bin/service/get_login_info";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthWeChatEnterpriseThirdQrcodeRequest.class;
}
},
/**
* 企业微信网页登录
*/
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";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthWeChatEnterpriseWebRequest.class;
}
},
/**
* 酷家乐
*
* @since 1.11.0
*/
KUJIALE {
@Override
public String authorize() {
return "https://oauth.kujiale.com/oauth2/show";
}
@Override
public String accessToken() {
return "https://oauth.kujiale.com/oauth2/auth/token";
}
@Override
public String userInfo() {
return "https://oauth.kujiale.com/oauth2/openapi/user";
}
@Override
public String refresh() {
return "https://oauth.kujiale.com/oauth2/auth/token/refresh";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthKujialeRequest.class;
}
},
/**
* Gitlab
*
* @since 1.11.0
*/
GITLAB {
@Override
public String authorize() {
return "https://gitlab.com/oauth/authorize";
}
@Override
public String accessToken() {
return "https://gitlab.com/oauth/token";
}
@Override
public String userInfo() {
return "https://gitlab.com/api/v4/user";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthGitlabRequest.class;
}
},
/**
* 美团
*
* @since 1.12.0
*/
MEITUAN {
@Override
public String authorize() {
return "https://openapi.waimai.meituan.com/oauth/authorize";
}
@Override
public String accessToken() {
return "https://openapi.waimai.meituan.com/oauth/access_token";
}
@Override
public String userInfo() {
return "https://openapi.waimai.meituan.com/oauth/userinfo";
}
@Override
public String refresh() {
return "https://openapi.waimai.meituan.com/oauth/refresh_token";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthMeituanRequest.class;
}
},
/**
* 饿了么
*
* 注:集成的是正式环境,非沙箱环境
*
* @since 1.12.0
*/
ELEME {
@Override
public String authorize() {
return "https://open-api.shop.ele.me/authorize";
}
@Override
public String accessToken() {
return "https://open-api.shop.ele.me/token";
}
@Override
public String userInfo() {
return "https://open-api.shop.ele.me/api/v1/";
}
@Override
public String refresh() {
return "https://open-api.shop.ele.me/token";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthElemeRequest.class;
}
},
/**
* Twitter
*
* @since 1.13.0
*/
TWITTER {
@Override
public String authorize() {
return "https://api.twitter.com/oauth/authenticate";
}
@Override
public String accessToken() {
return "https://api.twitter.com/oauth/access_token";
}
@Override
public String userInfo() {
return "https://api.twitter.com/1.1/account/verify_credentials.json";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthTwitterRequest.class;
}
},
/**
* 飞书平台,企业自建应用授权登录,原逻辑由 beacon 集成于 1.14.0 版,但最新的飞书 api 已修改,并且飞书平台一直为 {@code Deprecated} 状态
*
* 所以,最终修改该平台的实际发布版本为 1.15.9
*
* @since 1.15.9
*/
FEISHU {
@Override
public String authorize() {
return "https://open.feishu.cn/open-apis/authen/v1/index";
}
@Override
public String accessToken() {
return "https://open.feishu.cn/open-apis/authen/v1/access_token";
}
@Override
public String userInfo() {
return "https://open.feishu.cn/open-apis/authen/v1/user_info";
}
@Override
public String refresh() {
return "https://open.feishu.cn/open-apis/authen/v1/refresh_access_token";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthFeishuRequest.class;
}
},
/**
* 京东
*
* @since 1.15.0
*/
JD {
@Override
public String authorize() {
return "https://open-oauth.jd.com/oauth2/to_login";
}
@Override
public String accessToken() {
return "https://open-oauth.jd.com/oauth2/access_token";
}
@Override
public String userInfo() {
return "https://api.jd.com/routerjson";
}
@Override
public String refresh() {
return "https://open-oauth.jd.com/oauth2/refresh_token";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthJdRequest.class;
}
},
/**
* 阿里云
*/
ALIYUN {
@Override
public String authorize() {
return "https://signin.aliyun.com/oauth2/v1/auth";
}
@Override
public String accessToken() {
return "https://oauth.aliyun.com/v1/token";
}
@Override
public String userInfo() {
return "https://oauth.aliyun.com/v1/userinfo";
}
@Override
public String refresh() {
return "https://oauth.aliyun.com/v1/token";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthAliyunRequest.class;
}
},
/**
* 喜马拉雅
*/
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";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthXmlyRequest.class;
}
},
/**
* Amazon
*
* @since 1.16.0
*/
AMAZON {
@Override
public String authorize() {
return "https://www.amazon.com/ap/oa";
}
@Override
public String accessToken() {
return "https://api.amazon.com/auth/o2/token";
}
@Override
public String userInfo() {
return "https://api.amazon.com/user/profile";
}
@Override
public String refresh() {
return "https://api.amazon.com/auth/o2/token";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthAmazonRequest.class;
}
},
/**
* Slack
*
* @since 1.16.0
*/
SLACK {
@Override
public String authorize() {
return "https://slack.com/oauth/v2/authorize";
}
/**
* 该 API 获取到的是 access token
*
* https://slack.com/api/oauth.token 获取到的是 workspace token
*
* @return String
*/
@Override
public String accessToken() {
return "https://slack.com/api/oauth.v2.access";
}
@Override
public String userInfo() {
return "https://slack.com/api/users.info";
}
@Override
public String revoke() {
return "https://slack.com/api/auth.revoke";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthSlackRequest.class;
}
},
/**
* line
*
* @since 1.16.0
*/
LINE {
@Override
public String authorize() {
return "https://access.line.me/oauth2/v2.1/authorize";
}
@Override
public String accessToken() {
return "https://api.line.me/oauth2/v2.1/token";
}
@Override
public String userInfo() {
return "https://api.line.me/v2/profile";
}
@Override
public String refresh() {
return "https://api.line.me/oauth2/v2.1/token";
}
@Override
public String revoke() {
return "https://api.line.me/oauth2/v2.1/revoke";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthLineRequest.class;
}
},
/**
* Okta,
*
* 团队/组织的域名不同,此处通过配置动态组装
*
* @since 1.16.0
*/
OKTA {
@Override
public String authorize() {
return "https://%s.okta.com/oauth2/%s/v1/authorize";
}
@Override
public String accessToken() {
return "https://%s.okta.com/oauth2/%s/v1/token";
}
@Override
public String refresh() {
return "https://%s.okta.com/oauth2/%s/v1/token";
}
@Override
public String userInfo() {
return "https://%s.okta.com/oauth2/%s/v1/userinfo";
}
@Override
public String revoke() {
return "https://%s.okta.com/oauth2/%s/v1/revoke";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthOktaRequest.class;
}
},
/**
* 程序员客栈
*
* @since 1.16.2
*/
PROGINN {
@Override
public String authorize() {
return "https://www.proginn.com/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://www.proginn.com/oauth2/access_token";
}
@Override
public String userInfo() {
return "https://www.proginn.com/openapi/user/basic_info";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthProginnRequest.class;
}
},
/**
* 爱发电 爱发电
*/
AFDIAN {
@Override
public String authorize() {
return "https://afdian.net/oauth2/authorize";
}
@Override
public String accessToken() {
return "https://afdian.net/api/oauth2/access_token";
}
@Override
public String userInfo() {
return "";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthProginnRequest.class;
}
},
APPLE {
@Override
public String authorize() {
return "https://appleid.apple.com/auth/authorize";
}
/**
* @see generate_and_validate_tokens
*/
@Override
public String accessToken() {
return "https://appleid.apple.com/auth/token";
}
@Override
public String userInfo() {
return "";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthAppleRequest.class;
}
},
FIGMA{
@Override
public String authorize() {
return "https://www.figma.com/oauth";
}
@Override
public String accessToken() {
return "https://www.figma.com/api/oauth/token";
}
@Override
public String userInfo() {
return "https://api.figma.com/v1/me";
}
@Override
public String refresh() {
return "https://www.figma.com/api/oauth/refresh";
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthFigmaRequest.class;
}
},
/**
* 微信小程序授权登录
* @since yudaocode
*/
WECHAT_MINI_PROGRAM {
@Override
public String authorize() {
// 参见 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html 文档
throw new UnsupportedOperationException("不支持获取授权 url,请使用小程序内置函数 wx.login() 登录获取 code");
}
@Override
public String accessToken() {
// 参见 https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html 文档
// 获取 openid, unionId , session_key 等字段
return "https://api.weixin.qq.com/sns/jscode2session";
}
@Override
public String userInfo() {
// 参见 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html 文档
throw new UnsupportedOperationException("不支持获取用户信息 url,请使用小程序内置函数 wx.getUserProfile() 获取用户信息");
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthWechatMiniProgramRequest.class;
}
},
/**
* QQ小程序授权登录
*/
QQ_MINI_PROGRAM {
@Override
public String authorize() {
// 参见 https://q.qq.com/wiki/develop/miniprogram/frame/open_ability/open_userinfo.html 文档
throw new UnsupportedOperationException("不支持获取授权 url,请使用小程序内置函数 qq.login() 登录获取 code");
}
@Override
public String accessToken() {
// 参见 https://q.qq.com/wiki/develop/miniprogram/server/open_port/port_login.html 文档
// 获取 openid, unionId , session_key 等字段
return "https://api.q.qq.com/sns/jscode2session";
}
@Override
public String userInfo() {
// 参见 https://q.qq.com/wiki/develop/miniprogram/API/open_port/port_userinfo.html 文档
throw new UnsupportedOperationException("不支持获取用户信息 url,请使用小程序内置函数 qq.getUserInfo() 获取用户信息");
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return null;
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/config/AuthSource.java
================================================
package me.zhyd.oauth.config;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.request.AuthDefaultRequest;
/**
* OAuth平台的API地址的统一接口,提供以下方法:
* 1) {@link AuthSource#authorize()}: 获取授权url. 必须实现
* 2) {@link AuthSource#accessToken()}: 获取accessToken的url. 必须实现
* 3) {@link AuthSource#userInfo()}: 获取用户信息的url. 必须实现
* 4) {@link AuthSource#revoke()}: 获取取消授权的url. 非必须实现接口(部分平台不支持)
* 5) {@link AuthSource#refresh()}: 获取刷新授权的url. 非必须实现接口(部分平台不支持)
*
* 注:
* ①、如需通过JustAuth扩展实现第三方授权,请参考{@link AuthDefaultSource}自行创建对应的枚举类并实现{@link AuthSource}接口
* ②、如果不是使用的枚举类,那么在授权成功后获取用户信息时,需要单独处理source字段的赋值
* ③、如果扩展了对应枚举类时,在{@link me.zhyd.oauth.request.AuthRequest#login(AuthCallback)}中可以通过{@code xx.toString()}获取对应的source
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.12.0
*/
public interface AuthSource {
/**
* 授权的api
*
* @return url
*/
String authorize();
/**
* 获取accessToken的api
*
* @return url
*/
String accessToken();
/**
* 获取用户信息的api
*
* @return url
*/
String userInfo();
/**
* 取消授权的api
*
* @return url
*/
default String revoke() {
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
/**
* 刷新授权的api
*
* @return url
*/
default String refresh() {
throw new AuthException(AuthResponseStatus.UNSUPPORTED);
}
/**
* 获取Source的字符串名字
*
* @return name
*/
default String getName() {
if (this instanceof Enum) {
return String.valueOf(this);
}
return this.getClass().getSimpleName();
}
/**
* 平台对应的 AuthRequest 实现类,必须继承自 {@link AuthDefaultRequest}
*
* @return class
*/
Class extends AuthDefaultRequest> getTargetClass();
}
================================================
FILE: src/main/java/me/zhyd/oauth/config/JustAuthLogConfig.java
================================================
package me.zhyd.oauth.config;
import me.zhyd.oauth.log.Log;
/**
* JustAuth 日志配置类
*
* @author HeJin
*/
public class JustAuthLogConfig {
/**
* 设置日志级别
*
* @param level 日志级别
*/
public static void setLevel(Log.Level level) {
Log.Config.level = level;
}
/**
* 关闭日志
*/
public static void disable() {
Log.Config.enable = false;
}
/**
* 开启日志
*/
public static void enable() {
Log.Config.enable = true;
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/config/package-info.java
================================================
/**
* JustAuth 核心配置相关,包括{@code AuthConfig}和{@code AuthSource}
*/
package me.zhyd.oauth.config;
================================================
FILE: src/main/java/me/zhyd/oauth/enums/AuthResponseStatus.java
================================================
package me.zhyd.oauth.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* JustAuth通用的状态码对照表
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.8
*/
@Getter
@AllArgsConstructor
public enum AuthResponseStatus {
/**
* 2000:正常;
* other:调用异常,具体异常内容见{@code msg}
*/
SUCCESS(2000, "Success"),
FAILURE(5000, "Failure"),
NOT_IMPLEMENTED(5001, "Not Implemented"),
PARAMETER_INCOMPLETE(5002, "Parameter incomplete"),
UNSUPPORTED(5003, "Unsupported operation"),
NO_AUTH_SOURCE(5004, "AuthDefaultSource cannot be null"),
UNIDENTIFIED_PLATFORM(5005, "Unidentified platform"),
ILLEGAL_REDIRECT_URI(5006, "Illegal redirect uri"),
ILLEGAL_REQUEST(5007, "Illegal request"),
ILLEGAL_CODE(5008, "Illegal code"),
ILLEGAL_STATUS(5009, "Illegal state"),
REQUIRED_REFRESH_TOKEN(5010, "The refresh token is required; it must not be null"),
ILLEGAL_TOKEN(5011, "Invalid token"),
ILLEGAL_KID(5012, "Invalid key identifier(kid)"),
ILLEGAL_TEAM_ID(5013, "Invalid team id"),
ILLEGAL_CLIENT_ID(5014, "Invalid client id"),
ILLEGAL_CLIENT_SECRET(5015, "Invalid client secret"),
ILLEGAL_WECHAT_AGENT_ID(5016, "Illegal wechat agent id"),
;
private final int code;
private final String msg;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/AuthToutiaoErrorCode.java
================================================
package me.zhyd.oauth.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 今日头条授权登录时的异常状态码
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.8
*/
@Getter
@AllArgsConstructor
public enum AuthToutiaoErrorCode {
/**
* 0:正常;
* other:调用异常,具体异常内容见{@code desc}
*/
EC0(0, "接口调用成功"),
EC1(1, "API配置错误,未传入Client Key"),
EC2(2, "API配置错误,Client Key错误,请检查是否和开放平台的ClientKey一致"),
EC3(3, "没有授权信息"),
EC4(4, "响应类型错误"),
EC5(5, "授权类型错误"),
EC6(6, "client_secret错误"),
EC7(7, "authorize_code过期"),
EC8(8, "指定url的scheme不是https"),
EC9(9, "接口内部错误,请联系头条技术"),
EC10(10, "access_token过期"),
EC11(11, "缺少access_token"),
EC12(12, "参数缺失"),
EC13(13, "url错误"),
EC21(21, "域名与登记域名不匹配"),
EC999(999, "未知错误,请联系头条技术"),
;
private int code;
private String desc;
public static AuthToutiaoErrorCode getErrorCode(int errorCode) {
AuthToutiaoErrorCode[] errorCodes = AuthToutiaoErrorCode.values();
for (AuthToutiaoErrorCode code : errorCodes) {
if (code.getCode() == errorCode) {
return code;
}
}
return EC999;
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/AuthUserGender.java
================================================
package me.zhyd.oauth.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import me.zhyd.oauth.utils.StringUtils;
import java.util.Arrays;
/**
* 用户性别
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.8
*/
@Getter
@AllArgsConstructor
public enum AuthUserGender {
/**
* MALE/FAMALE为正常值,通过{@link AuthUserGender#getRealGender(String)}方法获取真实的性别
* UNKNOWN为容错值,部分平台不会返回用户性别,为了方便统一,使用UNKNOWN标记所有未知或不可测的用户性别信息
*/
MALE("1", "男"),
FEMALE("0", "女"),
UNKNOWN("-1", "未知");
private String code;
private String desc;
/**
* 获取用户的实际性别,常规网站
*
* @param originalGender 用户第三方标注的原始性别
* @return 用户性别
*/
public static AuthUserGender getRealGender(String originalGender) {
if (null == originalGender || UNKNOWN.getCode().equals(originalGender)) {
return UNKNOWN;
}
String[] males = {"m", "男", "1", "male"};
if (Arrays.asList(males).contains(originalGender.toLowerCase())) {
return MALE;
}
return FEMALE;
}
/**
* 获取微信平台用户的实际性别,0表示未定义,1表示男性,2表示女性
*
* @param originalGender 用户第三方标注的原始性别
* @return 用户性别
* @since 1.13.2
*/
public static AuthUserGender getWechatRealGender(String originalGender) {
if (StringUtils.isEmpty(originalGender) || "0".equals(originalGender)) {
return AuthUserGender.UNKNOWN;
}
return getRealGender(originalGender);
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/package-info.java
================================================
/**
* 提供一些必要的枚举类
*/
package me.zhyd.oauth.enums;
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthAmazonScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Amazon平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.16.0
*/
@Getter
@AllArgsConstructor
public enum AuthAmazonScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
R_LITEPROFILE("profile", "The profile scope includes a user's name and email address", true),
R_EMAILADDRESS("profile:user_id", "The profile:user_id scope only includes the user_id field of the profile", true),
W_MEMBER_SOCIAL("postal_code", "This includes the user's zip/postal code number from their primary shipping address", true);
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthAppleScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @see scope
*/
@Getter
@AllArgsConstructor
public enum AuthAppleScope implements AuthScope {
EMAIL("email", "用户邮箱", true),
NAME("name", "用户名", true),
;
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthBaiduScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 百度平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthBaiduScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
BASIC("basic", "用户基本权限,可以获取用户的基本信息 。", true),
SUPER_MSG("super_msg", "往用户的百度首页上发送消息提醒,相关API任何应用都能使用,但要想将消息提醒在百度首页显示,需要第三方在注册应用时额外填写相关信息。", false),
NETDISK("netdisk", "获取用户在个人云存储中存放的数据。", false),
PUBLIC("public", "可以访问公共的开放API。", false),
HAO123("hao123", "可以访问Hao123 提供的开放API接口。该权限需要申请开通,请将具体的理由和用途发邮件给tuangou@baidu.com。", false);
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthCodingScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Coding平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthCodingScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
USER("user", "读取用户的基本信息", false),
USER_EMAIL("user:email", "读取用户的邮件", false),
USER_PHONE("user:phone", "读取用户的手机号", false),
PROJECT("project", "授权项目信息、项目列表,仓库信息,公钥列表、成员", false),
PROJECT_DEPOT("project:depot", "完整的仓库控制权限", false),
PROJECT_WIKI("project:wiki", "授权读取与操作 wiki", false),
;
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthDingTalkScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 钉钉平台 OAuth 授权范围
*
* https://open.dingtalk.com/document/orgapp/obtain-identity-credentials#title-4up-u8w-5ug
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.16.7
*/
@Getter
@AllArgsConstructor
public enum AuthDingTalkScope implements AuthScope {
/**
* 无需申请 默认开启
*/
openid("openid", "授权后可获得用户userid", true),
/**
* 无需申请 默认开启
*/
corpid("corpid", "授权后可获得登录过程中用户选择的组织id", false)
;
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthDouyinScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 抖音平台 OAuth 授权范围
*
* https://open.douyin.com/platform/doc/6855240178122983437
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.16.1
*/
@Getter
@AllArgsConstructor
public enum AuthDouyinScope implements AuthScope {
/**
* 无需申请 默认开启
*/
USER_INFO("user_info", "返回抖音用户公开信息", true),
/**
* 无需申请 默认开启
*/
AWEME_SHARE("aweme.share", "抖音分享", false),
/**
* 普通权限,管理中心申请
*/
IM_SHARE("im.share", "分享给抖音好友", false),
RENEW_REFRESH_TOKEN("renew_refresh_token", "授权有效期动态续期", false),
FOLLOWING_LIST("following.list", "获取该用户的关注列表", false),
FANS_LIST("fans.list", "获取该用户的粉丝列表", false),
VIDEO_CREATE("video.create", "视频发布及管理", false),
VIDEO_DELETE("video.delete", "删除内容", false),
VIDEO_DATA("video.data", "查询授权用户的抖音视频数据", false),
VIDEO_LIST("video.list", "查询特定抖音视频的视频数据", false),
/**
* 特殊权限 默认关闭 管理中心申请
*/
SHARE_WITH_SOURCE("share_with_source", "分享携带来源标签,用户可点击标签进入转化页", false),
MOBILE("mobile", "用抖音帐号登录第三方平台,获得用户在抖音上的手机号码", false),
MOBILE_ALERT("mobile_alert", "用抖音帐号登录第三方平台,获得用户在抖音上的手机号码", false),
VIDEO_SEARCH("video.search", "关键词视频管理", false),
POI_SEARCH("poi.search", "查询POI信息", false),
LOGIN_ID("login_id", "静默授权直接获取该用户的open id", false),
/**
* 抖音数据权限, 默认关闭, 管理中心申请
*/
DATA_EXTERNAL_USER("data.external.user", "查询用户的获赞、评论、分享,主页访问等相关数据", false),
DATA_EXTERNAL_ITEM("data.external.item", "查询作品的获赞,评论,分享等相关数据", false),
FANS_DATA("fans.data", "获取用户粉丝画像数据", false),
HOTSEARCH("hotsearch", "获取抖音热门内容", false),
STAR_TOP_SCORE_DISPLAY("star_top_score_display", "星图达人与达人对应各指数评估分,以及星图6大热门维度下的达人榜单", false),
STAR_TOPS("star_tops", "星图达人与达人对应各指数评估分,以及星图6大热门维度下的达人榜单", false),
STAR_AUTHOR_SCORE_DISPLAY("star_author_score_display", "星图达人与达人对应各指数评估分,以及星图6大热门维度下的达人榜单", false),
notes("data.external.sdk_share", "获取用户通过分享SDK分享视频数据", false),
/**
* 定向开通 默认关闭 定向开通
*/
DISCOVERY_ENT("discovery.ent", "查询抖音电影榜、抖音剧集榜、抖音综艺榜数据", false),
;
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthFacebookScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Facebook 平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthFacebookScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
PUBLIC_PROFILE("public_profile", "权限允许应用读取用户默认的公开资料", true),
EMAIL("email", "获取用户的邮箱", false),
USER_AGE_RANGE("user_age_range", "允许应用程序访问用户的年龄范围", false),
USER_BIRTHDAY("user_birthday", "获取用户的生日", false),
USER_FRIENDS("user_friends", "获取用户的好友列表", false),
USER_GENDER("user_gender", "获取用户的性别", false),
USER_HOMETOWN("user_hometown", "获取用户的家乡信息", false),
USER_LIKES("user_likes", "获取用户的喜欢列表", false),
USER_LINK("user_link", "获取用户的个人链接", true),
USER_LOCATION("user_location", "获取用户的位置信息", false),
USER_PHOTOS("user_photos", "获取用户的相册信息", false),
USER_POSTS("user_posts", "获取用户发布的内容", false),
USER_VIDEOS("user_videos", "获取用户上传的视频信息", false),
GROUPS_ACCESS_MEMBER_INFO("groups_access_member_info", "获取公开的群组成员信息", false),
PUBLISH_TO_GROUPS("publish_to_groups", "授权您的应用程序代表某人将内容发布到组中,前提是他们已经授予您的应用程序访问权限", false),
;
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthFigmaScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Figma OAuth 授权范围
* ...
*
* @author xiangqian
* @since 1.16.6
*/
@Getter
@AllArgsConstructor
public enum AuthFigmaScope implements AuthScope {
FILE_CONTENT("files:read", "Read files, projects, users, versions, comments, components & styles, and webhooks", true),
VARIABLES("file_variables:read,file_variables:write", "Read and write to variables in Figma file. Note: this is only available to members in Enterprise organizations", false),
COMMENTS("file_comments:write", "Post and delete comments and comment reactions in files", false),
DEV_RESOURCES("file_dev_resources:read,file_dev_resources:write", "Read and write to dev resources in files", false),
LIBRARY_ANALYTICS("library_analytics:read", "Read your design system analytics", false),
WEBHOOKS("webhooks:write", "Create and manage webhooks", false);
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthGiteeScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Gitee 平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthGiteeScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
USER_INFO("user_info", "访问用户的个人信息、最新动态等", true),
PROJECTS("projects", "查看、创建、更新用户的项目", false),
PULL_REQUESTS("pull_requests", "查看、发布、更新用户的 Pull Request", false),
ISSUES("issues", "查看、发布、更新用户的 Issue", false),
NOTES("notes", "查看、发布、管理用户在项目、代码片段中的评论", false),
KEYS("keys", "查看、部署、删除用户的公钥", false),
HOOK("hook", "查看、部署、更新用户的 Webhook", false),
GROUPS("groups", "查看、管理用户的组织以及成员", false),
GISTS("gists", "查看、删除、更新用户的代码片段", false),
ENTERPRISES("enterprises", "查看、管理用户的企业以及成员", false),
EMAILS("emails", "查看用户的个人邮箱信息", false);
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthGithubScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Github平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthGithubScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
REPO_STATUS("repo:status", "Grants read/write access to public and private repository commit statuses. This scope is only necessary to grant other users or services access to private repository commit statuses without granting access to the code.", false),
REPO_DEPLOYMENT("repo_deployment", "Grants access to deployment statuses for public and private repositories. This scope is only necessary to grant other users or services access to deployment statuses, without granting access to the code.", false),
PUBLIC_REPO("public_repo", "Limits access to public repositories. That includes read/write access to code, commit statuses, repository projects, collaborators, and deployment statuses for public repositories and organizations. Also required for starring public repositories.", false),
REPO_INVITE("repo:invite", "Grants accept/decline abilities for invitations to collaborate on a repository. This scope is only necessary to grant other users or services access to invites without granting access to the code.", false),
SECURITY_EVENTS("security_events", "Grants read and write access to security events in the code scanning API.", false),
WRITE_REPO_HOOK("write:repo_hook", "Grants read, write, and ping access to hooks in public or private repositories.", false),
READ_REPO_HOOK("read:repo_hook", "Grants read and ping access to hooks in public or private repositories.", false),
ADMIN_ORG("admin:org", "Fully manage the organization and its teams, projects, and memberships.", false),
WRITE_ORG("write:org", "Read and write access to organization membership, organization projects, and team membership.", false),
READ_ORG("read:org", "Read-only access to organization membership, organization projects, and team membership.", false),
ADMIN_PUBLIC_KEY("admin:public_key", "Fully manage public keys.", false),
WRITE_PUBLIC_KEY("write:public_key", "Create, list, and view details for public keys.", false),
READ_PUBLIC_KEY("read:public_key", "List and view details for public keys.", false),
GIST("gist", "Grants write access to gists.", false),
NOTIFICATIONS("notifications", "Grants:
* read access to a user's notifications
* mark as read access to threads
* watch and unwatch access to a repository, and
* read, write, and delete access to thread subscriptions.", false),
USER("user", "Grants read/write access to profile info only. Note that this scope includes user:email and user:follow.", false),
READ_USER("read:user", "Grants access to read a user's profile data.", false),
USER_EMAIL("user:email", "Grants read access to a user's email addresses.", false),
USER_FOLLOW("user:follow", "Grants access to follow or unfollow other users.", false),
DELETE_REPO("delete_repo", "Grants access to delete adminable repositories.", false),
WRITE_DISCUSSION("write:discussion", "Allows read and write access for team discussions.", false),
READ_DISCUSSION("read:discussion", "Allows read access for team discussions.", false),
WRITE_PACKAGES("write:packages", "Grants access to upload or publish a package in GitHub Packages. For more information, see \"Publishing a package\" in the GitHub Help documentation.", false),
READ_PACKAGES("read:packages", "Grants access to download or install packages from GitHub Packages. For more information, see \"Installing a package\" in the GitHub Help documentation.", false),
DELETE_PACKAGES("delete:packages", "Grants access to delete packages from GitHub Packages. For more information, see \"Deleting packages\" in the GitHub Help documentation.", false),
ADMIN_GPG_KEY("admin:gpg_key", "Fully manage GPG keys.", false),
WRITE_GPG_KEY("write:gpg_key", "Create, list, and view details for GPG keys.", false),
READ_GPG_KEY("read:gpg_key", "List and view details for GPG keys.", false),
WORKFLOW("workflow", "Grants the ability to add and update GitHub Actions workflow files. Workflow files can be committed without this scope if the same file (with both the same path and contents) exists on another branch in the same repository.", false),
;
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthGitlabScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Gitlab 平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthGitlabScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
READ_USER("read_user", "Grants read-only access to the authenticated user's profile through the /user API endpoint, which includes username, public email, and full name. Also grants access to read-only API endpoints under /users.", true),
OPENID("openid", "Grants permission to authenticate with GitLab using OpenID Connect. Also gives read-only access to the user's profile and group memberships.", true),
PROFILE("profile", "Grants read-only access to the user's profile data using OpenID Connect.", true),
EMAIL("email", "Grants read-only access to the user's primary email address using OpenID Connect.", true),
READ_API("read_api", "Grants read access to the API, including all groups and projects, the container registry, and the package registry.", false),
READ_REPOSITORY("read_repository", "Grants read-only access to repositories on private projects using Git-over-HTTP or the Repository Files API.", false),
WRITE_REPOSITORY("write_repository", "Grants read-write access to repositories on private projects using Git-over-HTTP (not using the API).", false),
READ_REGISTRY("read_registry", "Grants read-only access to container registry images on private projects.", false),
WRITE_REGISTRY("write_registry", "Write Registry", false),
SUDO("sudo", "Grants permission to perform API actions as any user in the system, when authenticated as an admin user.", false),
API("api", "Grants complete read/write access to the API, including all groups and projects, the container registry, and the package registry.", false),
;
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthGoogleScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* Google 平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthGoogleScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
USER_OPENID("openid", "Associate you with your personal info on Google", true),
USER_EMAIL("email", "View your email address", true),
USER_PROFILE("profile", "View your basic profile info", true),
USER_PHONENUMBERS_READ("https://www.googleapis.com/auth/user.phonenumbers.read", "View your phone numbers", false),
USER_ORGANIZATION_READ("https://www.googleapis.com/auth/user.organization.read", "See your education, work history and org info", false),
USER_GENDER_READ("https://www.googleapis.com/auth/user.gender.read", "See your gender", false),
USER_EMAILS_READ("https://www.googleapis.com/auth/user.emails.read", "View your email addresses", false),
USER_BIRTHDAY_READ("https://www.googleapis.com/auth/user.birthday.read", "View your complete date of birth", false),
USER_ADDRESSES_READ("https://www.googleapis.com/auth/user.addresses.read", "View your street addresses", false),
USERINFO_PROFILE("https://www.googleapis.com/auth/userinfo.profile", "See your personal info, including any personal info you've made publicly available", false),
USERINFO_EMAIL("https://www.googleapis.com/auth/userinfo.email", "View your email address", false),
YT_ANALYTICS_READONLY("https://www.googleapis.com/auth/yt-analytics.readonly", "View YouTube Analytics reports for your YouTube content", false),
YT_ANALYTICS_MONETARY_READONLY("https://www.googleapis.com/auth/yt-analytics-monetary.readonly", "View monetary and non-monetary YouTube Analytics reports for your YouTube content", false),
YOUTUBEPARTNER_CHANNEL_AUDIT("https://www.googleapis.com/auth/youtubepartner-channel-audit", "View private information of your YouTube channel relevant during the audit process with a YouTube partner", false),
YOUTUBEPARTNER("https://www.googleapis.com/auth/youtubepartner", "View and manage your assets and associated content on YouTube", false),
YOUTUBE_UPLOAD("https://www.googleapis.com/auth/youtube.upload", "Manage your YouTube videos", false),
YOUTUBE_READONLY("https://www.googleapis.com/auth/youtube.readonly", "View your YouTube account", false),
YOUTUBE_FORCE_SSL("https://www.googleapis.com/auth/youtube.force-ssl", "See, edit, and permanently delete your YouTube videos, ratings, comments and captions", false),
YOUTUBE_CHANNEL_MEMBERSHIPS_CREATOR("https://www.googleapis.com/auth/youtube.channel-memberships.creator", "See a list of your current active channel members, their current level, and when they became a member", false),
YOUTUBE("https://www.googleapis.com/auth/youtube", "Manage your YouTube account", false),
WEBMASTERS_READONLY("https://www.googleapis.com/auth/webmasters.readonly", "View Search Console data for your verified sites", false),
WEBMASTERS("https://www.googleapis.com/auth/webmasters", "View and manage Search Console data for your verified sites", false),
VERIFIEDACCESS("https://www.googleapis.com/auth/verifiedaccess", "Verify your enterprise credentials", false),
TRACE_APPEND("https://www.googleapis.com/auth/trace.append", "Write Trace data for a project or application", false),
TASKS_READONLY("https://www.googleapis.com/auth/tasks.readonly", "View your tasks", false),
TASKS("https://www.googleapis.com/auth/tasks", "Create, edit, organize, and delete all your tasks", false),
TAGMANAGER_READONLY("https://www.googleapis.com/auth/tagmanager.readonly", "View your Google Tag Manager container and its subcomponents", false),
TAGMANAGER_PUBLISH("https://www.googleapis.com/auth/tagmanager.publish", "Publish your Google Tag Manager container versions", false),
TAGMANAGER_MANAGE_USERS("https://www.googleapis.com/auth/tagmanager.manage.users", "Manage user permissions of your Google Tag Manager account and container", false),
TAGMANAGER_MANAGE_ACCOUNTS("https://www.googleapis.com/auth/tagmanager.manage.accounts", "View and manage your Google Tag Manager accounts", false),
TAGMANAGER_EDIT_CONTAINERVERSIONS("https://www.googleapis.com/auth/tagmanager.edit.containerversions", "Manage your Google Tag Manager container versions", false),
TAGMANAGER_EDIT_CONTAINERS("https://www.googleapis.com/auth/tagmanager.edit.containers", "Manage your Google Tag Manager container and its subcomponents, excluding versioning and publishing", false),
TAGMANAGER_DELETE_CONTAINERS("https://www.googleapis.com/auth/tagmanager.delete.containers", "Delete your Google Tag Manager containers", false),
STREETVIEWPUBLISH("https://www.googleapis.com/auth/streetviewpublish", "Publish and manage your 360 photos on Google Street View", false),
SQLSERVICE_ADMIN("https://www.googleapis.com/auth/sqlservice.admin", "Manage your Google SQL Service instances", false),
SPREADSHEETS_READONLY("https://www.googleapis.com/auth/spreadsheets.readonly", "View your Google Spreadsheets", false),
SPREADSHEETS("https://www.googleapis.com/auth/spreadsheets", "See, edit, create, and delete your spreadsheets in Google Drive", false),
SPANNER_DATA("https://www.googleapis.com/auth/spanner.data", "View and manage the contents of your Spanner databases", false),
SPANNER_ADMIN("https://www.googleapis.com/auth/spanner.admin", "Administer your Spanner databases", false),
SOURCE_READ_WRITE("https://www.googleapis.com/auth/source.read_write", "Manage the contents of your source code repositories", false),
SOURCE_READ_ONLY("https://www.googleapis.com/auth/source.read_only", "View the contents of your source code repositories", false),
SOURCE_FULL_CONTROL("https://www.googleapis.com/auth/source.full_control", "Manage your source code repositories", false),
SITEVERIFICATION_VERIFY_ONLY("https://www.googleapis.com/auth/siteverification.verify_only", "Manage your new site verifications with Google", false),
SITEVERIFICATION("https://www.googleapis.com/auth/siteverification", "Manage the list of sites and domains you control", false),
SERVICECONTROL("https://www.googleapis.com/auth/servicecontrol", "Manage your Google Service Control data", false),
SERVICE_MANAGEMENT_READONLY("https://www.googleapis.com/auth/service.management.readonly", "View your Google API service configuration", false),
SERVICE_MANAGEMENT("https://www.googleapis.com/auth/service.management", "Manage your Google API service configuration", false),
SCRIPT_PROJECTS_READONLY("https://www.googleapis.com/auth/script.projects.readonly", "View Google Apps Script projects", false),
SCRIPT_PROJECTS("https://www.googleapis.com/auth/script.projects", "Create and update Google Apps Script projects", false),
SCRIPT_PROCESSES("https://www.googleapis.com/auth/script.processes", "View Google Apps Script processes", false),
SCRIPT_METRICS("https://www.googleapis.com/auth/script.metrics", "View Google Apps Script project's metrics", false),
SCRIPT_DEPLOYMENTS_READONLY("https://www.googleapis.com/auth/script.deployments.readonly", "View Google Apps Script deployments", false),
SCRIPT_DEPLOYMENTS("https://www.googleapis.com/auth/script.deployments", "Create and update Google Apps Script deployments", false),
PUBSUB("https://www.googleapis.com/auth/pubsub", "View and manage Pub/Sub topics and subscriptions", false),
PRESENTATIONS_READONLY("https://www.googleapis.com/auth/presentations.readonly", "View your Google Slides presentations", false),
PRESENTATIONS("https://www.googleapis.com/auth/presentations", "View and manage your Google Slides presentations", false),
PHOTOSLIBRARY_SHARING("https://www.googleapis.com/auth/photoslibrary.sharing", "Manage and add to shared albums on your behalf", false),
PHOTOSLIBRARY_READONLY_APPCREATEDDATA("https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata", "Manage photos added by this app", false),
PHOTOSLIBRARY_READONLY("https://www.googleapis.com/auth/photoslibrary.readonly", "View your Google Photos library", false),
PHOTOSLIBRARY_APPENDONLY("https://www.googleapis.com/auth/photoslibrary.appendonly", "Add to your Google Photos library", false),
PHOTOSLIBRARY("https://www.googleapis.com/auth/photoslibrary", "View and manage your Google Photos library", false),
NDEV_CLOUDMAN_READONLY("https://www.googleapis.com/auth/ndev.cloudman.readonly", "View your Google Cloud Platform management resources and deployment status information", false),
NDEV_CLOUDMAN("https://www.googleapis.com/auth/ndev.cloudman", "View and manage your Google Cloud Platform management resources and deployment status information", false),
NDEV_CLOUDDNS_READWRITE("https://www.googleapis.com/auth/ndev.clouddns.readwrite", "View and manage your DNS records hosted by Google Cloud DNS", false),
NDEV_CLOUDDNS_READONLY("https://www.googleapis.com/auth/ndev.clouddns.readonly", "View your DNS records hosted by Google Cloud DNS", false),
MONITORING_WRITE("https://www.googleapis.com/auth/monitoring.write", "Publish metric data to your Google Cloud projects", false),
MONITORING_READ("https://www.googleapis.com/auth/monitoring.read", "View monitoring data for all of your Google Cloud and third-party projects", false),
MONITORING("https://www.googleapis.com/auth/monitoring", "View and write monitoring data for all of your Google and third-party Cloud and API projects", false),
MANUFACTURERCENTER("https://www.googleapis.com/auth/manufacturercenter", "Manage your product listings for Google Manufacturer Center", false),
LOGGING_WRITE("https://www.googleapis.com/auth/logging.write", "Submit log data for your projects", false),
LOGGING_READ("https://www.googleapis.com/auth/logging.read", "View log data for your projects", false),
LOGGING_ADMIN("https://www.googleapis.com/auth/logging.admin", "Administrate log data for your projects", false),
JOBS("https://www.googleapis.com/auth/jobs", "Manage job postings", false),
INDEXING("https://www.googleapis.com/auth/indexing", "Submit data to Google for indexing", false),
GROUPS("https://www.googleapis.com/auth/groups", "View and manage your Google Groups", false),
GMAIL("https://mail.google.com/", "Read, compose, send, and permanently delete all your email from Gmail", false),
GMAIL_SETTINGS_SHARING("https://www.googleapis.com/auth/gmail.settings.sharing", "Manage your sensitive mail settings, including who can manage your mail", false),
GMAIL_SETTINGS_BASIC("https://www.googleapis.com/auth/gmail.settings.basic", "Manage your basic mail settings", false),
GMAIL_SEND("https://www.googleapis.com/auth/gmail.send", "Send email on your behalf", false),
GMAIL_READONLY("https://www.googleapis.com/auth/gmail.readonly", "View your email messages and settings", false),
GMAIL_MODIFY("https://www.googleapis.com/auth/gmail.modify", "View and modify but not delete your email", false),
GMAIL_METADATA("https://www.googleapis.com/auth/gmail.metadata", "View your email message metadata such as labels and headers, but not the email body", false),
GMAIL_LABELS("https://www.googleapis.com/auth/gmail.labels", "Manage mailbox labels", false),
GMAIL_INSERT("https://www.googleapis.com/auth/gmail.insert", "Insert mail into your mailbox", false),
GMAIL_COMPOSE("https://www.googleapis.com/auth/gmail.compose", "Manage drafts and send emails", false),
GMAIL_ADDONS_CURRENT_MESSAGE_READONLY("https://www.googleapis.com/auth/gmail.addons.current.message.readonly", "View your email messages when the add-on is running", false),
GMAIL_ADDONS_CURRENT_MESSAGE_METADATA("https://www.googleapis.com/auth/gmail.addons.current.message.metadata", "View your email message metadata when the add-on is running", false),
GMAIL_ADDONS_CURRENT_MESSAGE_ACTION("https://www.googleapis.com/auth/gmail.addons.current.message.action", "View your email messages when you interact with the add-on", false),
GMAIL_ADDONS_CURRENT_ACTION_COMPOSE("https://www.googleapis.com/auth/gmail.addons.current.action.compose", "Manage drafts and send emails when you interact with the add-on", false),
GENOMICS("https://www.googleapis.com/auth/genomics", "View and manage Genomics data", false),
GAMES("https://www.googleapis.com/auth/games", "Create, edit, and delete your Google Play Games activity", false),
FORMS_CURRENTONLY("https://www.googleapis.com/auth/forms.currentonly", "View and manage forms that this application has been installed in", false),
FORMS("https://www.googleapis.com/auth/forms", "View and manage your forms in Google Drive", false),
FITNESS_REPRODUCTIVE_HEALTH_WRITE("https://www.googleapis.com/auth/fitness.reproductive_health.write", "See and add info about your reproductive health in Google Fit. I consent to Google sharing my reporductive health information with this app.", false),
FITNESS_REPRODUCTIVE_HEALTH_READ("https://www.googleapis.com/auth/fitness.reproductive_health.read", "See info about your reproductive health in Google Fit. I consent to Google sharing my reporductive health information with this app.", false),
FITNESS_OXYGEN_SATURATION_WRITE("https://www.googleapis.com/auth/fitness.oxygen_saturation.write", "See and add info about your oxygen saturation in Google Fit. I consent to Google sharing my oxygen saturation information with this app.", false),
FITNESS_OXYGEN_SATURATION_READ("https://www.googleapis.com/auth/fitness.oxygen_saturation.read", "See info about your oxygen saturation in Google Fit. I consent to Google sharing my oxygen saturation information with this app.", false),
FITNESS_NUTRITION_WRITE("https://www.googleapis.com/auth/fitness.nutrition.write", "See and add to info about your nutrition in Google Fit", false),
FITNESS_NUTRITION_READ("https://www.googleapis.com/auth/fitness.nutrition.read", "See info about your nutrition in Google Fit", false),
FITNESS_LOCATION_WRITE("https://www.googleapis.com/auth/fitness.location.write", "See and add to your Google Fit location data", false),
FITNESS_LOCATION_READ("https://www.googleapis.com/auth/fitness.location.read", "See your Google Fit speed and distance data", false),
FITNESS_BODY_TEMPERATURE_WRITE("https://www.googleapis.com/auth/fitness.body_temperature.write", "See and add to info about your body temperature in Google Fit. I consent to Google sharing my body temperature information with this app.", false),
FITNESS_BODY_TEMPERATURE_READ("https://www.googleapis.com/auth/fitness.body_temperature.read", "See info about your body temperature in Google Fit. I consent to Google sharing my body temperature information with this app.", false),
FITNESS_BODY_WRITE("https://www.googleapis.com/auth/fitness.body.write", "See and add info about your body measurements and heart rate to Google Fit", false),
FITNESS_BODY_READ("https://www.googleapis.com/auth/fitness.body.read", "See info about your body measurements and heart rate in Google Fit", false),
FITNESS_BLOOD_PRESSURE_WRITE("https://www.googleapis.com/auth/fitness.blood_pressure.write", "See and add info about your blood pressure in Google Fit. I consent to Google sharing my blood pressure information with this app.", false),
FITNESS_BLOOD_PRESSURE_READ("https://www.googleapis.com/auth/fitness.blood_pressure.read", "See info about your blood pressure in Google Fit. I consent to Google sharing my blood pressure information with this app.", false),
FITNESS_BLOOD_GLUCOSE_WRITE("https://www.googleapis.com/auth/fitness.blood_glucose.write", "See and add info about your blood glucose to Google Fit. I consent to Google sharing my blood glucose information with this app.", false),
FITNESS_BLOOD_GLUCOSE_READ("https://www.googleapis.com/auth/fitness.blood_glucose.read", "See info about your blood glucose in Google Fit. I consent to Google sharing my blood glucose information with this app.", false),
FITNESS_ACTIVITY_WRITE("https://www.googleapis.com/auth/fitness.activity.write", "See and add to your Google Fit physical activity data", false),
FITNESS_ACTIVITY_READ("https://www.googleapis.com/auth/fitness.activity.read", "Use Google Fit to see and store your physical activity data", false),
FIREBASE_READONLY("https://www.googleapis.com/auth/firebase.readonly", "View all your Firebase data and settings", false),
FIREBASE("https://www.googleapis.com/auth/firebase", "View and administer all your Firebase data and settings", false),
EDISCOVERY_READONLY("https://www.googleapis.com/auth/ediscovery.readonly", "View your eDiscovery data", false),
EDISCOVERY("https://www.googleapis.com/auth/ediscovery", "Manage your eDiscovery data", false),
DRIVE_SCRIPTS("https://www.googleapis.com/auth/drive.scripts", "Modify your Google Apps Script scripts' behavior", false),
DRIVE_READONLY("https://www.googleapis.com/auth/drive.readonly", "See and download all your Google Drive files", false),
DRIVE_PHOTOS_READONLY("https://www.googleapis.com/auth/drive.photos.readonly", "View the photos, videos and albums in your Google Photos", false),
DRIVE_METADATA_READONLY("https://www.googleapis.com/auth/drive.metadata.readonly", "View metadata for files in your Google Drive", false),
DRIVE_METADATA("https://www.googleapis.com/auth/drive.metadata", "View and manage metadata of files in your Google Drive", false),
DRIVE_FILE("https://www.googleapis.com/auth/drive.file", "View and manage Google Drive files and folders that you have opened or created with this app", false),
DRIVE_APPDATA("https://www.googleapis.com/auth/drive.appdata", "View and manage its own configuration data in your Google Drive", false),
DRIVE_ACTIVITY_READONLY("https://www.googleapis.com/auth/drive.activity.readonly", "View the activity record of files in your Google Drive", false),
DRIVE_ACTIVITY("https://www.googleapis.com/auth/drive.activity", "View and add to the activity record of files in your Google Drive", false),
DRIVE("https://www.googleapis.com/auth/drive", "See, edit, create, and delete all of your Google Drive files", false),
ACTIVITY("https://www.googleapis.com/auth/activity", "View the activity history of your Google apps", false),
DOUBLECLICKSEARCH("https://www.googleapis.com/auth/doubleclicksearch", "View and manage your advertising data in DoubleClick Search", false),
DOUBLECLICKBIDMANAGER("https://www.googleapis.com/auth/doubleclickbidmanager", "View and manage your reports in DoubleClick Bid Manager", false),
DOCUMENTS_READONLY("https://www.googleapis.com/auth/documents.readonly", "View your Google Docs documents", false),
DOCUMENTS("https://www.googleapis.com/auth/documents", "View and manage your Google Docs documents", false),
DISPLAY_VIDEO("https://www.googleapis.com/auth/display-video", "Create, see, edit, and permanently delete your Display & Video 360 entities and reports", false),
DIRECTORY_READONLY("https://www.googleapis.com/auth/directory.readonly", "See and download your organization's GSuite directory", false),
DIALOGFLOW("https://www.googleapis.com/auth/dialogflow", "View, manage and query your Dialogflow agents", false),
DFATRAFFICKING("https://www.googleapis.com/auth/dfatrafficking", "View and manage your DoubleClick Campaign Manager's (DCM) display ad campaigns", false),
DFAREPORTING("https://www.googleapis.com/auth/dfareporting", "View and manage DoubleClick for Advertisers reports", false),
DEVSTORAGE_READ_WRITE("https://www.googleapis.com/auth/devstorage.read_write", "Manage your data in Google Cloud Storage", false),
DEVSTORAGE_READ_ONLY("https://www.googleapis.com/auth/devstorage.read_only", "View your data in Google Cloud Storage", false),
DEVSTORAGE_FULL_CONTROL("https://www.googleapis.com/auth/devstorage.full_control", "Manage your data and permissions in Google Cloud Storage", false),
DDMCONVERSIONS("https://www.googleapis.com/auth/ddmconversions", "Manage DoubleClick Digital Marketing conversions", false),
DATASTORE("https://www.googleapis.com/auth/datastore", "View and manage your Google Cloud Datastore data", false),
CONTENT("https://www.googleapis.com/auth/content", "Manage your product listings and accounts for Google Shopping", false),
CONTACTS_READONLY("https://www.googleapis.com/auth/contacts.readonly", "See and download your contacts", false),
CONTACTS_OTHER_READONLY("https://www.googleapis.com/auth/contacts.other.readonly", "See and download contact info automatically saved in your \"Other contacts\"", false),
CONTACTS("https://www.googleapis.com/auth/contacts", "See, edit, download, and permanently delete your contacts", false),
CONTACTS_FEEDS("https://www.google.com/m8/feeds", "See, edit, download, and permanently delete your contacts", false),
COMPUTE_READONLY("https://www.googleapis.com/auth/compute.readonly", "View your Google Compute Engine resources", false),
COMPUTE("https://www.googleapis.com/auth/compute", "View and manage your Google Compute Engine resources", false),
CLOUDRUNTIMECONFIG("https://www.googleapis.com/auth/cloudruntimeconfig", "Manage your Google Cloud Platform services' runtime configuration", false),
CLOUDKMS("https://www.googleapis.com/auth/cloudkms", "View and manage your keys and secrets stored in Cloud Key Management Service", false),
CLOUDIOT("https://www.googleapis.com/auth/cloudiot", "Register and manage devices in the Google Cloud IoT service", false),
CLOUD_SEARCH_STATS_INDEXING("https://www.googleapis.com/auth/cloud_search.stats.indexing", "Index and serve your organization's data with Cloud Search", false),
CLOUD_SEARCH_STATS("https://www.googleapis.com/auth/cloud_search.stats", "Index and serve your organization's data with Cloud Search", false),
CLOUD_SEARCH_SETTINGS_QUERY("https://www.googleapis.com/auth/cloud_search.settings.query", "Index and serve your organization's data with Cloud Search", false),
CLOUD_SEARCH_SETTINGS_INDEXING("https://www.googleapis.com/auth/cloud_search.settings.indexing", "Index and serve your organization's data with Cloud Search", false),
CLOUD_SEARCH_SETTINGS("https://www.googleapis.com/auth/cloud_search.settings", "Index and serve your organization's data with Cloud Search", false),
CLOUD_SEARCH_QUERY("https://www.googleapis.com/auth/cloud_search.query", "Search your organization's data in the Cloud Search index", false),
CLOUD_SEARCH_INDEXING("https://www.googleapis.com/auth/cloud_search.indexing", "Index and serve your organization's data with Cloud Search", false),
CLOUD_SEARCH_DEBUG("https://www.googleapis.com/auth/cloud_search.debug", "Index and serve your organization's data with Cloud Search", false),
CLOUD_SEARCH("https://www.googleapis.com/auth/cloud_search", "Index and serve your organization's data with Cloud Search", false),
CLOUD_DEBUGGER("https://www.googleapis.com/auth/cloud_debugger", "Use Stackdriver Debugger", false),
CLOUD_VISION("https://www.googleapis.com/auth/cloud-vision", "Apply machine learning models to understand and label images", false),
CLOUD_TRANSLATION("https://www.googleapis.com/auth/cloud-translation", "Translate text from one language to another using Google Translate", false),
CLOUD_PLATFORM_READ_ONLY("https://www.googleapis.com/auth/cloud-platform.read-only", "View your data across Google Cloud Platform services", false),
CLOUD_PLATFORM("https://www.googleapis.com/auth/cloud-platform", "View and manage your data across Google Cloud Platform services", false),
CLOUD_LANGUAGE("https://www.googleapis.com/auth/cloud-language", "Apply machine learning models to reveal the structure and meaning of text", false),
CLOUD_IDENTITY_GROUPS_READONLY("https://www.googleapis.com/auth/cloud-identity.groups.readonly", "See any Cloud Identity Groups that you can access, including group members and their emails", false),
CLOUD_IDENTITY_GROUPS("https://www.googleapis.com/auth/cloud-identity.groups", "See, change, create, and delete any of the Cloud Identity Groups that you can access, including the members of each group", false),
CLOUD_BIGTABLE_ADMIN_TABLE("https://www.googleapis.com/auth/cloud-bigtable.admin.table", "Administer your Cloud Bigtable tables", false),
CLOUD_BIGTABLE_ADMIN_CLUSTER("https://www.googleapis.com/auth/cloud-bigtable.admin.cluster", "Administer your Cloud Bigtable clusters", false),
CLOUD_BIGTABLE_ADMIN("https://www.googleapis.com/auth/cloud-bigtable.admin", "Administer your Cloud Bigtable tables and clusters", false),
CLASSROOM_TOPICS_READONLY("https://www.googleapis.com/auth/classroom.topics.readonly", "View topics in Google Classroom", false),
CLASSROOM_TOPICS("https://www.googleapis.com/auth/classroom.topics", "See, create, and edit topics in Google Classroom", false),
CLASSROOM_STUDENT_SUBMISSIONS_STUDENTS_READONLY("https://www.googleapis.com/auth/classroom.student-submissions.students.readonly", "View course work and grades for students in the Google Classroom classes you teach or administer", false),
CLASSROOM_STUDENT_SUBMISSIONS_ME_READONLY("https://www.googleapis.com/auth/classroom.student-submissions.me.readonly", "View your course work and grades in Google Classroom", false),
CLASSROOM_ROSTERS_READONLY("https://www.googleapis.com/auth/classroom.rosters.readonly", "View your Google Classroom class rosters", false),
CLASSROOM_ROSTERS("https://www.googleapis.com/auth/classroom.rosters", "Manage your Google Classroom class rosters", false),
CLASSROOM_PUSH_NOTIFICATIONS("https://www.googleapis.com/auth/classroom.push-notifications", "Receive notifications about your Google Classroom data", false),
CLASSROOM_PROFILE_PHOTOS("https://www.googleapis.com/auth/classroom.profile.photos", "View the profile photos of people in your classes", false),
CLASSROOM_PROFILE_EMAILS("https://www.googleapis.com/auth/classroom.profile.emails", "View the email addresses of people in your classes", false),
CLASSROOM_GUARDIANLINKS_STUDENTS_READONLY("https://www.googleapis.com/auth/classroom.guardianlinks.students.readonly", "View guardians for students in your Google Classroom classes", false),
CLASSROOM_GUARDIANLINKS_STUDENTS("https://www.googleapis.com/auth/classroom.guardianlinks.students", "View and manage guardians for students in your Google Classroom classes", false),
CLASSROOM_GUARDIANLINKS_ME_READONLY("https://www.googleapis.com/auth/classroom.guardianlinks.me.readonly", "View your Google Classroom guardians", false),
CLASSROOM_COURSEWORK_STUDENTS_READONLY("https://www.googleapis.com/auth/classroom.coursework.students.readonly", "View course work and grades for students in the Google Classroom classes you teach or administer", false),
CLASSROOM_COURSEWORK_STUDENTS("https://www.googleapis.com/auth/classroom.coursework.students", "Manage course work and grades for students in the Google Classroom classes you teach and view the course work and grades for classes you administer", false),
CLASSROOM_COURSEWORK_ME_READONLY("https://www.googleapis.com/auth/classroom.coursework.me.readonly", "View your course work and grades in Google Classroom", false),
CLASSROOM_COURSEWORK_ME("https://www.googleapis.com/auth/classroom.coursework.me", "Manage your course work and view your grades in Google Classroom", false),
CLASSROOM_COURSES_READONLY("https://www.googleapis.com/auth/classroom.courses.readonly", "View your Google Classroom classes", false),
CLASSROOM_COURSES("https://www.googleapis.com/auth/classroom.courses", "Manage your Google Classroom classes", false),
CLASSROOM_ANNOUNCEMENTS_READONLY("https://www.googleapis.com/auth/classroom.announcements.readonly", "View announcements in Google Classroom", false),
CLASSROOM_ANNOUNCEMENTS("https://www.googleapis.com/auth/classroom.announcements", "View and manage announcements in Google Classroom", false),
CALENDAR_SETTINGS_READONLY("https://www.googleapis.com/auth/calendar.settings.readonly", "View your Calendar settings", false),
CALENDAR_READONLY("https://www.googleapis.com/auth/calendar.readonly", "View your calendars", false),
CALENDAR_EVENTS_READONLY("https://www.googleapis.com/auth/calendar.events.readonly", "View events on all your calendars", false),
CALENDAR_EVENTS("https://www.googleapis.com/auth/calendar.events", "View and edit events on all your calendars", false),
CALENDAR("https://www.googleapis.com/auth/calendar", "See, edit, share, and permanently delete all the calendars you can access using Google Calendar", false),
CALENDAR_FEEDS("https://www.google.com/calendar/feeds", "See, edit, share, and permanently delete all the calendars you can access using Google Calendar", false),
BOOKS("https://www.googleapis.com/auth/books", "Manage your books", false),
BLOGGER_READONLY("https://www.googleapis.com/auth/blogger.readonly", "View your Blogger account", false),
BLOGGER("https://www.googleapis.com/auth/blogger", "Manage your Blogger account", false),
BIGTABLE_ADMIN_TABLE("https://www.googleapis.com/auth/bigtable.admin.table", "Administer your Cloud Bigtable tables", false),
BIGTABLE_ADMIN_INSTANCE("https://www.googleapis.com/auth/bigtable.admin.instance", "Administer your Cloud Bigtable clusters", false),
BIGTABLE_ADMIN_CLUSTER("https://www.googleapis.com/auth/bigtable.admin.cluster", "Administer your Cloud Bigtable clusters", false),
BIGTABLE_ADMIN("https://www.googleapis.com/auth/bigtable.admin", "Administer your Cloud Bigtable tables and clusters", false),
BIGQUERY_READONLY("https://www.googleapis.com/auth/bigquery.readonly", "View your data in Google BigQuery", false),
BIGQUERY_INSERTDATA("https://www.googleapis.com/auth/bigquery.insertdata", "Insert data into Google BigQuery", false),
BIGQUERY("https://www.googleapis.com/auth/bigquery", "View and manage your data in Google BigQuery", false),
APPS_ORDER_READONLY("https://www.googleapis.com/auth/apps.order.readonly", "Manage users on your domain", false),
APPS_ORDER("https://www.googleapis.com/auth/apps.order", "Manage users on your domain", false),
APPS_LICENSING("https://www.googleapis.com/auth/apps.licensing", "View and manage G Suite licenses for your domain", false),
APPS_GROUPS_SETTINGS("https://www.googleapis.com/auth/apps.groups.settings", "View and manage the settings of a G Suite group", false),
APPS_GROUPS_MIGRATION("https://www.googleapis.com/auth/apps.groups.migration", "Manage messages in groups on your domain", false),
APPS_ALERTS("https://www.googleapis.com/auth/apps.alerts", "See and delete your domain's G Suite alerts, and send alert feedback", false),
APPENGINE_ADMIN("https://www.googleapis.com/auth/appengine.admin", "View and manage your applications deployed on Google App Engine", false),
ANDROIDPUBLISHER("https://www.googleapis.com/auth/androidpublisher", "View and manage your Google Play Developer account", false),
ANDROIDMANAGEMENT("https://www.googleapis.com/auth/androidmanagement", "Manage Android devices and apps for your customers", false),
ANDROIDENTERPRISE("https://www.googleapis.com/auth/androidenterprise", "Manage corporate Android devices", false),
ANALYTICS_USER_DELETION("https://www.googleapis.com/auth/analytics.user.deletion", "Manage Google Analytics user deletion requests", false),
ANALYTICS_READONLY("https://www.googleapis.com/auth/analytics.readonly", "View your Google Analytics data", false),
ANALYTICS_PROVISION("https://www.googleapis.com/auth/analytics.provision", "Create a new Google Analytics account along with its default property and view", false),
ANALYTICS_MANAGE_USERS_READONLY("https://www.googleapis.com/auth/analytics.manage.users.readonly", "View Google Analytics user permissions", false),
ANALYTICS_MANAGE_USERS("https://www.googleapis.com/auth/analytics.manage.users", "Manage Google Analytics Account users by email address", false),
ANALYTICS_EDIT("https://www.googleapis.com/auth/analytics.edit", "Edit Google Analytics management entities", false),
ANALYTICS("https://www.googleapis.com/auth/analytics", "View and manage your Google Analytics data", false),
ADSENSEHOST("https://www.googleapis.com/auth/adsensehost", "View and manage your AdSense host data and associated accounts", false),
ADSENSE_READONLY("https://www.googleapis.com/auth/adsense.readonly", "View your AdSense data", false),
ADSENSE("https://www.googleapis.com/auth/adsense", "View and manage your AdSense data", false),
ADMIN_REPORTS_USAGE_READONLY("https://www.googleapis.com/auth/admin.reports.usage.readonly", "View usage reports for your G Suite domain", false),
ADMIN_REPORTS_AUDIT_READONLY("https://www.googleapis.com/auth/admin.reports.audit.readonly", "View audit reports for your G Suite domain", false),
ADMIN_DIRECTORY_USERSCHEMA_READONLY("https://www.googleapis.com/auth/admin.directory.userschema.readonly", "View user schemas on your domain", false),
ADMIN_DIRECTORY_USERSCHEMA("https://www.googleapis.com/auth/admin.directory.userschema", "View and manage the provisioning of user schemas on your domain", false),
ADMIN_DIRECTORY_USER_SECURITY("https://www.googleapis.com/auth/admin.directory.user.security", "Manage data access permissions for users on your domain", false),
ADMIN_DIRECTORY_USER_READONLY("https://www.googleapis.com/auth/admin.directory.user.readonly", "View users on your domain", false),
ADMIN_DIRECTORY_USER_ALIAS_READONLY("https://www.googleapis.com/auth/admin.directory.user.alias.readonly", "View user aliases on your domain", false),
ADMIN_DIRECTORY_USER_ALIAS("https://www.googleapis.com/auth/admin.directory.user.alias", "View and manage user aliases on your domain", false),
ADMIN_DIRECTORY_USER("https://www.googleapis.com/auth/admin.directory.user", "View and manage the provisioning of users on your domain", false),
ADMIN_DIRECTORY_ROLEMANAGEMENT_READONLY("https://www.googleapis.com/auth/admin.directory.rolemanagement.readonly", "View delegated admin roles for your domain", false),
ADMIN_DIRECTORY_ROLEMANAGEMENT("https://www.googleapis.com/auth/admin.directory.rolemanagement", "Manage delegated admin roles for your domain", false),
ADMIN_DIRECTORY_RESOURCE_CALENDAR_READONLY("https://www.googleapis.com/auth/admin.directory.resource.calendar.readonly", "View calendar resources on your domain", false),
ADMIN_DIRECTORY_RESOURCE_CALENDAR("https://www.googleapis.com/auth/admin.directory.resource.calendar", "View and manage the provisioning of calendar resources on your domain", false),
ADMIN_DIRECTORY_ORGUNIT_READONLY("https://www.googleapis.com/auth/admin.directory.orgunit.readonly", "View organization units on your domain", false),
ADMIN_DIRECTORY_ORGUNIT("https://www.googleapis.com/auth/admin.directory.orgunit", "View and manage organization units on your domain", false),
ADMIN_DIRECTORY_NOTIFICATIONS("https://www.googleapis.com/auth/admin.directory.notifications", "View and manage notifications received on your domain", false),
ADMIN_DIRECTORY_GROUP_READONLY("https://www.googleapis.com/auth/admin.directory.group.readonly", "View groups on your domain", false),
ADMIN_DIRECTORY_GROUP_MEMBER_READONLY("https://www.googleapis.com/auth/admin.directory.group.member.readonly", "View group subscriptions on your domain", false),
ADMIN_DIRECTORY_GROUP_MEMBER("https://www.googleapis.com/auth/admin.directory.group.member", "View and manage group subscriptions on your domain", false),
ADMIN_DIRECTORY_GROUP("https://www.googleapis.com/auth/admin.directory.group", "View and manage the provisioning of groups on your domain", false),
ADMIN_DIRECTORY_DOMAIN_READONLY("https://www.googleapis.com/auth/admin.directory.domain.readonly", "View domains related to your customers", false),
ADMIN_DIRECTORY_DOMAIN("https://www.googleapis.com/auth/admin.directory.domain", "View and manage the provisioning of domains for your customers", false),
ADMIN_DIRECTORY_DEVICE_MOBILE_READONLY("https://www.googleapis.com/auth/admin.directory.device.mobile.readonly", "View your mobile devices' metadata", false),
ADMIN_DIRECTORY_DEVICE_MOBILE_ACTION("https://www.googleapis.com/auth/admin.directory.device.mobile.action", "Manage your mobile devices by performing administrative tasks", false),
ADMIN_DIRECTORY_DEVICE_MOBILE("https://www.googleapis.com/auth/admin.directory.device.mobile", "View and manage your mobile devices' metadata", false),
ADMIN_DIRECTORY_DEVICE_CHROMEOS_READONLY("https://www.googleapis.com/auth/admin.directory.device.chromeos.readonly", "View your Chrome OS devices' metadata", false),
ADMIN_DIRECTORY_DEVICE_CHROMEOS("https://www.googleapis.com/auth/admin.directory.device.chromeos", "View and manage your Chrome OS devices' metadata", false),
ADMIN_DIRECTORY_CUSTOMER_READONLY("https://www.googleapis.com/auth/admin.directory.customer.readonly", "View customer related information", false),
ADMIN_DIRECTORY_CUSTOMER("https://www.googleapis.com/auth/admin.directory.customer", "View and manage customer related information", false),
ADMIN_DATATRANSFER_READONLY("https://www.googleapis.com/auth/admin.datatransfer.readonly", "View data transfers between users in your organization", false),
ADMIN_DATATRANSFER("https://www.googleapis.com/auth/admin.datatransfer", "View and manage data transfers between users in your organization", false),
ADEXCHANGE_BUYER("https://www.googleapis.com/auth/adexchange.buyer", "Manage your Ad Exchange buyer account configuration", false),
;
private final String scope;
private final String description;
private final boolean isDefault;
public static List getAdminDirectoryScopes() {
return Arrays.stream(new AuthGoogleScope[]{
ADMIN_DIRECTORY_USERSCHEMA_READONLY,
ADMIN_DIRECTORY_USERSCHEMA,
ADMIN_DIRECTORY_USER_SECURITY,
ADMIN_DIRECTORY_USER_READONLY,
ADMIN_DIRECTORY_USER_ALIAS_READONLY,
ADMIN_DIRECTORY_USER_ALIAS,
ADMIN_DIRECTORY_USER,
ADMIN_DIRECTORY_ROLEMANAGEMENT_READONLY,
ADMIN_DIRECTORY_ROLEMANAGEMENT,
ADMIN_DIRECTORY_RESOURCE_CALENDAR_READONLY,
ADMIN_DIRECTORY_RESOURCE_CALENDAR,
ADMIN_DIRECTORY_ORGUNIT_READONLY,
ADMIN_DIRECTORY_ORGUNIT,
ADMIN_DIRECTORY_NOTIFICATIONS,
ADMIN_DIRECTORY_GROUP_READONLY,
ADMIN_DIRECTORY_GROUP_MEMBER_READONLY,
ADMIN_DIRECTORY_GROUP_MEMBER,
ADMIN_DIRECTORY_GROUP,
ADMIN_DIRECTORY_DOMAIN_READONLY,
ADMIN_DIRECTORY_DOMAIN,
ADMIN_DIRECTORY_DEVICE_MOBILE_READONLY,
ADMIN_DIRECTORY_DEVICE_MOBILE_ACTION,
ADMIN_DIRECTORY_DEVICE_MOBILE,
ADMIN_DIRECTORY_DEVICE_CHROMEOS_READONLY,
ADMIN_DIRECTORY_DEVICE_CHROMEOS,
ADMIN_DIRECTORY_CUSTOMER_READONLY,
ADMIN_DIRECTORY_CUSTOMER
}).map(AuthGoogleScope::getScope).collect(Collectors.toList());
}
/**
* View And manage user's mail in Gmail.
*
* @return List
*/
public static List getGmailScopes() {
return Arrays.stream(new AuthGoogleScope[]{
GMAIL,
GMAIL_SETTINGS_SHARING,
GMAIL_SETTINGS_BASIC,
GMAIL_SEND,
GMAIL_READONLY,
GMAIL_MODIFY,
GMAIL_METADATA,
GMAIL_LABELS,
GMAIL_INSERT,
GMAIL_COMPOSE,
GMAIL_ADDONS_CURRENT_MESSAGE_READONLY,
GMAIL_ADDONS_CURRENT_MESSAGE_METADATA,
GMAIL_ADDONS_CURRENT_MESSAGE_ACTION,
GMAIL_ADDONS_CURRENT_ACTION_COMPOSE
}).map(AuthGoogleScope::getScope).collect(Collectors.toList());
}
/**
* Used for OIDC authorization and certification
*
* @return List
*/
public static List getOidcScopes() {
return Arrays.stream(new AuthGoogleScope[]{
USER_OPENID,
USER_EMAIL,
USER_PROFILE
}).map(AuthGoogleScope::getScope).collect(Collectors.toList());
}
/**
* View And manage user's detail and Google Contacts.
*
* @return List
*/
public static List getPeopleScopes() {
return Arrays.stream(new AuthGoogleScope[]{
CONTACTS_READONLY,
CONTACTS_OTHER_READONLY,
CONTACTS,
CONTACTS_FEEDS,
DIRECTORY_READONLY,
USER_PHONENUMBERS_READ,
USER_ORGANIZATION_READ,
USER_GENDER_READ,
USER_EMAILS_READ,
USER_BIRTHDAY_READ,
USER_ADDRESSES_READ,
USERINFO_PROFILE,
USERINFO_EMAIL
}).map(AuthGoogleScope::getScope).collect(Collectors.toList());
}
/**
* View and manage user's photo library.
*
* @return List
*/
public static List getPhotosLibraryScopes() {
return Arrays.stream(new AuthGoogleScope[]{
PHOTOSLIBRARY_SHARING,
PHOTOSLIBRARY_READONLY_APPCREATEDDATA,
PHOTOSLIBRARY_READONLY,
PHOTOSLIBRARY_APPENDONLY,
PHOTOSLIBRARY
}).map(AuthGoogleScope::getScope).collect(Collectors.toList());
}
/**
* View And manage user's videos, activity and playlists.
*
* @return List
*/
public static List getYouTubeScopes() {
return Arrays.stream(new AuthGoogleScope[]{
YT_ANALYTICS_READONLY,
YT_ANALYTICS_MONETARY_READONLY,
YOUTUBEPARTNER_CHANNEL_AUDIT,
YOUTUBEPARTNER,
YOUTUBE_UPLOAD,
YOUTUBE_READONLY,
YOUTUBE_FORCE_SSL,
YOUTUBE_CHANNEL_MEMBERSHIPS_CREATOR,
YOUTUBE
}).map(AuthGoogleScope::getScope).collect(Collectors.toList());
}
/**
* View And manage user's Google Analytics.
*
* @return List
*/
public static List getGoogleAnalyticsScopes() {
return Arrays.stream(new AuthGoogleScope[]{
ANALYTICS_USER_DELETION,
ANALYTICS_READONLY,
ANALYTICS_PROVISION,
ANALYTICS_MANAGE_USERS_READONLY,
ANALYTICS_MANAGE_USERS,
ANALYTICS_EDIT,
ANALYTICS
}).map(AuthGoogleScope::getScope).collect(Collectors.toList());
}
/**
* View And manage user's calendars in Google Calendar.
*
* @return List
*/
public static List getCalendarScopes() {
return Arrays.stream(new AuthGoogleScope[]{
CALENDAR_SETTINGS_READONLY,
CALENDAR_READONLY,
CALENDAR_EVENTS_READONLY,
CALENDAR_EVENTS,
CALENDAR,
CALENDAR_FEEDS
}).map(AuthGoogleScope::getScope).collect(Collectors.toList());
}
/**
* List, download, create, move, edit, share and search all of user's documents and files in Google Drive.
*
* @return List
*/
public static List getDriveScopes() {
return Arrays.stream(new AuthGoogleScope[]{
DRIVE_SCRIPTS,
DRIVE_READONLY,
DRIVE_PHOTOS_READONLY,
DRIVE_METADATA_READONLY,
DRIVE_METADATA,
DRIVE_FILE,
DRIVE_APPDATA,
DRIVE_ACTIVITY_READONLY,
DRIVE_ACTIVITY,
DRIVE,
ACTIVITY
}).map(AuthGoogleScope::getScope).collect(Collectors.toList());
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthHuaweiScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 华为平台 OAuth 授权范围
*
* 当前方式未来可能被废弃,建议使用 {@link AuthHuaweiV3Scope}
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
@Deprecated
public enum AuthHuaweiScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
BASE_PROFILE("https://www.huawei.com/auth/account/base.profile", "获取用户的基本信息", true),
MOBILE_NUMBER("https://www.huawei.com/auth/account/mobile.number", "获取用户的手机号", false),
ACCOUNTLIST("https://www.huawei.com/auth/account/accountlist", "获取用户的账单列表", false),
/**
* 以下两个 scope 不需要经过华为评估和验证
*/
SCOPE_DRIVE_FILE("https://www.huawei.com/auth/drive.file", "只允许访问由应用程序创建或打开的文件", false),
SCOPE_DRIVE_APPDATA("https://www.huawei.com/auth/drive.appdata", "只允许访问由应用程序创建或打开的文件", false),
/**
* 以下四个 scope 使用前需要向drivekit@huawei.com提交申请
*
* 参考:https://developer.huawei.com/consumer/cn/doc/development/HMSCore-Guides-V5/server-dev-0000001050039664-V5#ZH-CN_TOPIC_0000001050039664__section1618418855716
*/
SCOPE_DRIVE("https://www.huawei.com/auth/drive", "只允许访问由应用程序创建或打开的文件", false),
SCOPE_DRIVE_READONLY("https://www.huawei.com/auth/drive.readonly", "只允许访问由应用程序创建或打开的文件", false),
SCOPE_DRIVE_METADATA("https://www.huawei.com/auth/drive.metadata", "只允许访问由应用程序创建或打开的文件", false),
SCOPE_DRIVE_METADATA_READONLY("https://www.huawei.com/auth/drive.metadata.readonly", "只允许访问由应用程序创建或打开的文件", false),
;
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthHuaweiV3Scope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 华为平台 V3 版本 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.16.7
*/
@Getter
@AllArgsConstructor
public enum AuthHuaweiV3Scope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
OPENID("openid", "基础scope,v3必选", true),
/**
* {@code scope} 含义,以{@code description} 为准
*/
BASE_PROFILE("https://www.huawei.com/auth/account/base.profile", "获取用户的基本信息", true),
MOBILE_NUMBER("https://www.huawei.com/auth/account/mobile.number", "获取用户的手机号", false),
ACCOUNTLIST("https://www.huawei.com/auth/account/accountlist", "获取用户的账单列表", false),
/**
* 以下两个 scope 不需要经过华为评估和验证
*/
SCOPE_DRIVE_FILE("https://www.huawei.com/auth/drive.file", "只允许访问由应用程序创建或打开的文件", false),
SCOPE_DRIVE_APPDATA("https://www.huawei.com/auth/drive.appdata", "只允许访问由应用程序创建或打开的文件", false),
/**
* 以下四个 scope 使用前需要向drivekit@huawei.com提交申请
*
* 参考:https://developer.huawei.com/consumer/cn/doc/development/HMSCore-Guides-V5/server-dev-0000001050039664-V5#ZH-CN_TOPIC_0000001050039664__section1618418855716
*/
SCOPE_DRIVE("https://www.huawei.com/auth/drive", "只允许访问由应用程序创建或打开的文件", false),
SCOPE_DRIVE_READONLY("https://www.huawei.com/auth/drive.readonly", "只允许访问由应用程序创建或打开的文件", false),
SCOPE_DRIVE_METADATA("https://www.huawei.com/auth/drive.metadata", "只允许访问由应用程序创建或打开的文件", false),
SCOPE_DRIVE_METADATA_READONLY("https://www.huawei.com/auth/drive.metadata.readonly", "只允许访问由应用程序创建或打开的文件", false),
;
;
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthJdScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 京东平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthJdScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
SNSAPI_BASE("snsapi_base", "基础授权", true);
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthKujialeScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 酷家乐平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthKujialeScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
GET_USER_INFO("get_user_info", "获取用户的基本信息", true),
GET_DESIGN("get_design", "获取指定方案详情", false),
GET_BUDGET_LIST("get_budget_list", "获取清单预算概览数据", false);
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthLineScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Line 平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.0
*/
@Getter
@AllArgsConstructor
public enum AuthLineScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
PROFILE("profile", "Get profile details", true),
OPENID("openid", "Get id token", true),
EMAIL("email", "Get email (separate authorization required)", false);
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthLinkedinScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 领英平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthLinkedinScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
R_LITEPROFILE("r_liteprofile", "Use your name, headline, and photo", true),
R_EMAILADDRESS("r_emailaddress", "Use the primary email address associated with your LinkedIn account", true),
W_MEMBER_SOCIAL("w_member_social", "Post, comment and like posts on your behalf", true),
R_MEMBER_SOCIAL("r_member_social", "Retrieve your posts, comments, likes, and other engagement data", false),
R_AD_CAMPAIGNS("r_ad_campaigns", "View advertising campaigns you manage", false),
R_ADS("r_ads", "Retrieve your advertising accounts", false),
R_ADS_LEADGEN_AUTOMATION("r_ads_leadgen_automation", "Access your Lead Gen Forms and retrieve leads", false),
R_ADS_REPORTING("r_ads_reporting", "Retrieve reporting for your advertising accounts", false),
R_BASICPROFILE("r_basicprofile", "Use your basic profile including your name, photo, headline, and current positions", false),
R_ORGANIZATION_SOCIAL("r_organization_social", "Retrieve your organizations' posts, including any comments, likes and other engagement data", false),
RW_AD_CAMPAIGNS("rw_ad_campaigns", "Manage your advertising campaigns", false),
RW_ADS("rw_ads", "Manage your advertising accounts", false),
RW_COMPANY_ADMIN("rw_company_admin", "For V1 callsManage your organization's page and post updates", false),
RW_DMP_SEGMENTS("rw_dmp_segments", "Create and manage your matched audiences", false),
RW_ORGANIZATION_ADMIN("rw_organization_admin", "Manage your organizations' pages and retrieve reporting data", false),
RW_ORGANIZATION("rw_organization", "For V2 callsManage your organization's page and post updates", false),
W_ORGANIZATION_SOCIAL("w_organization_social", "Post, comment and like posts on your organization's behalf", false),
W_SHARE("w_share", "Post updates to LinkedIn as you", false);
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthMiScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 小米平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthMiScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
profile("user/profile", "获取用户的基本信息", true),
OPENID("user/openIdV2", "获取用户的OpenID", true),
PHONE_EMAIL("user/phoneAndEmail", "获取用户的手机号和邮箱", true);
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthMicrosoftScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 微软平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthMicrosoftScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
PROFILE("profile", "允许应用查看用户的基本个人资料(名称、图片、用户名称)", true),
EMAIL("email", "允许应用读取用户的主电子邮件地址", true),
OPENID("openid", "允许用户以其工作或学校帐户登录应用,并允许应用查看用户的基本个人资料信息", true),
OFFLINE_ACCESS("offline_access", "允许应用读取和更新用户数据,即使用户当前没有在使用此应用,也不例外", true),
USER_READ("User.Read", "登录并读取用户个人资料", false),
USER_READWRITE("User.ReadWrite", "对用户个人资料的读写权限", false),
USER_READBASIC_ALL("User.ReadBasic.All", "读取所有用户的基本个人资料", false),
USER_READ_ALL("User.Read.All", "读取所有用户的完整个人资料", false),
USER_READWRITE_ALL("User.ReadWrite.All", "读取和写入所有用户的完整个人资料", false),
USER_INVITE_ALL("User.Invite.All", "将来宾用户邀请到组织", false),
USER_EXPORT_ALL("User.Export.All", "导出用户数据", false),
USER_MANAGEIDENTITIES_ALL("User.ManageIdentities.All", "管理所有用户标识", false),
USERACTIVITY_READWRITE_CREATEDBYAPP("UserActivity.ReadWrite.CreatedByApp", "将应用活动读取和写入到用户的活动源", false),
FILES_READ("Files.Read", "允许应用读取登录用户的文件", false),
FILES_READ_ALL("Files.Read.All", "允许应用读取登录用户可以访问的所有文件", false),
FILES_READWRITE("Files.ReadWrite", "允许应用读取、创建、更新和删除登录用户的文件", false),
FILES_READWRITE_ALL("Files.ReadWrite.All", "允许应用读取、创建、更新和删除登录用户可以访问的所有文件", false),
FILES_READWRITE_APPFOLDER("Files.ReadWrite.AppFolder", "允许应用读取、创建、更新和删除应用程序文件夹中的文件", false),
FILES_READ_SELECTED("Files.Read.Selected", "允许应用读取用户选择的文件。在用户选择文件后,应用有几个小时的访问权限", false),
FILES_READWRITE_SELECTED("Files.ReadWrite.Selected", "允许应用读取和写入用户选择的文件。在用户选择文件后,应用有几个小时的访问权限", false),
ORGCONTACT_READ_ALL("OrgContact.Read.All", "允许应用代表已登录用户读取所有组织联系人。 这些联系人由组织管理,不同于用户的个人联系人", false),
MAIL_READ("Mail.Read", "允许应用读取用户邮箱中的电子邮件", false),
MAIL_READBASIC("Mail.ReadBasic", "允许应用读取已登录用户的邮箱,但不读取 body、bodyPreview、uniqueBody、attachments、extensions 和任何扩展属性。 不包含邮件搜索权限", false),
MAIL_READWRITE("Mail.ReadWrite", "允许应用创建、读取、更新和删除用户邮箱中的电子邮件。不包括发送电子邮件的权限", false),
MAIL_READ_SHARED("Mail.Read.Shared", "允许应用读取用户可以访问的邮件,包括用户个人邮件和共享邮件", false),
MAIL_READWRITE_SHARED("Mail.ReadWrite.Shared", "允许应用创建、读取、更新和删除用户有权访问的邮件,包括用户个人邮件和共享邮件。不包括邮件发送权限", false),
MAIL_SEND("Mail.Send", "允许应用以组织用户身份发送邮件", false),
MAIL_SEND_SHARED("Mail.Send.Shared", "允许应用以登录用户身份发送邮件,包括代表他人发送邮件", false),
MAILBOXSETTINGS_READ("MailboxSettings.Read", "允许应用读取用户的邮箱设置。不包括邮件发送权限", false),
MAILBOXSETTINGS_READWRITE("MailboxSettings.ReadWrite", "允许应用创建、读取、更新和删除用户邮箱设置。 不包含直接发送邮件的权限,但允许应用创建能够转发或重定向邮件的规则", false),
NOTES_READ("Notes.Read", "允许应用代表已登录用户读取 OneNote 笔记本和分区标题并创建新的页面、笔记本和分区", false),
NOTES_CREATE("Notes.Create", "允许应用代创建用户 OneNote 笔记本", false),
NOTES_READWRITE("Notes.ReadWrite", "允许应用代表已登录用户读取、共享和修改 OneNote 笔记本", false),
NOTES_READ_ALL("Notes.Read.All", "允许应用读取登录用户在组织中有权访问的 OneNote 笔记本", false),
NOTES_READWRITE_ALL("Notes.ReadWrite.All", "允许应用读取、共享和修改已登录用户在组织中有权访问的 OneNote 笔记本", false),
;
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthOktaScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Okta 平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.0
*/
@Getter
@AllArgsConstructor
public enum AuthOktaScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
OPENID("openid", "Signals that a request is an OpenID request.", true),
PROFILE("profile", "The exact data varies based on what profile information you have provided, such as: name, time zone, picture, or birthday.", true),
EMAIL("email", "This allows the app to view your email address.", true),
ADDRESS("address", "This allows the app to view your address, such as: street address, city, state, and zip code.", true),
PHONE("phone", "This allows the app to view your phone number.", true),
OFFLINE_ACCESS("offline_access", "This keeps you signed in to the app, even when you are not using it.", true),
OKTA_USERS_MANAGE("okta.users.manage", "Allows the app to create and manage users and read all profile and credential information for users", false),
OKTA_USERS_READ("okta.users.read", "Allows the app to read any user's profile and credential information", false),
OKTA_USERS_MANAGE_SELF("okta.users.manage.self", "Allows the app to manage the currently signed-in user's profile. Currently only supports user profile attribute updates.", false),
OKTA_USERS_READ_SELF("okta.users.read.self", "Allows the app to read the currently signed-in user's profile and credential information", false),
OKTA_APPS_MANAGE("okta.apps.manage", "Allows the app to create and manage Apps in your Okta organization", false),
OKTA_APPS_READ("okta.apps.read", "Allows the app to read information about Apps in your Okta organization", false),
OKTA_AUTHORIZATIONSERVERS_MANAGE("okta.authorizationServers.manage", "Allows the app to manage authorization servers", false),
OKTA_AUTHORIZATIONSERVERS_READ("okta.authorizationServers.read", "Allows the app to read authorization server information", false),
OKTA_CLIENTS_MANAGE("okta.clients.manage", "Allows the app to manage all OAuth/OIDC clients and to create new clients", false),
OKTA_CLIENTS_READ("okta.clients.read", "Allows the app to read information for all OAuth/OIDC clients", false),
OKTA_CLIENTS_REGISTER("okta.clients.register", "Allows the app to register (create) new OAuth/OIDC clients (but not read information about existing clients)", false),
OKTA_EVENTHOOKS_MANAGE("okta.eventHooks.manage", "Allows the app to create and manage Event Hooks in your Okta organization", false),
OKTA_EVENTHOOKS_READ("okta.eventHooks.read", "Allows the app to read information about Event Hooks in your Okta organization", false),
OKTA_FACTORS_MANAGE("okta.factors.manage", "Allows the app to manage all admin operations for org factors (for example, activate, deactive, read)", false),
OKTA_FACTORS_READ("okta.factors.read", "Allows the app to read org factors information", false),
OKTA_GROUPS_MANAGE("okta.groups.manage", "Allows the app to manage groups in your Okta organization", false),
OKTA_GROUPS_READ("okta.groups.read", "Allows the app to read information about groups and their members in your Okta organization", false),
OKTA_IDPS_MANAGE("okta.idps.manage", "Allows the app to create and manage Identity Providers in your Okta organization", false),
OKTA_IDPS_READ("okta.idps.read", "Allows the app to read information about Identity Providers in your Okta organization", false),
OKTA_INLINEHOOKS_MANAGE("okta.inlineHooks.manage", "Allows the app to create and manage Inline Hooks in your Okta organization.", false),
OKTA_INLINEHOOKS_READ("okta.inlineHooks.read", "Allows the app to read information about Inline Hooks in your Okta organization.", false),
OKTA_LINKEDOBJECTS_MANAGE("okta.linkedObjects.manage", "Allows the app to manage Linked Object definitions in your Okta organization.", false),
OKTA_LINKEDOBJECTS_READ("okta.linkedObjects.read", "Allows the app to read Linked Object definitions in your Okta organization.", false),
OKTA_LOGS_READ("okta.logs.read", "Allows the app to read information about System Log entries in your Okta organization", false),
OKTA_ROLES_MANAGE("okta.roles.manage", "Allows the app to create and manage Administrator Roles in your Okta organization", false),
OKTA_ROLES_READ("okta.roles.read", "Allows the app to read information about Administrator Roles in your Okta organization", false),
OKTA_SCHEMAS_MANAGE("okta.schemas.manage", "Allows the app to create and manage Schemas in your Okta organization", false),
OKTA_SCHEMAS_READ("okta.schemas.read", "Allows the app to read information about Schemas in your Okta organization", false),
OKTA_SESSIONS_MANAGE("okta.sessions.manage", "Allows the app to manage all sessions in your Okta organization", false),
OKTA_SESSIONS_READ("okta.sessions.read", "Allows the app to read all sessions in your Okta organization", false),
OKTA_TEMPLATES_MANAGE("okta.templates.manage", "Allows the app to manage all custom templates in your Okta organization", false),
OKTA_TEMPLATES_READ("okta.templates.read", "Allows the app to read all custom templates in your Okta organization", false),
OKTA_TRUSTEDORIGINS_MANAGE("okta.trustedOrigins.manage", "Allows the app to manage all Trusted Origins in your Okta organization", false),
OKTA_TRUSTEDORIGINS_READ("okta.trustedOrigins.read", "Allows the app to read all Trusted Origins in your Okta organization", false),
OKTA_POLICIES_MANAGE("okta.policies.manage", "Allows the app to manage Policies in your Okta organization", false),
OKTA_POLICIES_READ("okta.policies.read", "Allows the app to read information about Policies in your Okta organization", false),;
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthPinterestScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Pinterest 平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthPinterestScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
READ_PUBLIC("read_public", "Use GET method on a user’s Pins, boards.", true),
WRITE_PUBLIC("write_public", "Use PATCH, POST and DELETE methods on a user’s Pins and boards.", false),
READ_RELATIONSHIPS("read_relationships", "Use GET method on a user’s follows and followers (on boards, users and interests).", false),
WRITE_RELATIONSHIPS("write_relationships", "Use PATCH, POST and DELETE methods on a user’s follows and followers (on boards, users and interests).", false),
;
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthProginnScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Gitee 平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthProginnScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
BASIC("basic", "访问用户的基本信息", true),
/**
* 以上 scope 需要单独向程序员客栈平台申请,否则不可使用
*/
email("email", "获取用户的邮箱", false),
realname("realname", "获取用户的真实姓名", false),
cellphone("cellphone", "获取用户的手机号码", false),
;
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthQqScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* QQ 平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthQqScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
GET_USER_INFO("get_user_info", "获取登录用户的昵称、头像、性别", true),
/**
* 以下 scope 需要申请:http://wiki.connect.qq.com/openapi%e6%9d%83%e9%99%90%e7%94%b3%e8%af%b7
*/
GET_VIP_INFO("get_vip_info", "获取QQ会员的基本信息", false),
GET_VIP_RICH_INFO("get_vip_rich_info", "获取QQ会员的高级信息", false),
LIST_ALBUM("list_album", "获取用户QQ空间相册列表", false),
UPLOAD_PIC("upload_pic", "上传一张照片到QQ空间相册", false),
ADD_ALBUM("add_album", "在用户的空间相册里,创建一个新的个人相册", false),
LIST_PHOTO("list_photo", "获取用户QQ空间相册中的照片列表", false);
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthRenrenScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 人人平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthRenrenScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
READ_USER_BLOG("read_user_blog", "获取用户日志时需要用户授予的权限。", false),
READ_USER_CHECKIN("read_user_checkin", "获取用户报到信息时需要用户授予的权限。", false),
READ_USER_FEED("read_user_feed", "获取用户新鲜事时需要用户授予的权限。", false),
READ_USER_GUESTBOOK("read_user_guestbook", "获取用户留言板时需要用户授予的权限。", false),
READ_USER_INVITATION("read_user_invitation", "获取用户被邀请的状况时需要用户授予的权限。", false),
READ_USER_LIKE_HISTORY("read_user_like_history", "获取用户喜欢的历史信息时需要用户授予的权限。", false),
READ_USER_MESSAGE("read_user_message", "获取用户站内信时需要用户授予的权限。", false),
READ_USER_NOTIFICATION("read_user_notification", "获取用户已收到的通知时需要用户授予的权限。", false),
READ_USER_PHOTO("read_user_photo", "获取用户相册相关信息时需要用户授予的权限。", false),
READ_USER_STATUS("read_user_status", "获取用户状态相关信息时需要用户授予的权限。", false),
READ_USER_ALBUM("read_user_album", "获取用户相册相关信息时需要用户授予的权限。", false),
READ_USER_COMMENT("read_user_comment", "获取用户评论相关信息时需要用户授予的权限。", false),
READ_USER_SHARE("read_user_share", "获取用户分享相关信息时需要用户授予的权限。", false),
READ_USER_REQUEST("read_user_request", "获取用户好友请求、圈人请求等信息时需要用户授予的权限。", false),
PUBLISH_BLOG("publish_blog", "以用户身份发布日志时需要用户授予的权限。", false),
PUBLISH_CHECKIN("publish_checkin", "以用户身份发布报到时需要用户授予的权限。", false),
PUBLISH_FEED("publish_feed", "以用户身份发送新鲜事时需要用户授予的权限。", false),
PUBLISH_SHARE("publish_share", "以用户身份发送分享时需要用户授予的权限。", false),
WRITE_GUESTBOOK("write_guestbook", "以用户身份进行留言时需要用户授予的权限。", false),
SEND_INVITATION("send_invitation", "以用户身份发送邀请时需要用户授予的权限。", false),
SEND_REQUEST("send_request", "以用户身份发送好友申请、圈人请求等时需要用户授予的权限。", false),
SEND_MESSAGE("send_message", "以用户身份发送站内信时需要用户授予的权限。", false),
SEND_NOTIFICATION("send_notification", "以用户身份发送通知(user_to_user)时需要用户授予的权限。", false),
PHOTO_UPLOAD("photo_upload", "以用户身份上传照片时需要用户授予的权限。", false),
STATUS_UPDATE("status_update", "以用户身份发布状态时需要用户授予的权限。", false),
CREATE_ALBUM("create_album", "以用户身份发布相册时需要用户授予的权限。", false),
PUBLISH_COMMENT("publish_comment", "以用户身份发布评论时需要用户授予的权限。", false),
OPERATE_LIKE("operate_like", "以用户身份执行喜欢操作时需要用户授予的权限。", false),
ADMIN_PAGE("admin_page", "以用户的身份,管理其可以管理的公共主页的权限。", false),
;
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthScope.java
================================================
package me.zhyd.oauth.enums.scope;
/**
* 各个平台 scope 类的统一接口
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.15.7
*/
public interface AuthScope {
/**
* 获取字符串 {@code scope},对应为各平台实际使用的 {@code scope}
*
* @return String
*/
String getScope();
/**
* 判断当前 {@code scope} 是否为各平台默认启用的
*
* @return boolean
*/
boolean isDefault();
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthSlackScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Slack 平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.0
*/
@Getter
@AllArgsConstructor
public enum AuthSlackScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
USERS_PROFILE_READ("users.profile:read", "View profile details about people in a workspace", true),
USERS_READ("users:read", "View people in a workspace", true),
USERS_READ_EMAIL("users:read.email", "View email addresses of people in a workspace", true),
USERS_PROFILE_WRITE("users.profile:write", "Edit a user’s profile information and status", false),
USERS_PROFILE_WRITE_USER("users.profile:write:user", "Change the user's profile fields", false),
USERS_WRITE("users:write", "Set presence for your slack app", false),
ADMIN("admin", "Administer a workspace", false),
ADMIN_ANALYTICS_READ("admin.analytics:read", "Access analytics data about the organization", false),
ADMIN_APPS_READ("admin.apps:read", "View apps and app requests in a workspace", false),
ADMIN_APPS_WRITE("admin.apps:write", "Manage apps in a workspace", false),
ADMIN_BARRIERS_READ("admin.barriers:read", "Read information barriers in the organization", false),
ADMIN_BARRIERS_WRITE("admin.barriers:write", "Manage information barriers in the organization", false),
ADMIN_CONVERSATIONS_READ("admin.conversations:read", "View the channel’s member list, topic, purpose and channel name", false),
ADMIN_CONVERSATIONS_WRITE("admin.conversations:write", "Start a new conversation, modify a conversation and modify channel details", false),
ADMIN_INVITES_READ("admin.invites:read", "Gain information about invite requests in a Grid organization.", false),
ADMIN_INVITES_WRITE("admin.invites:write", "Approve or deny invite requests in a Grid organization.", false),
ADMIN_TEAMS_READ("admin.teams:read", "Access information about a workspace", false),
ADMIN_TEAMS_WRITE("admin.teams:write", "Make changes to a workspace", false),
ADMIN_USERGROUPS_READ("admin.usergroups:read", "Access information about user groups", false),
ADMIN_USERGROUPS_WRITE("admin.usergroups:write", "Make changes to your usergroups", false),
ADMIN_USERS_READ("admin.users:read", "Access a workspace’s profile information", false),
ADMIN_USERS_WRITE("admin.users:write", "Modify account information", false),
APP_MENTIONS_READ("app_mentions:read", "View messages that directly mention @your_slack_app in conversations that the app is in", false),
AUDITLOGS_READ("auditlogs:read", "View events from all workspaces, channels and users (Enterprise Grid only)", false),
BOT("bot", "Add the ability for people to direct message or mention @your_slack_app", false),
CALLS_READ("calls:read", "View information about ongoing and past calls", false),
CALLS_WRITE("calls:write", "Start and manage calls in a workspace", false),
CHANNELS_HISTORY("channels:history", "View messages and other content in public channels that your slack app has been added to", false),
CHANNELS_JOIN("channels:join", "Join public channels in a workspace", false),
CHANNELS_MANAGE("channels:manage", "Manage public channels that your slack app has been added to and create new ones", false),
CHANNELS_READ("channels:read", "View basic information about public channels in a workspace", false),
CHANNELS_WRITE("channels:write", "Manage a user’s public channels and create new ones on a user’s behalf", false),
CHAT_WRITE("chat:write", "Post messages in approved channels & conversations", false),
CHAT_WRITE_CUSTOMIZE("chat:write.customize", "Send messages as @your_slack_app with a customized username and avatar", false),
CHAT_WRITE_PUBLIC("chat:write.public", "Send messages to channels @your_slack_app isn't a member of", false),
CHAT_WRITE_BOT("chat:write:bot", "Send messages as your slack app", false),
CHAT_WRITE_USER("chat:write:user", "Send messages on a user’s behalf", false),
CLIENT("client", "Receive all events from a workspace in real time", false),
COMMANDS("commands", "Add shortcuts and/or slash commands that people can use", false),
CONVERSATIONS_HISTORY("conversations:history", "Deprecated: Retrieve conversation history for legacy workspace apps", false),
CONVERSATIONS_READ("conversations:read", "Deprecated: Retrieve information on conversations for legacy workspace apps", false),
CONVERSATIONS_WRITE("conversations:write", "Deprecated: Edit conversation attributes for legacy workspace apps", false),
DND_READ("dnd:read", "View Do Not Disturb settings for people in a workspace", false),
DND_WRITE("dnd:write", "Edit a user’s Do Not Disturb settings", false),
DND_WRITE_USER("dnd:write:user", "Change the user's Do Not Disturb settings", false),
EMOJI_READ("emoji:read", "View custom emoji in a workspace", false),
FILES_READ("files:read", "View files shared in channels and conversations that your slack app has been added to", false),
FILES_WRITE("files:write", "Upload, edit, and delete files as your slack app", false),
FILES_WRITE_USER("files:write:user", "Upload, edit, and delete files as your slack app", false),
GROUPS_HISTORY("groups:history", "View messages and other content in private channels that your slack app has been added to", false),
GROUPS_READ("groups:read", "View basic information about private channels that your slack app has been added to", false),
GROUPS_WRITE("groups:write", "Manage private channels that your slack app has been added to and create new ones", false),
IDENTIFY("identify", "View information about a user’s identity", false),
IDENTITY_AVATAR("identity.avatar", "View a user’s Slack avatar", false),
IDENTITY_AVATAR_READ_USER("identity.avatar:read:user", "View the user's profile picture", false),
IDENTITY_BASIC("identity.basic", "View information about a user’s identity", false),
IDENTITY_EMAIL("identity.email", "View a user’s email address", false),
IDENTITY_EMAIL_READ_USER("identity.email:read:user", "This scope is not yet described.", false),
IDENTITY_TEAM("identity.team", "View a user’s Slack workspace name", false),
IDENTITY_TEAM_READ_USER("identity.team:read:user", "View the workspace's name, domain, and icon", false),
IDENTITY_READ_USER("identity:read:user", "This scope is not yet described.", false),
IM_HISTORY("im:history", "View messages and other content in direct messages that your slack app has been added to", false),
IM_READ("im:read", "View basic information about direct messages that your slack app has been added to", false),
IM_WRITE("im:write", "Start direct messages with people", false),
INCOMING_WEBHOOK("incoming-webhook", "Create one-way webhooks to post messages to a specific channel", false),
LINKS_READ("links:read", "View URLs in messages", false),
LINKS_WRITE("links:write", "Show previews of URLs in messages", false),
MPIM_HISTORY("mpim:history", "View messages and other content in group direct messages that your slack app has been added to", false),
MPIM_READ("mpim:read", "View basic information about group direct messages that your slack app has been added to", false),
MPIM_WRITE("mpim:write", "Start group direct messages with people", false),
NONE("none", "Execute methods without needing a scope", false),
PINS_READ("pins:read", "View pinned content in channels and conversations that your slack app has been added to", false),
PINS_WRITE("pins:write", "Add and remove pinned messages and files", false),
POST("post", "Post messages to a workspace", false),
REACTIONS_READ("reactions:read", "View emoji reactions and their associated content in channels and conversations that your slack app has been added to", false),
REACTIONS_WRITE("reactions:write", "Add and edit emoji reactions", false),
READ("read", "View all content in a workspace", false),
REMINDERS_READ("reminders:read", "View reminders created by your slack app", false),
REMINDERS_READ_USER("reminders:read:user", "Access reminders created by a user or for a user", false),
REMINDERS_WRITE("reminders:write", "Add, remove, or mark reminders as complete", false),
REMINDERS_WRITE_USER("reminders:write:user", "Add, remove, or complete reminders for the user", false),
REMOTE_FILES_READ("remote_files:read", "View remote files added by the app in a workspace", false),
REMOTE_FILES_SHARE("remote_files:share", "Share remote files on a user’s behalf", false),
REMOTE_FILES_WRITE("remote_files:write", "Add, edit, and delete remote files on a user’s behalf", false),
SEARCH_READ("search:read", "Search a workspace’s content", false),
STARS_READ("stars:read", "View messages and files that your slack app has starred", false),
STARS_WRITE("stars:write", "Add or remove stars", false),
TEAM_READ("team:read", "View the name, email domain, and icon for workspaces your slack app is connected to", false),
TOKENS_BASIC("tokens.basic", "Execute methods without needing a scope", false),
USERGROUPS_READ("usergroups:read", "View user groups in a workspace", false),
USERGROUPS_WRITE("usergroups:write", "Create and manage user groups", false),
WORKFLOW_STEPS_EXECUTE("workflow.steps:execute", "Add steps that people can use in Workflow Builder", false);
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthStackoverflowScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Stackoverflow 平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthStackoverflowScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
read_inbox("read_inbox", "access a user's global inbox", true),
NO_EXPIRY("no_expiry", "access_token's with this scope do not expire", false),
WRITE_ACCESS("write_access", "perform write operations as a user", false),
PRIVATE_INFO("private_info", "access full history of a user's private actions on the site", false);
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthWeChatEnterpriseWebScope.java
================================================
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 final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthWechatMpScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 微信公众平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthWechatMpScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
SNSAPI_USERINFO("snsapi_userinfo", "弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息", true),
SNSAPI_BASE("snsapi_base", "不弹出授权页面,直接跳转,只能获取用户openid", false);
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/AuthWeiboScope.java
================================================
package me.zhyd.oauth.enums.scope;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 微博平台 OAuth 授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum AuthWeiboScope implements AuthScope {
/**
* {@code scope} 含义,以{@code description} 为准
*/
ALL("all", "获取所有权限", true),
EMAIL("email", "用户的联系邮箱,接口文档", false),
DIRECT_MESSAGES_WRITE("direct_messages_write", "私信发送接口,接口文档", false),
DIRECT_MESSAGES_READ("direct_messages_read", "私信读取接口,接口文档", false),
INVITATION_WRITE("invitation_write", "邀请发送接口,接口文档", false),
FRIENDSHIPS_GROUPS_READ("friendships_groups_read", "好友分组读取接口组,接口文档", false),
FRIENDSHIPS_GROUPS_WRITE("friendships_groups_write", "好友分组写入接口组,接口文档", false),
STATUSES_TO_ME_READ("statuses_to_me_read", "定向微博读取接口组,接口文档", false),
FOLLOW_APP_OFFICIAL_MICROBLOG("follow_app_official_microblog", "关注应用官方微博,该参数不对应具体接口,只需在应用控制台填写官方帐号即可。填写的路径:我的应用-选择自己的应用-应用信息-基本信息-官方运营账号(默认值是应用开发者帐号)", false);
private final String scope;
private final String description;
private final boolean isDefault;
}
================================================
FILE: src/main/java/me/zhyd/oauth/enums/scope/package-info.java
================================================
/**
* 各个平台的授权范围
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.15.7
*/
package me.zhyd.oauth.enums.scope;
================================================
FILE: src/main/java/me/zhyd/oauth/exception/AuthException.java
================================================
package me.zhyd.oauth.exception;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
/**
* JustAuth通用异常类
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.8
*/
public class AuthException extends RuntimeException {
private int errorCode;
private String errorMsg;
public AuthException(String errorMsg) {
this(AuthResponseStatus.FAILURE.getCode(), errorMsg);
}
public AuthException(String errorMsg, AuthSource source) {
this(AuthResponseStatus.FAILURE.getCode(), errorMsg, source);
}
public AuthException(int errorCode, String errorMsg) {
super(errorMsg);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public AuthException(AuthResponseStatus status) {
this(status.getCode(), status.getMsg());
}
public AuthException(int errorCode, String errorMsg, AuthSource source) {
this(errorCode, String.format("%s [%s]", errorMsg, source.getName()));
}
public AuthException(AuthResponseStatus status, AuthSource source) {
this(status.getCode(), status.getMsg(), source);
}
public AuthException(String message, Throwable cause) {
super(message, cause);
}
public AuthException(Throwable cause) {
super(cause);
}
public int getErrorCode() {
return errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/exception/package-info.java
================================================
/**
* JustAuth专用异常封装
*/
package me.zhyd.oauth.exception;
================================================
FILE: src/main/java/me/zhyd/oauth/log/Log.java
================================================
package me.zhyd.oauth.log;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.io.PrintStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 针对JustAuth提供的轻量级的日志打印工具
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @see Log#debug(String)
* @see Log#debug(String, Throwable)
* @see Log#warn(String)
* @see Log#warn(String, Throwable)
* @see Log#error(String)
* @see Log#error(String, Throwable)
* @since 1.10.0
*/
public class Log {
public static void debug(String msg) {
debug(msg, null);
}
public static void warn(String msg) {
warn(msg, null);
}
public static void error(String msg) {
error(msg, null);
}
public static void debug(String msg, Throwable t) {
print(Level.DEBUG, msg, t, System.out);
}
public static void warn(String msg, Throwable t) {
print(Level.WARN, msg, t, System.out);
}
public static void error(String msg, Throwable t) {
print(Level.ERROR, msg, t, System.err);
}
/**
* 打印日志内容,格式:2019-08-02 20:44:07 main me.zhyd.oauth.log.Log(debug:39) [DEBUG] - xxxx
*
* @param level 日志级别
* @param msg 日志内容
* @param t 异常信息
* @param ps 实际执行打印的PrintStream
*/
private static void print(Level level, String msg, Throwable t, PrintStream ps) {
if (Config.enable) {
if (level.getLevelNum() >= Config.level.getLevelNum()) {
ps.println(String.format("%s %s %s [%s] - %s", getDate(), Thread.currentThread().getName(), getCaller(), level, msg));
writeThrowable(t, ps);
ps.flush();
}
}
}
/**
* 获取调用方的信息
*
* @return 返回调用方的信息,格式:class(method:lineNumber)
*/
private static String getCaller() {
int offset = 2;
StackTraceElement[] stackTraceArr = (new Throwable()).getStackTrace();
StackTraceElement stackTrace = null;
if (offset >= stackTraceArr.length) {
offset = offset - 1;
}
stackTrace = stackTraceArr[offset];
return stackTrace.getClassName() +
"(" +
stackTrace.getMethodName() +
':' +
stackTrace.getLineNumber() +
")";
}
/**
* 获取格式化后的日期
*
* @return string
*/
private static String getDate() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
/**
* 打印异常信息
*
* @param t 异常
* @param targetStream 实际执行打印的PrintStream
*/
private static void writeThrowable(Throwable t, PrintStream targetStream) {
if (t != null) {
t.printStackTrace(targetStream);
}
}
/**
* 日志级别
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.10.0
*/
@Getter
@AllArgsConstructor
public enum Level {
/**
* DEBUG: 普通级别
*/
DEBUG(10),
/**
* WARN: 警告级别
*/
WARN(30),
/**
* ERROR: 异常级别
*/
ERROR(40);
private int levelNum;
}
/**
* 日志配置
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.10.0
*/
public static class Config {
/**
* 需要打印的日志级别
*/
public static Level level = Level.DEBUG;
/**
* 是否启用日志打印功能,默认启用
*/
public static boolean enable = true;
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/log/package-info.java
================================================
/**
* 针对JustAuth简单封装的日志打印工具,可用过{@link me.zhyd.oauth.config.JustAuthLogConfig}开关日志和指定日志级别
*/
package me.zhyd.oauth.log;
================================================
FILE: src/main/java/me/zhyd/oauth/model/AuthCallback.java
================================================
package me.zhyd.oauth.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import me.zhyd.oauth.utils.StringUtils;
import java.io.Serializable;
/**
* 授权回调时的参数类
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.8.0
*/
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class AuthCallback implements Serializable {
/**
* 访问AuthorizeUrl后回调时带的参数code
*/
private String code;
/**
* 访问AuthorizeUrl后回调时带的参数auth_code,该参数目前只使用于支付宝登录
*/
private String auth_code;
/**
* 访问AuthorizeUrl后回调时带的参数state,用于和请求AuthorizeUrl前的state比较,防止CSRF攻击
*/
private String state;
/**
* 华为授权登录接受code的参数名
*
* @since 1.10.0
*/
private String authorization_code;
/**
* Twitter回调后返回的oauth_token
*
* @since 1.13.0
*/
private String oauth_token;
/**
* Twitter回调后返回的oauth_verifier
*
* @since 1.13.0
*/
private String oauth_verifier;
/**
* 苹果仅在用户首次授权应用程序时返回此值。如果您的应用程序已经获得了用户的授权,那么苹果将不会再次返回此值
* @see user info
*/
private String user;
/**
* 苹果错误信息,仅在用户取消授权时返回此值
* @see error response
*/
private String error;
public String getCode() {
return StringUtils.isEmpty(code) ? auth_code : code;
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/model/AuthResponse.java
================================================
package me.zhyd.oauth.model;
import lombok.*;
import me.zhyd.oauth.enums.AuthResponseStatus;
import java.io.Serializable;
/**
* JustAuth统一授权响应类
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.8
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AuthResponse implements Serializable {
/**
* 授权响应状态码
*/
private int code;
/**
* 授权响应信息
*/
private String msg;
/**
* 授权响应数据,当且仅当 code = 2000 时返回
*/
private T data;
/**
* 是否请求成功
*
* @return true or false
*/
public boolean ok() {
return this.code == AuthResponseStatus.SUCCESS.getCode();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/model/AuthToken.java
================================================
package me.zhyd.oauth.model;
import lombok.*;
import java.io.Serializable;
/**
* 授权所需的token
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.8
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
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;
private String unionId;
/**
* Google附带属性
*/
private String scope;
private String tokenType;
private String idToken;
/**
* 小米附带属性
*/
private String macAlgorithm;
private String macKey;
/**
* 企业微信附带属性
*
* @since 1.10.0
*/
private String code;
/**
* 微信公众号 - 网页授权的登录时可用
*
* 微信针对网页授权登录,增加了一个快照页的逻辑,快照页获取到的微信用户的 uid oid 和头像昵称都是虚拟的信息
*/
private boolean snapshotUser;
/**
* Twitter附带属性
*
* @since 1.13.0
*/
private String oauthToken;
private String oauthTokenSecret;
private String userId;
private String screenName;
private Boolean oauthCallbackConfirmed;
/**
* Apple附带属性
*/
private String username;
/**
* 新版钉钉附带属性
*
* @since 1.16.7
*/
private String corpId;
}
================================================
FILE: src/main/java/me/zhyd/oauth/model/AuthUser.java
================================================
package me.zhyd.oauth.model;
import com.alibaba.fastjson.JSONObject;
import lombok.*;
import me.zhyd.oauth.enums.AuthUserGender;
import java.io.Serializable;
/**
* 授权成功后的用户信息,根据授权平台的不同,获取的数据完整性也不同
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.8
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AuthUser implements Serializable {
/**
* 用户第三方系统的唯一id。在调用方集成该组件时,可以用uuid + source唯一确定一个用户
*
* @since 1.3.3
*/
private String uuid;
/**
* 用户名
*/
private String username;
/**
* 用户昵称
*/
private String nickname;
/**
* 用户头像
*/
private String avatar;
/**
* 用户网址
*/
private String blog;
/**
* 所在公司
*/
private String company;
/**
* 位置
*/
private String location;
/**
* 用户邮箱
*/
private String email;
/**
* 用户备注(各平台中的用户个人介绍)
*/
private String remark;
/**
* 性别
*/
private AuthUserGender gender;
/**
* 用户来源
*/
private String source;
/**
* 用户授权的token信息
*/
private AuthToken token;
/**
* 第三方平台返回的原始用户信息
*/
private JSONObject rawUserInfo;
/**
* 微信公众号 - 网页授权的登录时可用
*
* 微信针对网页授权登录,增加了一个快照页的逻辑,快照页获取到的微信用户的 uid oid 和头像昵称都是虚拟的信息
*/
private boolean snapshotUser;
}
================================================
FILE: src/main/java/me/zhyd/oauth/model/package-info.java
================================================
/**
* JustAuth核心模型类,封装了用户、token、响应和callback等实体类
*/
package me.zhyd.oauth.model;
================================================
FILE: src/main/java/me/zhyd/oauth/package-info.java
================================================
/**
* JustAuth,如你所见,它仅仅是一个第三方授权登录的工具类库,它可以让我们脱离繁琐的第三方登录SDK,让登录变得So easy!
*
* 史上最全的整合第三方登录的开源库。目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、
* 支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软、今日头条、Teambition、StackOverflow、Pinterest、
* 人人、华为和企业微信等第三方平台的授权登录。 Login, so easy!
*/
package me.zhyd.oauth;
================================================
FILE: src/main/java/me/zhyd/oauth/request/AbstractAuthDingtalkRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
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.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
*
* 钉钉登录抽象类,负责处理使用钉钉账号登录第三方网站和扫码登录第三方网站两种钉钉的登录方式
*
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.0
*/
public abstract class AbstractAuthDingtalkRequest extends AuthDefaultRequest {
public AbstractAuthDingtalkRequest(AuthConfig config, AuthSource source) {
super(config, source);
}
public AbstractAuthDingtalkRequest(AuthConfig config, AuthSource source, AuthStateCache authStateCache) {
super(config, source, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
return AuthToken.builder().accessCode(authCallback.getCode()).build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String code = authToken.getAccessCode();
JSONObject param = new JSONObject();
param.put("tmp_auth_code", code);
String response = new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken), param.toJSONString()).getBody();
JSONObject object = JSON.parseObject(response);
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();
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("unionid"))
.nickname(object.getString("nick"))
.username(object.getString("nick"))
.gender(AuthUserGender.UNKNOWN)
.source(source.toString())
.token(token)
.build();
}
/**
* 返回带{@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("response_type", "code")
.queryParam("appid", config.getClientId())
.queryParam("scope", "snsapi_login")
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
// 根据timestamp, appSecret计算签名值
String timestamp = System.currentTimeMillis() + "";
String urlEncodeSignature = GlobalAuthUtils.generateDingTalkSignature(config.getClientSecret(), timestamp);
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("signature", urlEncodeSignature)
.queryParam("timestamp", timestamp)
.queryParam("accessKey", config.getClientId())
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AbstractAuthMicrosoftRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import com.xkcoding.http.support.HttpHeader;
import com.xkcoding.http.util.MapUtil;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.enums.scope.AuthMicrosoftScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.Map;
/**
* 微软登录抽象类,负责处理使用微软国际和微软中国账号登录第三方网站的登录方式
*
* @author mroldx (xzfqq5201314@gmail.com)
* @since 1.16.4
*/
public abstract class AbstractAuthMicrosoftRequest extends AuthDefaultRequest {
public AbstractAuthMicrosoftRequest(AuthConfig config, AuthSource source) {
super(config, source);
}
public AbstractAuthMicrosoftRequest(AuthConfig config, AuthSource source, AuthStateCache authStateCache) {
super(config, source, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
return getToken(accessTokenUrl(authCallback.getCode()));
}
/**
* 获取token,适用于获取access_token和刷新token
*
* @param accessTokenUrl 实际请求token的地址
* @return token对象
*/
private AuthToken getToken(String accessTokenUrl) {
HttpHeader httpHeader = new HttpHeader();
Map form = MapUtil.parseStringToMap(accessTokenUrl, false);
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl, form, httpHeader, false).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
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();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String token = authToken.getAccessToken();
String tokenType = authToken.getTokenType();
String jwt = tokenType + " " + token;
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Authorization", jwt);
String userInfo = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false).getBody();
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.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(source.toString())
.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();
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
// 兼容 Microsoft Entra ID 登录(原微软 AAD)
// @since 1.16.6
String tenantId = StringUtils.isEmpty(config.getTenantId()) ? "common" : config.getTenantId();
return UrlBuilder.fromBaseUrl(String.format(source.authorize(), tenantId))
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(state))
.queryParam("response_mode", "query")
.queryParam("scope", this.getScopes(" ", false, AuthScopeUtils.getDefaultScopes(AuthMicrosoftScope.values())))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权code
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
String tenantId = StringUtils.isEmpty(config.getTenantId()) ? "common" : config.getTenantId();
return UrlBuilder.fromBaseUrl(String.format(source.accessToken(), tenantId))
.queryParam("code", code)
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("grant_type", "authorization_code")
.queryParam("scope", this.getScopes(" ", false, AuthScopeUtils.getDefaultScopes(AuthMicrosoftScope.values())))
.queryParam("redirect_uri", config.getRedirectUri())
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo()).build();
}
/**
* 返回获取accessToken的url
*
* @param refreshToken 用户授权后的token
* @return 返回获取accessToken的url
*/
@Override
protected String refreshTokenUrl(String refreshToken) {
String tenantId = StringUtils.isEmpty(config.getTenantId()) ? "common" : config.getTenantId();
return UrlBuilder.fromBaseUrl(String.format(source.refresh(), tenantId))
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("refresh_token", refreshToken)
.queryParam("grant_type", "refresh_token")
.queryParam("scope", this.getScopes(" ", false, AuthScopeUtils.getDefaultScopes(AuthMicrosoftScope.values())))
.queryParam("redirect_uri", config.getRedirectUri())
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AbstractAuthWeChatEnterpriseRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
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.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
*
* 企业微信登录父类
*
*
* @author liguanhua (347826496(a)qq.com)
* @since 1.15.9
*/
public abstract class AbstractAuthWeChatEnterpriseRequest extends AuthDefaultRequest {
public AbstractAuthWeChatEnterpriseRequest(AuthConfig config, AuthSource source) {
super(config,source);
}
public AbstractAuthWeChatEnterpriseRequest(AuthConfig config, AuthSource source, AuthStateCache authStateCache) {
super(config, source, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doGetAuthorizationCode(accessTokenUrl(null));
JSONObject object = this.checkResponse(response);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("expires_in"))
.code(authCallback.getCode())
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String response = doGetUserInfo(authToken);
JSONObject object = this.checkResponse(response);
// 返回 OpenId 或其他,均代表非当前企业用户,不支持
if (!object.containsKey("UserId")) {
throw new AuthException(AuthResponseStatus.UNIDENTIFIED_PLATFORM, source);
}
String userId = object.getString("UserId");
String userTicket = object.getString("user_ticket");
JSONObject userDetail = getUserDetail(authToken.getAccessToken(), userId, userTicket);
return AuthUser.builder()
.rawUserInfo(userDetail)
.username(userDetail.getString("name"))
.nickname(userDetail.getString("alias"))
.avatar(userDetail.getString("avatar"))
.location(userDetail.getString("address"))
.email(userDetail.getString("email"))
.uuid(userId)
.gender(AuthUserGender.getWechatRealGender(userDetail.getString("gender")))
.token(authToken)
.source(source.toString())
.build();
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常,则返回JSONObject
*/
private JSONObject checkResponse(String response) {
JSONObject object = JSONObject.parseObject(response);
if (object.containsKey("errcode") && object.getIntValue("errcode") != 0) {
throw new AuthException(object.getString("errmsg"), source);
}
return object;
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("corpid", config.getClientId())
.queryParam("corpsecret", config.getClientSecret())
.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("code", authToken.getCode())
.build();
}
/**
* 用户详情
*
* @param accessToken accessToken
* @param userId 企业内用户id
* @param userTicket 成员票据,用于获取用户信息或敏感信息
* @return 用户详情
*/
private JSONObject getUserDetail(String accessToken, String userId, String userTicket) {
// 用户基础信息
String userInfoUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/user/get")
.queryParam("access_token", accessToken)
.queryParam("userid", userId)
.build();
String userInfoResponse = new HttpUtils(config.getHttpConfig()).get(userInfoUrl).getBody();
JSONObject userInfo = checkResponse(userInfoResponse);
// 用户敏感信息
if (StringUtils.isNotEmpty(userTicket)) {
String userDetailUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/auth/getuserdetail")
.queryParam("access_token", accessToken)
.build();
JSONObject param = new JSONObject();
param.put("user_ticket", userTicket);
String userDetailResponse = new HttpUtils(config.getHttpConfig()).post(userDetailUrl, param.toJSONString()).getBody();
JSONObject userDetail = checkResponse(userDetailResponse);
userInfo.putAll(userDetail);
}
return userInfo;
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthAfDianRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.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;
import java.util.HashMap;
import java.util.Map;
/**
* 爱发电
*
* @author handy
*/
public class AuthAfDianRequest extends AuthDefaultRequest {
public AuthAfDianRequest(AuthConfig config) {
super(config, AuthDefaultSource.AFDIAN);
}
public AuthAfDianRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.AFDIAN, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
Map params = new HashMap<>();
params.put("grant_type", "authorization_code");
params.put("client_id", config.getClientId());
params.put("client_secret", config.getClientSecret());
params.put("code", authCallback.getCode());
params.put("redirect_uri", config.getRedirectUri());
String response = new HttpUtils(config.getHttpConfig()).post(AuthDefaultSource.AFDIAN.accessToken(), params, false).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
String userId = accessTokenObject.getJSONObject("data").getString("user_id");
return AuthToken.builder().userId(userId).build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
return AuthUser.builder()
.uuid(authToken.getUserId())
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("scope", "basic")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(state))
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthAlipayCertRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.request.AlipayUserInfoShareRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.alipay.api.response.AlipayUserInfoShareResponse;
import me.zhyd.oauth.config.AuthConfig;
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;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import static me.zhyd.oauth.config.AuthDefaultSource.ALIPAY;
/**
* 支付宝证书模式登录
*
* @since 1.16.7
*/
public class AuthAlipayCertRequest extends AuthDefaultRequest {
private final AlipayClient alipayClient;
public AuthAlipayCertRequest(AuthConfig config, AlipayConfig alipayConfig) {
super(config, ALIPAY);
try {
this.alipayClient = new DefaultAlipayClient(alipayConfig);
} catch (AlipayApiException e) {
throw new AuthException(e);
}
}
@Override
protected void checkCode(AuthCallback authCallback) {
if (StringUtils.isEmpty(authCallback.getAuth_code())) {
throw new AuthException(AuthResponseStatus.ILLEGAL_CODE, source);
}
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
request.setGrantType("authorization_code");
request.setCode(authCallback.getAuth_code());
AlipaySystemOauthTokenResponse response;
try {
response = this.alipayClient.certificateExecute(request);
} catch (Exception 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();
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
@Override
public AuthResponse refresh(AuthToken authToken) {
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
request.setGrantType("refresh_token");
request.setRefreshToken(authToken.getRefreshToken());
AlipaySystemOauthTokenResponse response = null;
try {
response = this.alipayClient.certificateExecute(request);
} catch (Exception e) {
throw new AuthException(e);
}
if (!response.isSuccess()) {
throw new AuthException(response.getSubMsg());
}
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(response.getAccessToken())
.uid(response.getUserId())
.expireIn(Integer.parseInt(response.getExpiresIn()))
.refreshToken(response.getRefreshToken())
.build())
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
AlipayUserInfoShareRequest request = new AlipayUserInfoShareRequest();
AlipayUserInfoShareResponse response = null;
try {
response = this.alipayClient.certificateExecute(request, accessToken);
} catch (AlipayApiException e) {
throw new AuthException(e.getErrMsg(), e);
}
if (!response.isSuccess()) {
throw new AuthException(response.getSubMsg());
}
String province = response.getProvince(), city = response.getCity();
String location = String.format("%s %s", StringUtils.isEmpty(province) ? "" : province, StringUtils.isEmpty(city) ? "" : city);
return AuthUser.builder()
.rawUserInfo(JSONObject.parseObject(JSONObject.toJSONString(response)))
.uuid(response.getOpenId())
.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(source.toString())
.build();
}
/**
* 返回带{@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("app_id", config.getClientId())
.queryParam("scope", "auth_user")
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(state))
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthAlipayRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.request.AlipayUserInfoShareRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.alipay.api.response.AlipayUserInfoShareResponse;
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;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.AuthChecker;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.net.InetSocketAddress;
/**
* 支付宝公钥模式登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.1
*/
public class AuthAlipayRequest extends AuthDefaultRequest {
/**
* 支付宝公钥:当选择支付宝登录时,该值可用
* 对应“RSA2(SHA256)密钥”中的“支付宝公钥”
*/
private final String alipayPublicKey;
private final AlipayClient alipayClient;
private static final String GATEWAY = "https://openapi.alipay.com/gateway.do";
/**
* @see AuthAlipayRequest#AuthAlipayRequest(me.zhyd.oauth.config.AuthConfig, java.lang.String)
* @deprecated 请使用带有"alipayPublicKey"参数的构造方法
*/
@Deprecated
public AuthAlipayRequest(AuthConfig config) {
this(config, (String) null);
}
/**
* @see AuthAlipayRequest#AuthAlipayRequest(me.zhyd.oauth.config.AuthConfig, java.lang.String, me.zhyd.oauth.cache.AuthStateCache)
* @deprecated 请使用带有"alipayPublicKey"参数的构造方法
*/
@Deprecated
public AuthAlipayRequest(AuthConfig config, AuthStateCache authStateCache) {
this(config, null, authStateCache);
}
/**
* @see AuthAlipayRequest#AuthAlipayRequest(me.zhyd.oauth.config.AuthConfig, java.lang.String, me.zhyd.oauth.cache.AuthStateCache, java.lang.String, java.lang.Integer)
* @deprecated 请使用带有"alipayPublicKey"参数的构造方法
*/
@Deprecated
public AuthAlipayRequest(AuthConfig config, AuthStateCache authStateCache, String proxyHost, Integer proxyPort) {
this(config, null, authStateCache, proxyHost, proxyPort);
}
/**
* 构造方法,需要设置"alipayPublicKey"
*
* @param config 公共的OAuth配置
* @param alipayPublicKey 支付宝公钥
* @see AuthAlipayRequest#AuthAlipayRequest(me.zhyd.oauth.config.AuthConfig)
*/
public AuthAlipayRequest(AuthConfig config, String alipayPublicKey) {
super(config, AuthDefaultSource.ALIPAY);
this.alipayPublicKey = determineAlipayPublicKey(alipayPublicKey, config);
check(config);
this.alipayClient = new DefaultAlipayClient(GATEWAY, config.getClientId(), config.getClientSecret(), "json", "UTF-8", this.alipayPublicKey, "RSA2");
}
/**
* 构造方法,需要设置"alipayPublicKey"
*
* @param config 公共的OAuth配置
* @param alipayPublicKey 支付宝公钥
* @see AuthAlipayRequest#AuthAlipayRequest(me.zhyd.oauth.config.AuthConfig, me.zhyd.oauth.cache.AuthStateCache)
*/
public AuthAlipayRequest(AuthConfig config, String alipayPublicKey, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.ALIPAY, authStateCache);
this.alipayPublicKey = determineAlipayPublicKey(alipayPublicKey, config);
check(config);
if (config.getHttpConfig() != null && config.getHttpConfig().getProxy() != null
&& config.getHttpConfig().getProxy().address() instanceof InetSocketAddress) {
InetSocketAddress address = (InetSocketAddress) config.getHttpConfig().getProxy().address();
this.alipayClient = new DefaultAlipayClient(GATEWAY, config.getClientId(), config.getClientSecret(),
"json", "UTF-8", this.alipayPublicKey, "RSA2", address.getHostName(), address.getPort());
} else {
this.alipayClient = new DefaultAlipayClient(GATEWAY, config.getClientId(), config.getClientSecret(),
"json", "UTF-8", this.alipayPublicKey, "RSA2");
}
}
/**
* 构造方法,需要设置"alipayPublicKey"
*
* @param config 公共的OAuth配置
* @param alipayPublicKey 支付宝公钥
* @see AuthAlipayRequest#AuthAlipayRequest(me.zhyd.oauth.config.AuthConfig, me.zhyd.oauth.cache.AuthStateCache, java.lang.String, java.lang.Integer)
*/
public AuthAlipayRequest(AuthConfig config, String alipayPublicKey, AuthStateCache authStateCache, String proxyHost, Integer proxyPort) {
super(config, AuthDefaultSource.ALIPAY, authStateCache);
this.alipayPublicKey = determineAlipayPublicKey(alipayPublicKey, config);
check(config);
this.alipayClient = new DefaultAlipayClient(GATEWAY, config.getClientId(), config.getClientSecret(),
"json", "UTF-8", this.alipayPublicKey, "RSA2", proxyHost, proxyPort);
}
private String determineAlipayPublicKey(String alipayPublicKey, AuthConfig config) {
return alipayPublicKey != null ? alipayPublicKey : config.getAlipayPublicKey();
}
protected void check(AuthConfig config) {
AuthChecker.checkConfig(config, AuthDefaultSource.ALIPAY);
if (!StringUtils.isNotEmpty(alipayPublicKey)) {
throw new AuthException(AuthResponseStatus.PARAMETER_INCOMPLETE, AuthDefaultSource.ALIPAY);
}
// 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1
if (GlobalAuthUtils.isLocalHost(config.getRedirectUri())) {
// The redirect uri of alipay is forbidden to use localhost or 127.0.0.1
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, AuthDefaultSource.ALIPAY);
}
}
@Override
protected void checkCode(AuthCallback authCallback) {
if (StringUtils.isEmpty(authCallback.getAuth_code())) {
throw new AuthException(AuthResponseStatus.ILLEGAL_CODE, source);
}
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
request.setGrantType("authorization_code");
request.setCode(authCallback.getAuth_code());
AlipaySystemOauthTokenResponse response;
try {
response = this.alipayClient.execute(request);
} catch (Exception 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();
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
@Override
public AuthResponse refresh(AuthToken authToken) {
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
request.setGrantType("refresh_token");
request.setRefreshToken(authToken.getRefreshToken());
AlipaySystemOauthTokenResponse response = null;
try {
response = this.alipayClient.execute(request);
} catch (Exception e) {
throw new AuthException(e);
}
if (!response.isSuccess()) {
throw new AuthException(response.getSubMsg());
}
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(response.getAccessToken())
.uid(response.getUserId())
.expireIn(Integer.parseInt(response.getExpiresIn()))
.refreshToken(response.getRefreshToken())
.build())
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
AlipayUserInfoShareRequest request = new AlipayUserInfoShareRequest();
AlipayUserInfoShareResponse response = null;
try {
response = this.alipayClient.execute(request, accessToken);
} catch (AlipayApiException e) {
throw new AuthException(e.getErrMsg(), e);
}
if (!response.isSuccess()) {
throw new AuthException(response.getSubMsg());
}
String province = response.getProvince(), city = response.getCity();
String location = String.format("%s %s", StringUtils.isEmpty(province) ? "" : province, StringUtils.isEmpty(city) ? "" : city);
return AuthUser.builder()
.rawUserInfo(JSONObject.parseObject(JSONObject.toJSONString(response)))
.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(source.toString())
.build();
}
/**
* 返回带{@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("app_id", config.getClientId())
.queryParam("scope", "auth_user")
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(state))
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthAliyunRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
/**
* 阿里云登录
*
* @author snippet0809 (https://github.com/snippet0809)
* @since 1.15.5
*/
public class AuthAliyunRequest extends AuthDefaultRequest {
public AuthAliyunRequest(AuthConfig config) {
super(config, AuthDefaultSource.ALIYUN);
}
public AuthAliyunRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.ALIYUN, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.tokenType(accessTokenObject.getString("token_type"))
.idToken(accessTokenObject.getString("id_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String userInfo = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(userInfo);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("sub"))
.username(object.getString("login_name"))
.nickname(object.getString("name"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthAmazonRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import com.xkcoding.http.constants.Constants;
import com.xkcoding.http.support.HttpHeader;
import com.xkcoding.http.util.UrlUtil;
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.enums.scope.AuthAmazonScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.PkceUtil;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Amazon登录
* Login with Amazon for Websites Overview: https://developer.amazon.com/zh/docs/login-with-amazon/register-web.html
* Login with Amazon SDK for JavaScript Reference Guide:https://developer.amazon.com/zh/docs/login-with-amazon/javascript-sdk-reference.html
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.0
*/
public class AuthAmazonRequest extends AuthDefaultRequest {
public AuthAmazonRequest(AuthConfig config) {
super(config, AuthDefaultSource.AMAZON);
}
public AuthAmazonRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.AMAZON, authStateCache);
}
/**
* https://developer.amazon.com/zh/docs/login-with-amazon/authorization-code-grant.html#authorization-request
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return String
*/
@Override
public String authorize(String state) {
String realState = getRealState(state);
UrlBuilder builder = UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("client_id", config.getClientId())
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthAmazonScope.values())))
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("response_type", "code")
.queryParam("state", realState);
if (config.isPkce()) {
String cacheKey = this.source.getName().concat(":code_verifier:").concat(realState);
String codeVerifier = PkceUtil.generateCodeVerifier();
String codeChallengeMethod = "S256";
String codeChallenge = PkceUtil.generateCodeChallenge(codeChallengeMethod, codeVerifier);
builder.queryParam("code_challenge", codeChallenge)
.queryParam("code_challenge_method", codeChallengeMethod);
// 缓存 codeVerifier 十分钟
this.authStateCache.cache(cacheKey, codeVerifier, TimeUnit.MINUTES.toMillis(10));
}
return builder.build();
}
/**
* https://developer.amazon.com/zh/docs/login-with-amazon/authorization-code-grant.html#access-token-request
*
* @return access token
*/
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
Map form = new HashMap<>(9);
form.put("grant_type", "authorization_code");
form.put("code", authCallback.getCode());
form.put("redirect_uri", config.getRedirectUri());
form.put("client_id", config.getClientId());
form.put("client_secret", config.getClientSecret());
if (config.isPkce()) {
String cacheKey = this.source.getName().concat(":code_verifier:").concat(authCallback.getState());
String codeVerifier = this.authStateCache.get(cacheKey);
form.put("code_verifier", codeVerifier);
}
return getToken(form, this.source.accessToken());
}
@Override
public AuthResponse refresh(AuthToken authToken) {
Map form = new HashMap<>(7);
form.put("grant_type", "refresh_token");
form.put("refresh_token", authToken.getRefreshToken());
form.put("client_id", config.getClientId());
form.put("client_secret", config.getClientSecret());
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(getToken(form, this.source.refresh()))
.build();
}
private AuthToken getToken(Map param, String url) {
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Host", "api.amazon.com");
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded;charset=UTF-8");
String response = new HttpUtils(config.getHttpConfig()).post(url, param, httpHeader, false).getBody();
JSONObject jsonObject = JSONObject.parseObject(response);
this.checkResponse(jsonObject);
return AuthToken.builder()
.accessToken(jsonObject.getString("access_token"))
.tokenType(jsonObject.getString("token_type"))
.expireIn(jsonObject.getIntValue("expires_in"))
.refreshToken(jsonObject.getString("refresh_token"))
.build();
}
/**
* 校验响应内容是否正确
*
* @param jsonObject 响应内容
*/
private void checkResponse(JSONObject jsonObject) {
if (jsonObject.containsKey("error")) {
throw new AuthException(jsonObject.getString("error_description").concat(" ") + jsonObject.getString("error_description"));
}
}
/**
* https://developer.amazon.com/zh/docs/login-with-amazon/obtain-customer-profile.html#call-profile-endpoint
*
* @param authToken token信息
* @return AuthUser
*/
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
this.checkToken(accessToken);
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Host", "api.amazon.com");
httpHeader.add("Authorization", "bearer " + accessToken);
String userInfo = new HttpUtils(config.getHttpConfig()).get(this.source.userInfo(), new HashMap<>(0), httpHeader, false).getBody();
JSONObject jsonObject = JSONObject.parseObject(userInfo);
this.checkResponse(jsonObject);
return AuthUser.builder()
.rawUserInfo(jsonObject)
.uuid(jsonObject.getString("user_id"))
.username(jsonObject.getString("name"))
.nickname(jsonObject.getString("name"))
.email(jsonObject.getString("email"))
.gender(AuthUserGender.UNKNOWN)
.source(source.toString())
.token(authToken)
.build();
}
private void checkToken(String accessToken) {
String tokenInfo = new HttpUtils(config.getHttpConfig()).get("https://api.amazon.com/auth/o2/tokeninfo?access_token=" + UrlUtil.urlEncode(accessToken)).getBody();
JSONObject jsonObject = JSONObject.parseObject(tokenInfo);
if (!config.getClientId().equals(jsonObject.getString("aud"))) {
throw new AuthException(AuthResponseStatus.ILLEGAL_TOKEN);
}
}
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("user_id", authToken.getUserId())
.queryParam("screen_name", authToken.getScreenName())
.queryParam("include_entities", true)
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthAppleRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.impl.security.AbstractJwk;
import lombok.Data;
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.scope.AuthAppleScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import java.io.IOException;
import java.io.StringReader;
import java.security.PrivateKey;
import java.util.Base64;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class AuthAppleRequest extends AuthDefaultRequest {
private static final String AUD = "https://appleid.apple.com";
private volatile PrivateKey privateKey;
public AuthAppleRequest(AuthConfig config) {
super(config, AuthDefaultSource.APPLE);
}
public AuthAppleRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.APPLE, authStateCache);
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("response_mode", "form_post")
.queryParam("scope", this.getScopes(" ", false, AuthScopeUtils.getDefaultScopes(AuthAppleScope.values())))
.build();
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
if (!StringUtils.isEmpty(authCallback.getError())) {
throw new AuthException(authCallback.getError());
}
this.config.setClientSecret(this.getToken());
// if failed will throw AuthException
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
// https://developer.apple.com/documentation/sign_in_with_apple/tokenresponse
AuthToken.AuthTokenBuilder builder = AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.tokenType(accessTokenObject.getString("token_type"))
.idToken(accessTokenObject.getString("id_token"));
if (!StringUtils.isEmpty(authCallback.getUser())) {
try {
AppleUserInfo userInfo = JSONObject.parseObject(authCallback.getUser(), AppleUserInfo.class);
builder.username(userInfo.getName().getFirstName() + " " + userInfo.getName().getLastName());
} catch (Exception ignored) {
}
}
return builder.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
Base64.Decoder urlDecoder = Base64.getUrlDecoder();
String[] idToken = authToken.getIdToken().split("\\.");
String payload = new String(urlDecoder.decode(idToken[1]));
JSONObject object = JSONObject.parseObject(payload);
// https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple#3383773
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("sub"))
.email(object.getString("email"))
.username(authToken.getUsername())
.token(authToken)
.source(source.toString())
.build();
}
@Override
protected void checkConfig(AuthConfig config) {
super.checkConfig(config);
if (StringUtils.isEmpty(config.getClientId())) {
throw new AuthException(AuthResponseStatus.ILLEGAL_CLIENT_ID, source);
}
if (StringUtils.isEmpty(config.getClientSecret())) {
throw new AuthException(AuthResponseStatus.ILLEGAL_CLIENT_SECRET, source);
}
if (StringUtils.isEmpty(config.getKid())) {
throw new AuthException(AuthResponseStatus.ILLEGAL_KID, source);
}
if (StringUtils.isEmpty(config.getTeamId())) {
throw new AuthException(AuthResponseStatus.ILLEGAL_TEAM_ID, source);
}
}
/**
* 获取token
* @see creating-a-client-secret
* @return jwt token
*/
private String getToken() {
return Jwts.builder().header().add(AbstractJwk.KID.getId(), this.config.getKid()).and()
.issuer(this.config.getTeamId())
.subject(this.config.getClientId())
.audience().add(AUD).and()
.expiration(new Date(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(3)))
.issuedAt(new Date())
.signWith(getPrivateKey())
.compact();
}
private PrivateKey getPrivateKey() {
if (this.privateKey == null) {
synchronized (this) {
if (this.privateKey == null) {
try (PEMParser pemParser = new PEMParser(new StringReader(this.config.getClientSecret()))) {
JcaPEMKeyConverter pemKeyConverter = new JcaPEMKeyConverter();
PrivateKeyInfo keyInfo = (PrivateKeyInfo) pemParser.readObject();
this.privateKey = pemKeyConverter.getPrivateKey(keyInfo);
} catch (IOException e) {
throw new AuthException("Failed to get apple private key", e);
}
}
}
}
return this.privateKey;
}
@Data
static class AppleUserInfo {
private AppleUsername name;
private String email;
}
@Data
static class AppleUsername {
private String firstName;
private String lastName;
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthBaiduRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.enums.scope.AuthBaiduScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 百度账号登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
public class AuthBaiduRequest extends AuthDefaultRequest {
public AuthBaiduRequest(AuthConfig config) {
super(config, AuthDefaultSource.BAIDU);
}
public AuthBaiduRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.BAIDU, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
return getAuthToken(response);
}
/**
* https://openapi.baidu.com/rest/2.0/passport/users/getInfo?access_token=121.c86e87cc0828cc1dabb8faee540531d4.YsUIAWvYbgqVni1VhkgKgyLh8nEyELbDOEZs_OA.OgDgmA
* https://openapi.baidu.com/rest/2.0/passport/users/getInfo?access_token=121.2907d9facf9fb97adf7287fa75496eda.Y3NSjR3-3HKt1RgT0HEl7GgxRXT5gOOVdngXezY.OcC_7g
* 新旧应用返回的用户信息不一致
*
* @param authToken token信息
* @return AuthUser
*/
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String userInfo = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.containsKey("userid") ? object.getString("userid") : object.getString("openid"))
.username(object.getString("username"))
.nickname(object.getString("username"))
.avatar(getAvatar(object))
.remark(object.getString("userdetail"))
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.token(authToken)
.source(source.toString())
.build();
}
private String getAvatar(JSONObject object) {
String protrait = object.getString("portrait");
return StringUtils.isEmpty(protrait) ? null : String.format("http://himg.bdimg.com/sys/portrait/item/%s.jpg", protrait);
}
@Override
public AuthResponse revoke(AuthToken authToken) {
String response = doGetRevoke(authToken);
JSONObject object = JSONObject.parseObject(response);
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();
String response = new HttpUtils(config.getHttpConfig()).get(refreshUrl).getBody();
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(this.getAuthToken(response))
.build();
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("display", "popup")
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthBaiduScope.values())))
.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);
}
}
private AuthToken getAuthToken(String response) {
JSONObject accessTokenObject = JSONObject.parseObject(response);
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();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthCodingRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.enums.scope.AuthCodingScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* Coding登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
public class AuthCodingRequest extends AuthDefaultRequest {
public AuthCodingRequest(AuthConfig config) {
super(config, AuthDefaultSource.CODING);
}
public AuthCodingRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.CODING, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doGetAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
object = object.getJSONObject("data");
return AuthUser.builder()
.rawUserInfo(object)
.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(source.toString())
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.getIntValue("code") != 0) {
throw new AuthException(object.getString("msg"));
}
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(String.format(source.authorize(), config.getDomainPrefix()))
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthCodingScope.values())))
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
@Override
public String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(String.format(source.accessToken(), config.getDomainPrefix()))
.queryParam("code", code)
.queryParam("client_id", config.getClientId())
.queryParam("client_secret", config.getClientSecret())
.queryParam("grant_type", "authorization_code")
.queryParam("redirect_uri", config.getRedirectUri())
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken token
* @return 返回获取userInfo的url
*/
@Override
public String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(String.format(source.userInfo(), config.getDomainPrefix()))
.queryParam("access_token", authToken.getAccessToken())
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthCsdnRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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;
/**
* CSDN登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
@Deprecated
public class AuthCsdnRequest extends AuthDefaultRequest {
public AuthCsdnRequest(AuthConfig config) {
super(config, AuthDefaultSource.CSDN);
}
public AuthCsdnRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.CSDN, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder().accessToken(accessTokenObject.getString("access_token")).build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("username"))
.username(object.getString("username"))
.remark(object.getString("description"))
.blog(object.getString("website"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error_code")) {
throw new AuthException(object.getString("error"));
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthDefaultRequest.java
================================================
package me.zhyd.oauth.request;
import com.xkcoding.http.util.UrlUtil;
import me.zhyd.oauth.cache.AuthDefaultStateCache;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.log.Log;
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.*;
import java.util.List;
/**
* 默认的request处理类
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @author yangkai.shen (https://xkcoding.com)
* @since 1.0.0
*/
public abstract class AuthDefaultRequest implements AuthRequest {
protected AuthConfig config;
protected AuthSource source;
protected AuthStateCache authStateCache;
public AuthDefaultRequest(AuthConfig config, AuthSource source) {
this(config, source, AuthDefaultStateCache.INSTANCE);
}
public AuthDefaultRequest(AuthConfig config, AuthSource source, AuthStateCache authStateCache) {
this.config = config;
this.source = source;
this.authStateCache = authStateCache;
if (!AuthChecker.isSupportedAuth(config, source)) {
throw new AuthException(AuthResponseStatus.PARAMETER_INCOMPLETE, source);
}
// 校验配置合法性
this.checkConfig(config);
}
/**
* 统一的登录入口。当通过{@link AuthDefaultRequest#authorize(String)}授权成功后,会跳转到调用方的相关回调方法中
* 方法的入参可以使用{@code AuthCallback},{@code AuthCallback}类中封装好了OAuth2授权回调所需要的参数
*
* @param authCallback 用于接收回调参数的实体
* @return AuthResponse
*/
@Override
public AuthResponse login(AuthCallback authCallback) {
try {
checkCode(authCallback);
if (!config.isIgnoreCheckState()) {
AuthChecker.checkState(authCallback.getState(), source, authStateCache);
}
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);
}
}
protected void checkCode(AuthCallback authCallback) {
AuthChecker.checkCode(source, authCallback);
}
/**
* 处理{@link AuthDefaultRequest#login(AuthCallback)} 发生异常的情况,统一响应参数
*
* @param e 具体的异常
* @return AuthResponse
*/
AuthResponse responseError(Exception e) {
int errorCode = AuthResponseStatus.FAILURE.getCode();
String errorMsg = e.getMessage();
if (e instanceof AuthException) {
AuthException authException = ((AuthException) e);
errorCode = authException.getErrorCode();
if (StringUtils.isNotEmpty(authException.getErrorMsg())) {
errorMsg = authException.getErrorMsg();
}
}
return AuthResponse.builder().code(errorCode).msg(errorMsg).build();
}
/**
* 返回授权url,可自行跳转页面
*
* 不建议使用该方式获取授权地址,不带{@code state}的授权地址,容易受到csrf攻击。
* 建议使用{@link AuthDefaultRequest#authorize(String)}方法生成授权地址,在回调方法中对{@code state}进行校验
*
* @return 返回授权地址
* @see AuthDefaultRequest#authorize(String)
*/
@Deprecated
@Override
public String authorize() {
return this.authorize(null);
}
/**
* 返回带{@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("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @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
*
* @param refreshToken refreshToken
* @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
*
* @param authToken token
* @return 返回获取userInfo的url
*/
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo()).queryParam("access_token", authToken.getAccessToken()).build();
}
/**
* 返回获取revoke authorization的url
*
* @param authToken token
* @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) {
if (StringUtils.isEmpty(state)) {
state = UuidUtils.getUUID();
}
// 缓存state
authStateCache.cache(state, state);
return state;
}
/**
* 通用的 authorizationCode 协议
*
* @param code code码
* @return Response
*/
protected String doPostAuthorizationCode(String code) {
return new HttpUtils(config.getHttpConfig()).post(accessTokenUrl(code)).getBody();
}
/**
* 通用的 authorizationCode 协议
*
* @param code code码
* @return Response
*/
protected String doGetAuthorizationCode(String code) {
return new HttpUtils(config.getHttpConfig()).get(accessTokenUrl(code)).getBody();
}
/**
* 通用的 用户信息
*
* @param authToken token封装
* @return Response
*/
@Deprecated
protected String doPostUserInfo(AuthToken authToken) {
return new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken)).getBody();
}
/**
* 通用的 用户信息
*
* @param authToken token封装
* @return Response
*/
protected String doGetUserInfo(AuthToken authToken) {
return new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken)).getBody();
}
/**
* 通用的post形式的取消授权方法
*
* @param authToken token封装
* @return Response
*/
@Deprecated
protected String doPostRevoke(AuthToken authToken) {
return new HttpUtils(config.getHttpConfig()).post(revokeUrl(authToken)).getBody();
}
/**
* 通用的post形式的取消授权方法
*
* @param authToken token封装
* @return Response
*/
protected String doGetRevoke(AuthToken authToken) {
return new HttpUtils(config.getHttpConfig()).get(revokeUrl(authToken)).getBody();
}
/**
* 获取以 {@code separator}分割过后的 scope 信息
*
* @param separator 多个 {@code scope} 间的分隔符
* @param encode 是否 encode 编码
* @param defaultScopes 默认的 scope, 当客户端没有配置 {@code scopes} 时启用
* @return String
* @since 1.16.7
*/
protected String getScopes(String separator, boolean encode, List defaultScopes) {
List scopes = config.getScopes();
if (null == scopes || scopes.isEmpty()) {
if (null == defaultScopes || defaultScopes.isEmpty()) {
return "";
}
scopes = defaultScopes;
}
if (null == separator) {
// 默认为空格
separator = " ";
}
String scopeStr = String.join(separator, scopes);
return encode ? UrlUtil.urlEncode(scopeStr) : scopeStr;
}
protected void checkConfig(AuthConfig config) {
AuthChecker.checkConfig(config, source);
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthDingTalkAccountRequest.java
================================================
package me.zhyd.oauth.request;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
/**
* 钉钉账号登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
public class AuthDingTalkAccountRequest extends AbstractAuthDingtalkRequest {
public AuthDingTalkAccountRequest(AuthConfig config) {
super(config, AuthDefaultSource.DINGTALK_ACCOUNT);
}
public AuthDingTalkAccountRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.DINGTALK_ACCOUNT, authStateCache);
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthDingTalkRequest.java
================================================
package me.zhyd.oauth.request;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
/**
* 钉钉二维码登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
public class AuthDingTalkRequest extends AbstractAuthDingtalkRequest {
public AuthDingTalkRequest(AuthConfig config) {
super(config, AuthDefaultSource.DINGTALK);
}
public AuthDingTalkRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.DINGTALK, authStateCache);
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthDingTalkV2Request.java
================================================
package me.zhyd.oauth.request;
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.scope.AuthDingTalkScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.HashMap;
import java.util.Map;
/**
* 新版钉钉二维码登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.7
*/
public class AuthDingTalkV2Request extends AuthDefaultRequest {
public AuthDingTalkV2Request(AuthConfig config) {
super(config, AuthDefaultSource.DINGTALK_V2);
}
public AuthDingTalkV2Request(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.DINGTALK_V2, authStateCache);
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("scope", this.getScopes(",", true, AuthScopeUtils.getDefaultScopes(AuthDingTalkScope.values())))
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("prompt", "consent")
.queryParam("org_type", config.getDingTalkOrgType())
.queryParam("corpId", config.getDingTalkCorpId())
.queryParam("exclusiveLogin", config.isDingTalkExclusiveLogin())
.queryParam("exclusiveCorpId", config.getDingTalkExclusiveCorpId())
.queryParam("state", getRealState(state))
.build();
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
Map params = new HashMap<>();
params.put("grantType", "authorization_code");
params.put("clientId", config.getClientId());
params.put("clientSecret", config.getClientSecret());
params.put("code", authCallback.getCode());
String response = new HttpUtils(config.getHttpConfig()).post(this.source.accessToken(), JSONObject.toJSONString(params)).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
if (!accessTokenObject.containsKey("accessToken")) {
throw new AuthException(JSONObject.toJSONString(response), source);
}
return AuthToken.builder()
.accessToken(accessTokenObject.getString("accessToken"))
.refreshToken(accessTokenObject.getString("refreshToken"))
.expireIn(accessTokenObject.getIntValue("expireIn"))
.corpId(accessTokenObject.getString("corpId"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
HttpHeader header = new HttpHeader();
header.add("x-acs-dingtalk-access-token", authToken.getAccessToken());
String response = new HttpUtils(config.getHttpConfig()).get(this.source.userInfo(), null, header, false).getBody();
JSONObject object = JSONObject.parseObject(response);
authToken.setOpenId(object.getString("openId"));
authToken.setUnionId(object.getString("unionId"));
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("unionId"))
.username(object.getString("nick"))
.nickname(object.getString("nick"))
.avatar(object.getString("avatarUrl"))
.snapshotUser(object.getBooleanValue("visitor"))
.token(authToken)
.source(source.toString())
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("clientId", config.getClientId())
.queryParam("clientSecret", config.getClientSecret())
.queryParam("grantType", "authorization_code")
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthDouyinRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.enums.scope.AuthDouyinScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 抖音登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.4.0
*/
public class AuthDouyinRequest extends AuthDefaultRequest {
public AuthDouyinRequest(AuthConfig config) {
super(config, AuthDefaultSource.DOUYIN);
}
public AuthDouyinRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.DOUYIN, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
return this.getToken(accessTokenUrl(authCallback.getCode()));
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String response = doGetUserInfo(authToken);
JSONObject userInfoObject = JSONObject.parseObject(response);
this.checkResponse(userInfoObject);
JSONObject object = userInfoObject.getJSONObject("data");
authToken.setUnionId(object.getString("union_id"));
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("union_id"))
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(object.getString("avatar"))
.remark(object.getString("description"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.location(String.format("%s %s %s", object.getString("country"), object.getString("province"), object.getString("city")))
.token(authToken)
.source(source.toString())
.build();
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(getToken(refreshTokenUrl(oldToken.getRefreshToken())))
.build();
}
/**
* 检查响应内容是否正确
*
* @param 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"));
}
}
/**
* 获取token,适用于获取access_token和刷新token
*
* @param accessTokenUrl 实际请求token的地址
* @return token对象
*/
private AuthToken getToken(String accessTokenUrl) {
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
JSONObject dataObj = object.getJSONObject("data");
return AuthToken.builder()
.accessToken(dataObj.getString("access_token"))
.openId(dataObj.getString("open_id"))
.expireIn(dataObj.getIntValue("expires_in"))
.refreshToken(dataObj.getString("refresh_token"))
.refreshTokenExpireIn(dataObj.getIntValue("refresh_expires_in"))
.scope(dataObj.getString("scope"))
.build();
}
/**
* 返回带{@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("response_type", "code")
.queryParam("client_key", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("scope", this.getScopes(",", true, AuthScopeUtils.getDefaultScopes(AuthDouyinScope.values())))
.queryParam("state", getRealState(state))
.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();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthElemeRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
import com.xkcoding.http.constants.Constants;
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;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.Base64Utils;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import me.zhyd.oauth.utils.UuidUtils;
import java.util.HashMap;
import java.util.Map;
/**
* 饿了么
*
* 注:集成的是正式环境,非沙箱环境
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.12.0
*/
public class AuthElemeRequest extends AuthDefaultRequest {
private static final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded;charset=UTF-8";
private static final String CONTENT_TYPE_JSON = "application/json; charset=utf-8";
public AuthElemeRequest(AuthConfig config) {
super(config, AuthDefaultSource.ELEME);
}
public AuthElemeRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.ELEME, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
Map form = new HashMap<>(7);
form.put("client_id", config.getClientId());
form.put("redirect_uri", config.getRedirectUri());
form.put("code", authCallback.getCode());
form.put("grant_type", "authorization_code");
HttpHeader httpHeader = this.buildHeader(CONTENT_TYPE_FORM, this.getRequestId(), true);
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, httpHeader, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.refreshToken(object.getString("refresh_token"))
.tokenType(object.getString("token_type"))
.expireIn(object.getIntValue("expires_in"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
Map parameters = new HashMap<>(4);
// 获取商户账号信息的API接口名称
String action = "eleme.user.getUser";
// 时间戳,单位秒。API服务端允许客户端请求最大时间误差为正负5分钟。
final long timestamp = System.currentTimeMillis();
// 公共参数
Map metasHashMap = new HashMap<>(4);
metasHashMap.put("app_key", config.getClientId());
metasHashMap.put("timestamp", timestamp);
String signature = GlobalAuthUtils.generateElemeSignature(config.getClientId(), config.getClientSecret(), timestamp, action, authToken
.getAccessToken(), parameters);
String requestId = this.getRequestId();
Map paramsMap = new HashMap<>();
paramsMap.put("nop", "1.0.0");
paramsMap.put("id", requestId);
paramsMap.put("action", action);
paramsMap.put("token", authToken.getAccessToken());
paramsMap.put("metas", metasHashMap);
paramsMap.put("params", parameters);
paramsMap.put("signature", signature);
HttpHeader httpHeader = this.buildHeader(CONTENT_TYPE_JSON, requestId, false);
String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), JSONObject.toJSONString(paramsMap), httpHeader).getBody();
JSONObject object = JSONObject.parseObject(response);
// 校验请求
if (object.containsKey("name")) {
throw new AuthException(object.getString("message"));
}
if (object.containsKey("error") && null != object.get("error")) {
throw new AuthException(object.getJSONObject("error").getString("message"));
}
JSONObject result = object.getJSONObject("result");
return AuthUser.builder()
.rawUserInfo(result)
.uuid(result.getString("userId"))
.username(result.getString("userName"))
.nickname(result.getString("userName"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
Map form = new HashMap<>(4);
form.put("refresh_token", oldToken.getRefreshToken());
form.put("grant_type", "refresh_token");
HttpHeader httpHeader = this.buildHeader(CONTENT_TYPE_FORM, this.getRequestId(), true);
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, httpHeader, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(object.getString("access_token"))
.refreshToken(object.getString("refresh_token"))
.tokenType(object.getString("token_type"))
.expireIn(object.getIntValue("expires_in"))
.build())
.build();
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state)).queryParam("scope", "all").build();
}
private String getBasic(String appKey, String appSecret) {
StringBuilder sb = new StringBuilder();
String encodeToString = Base64Utils.encode((appKey + ":" + appSecret).getBytes());
sb.append("Basic").append(" ").append(encodeToString);
return sb.toString();
}
private HttpHeader buildHeader(String contentType, String requestId, boolean auth) {
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Accept", "text/xml,text/javascript,text/html");
httpHeader.add(Constants.CONTENT_TYPE, contentType);
httpHeader.add("Accept-Encoding", "gzip");
httpHeader.add("User-Agent", "eleme-openapi-java-sdk");
httpHeader.add("x-eleme-requestid", requestId);
if (auth) {
httpHeader.add("Authorization", this.getBasic(config.getClientId(), config.getClientSecret()));
}
return httpHeader;
}
private String getRequestId() {
return (UuidUtils.getUUID() + "|" + System.currentTimeMillis()).toUpperCase();
}
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthFacebookRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.enums.scope.AuthFacebookScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* Facebook登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.3.0
*/
public class AuthFacebookRequest extends AuthDefaultRequest {
public AuthFacebookRequest(AuthConfig config) {
super(config, AuthDefaultSource.FACEBOOK);
}
public AuthFacebookRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.FACEBOOK, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.tokenType(accessTokenObject.getString("token_type"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String userInfo = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("id"))
.username(object.getString("name"))
.nickname(object.getString("name"))
.blog(object.getString("link"))
.avatar(getUserPicture(object))
.location(object.getString("locale"))
.email(object.getString("email"))
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(source.toString())
.build();
}
private String getUserPicture(JSONObject object) {
String picture = null;
if (object.containsKey("picture")) {
JSONObject pictureObj = object.getJSONObject("picture");
pictureObj = pictureObj.getJSONObject("data");
if (null != pictureObj) {
picture = pictureObj.getString("url");
}
}
return picture;
}
/**
* 返回获取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("fields", "id,name,birthday,gender,hometown,email,devices,picture.width(400),link")
.build();
}
@Override
protected void checkConfig(AuthConfig config) {
super.checkConfig(config);
// facebook的回调地址必须为https的链接
if (AuthDefaultSource.FACEBOOK == source && !GlobalAuthUtils.isHttpsProtocol(config.getRedirectUri())) {
// Facebook's redirect uri must use the HTTPS protocol
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
}
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getJSONObject("error").getString("message"));
}
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("scope", this.getScopes(",", false, AuthScopeUtils.getDefaultScopes(AuthFacebookScope.values())))
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthFeishuRequest.java
================================================
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;
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;
/**
* 飞书平台,企业自建应用授权登录,原逻辑由 beacon 集成于 1.14.0 版,但最新的飞书 api 已修改,并且飞书平台一直为 {@code Deprecated} 状态
*
* 所以,最终修改该平台的实际发布版本为 1.15.9
*
* @author beacon
* @author yadong.zhang (yadong.zhang0415(a)gmail.com) 重构业务逻辑 20210101
* @since 1.15.9
*/
public class AuthFeishuRequest extends AuthDefaultRequest {
public AuthFeishuRequest(AuthConfig config) {
super(config, AuthDefaultSource.FEISHU);
}
public AuthFeishuRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.FEISHU, authStateCache);
}
/**
* 获取 app_access_token(企业自建应用)
*
* 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")).getBody();
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
public 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());
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
String response = new HttpUtils(config.getHttpConfig()).get(source.userInfo(), null, new HttpHeader()
.add("Content-Type", "application/json")
.add("Authorization", "Bearer " + accessToken), false).getBody();
JSONObject object = JSON.parseObject(response);
this.checkResponse(object);
JSONObject data = object.getJSONObject("data");
return AuthUser.builder()
.rawUserInfo(object)
.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_access_token", this.getAppAccessToken());
requestObject.put("grant_type", "refresh_token");
requestObject.put("refresh_token", authToken.getRefreshToken());
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")).getBody();
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
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("app_id", config.getClientId())
.queryParam("redirect_uri", GlobalAuthUtils.urlEncode(config.getRedirectUri()))
.queryParam("state", getRealState(state))
.build();
}
/**
* 校验响应内容是否正确
*
* @param jsonObject 响应内容
*/
private void checkResponse(JSONObject jsonObject) {
if (jsonObject.getIntValue("code") != 0) {
throw new AuthException(jsonObject.getString("message"));
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthFigmaRequest.java
================================================
package me.zhyd.oauth.request;
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.scope.AuthFigmaScope;
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.*;
/**
* Figma登录
* @author xiangqian
* @since 1.16.6
*/
public class AuthFigmaRequest extends AuthDefaultRequest {
public AuthFigmaRequest(AuthConfig config) {
super(config, AuthDefaultSource.FIGMA);
}
public AuthFigmaRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.FIGMA, authStateCache);
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("scope", this.getScopes(",", true, AuthScopeUtils.getDefaultScopes(AuthFigmaScope.values())))
.build();
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
HttpHeader header = new HttpHeader()
.add("content-type", "application/x-www-form-urlencoded")
.add("Authorization", "Basic " + Base64Utils.encode(config.getClientId().concat(":").concat(config.getClientSecret())));
String response = new HttpUtils(config.getHttpConfig()).post(super.accessTokenUrl(authCallback.getCode()), null, header, true).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.scope(accessTokenObject.getString("scope"))
.userId(accessTokenObject.getString("user_id"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.build();
}
@Override
public AuthResponse refresh(AuthToken authToken) {
HttpHeader header = new HttpHeader().add("content-type", "application/x-www-form-urlencoded");
String response = new HttpUtils(config.getHttpConfig()).post(this.refreshTokenUrl(authToken.getRefreshToken()), null, header, false).getBody();
JSONObject dataObj = JSONObject.parseObject(response);
this.checkResponse(dataObj);
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(dataObj.getString("access_token"))
.openId(dataObj.getString("open_id"))
.expireIn(dataObj.getIntValue("expires_in"))
.refreshToken(dataObj.getString("refresh_token"))
.scope(dataObj.getString("scope"))
.build())
.build();
}
@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)
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
HttpHeader header = new HttpHeader().add("Authorization", "Bearer " + authToken.getAccessToken());
String response = new HttpUtils(config.getHttpConfig()).get(super.userInfoUrl(authToken), null, header, false).getBody();
JSONObject dataObj = JSONObject.parseObject(response);
this.checkResponse(dataObj);
return AuthUser.builder()
.rawUserInfo(dataObj)
.uuid(dataObj.getString("id"))
.username(dataObj.getString("handle"))
.avatar(dataObj.getString("img_url"))
.email(dataObj.getString("email"))
.token(authToken)
.source(source.toString())
.build();
}
/**
* 校验响应结果
*
* @param object 接口返回的结果
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error") + ":" + object.getString("message"));
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthGiteeRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.enums.scope.AuthGiteeScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* Gitee登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
public class AuthGiteeRequest extends AuthDefaultRequest {
public AuthGiteeRequest(AuthConfig config) {
super(config, AuthDefaultSource.GITEE);
}
public AuthGiteeRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.GITEE, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
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
public AuthUser getUserInfo(AuthToken authToken) {
String userInfo = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.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(source.toString())
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthGiteeScope.values())))
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthGithubRequest.java
================================================
package me.zhyd.oauth.request;
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.AuthUserGender;
import me.zhyd.oauth.enums.scope.AuthGithubScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.Map;
/**
* Github登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
public class AuthGithubRequest extends AuthDefaultRequest {
public AuthGithubRequest(AuthConfig config) {
super(config, AuthDefaultSource.GITHUB);
}
public AuthGithubRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.GITHUB, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
Map res = GlobalAuthUtils.parseStringToMap(response);
this.checkResponse(res.containsKey("error"), res.get("error_description"));
return AuthToken.builder()
.accessToken(res.get("access_token"))
.scope(res.get("scope"))
.tokenType(res.get("token_type"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
HttpHeader header = new HttpHeader();
header.add("Authorization", "token " + authToken.getAccessToken());
String response = new HttpUtils(config.getHttpConfig()).get(UrlBuilder.fromBaseUrl(source.userInfo()).build(), null, header, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object.containsKey("error"), object.getString("error_description"));
return AuthUser.builder()
.rawUserInfo(object)
.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(source.toString())
.build();
}
private void checkResponse(boolean error, String errorDescription) {
if (error) {
throw new AuthException(errorDescription);
}
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthGithubScope.values())))
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthGitlabRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.enums.scope.AuthGitlabScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* Gitlab登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.11.0
*/
public class AuthGitlabRequest extends AuthDefaultRequest {
public AuthGitlabRequest(AuthConfig config) {
super(config, AuthDefaultSource.GITLAB);
}
public AuthGitlabRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.GITLAB, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.refreshToken(object.getString("refresh_token"))
.idToken(object.getString("id_token"))
.tokenType(object.getString("token_type"))
.scope(object.getString("scope"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("id"))
.username(object.getString("username"))
.nickname(object.getString("name"))
.avatar(object.getString("avatar_url"))
.blog(object.getString("web_url"))
.company(object.getString("organization"))
.location(object.getString("location"))
.email(object.getString("email"))
.remark(object.getString("bio"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
private void checkResponse(JSONObject object) {
// oauth/token 验证异常
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
// user 验证异常
if (object.containsKey("message")) {
throw new AuthException(object.getString("message"));
}
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.11.0
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("scope", this.getScopes("+", false, AuthScopeUtils.getDefaultScopes(AuthGitlabScope.values())))
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthGoogleRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
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.AuthUserGender;
import me.zhyd.oauth.enums.scope.AuthGoogleScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* Google登录
*
* @author yangkai.shen (https://xkcoding.com)
* @since 1.3.0
*/
public class AuthGoogleRequest extends AuthDefaultRequest {
public AuthGoogleRequest(AuthConfig config) {
super(config, AuthDefaultSource.GOOGLE);
}
public AuthGoogleRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.GOOGLE, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
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();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Authorization", "Bearer " + authToken.getAccessToken());
String userInfo = new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken), null, httpHeader).getBody();
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("sub"))
.username(object.getString("email"))
.avatar(object.getString("picture"))
.nickname(object.getString("name"))
.location(object.getString("locale"))
.email(object.getString("email"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("access_type", "offline")
.queryParam("scope", this.getScopes(" ", false, AuthScopeUtils.getDefaultScopes(AuthGoogleScope.values())))
.queryParam("prompt","select_account")
.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()).build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error") || object.containsKey("error_description")) {
throw new AuthException(object.containsKey("error") + ":" + object.getString("error_description"));
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthHuaweiRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.enums.scope.AuthHuaweiScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.HashMap;
import java.util.Map;
import static me.zhyd.oauth.enums.AuthResponseStatus.SUCCESS;
/**
* 华为授权登录
*
* 当前方式未来可能被废弃,建议使用 {@link AuthHuaweiV3Request}
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.10.0
*/
@Deprecated
public class AuthHuaweiRequest extends AuthDefaultRequest {
public AuthHuaweiRequest(AuthConfig config) {
super(config, AuthDefaultSource.HUAWEI);
}
public AuthHuaweiRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.HUAWEI, authStateCache);
}
/**
* 获取access token
*
* @param authCallback 授权成功后的回调参数
* @return token
* @see AuthDefaultRequest#authorize()
* @see AuthDefaultRequest#authorize(String)
*/
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
Map form = new HashMap<>(8);
form.put("grant_type", "authorization_code");
form.put("code", authCallback.getAuthorization_code());
form.put("client_id", config.getClientId());
form.put("client_secret", config.getClientSecret());
form.put("redirect_uri", config.getRedirectUri());
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, false).getBody();
return getAuthToken(response);
}
/**
* 使用token换取用户信息
*
* @param authToken token信息
* @return 用户信息
* @see AuthDefaultRequest#getAccessToken(AuthCallback)
*/
@Override
public AuthUser getUserInfo(AuthToken authToken) {
Map form = new HashMap<>(7);
form.put("nsp_ts", System.currentTimeMillis() + "");
form.put("access_token", authToken.getAccessToken());
form.put("nsp_fmt", "JS");
form.put("open_id", "OPENID");
// form.put("nsp_svc", "OpenUP.User.getInfo");
form.put("nsp_svc", "huawei.oauth2.user.getTokenInfo");
String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), form, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
AuthUserGender gender = getRealGender(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("userID"))
.username(object.getString("userName"))
.nickname(object.getString("userName"))
.gender(gender)
.avatar(object.getString("headPictureURL"))
.token(authToken)
.source(source.toString())
.build();
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
@Override
public AuthResponse refresh(AuthToken authToken) {
Map form = new HashMap<>(7);
form.put("client_id", config.getClientId());
form.put("client_secret", config.getClientSecret());
form.put("refresh_token", authToken.getRefreshToken());
form.put("grant_type", "refresh_token");
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, false).getBody();
return AuthResponse.builder().code(SUCCESS.getCode()).data(getAuthToken(response)).build();
}
private AuthToken getAuthToken(String response) {
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("expires_in"))
.refreshToken(object.getString("refresh_token"))
.build();
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("access_type", "offline")
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthHuaweiScope.values())))
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("nsp_ts", System.currentTimeMillis())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("nsp_fmt", "JS")
.queryParam("nsp_svc", "OpenUP.User.getInfo")
.build();
}
/**
* 获取用户的实际性别。华为系统中,用户的性别:1表示女,0表示男
*
* @param object obj
* @return AuthUserGender
*/
private AuthUserGender getRealGender(JSONObject object) {
int genderCodeInt = object.getIntValue("gender");
String genderCode = genderCodeInt == 1 ? "0" : (genderCodeInt == 0) ? "1" : genderCodeInt + "";
return AuthUserGender.getRealGender(genderCode);
}
/**
* 校验响应结果
*
* @param object 接口返回的结果
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("NSP_STATUS")) {
throw new AuthException(object.getString("error"));
}
if (object.containsKey("error")) {
throw new AuthException(object.getString("sub_error") + ":" + object.getString("error_description"));
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthHuaweiV3Request.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import com.xkcoding.http.constants.Constants;
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.AuthUserGender;
import me.zhyd.oauth.enums.scope.AuthHuaweiV3Scope;
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.*;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static me.zhyd.oauth.enums.AuthResponseStatus.SUCCESS;
/**
* 华为授权登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.16.7
*/
public class AuthHuaweiV3Request extends AuthDefaultRequest {
public AuthHuaweiV3Request(AuthConfig config) {
super(config, AuthDefaultSource.HUAWEI_V3);
}
public AuthHuaweiV3Request(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.HUAWEI_V3, authStateCache);
}
/**
* 获取access token
*
* @param authCallback 授权成功后的回调参数
* @return token
* @see AuthDefaultRequest#authorize()
* @see AuthDefaultRequest#authorize(String)
*/
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
Map form = new HashMap<>(8);
form.put("grant_type", "authorization_code");
form.put("code", authCallback.getCode());
form.put("client_id", config.getClientId());
form.put("client_secret", config.getClientSecret());
form.put("redirect_uri", config.getRedirectUri());
if (config.isPkce()) {
String cacheKey = this.source.getName().concat(":code_verifier:").concat(authCallback.getState());
String codeVerifier = this.authStateCache.get(cacheKey);
form.put("code_verifier", codeVerifier);
}
HttpHeader httpHeader = new HttpHeader();
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, httpHeader, false).getBody();
return getAuthToken(response);
}
/**
* 使用token换取用户信息
*
* @param authToken token信息
* @return 用户信息
* @see AuthDefaultRequest#getAccessToken(AuthCallback)
*/
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String idToken = authToken.getIdToken();
if (StringUtils.isEmpty(idToken)) {
Map form = new HashMap<>(7);
form.put("access_token", authToken.getAccessToken());
form.put("getNickName", "1");
form.put("nsp_svc", "GOpen.User.getInfo");
HttpHeader httpHeader = new HttpHeader();
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), form, httpHeader, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("unionID"))
.username(object.getString("displayName"))
.nickname(object.getString("displayName"))
.gender(AuthUserGender.UNKNOWN)
.avatar(object.getString("headPictureURL"))
.token(authToken)
.source(source.toString())
.build();
}
String payload = new String(Base64.getUrlDecoder().decode(idToken.split("\\.")[1]), StandardCharsets.UTF_8);
JSONObject object = JSONObject.parseObject(payload);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("sub"))
.username(object.getString("name"))
.nickname(object.getString("nickname"))
.gender(AuthUserGender.UNKNOWN)
.avatar(object.getString("picture"))
.token(authToken)
.source(source.toString())
.build();
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
@Override
public AuthResponse refresh(AuthToken authToken) {
Map form = new HashMap<>(7);
form.put("client_id", config.getClientId());
form.put("client_secret", config.getClientSecret());
form.put("refresh_token", authToken.getRefreshToken());
form.put("grant_type", "refresh_token");
HttpHeader httpHeader = new HttpHeader();
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, httpHeader, false).getBody();
return AuthResponse.builder().code(SUCCESS.getCode()).data(getAuthToken(response)).build();
}
private AuthToken getAuthToken(String response) {
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("expires_in"))
.refreshToken(object.getString("refresh_token"))
.idToken(object.getString("id_token"))
.build();
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
String realState = getRealState(state);
UrlBuilder builder = UrlBuilder.fromBaseUrl(super.authorize(realState))
.queryParam("access_type", "offline")
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthHuaweiV3Scope.values())));
if (config.isPkce()) {
String cacheKey = this.source.getName().concat(":code_verifier:").concat(realState);
String codeVerifier = PkceUtil.generateCodeVerifier();
String codeChallengeMethod = "S256";
String codeChallenge = PkceUtil.generateCodeChallenge(codeChallengeMethod, codeVerifier);
builder.queryParam("code_challenge", codeChallenge)
.queryParam("code_challenge_method", codeChallengeMethod);
// 缓存 codeVerifier 十分钟
this.authStateCache.cache(cacheKey, codeVerifier, TimeUnit.MINUTES.toMillis(10));
}
return builder.build();
}
/**
* 校验响应结果
*
* @param object 接口返回的结果
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("NSP_STATUS")) {
throw new AuthException(object.getString("error"));
}
if (object.containsKey("error")) {
throw new AuthException(object.getString("sub_error") + ":" + object.getString("error_description"));
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthJdRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.enums.scope.AuthJdScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
/**
* 京东登录
*
* @author harry.lee (harryleexyz@qq.com)
* @since 1.15.0
*/
public class AuthJdRequest extends AuthDefaultRequest {
public AuthJdRequest(AuthConfig config) {
super(config, AuthDefaultSource.JD);
}
public AuthJdRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.JD, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
Map params = new HashMap<>(7);
params.put("app_key", config.getClientId());
params.put("app_secret", config.getClientSecret());
params.put("grant_type", "authorization_code");
params.put("code", authCallback.getCode());
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), params, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("expires_in"))
.refreshToken(object.getString("refresh_token"))
.scope(object.getString("scope"))
.openId(object.getString("open_id"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
UrlBuilder urlBuilder = UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("app_key", config.getClientId())
.queryParam("method", "jingdong.user.getUserInfoByOpenId")
.queryParam("360buy_param_json", "{\"openId\":\"" + authToken.getOpenId() + "\"}")
.queryParam("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
.queryParam("v", "2.0");
urlBuilder.queryParam("sign", GlobalAuthUtils.generateJdSignature(config.getClientSecret(), urlBuilder.getReadOnlyParams()));
String response = new HttpUtils(config.getHttpConfig()).post(urlBuilder.build(true)).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
JSONObject data = this.getUserDataJsonObject(object);
return AuthUser.builder()
.rawUserInfo(data)
.uuid(authToken.getOpenId())
.username(data.getString("nickName"))
.nickname(data.getString("nickName"))
.avatar(data.getString("imageUrl"))
.gender(AuthUserGender.getRealGender(data.getString("gendar")))
.token(authToken)
.source(source.toString())
.build();
}
/**
* 个人用户无法申请应用
* 暂时只能参考官网给出的返回结果解析
*
* @param object 请求返回结果
* @return data JSONObject
*/
private JSONObject getUserDataJsonObject(JSONObject object) {
return object.getJSONObject("jingdong_user_getUserInfoByOpenId_response")
.getJSONObject("getuserinfobyappidandopenid_result")
.getJSONObject("data");
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
Map params = new HashMap<>(7);
params.put("app_key", config.getClientId());
params.put("app_secret", config.getClientSecret());
params.put("grant_type", "refresh_token");
params.put("refresh_token", oldToken.getRefreshToken());
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), params, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("expires_in"))
.refreshToken(object.getString("refresh_token"))
.scope(object.getString("scope"))
.openId(object.getString("open_id"))
.build())
.build();
}
private void checkResponse(JSONObject object) {
if (object.containsKey("error_response")) {
throw new AuthException(object.getJSONObject("error_response").getString("zh_desc"));
}
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("app_key", config.getClientId())
.queryParam("response_type", "code")
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthJdScope.values())))
.queryParam("state", getRealState(state))
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthKujialeRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.scope.AuthKujialeScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 酷家乐授权登录
*
* @author shahuang
* @since 1.11.0
*/
public class AuthKujialeRequest extends AuthDefaultRequest {
public AuthKujialeRequest(AuthConfig config) {
super(config, AuthDefaultSource.KUJIALE);
}
public AuthKujialeRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.KUJIALE, authStateCache);
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
* 默认只向用户请求用户信息授权
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.11.0
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("scope", this.getScopes(",", false, AuthScopeUtils.getDefaultScopes(AuthKujialeScope.values())))
.build();
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
return getAuthToken(response);
}
private AuthToken getAuthToken(String response) {
JSONObject accessTokenObject = checkResponse(response);
JSONObject resultObject = accessTokenObject.getJSONObject("d");
return AuthToken.builder()
.accessToken(resultObject.getString("accessToken"))
.refreshToken(resultObject.getString("refreshToken"))
.expireIn(resultObject.getIntValue("expiresIn"))
.build();
}
private JSONObject checkResponse(String response) {
JSONObject accessTokenObject = JSONObject.parseObject(response);
if (!"0".equals(accessTokenObject.getString("c"))) {
throw new AuthException(accessTokenObject.getString("m"));
}
return accessTokenObject;
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String openId = this.getOpenId(authToken);
String response = new HttpUtils(config.getHttpConfig()).get(UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("open_id", openId)
.build()).getBody();
JSONObject object = JSONObject.parseObject(response);
if (!"0".equals(object.getString("c"))) {
throw new AuthException(object.getString("m"));
}
JSONObject resultObject = object.getJSONObject("d");
return AuthUser.builder()
.rawUserInfo(resultObject)
.username(resultObject.getString("userName"))
.nickname(resultObject.getString("userName"))
.avatar(resultObject.getString("avatar"))
.uuid(resultObject.getString("openId"))
.token(authToken)
.source(source.toString())
.build();
}
/**
* 获取酷家乐的openId,此id在当前client范围内可以唯一识别授权用户
*
* @param authToken 通过{@link AuthKujialeRequest#getAccessToken(AuthCallback)}获取到的{@code authToken}
* @return openId
*/
private String getOpenId(AuthToken authToken) {
String response = new HttpUtils(config.getHttpConfig()).get(UrlBuilder.fromBaseUrl("https://oauth.kujiale.com/oauth2/auth/user")
.queryParam("access_token", authToken.getAccessToken())
.build()).getBody();
JSONObject accessTokenObject = checkResponse(response);
return accessTokenObject.getString("d");
}
@Override
public AuthResponse refresh(AuthToken authToken) {
String response = new HttpUtils(config.getHttpConfig()).post(refreshTokenUrl(authToken.getRefreshToken())).getBody();
return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).data(getAuthToken(response)).build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthLineRequest.java
================================================
package me.zhyd.oauth.request;
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.enums.scope.AuthLineScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.HashMap;
import java.util.Map;
/**
* LINE 登录, line.biz
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.0
*/
public class AuthLineRequest extends AuthDefaultRequest {
public AuthLineRequest(AuthConfig config) {
super(config, AuthDefaultSource.LINE);
}
public AuthLineRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.LINE, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
Map params = new HashMap<>();
params.put("grant_type", "authorization_code");
params.put("code", authCallback.getCode());
params.put("redirect_uri", config.getRedirectUri());
params.put("client_id", config.getClientId());
params.put("client_secret", config.getClientSecret());
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), params, false).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.idToken(accessTokenObject.getString("id_token"))
.scope(accessTokenObject.getString("scope"))
.tokenType(accessTokenObject.getString("token_type"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String userInfo = new HttpUtils(config.getHttpConfig()).get(source.userInfo(), null, new HttpHeader()
.add("Content-Type", "application/x-www-form-urlencoded")
.add("Authorization", "Bearer ".concat(authToken.getAccessToken())), false).getBody();
JSONObject object = JSONObject.parseObject(userInfo);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("userId"))
.username(object.getString("displayName"))
.nickname(object.getString("displayName"))
.avatar(object.getString("pictureUrl"))
.remark(object.getString("statusMessage"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
@Override
public AuthResponse revoke(AuthToken authToken) {
Map params = new HashMap<>(5);
params.put("access_token", authToken.getAccessToken());
params.put("client_id", config.getClientId());
params.put("client_secret", config.getClientSecret());
String userInfo = new HttpUtils(config.getHttpConfig()).post(source.revoke(), params, false).getBody();
JSONObject object = JSONObject.parseObject(userInfo);
// 返回1表示取消授权成功,否则失败
AuthResponseStatus status = object.getBooleanValue("revoked") ? AuthResponseStatus.SUCCESS : AuthResponseStatus.FAILURE;
return AuthResponse.builder().code(status.getCode()).msg(status.getMsg()).build();
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
Map params = new HashMap<>();
params.put("grant_type", "refresh_token");
params.put("refresh_token", oldToken.getRefreshToken());
params.put("client_id", config.getClientId());
params.put("client_secret", config.getClientSecret());
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), params, false).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.idToken(accessTokenObject.getString("id_token"))
.scope(accessTokenObject.getString("scope"))
.tokenType(accessTokenObject.getString("token_type"))
.build())
.build();
}
@Override
public String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("user", authToken.getUid())
.build();
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("nonce", state)
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthLineScope.values())))
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import com.xkcoding.http.constants.Constants;
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.AuthUserGender;
import me.zhyd.oauth.enums.scope.AuthLinkedinScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 领英登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.4.0
*/
public class AuthLinkedinRequest extends AuthDefaultRequest {
public AuthLinkedinRequest(AuthConfig config) {
super(config, AuthDefaultSource.LINKEDIN);
}
public AuthLinkedinRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.LINKEDIN, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
return this.getToken(accessTokenUrl(authCallback.getCode()));
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Host", "api.linkedin.com");
httpHeader.add("Connection", "Keep-Alive");
httpHeader.add("Authorization", "Bearer " + accessToken);
String response = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false).getBody();
JSONObject userInfoObject = JSONObject.parseObject(response);
this.checkResponse(userInfoObject);
String userName = getUserName(userInfoObject);
// 获取用户头像
String avatar = this.getAvatar(userInfoObject);
// 获取用户邮箱地址
String email = this.getUserEmail(accessToken);
return AuthUser.builder()
.rawUserInfo(userInfoObject)
.uuid(userInfoObject.getString("id"))
.username(userName)
.nickname(userName)
.avatar(avatar)
.email(email)
.token(authToken)
.gender(AuthUserGender.UNKNOWN)
.source(source.toString())
.build();
}
/**
* 获取用户的真实名
*
* @param userInfoObject 用户json对象
* @return 用户名
*/
private String getUserName(JSONObject userInfoObject) {
String firstName, lastName;
// 获取firstName
if (userInfoObject.containsKey("localizedFirstName")) {
firstName = userInfoObject.getString("localizedFirstName");
} else {
firstName = getUserName(userInfoObject, "firstName");
}
// 获取lastName
if (userInfoObject.containsKey("localizedLastName")) {
lastName = userInfoObject.getString("localizedLastName");
} else {
lastName = getUserName(userInfoObject, "lastName");
}
return firstName + " " + lastName;
}
/**
* 获取用户的头像
*
* @param userInfoObject 用户json对象
* @return 用户的头像地址
*/
private String getAvatar(JSONObject userInfoObject) {
JSONObject profilePictureObject = userInfoObject.getJSONObject("profilePicture");
if (null == profilePictureObject || !profilePictureObject.containsKey("displayImage~")) {
return null;
}
JSONObject displayImageObject = profilePictureObject.getJSONObject("displayImage~");
if (null == displayImageObject || !displayImageObject.containsKey("elements")) {
return null;
}
JSONArray displayImageElements = displayImageObject.getJSONArray("elements");
if (null == displayImageElements || displayImageElements.isEmpty()) {
return null;
}
JSONObject largestImageObj = displayImageElements.getJSONObject(displayImageElements.size() - 1);
if (null == largestImageObj || !largestImageObj.containsKey("identifiers")) {
return null;
}
JSONArray identifiers = largestImageObj.getJSONArray("identifiers");
if (null == identifiers || identifiers.isEmpty()) {
return null;
}
return identifiers.getJSONObject(0).getString("identifier");
}
/**
* 获取用户的email
*
* @param accessToken 用户授权后返回的token
* @return 用户的邮箱地址
*/
private String getUserEmail(String accessToken) {
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Host", "api.linkedin.com");
httpHeader.add("Connection", "Keep-Alive");
httpHeader.add("Authorization", "Bearer " + accessToken);
String emailResponse = new HttpUtils(config.getHttpConfig())
.get("https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))", null, httpHeader, false)
.getBody();
JSONObject emailObj = JSONObject.parseObject(emailResponse);
this.checkResponse(emailObj);
Object obj = JSONPath.eval(emailObj, "$['elements'][0]['handle~']['emailAddress']");
return null == obj ? null : (String) obj;
}
private String getUserName(JSONObject userInfoObject, String nameKey) {
String firstName;
JSONObject firstNameObj = userInfoObject.getJSONObject(nameKey);
JSONObject localizedObj = firstNameObj.getJSONObject("localized");
JSONObject preferredLocaleObj = firstNameObj.getJSONObject("preferredLocale");
firstName = localizedObj.getString(preferredLocaleObj.getString("language") + "_" + preferredLocaleObj.getString("country"));
return firstName;
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"), source);
}
}
/**
* 获取token,适用于获取access_token和刷新token
*
* @param accessTokenUrl 实际请求token的地址
* @return token对象
*/
private AuthToken getToken(String accessTokenUrl) {
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Host", "www.linkedin.com");
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl, null, httpHeader).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("scope", this.getScopes(" ", false, AuthScopeUtils.getDefaultScopes(AuthLinkedinScope.values())))
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("projection", "(id,firstName,lastName,profilePicture(displayImage~:playableStreams))")
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthMeituanRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.HashMap;
import java.util.Map;
/**
* 美团登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.12.0
*/
public class AuthMeituanRequest extends AuthDefaultRequest {
public AuthMeituanRequest(AuthConfig config) {
super(config, AuthDefaultSource.MEITUAN);
}
public AuthMeituanRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.MEITUAN, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
Map form = new HashMap<>(7);
form.put("app_id", config.getClientId());
form.put("secret", config.getClientSecret());
form.put("code", authCallback.getCode());
form.put("grant_type", "authorization_code");
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.refreshToken(object.getString("refresh_token"))
.expireIn(object.getIntValue("expires_in"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
Map form = new HashMap<>(5);
form.put("app_id", config.getClientId());
form.put("secret", config.getClientSecret());
form.put("access_token", authToken.getAccessToken());
String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), form, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("openid"))
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(object.getString("avatar"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
Map form = new HashMap<>(7);
form.put("app_id", config.getClientId());
form.put("secret", config.getClientSecret());
form.put("refresh_token", oldToken.getRefreshToken());
form.put("grant_type", "refresh_token");
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.accessToken(object.getString("access_token"))
.refreshToken(object.getString("refresh_token"))
.expireIn(object.getIntValue("expires_in"))
.build())
.build();
}
private void checkResponse(JSONObject object) {
if (object.containsKey("error_code")) {
throw new AuthException(object.getString("erroe_msg"));
}
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("scope", "")
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthMiRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import com.xkcoding.http.constants.Constants;
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.enums.scope.AuthMiScope;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.log.Log;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.text.MessageFormat;
/**
* 小米登录
*
* @author yangkai.shen (https://xkcoding.com)
* @since 1.5.0
*/
public class AuthMiRequest extends AuthDefaultRequest {
private static final String PREFIX = "&&&START&&&";
public AuthMiRequest(AuthConfig config) {
super(config, AuthDefaultSource.MI);
}
public AuthMiRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.MI, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
return getToken(accessTokenUrl(authCallback.getCode()));
}
private AuthToken getToken(String accessTokenUrl) {
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl).getBody();
String jsonStr = response.replace(PREFIX, Constants.EMPTY);
JSONObject accessTokenObject = JSONObject.parseObject(jsonStr);
if (accessTokenObject.containsKey("error")) {
throw new AuthException(accessTokenObject.getString("error_description"));
}
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();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
// 获取用户信息
String userResponse = doGetUserInfo(authToken);
JSONObject userProfile = JSONObject.parseObject(userResponse);
if ("error".equalsIgnoreCase(userProfile.getString("result"))) {
throw new AuthException(userProfile.getString("description"));
}
JSONObject object = userProfile.getJSONObject("data");
AuthUser authUser = AuthUser.builder()
.rawUserInfo(object)
.uuid(authToken.getOpenId())
.username(object.getString("miliaoNick"))
.nickname(object.getString("miliaoNick"))
.avatar(object.getString("miliaoIcon"))
.email(object.getString("mail"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
// 获取用户邮箱手机号等信息
String emailPhoneUrl = MessageFormat.format("{0}?clientId={1}&token={2}", "https://open.account.xiaomi.com/user/phoneAndEmail", config
.getClientId(), authToken.getAccessToken());
String emailResponse = new HttpUtils(config.getHttpConfig()).get(emailPhoneUrl).getBody();
JSONObject userEmailPhone = JSONObject.parseObject(emailResponse);
if (!"error".equalsIgnoreCase(userEmailPhone.getString("result"))) {
JSONObject emailPhone = userEmailPhone.getJSONObject("data");
authUser.setEmail(emailPhone.getString("email"));
} 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();
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("skip_confirm", "false")
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthMiScope.values())))
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("clientId", config.getClientId())
.queryParam("token", authToken.getAccessToken())
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthMicrosoftCnRequest.java
================================================
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.AuthResponseStatus;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.utils.GlobalAuthUtils;
/**
* 微软中国登录(世纪华联)
*
* @author mroldx (xzfqq5201314@gmail.com)
* @since 1.16.4
*/
public class AuthMicrosoftCnRequest extends AbstractAuthMicrosoftRequest {
public AuthMicrosoftCnRequest(AuthConfig config) {
super(config, AuthDefaultSource.MICROSOFT_CN);
}
public AuthMicrosoftCnRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.MICROSOFT_CN, authStateCache);
}
@Override
protected void checkConfig(AuthConfig config) {
super.checkConfig(config);
// 微软中国的回调地址必须为https的链接或者localhost,不允许使用http
if (AuthDefaultSource.MICROSOFT_CN == source && !GlobalAuthUtils.isHttpsProtocolOrLocalHost(config.getRedirectUri())) {
// Microsoft's redirect uri must use the HTTPS or localhost
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthMicrosoftRequest.java
================================================
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.AuthResponseStatus;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.utils.GlobalAuthUtils;
/**
* 微软登录
* update 2021-08-24 mroldx (xzfqq5201314@gmail.com)
*
* @author yangkai.shen (https://xkcoding.com)
* @since 1.5.0
*/
public class AuthMicrosoftRequest extends AbstractAuthMicrosoftRequest {
public AuthMicrosoftRequest(AuthConfig config) {
super(config, AuthDefaultSource.MICROSOFT);
}
public AuthMicrosoftRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.MICROSOFT, authStateCache);
}
@Override
protected void checkConfig(AuthConfig config) {
super.checkConfig(config);
// 微软的回调地址必须为https的链接或者localhost,不允许使用http
if (AuthDefaultSource.MICROSOFT == source && !GlobalAuthUtils.isHttpsProtocolOrLocalHost(config.getRedirectUri())) {
// Microsoft's redirect uri must use the HTTPS or localhost
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthOktaRequest.java
================================================
package me.zhyd.oauth.request;
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.enums.scope.AuthOktaScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.Base64Utils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.HashMap;
import java.util.Map;
/**
* Okta 登录
*
* https://{domainPrefix}.okta.com/oauth2/default/.well-known/oauth-authorization-server
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.0
*/
public class AuthOktaRequest extends AuthDefaultRequest {
public AuthOktaRequest(AuthConfig config) {
super(config, AuthDefaultSource.OKTA);
}
public AuthOktaRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.OKTA, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String tokenUrl = accessTokenUrl(authCallback.getCode());
return getAuthToken(tokenUrl);
}
private AuthToken getAuthToken(String tokenUrl) {
HttpHeader header = new HttpHeader()
.add("accept", "application/json")
.add("content-type", "application/x-www-form-urlencoded")
.add("Authorization", "Basic " + Base64Utils.encode(config.getClientId().concat(":").concat(config.getClientSecret())));
String response = new HttpUtils(config.getHttpConfig()).post(tokenUrl, null, header, false).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.tokenType(accessTokenObject.getString("token_type"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.scope(accessTokenObject.getString("scope"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.idToken(accessTokenObject.getString("id_token"))
.build();
}
@Override
public AuthResponse refresh(AuthToken authToken) {
if (null == authToken.getRefreshToken()) {
return AuthResponse.builder()
.code(AuthResponseStatus.ILLEGAL_TOKEN.getCode())
.msg(AuthResponseStatus.ILLEGAL_TOKEN.getMsg())
.build();
}
String refreshUrl = refreshTokenUrl(authToken.getRefreshToken());
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(this.getAuthToken(refreshUrl))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
HttpHeader header = new HttpHeader()
.add("Authorization", "Bearer " + authToken.getAccessToken());
String response = new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken), null, header, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
JSONObject address = object.getJSONObject("address");
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("sub"))
.username(object.getString("name"))
.nickname(object.getString("nickname"))
.email(object.getString("email"))
.location(null == address ? null : address.getString("street_address"))
.gender(AuthUserGender.getRealGender(object.getString("sex")))
.token(authToken)
.source(source.toString())
.build();
}
@Override
public AuthResponse revoke(AuthToken authToken) {
Map params = new HashMap<>(4);
params.put("token", authToken.getAccessToken());
params.put("token_type_hint", "access_token");
HttpHeader header = new HttpHeader()
.add("Authorization", "Basic " + Base64Utils.encode(config.getClientId().concat(":").concat(config.getClientSecret())));
new HttpUtils(config.getHttpConfig()).post(revokeUrl(authToken), params, header, false);
AuthResponseStatus status = AuthResponseStatus.SUCCESS;
return AuthResponse.builder().code(status.getCode()).msg(status.getMsg()).build();
}
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(String.format(source.authorize(), config.getDomainPrefix(), config.getAuthServerId()))
.queryParam("response_type", "code")
.queryParam("prompt", "consent")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthOktaScope.values())))
.queryParam("state", getRealState(state))
.build();
}
@Override
public String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(String.format(source.accessToken(), config.getDomainPrefix(), config.getAuthServerId()))
.queryParam("code", code)
.queryParam("grant_type", "authorization_code")
.queryParam("redirect_uri", config.getRedirectUri())
.build();
}
@Override
protected String refreshTokenUrl(String refreshToken) {
return UrlBuilder.fromBaseUrl(String.format(source.refresh(), config.getDomainPrefix(), config.getAuthServerId()))
.queryParam("refresh_token", refreshToken)
.queryParam("grant_type", "refresh_token")
.build();
}
@Override
protected String revokeUrl(AuthToken authToken) {
return String.format(source.revoke(), config.getDomainPrefix(), config.getAuthServerId());
}
@Override
public String userInfoUrl(AuthToken authToken) {
return String.format(source.userInfo(), config.getDomainPrefix(), config.getAuthServerId());
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthOschinaRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.UrlBuilder;
/**
* oschina登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
public class AuthOschinaRequest extends AuthDefaultRequest {
public AuthOschinaRequest(AuthConfig config) {
super(config, AuthDefaultSource.OSCHINA);
}
public AuthOschinaRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.OSCHINA, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
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();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.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(source.toString())
.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();
}
/**
* 返回获取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("dataType", "json")
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthPinterestRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.enums.scope.AuthPinterestScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.Objects;
import static me.zhyd.oauth.config.AuthDefaultSource.PINTEREST;
/**
* Pinterest登录
*
* @author hongwei.peng (pengisgood(at)gmail(dot)com)
* @since 1.9.0
*/
public class AuthPinterestRequest extends AuthDefaultRequest {
private static final String FAILURE = "failure";
public AuthPinterestRequest(AuthConfig config) {
super(config, PINTEREST);
}
public AuthPinterestRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, PINTEREST, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.tokenType(accessTokenObject.getString("token_type"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String userinfoUrl = userInfoUrl(authToken);
// TODO: 是否需要 .setFollowRedirects(true)
String response = new HttpUtils(config.getHttpConfig()).get(userinfoUrl).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
JSONObject userObj = object.getJSONObject("data");
return AuthUser.builder()
.rawUserInfo(userObj)
.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(source.toString())
.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");
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("scope", this.getScopes(",", false, AuthScopeUtils.getDefaultScopes(AuthPinterestScope.values())))
.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("fields", "id,username,first_name,last_name,bio,image")
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (!object.containsKey("status") && FAILURE.equals(object.getString("status"))) {
throw new AuthException(object.getString("message"));
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthProginnRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.enums.scope.AuthProginnScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.HashMap;
import java.util.Map;
/**
* 程序员客栈
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.2
*/
public class AuthProginnRequest extends AuthDefaultRequest {
public AuthProginnRequest(AuthConfig config) {
super(config, AuthDefaultSource.PROGINN);
}
public AuthProginnRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.PROGINN, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
Map params = new HashMap<>();
params.put("code", authCallback.getCode());
params.put("client_id", config.getClientId());
params.put("client_secret", config.getClientSecret());
params.put("grant_type", "authorization_code");
params.put("redirect_uri", config.getRedirectUri());
String response = new HttpUtils(config.getHttpConfig()).post(AuthDefaultSource.PROGINN.accessToken(), params, false).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.uid(accessTokenObject.getString("uid"))
.tokenType(accessTokenObject.getString("token_type"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String userInfo = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
return AuthUser.builder()
.rawUserInfo(object)
.uuid(object.getString("uid"))
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(object.getString("avatar"))
.email(object.getString("email"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthProginnScope.values())))
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthQQMiniProgramRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
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;
/**
* QQ小程序登陆 Request 请求
*
* 参照微信小程序实现
*
* @author hurentian
* @since 2024-10-08
*/
public class AuthQQMiniProgramRequest extends AuthDefaultRequest {
public AuthQQMiniProgramRequest(AuthConfig config) {
super(config, AuthDefaultSource.QQ_MINI_PROGRAM);
}
public AuthQQMiniProgramRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.QQ_MINI_PROGRAM, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
// 参见 https://q.qq.com/wiki/develop/miniprogram/server/open_port/port_login.html#code2session 文档
// 使用 code 获取对应的 openId、unionId 等字段
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl(authCallback.getCode())).getBody();
JSCode2SessionResponse accessTokenObject = JSONObject.parseObject(response, JSCode2SessionResponse.class);
assert accessTokenObject != null;
checkResponse(accessTokenObject);
// 拼装结果
return AuthToken.builder()
.openId(accessTokenObject.getOpenid())
.unionId(accessTokenObject.getUnionId())
.accessToken(accessTokenObject.getSessionKey())
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
// 参见 https://q.qq.com/wiki/develop/game/API/open-port/user-info.html#qq-getuserinfo 文档
// 如果需要用户信息,需要在小程序调用函数后传给后端
return AuthUser.builder()
.username("")
.nickname("")
.avatar("")
.uuid(authToken.getOpenId())
.token(authToken)
.source(source.toString())
.build();
}
/**
* 检查响应内容是否正确
*
* @param response 请求响应内容
*/
private void checkResponse(JSCode2SessionResponse response) {
if (response.getErrorCode() != 0) {
throw new AuthException(response.getErrorCode(), response.getErrorMsg());
}
}
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("appid", config.getClientId())
.queryParam("secret", config.getClientSecret())
.queryParam("js_code", code)
.queryParam("grant_type", "authorization_code")
.build();
}
@Data
@SuppressWarnings("SpellCheckingInspection")
private static class JSCode2SessionResponse {
@JSONField(name = "errcode")
private int errorCode;
@JSONField(name = "errmsg")
private String errorMsg;
@JSONField(name = "session_key")
private String sessionKey;
private String openid;
@JSONField(name = "unionid")
private String unionId;
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthQqRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.enums.scope.AuthQqScope;
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.*;
import java.util.Map;
/**
* qq登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @author yangkai.shen (https://xkcoding.com)
* @since 1.1.0
*/
public class AuthQqRequest extends AuthDefaultRequest {
public AuthQqRequest(AuthConfig config) {
super(config, AuthDefaultSource.QQ);
}
public AuthQqRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.QQ, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doGetAuthorizationCode(authCallback.getCode());
return getAuthToken(response);
}
@Override
public AuthResponse refresh(AuthToken authToken) {
String response = new HttpUtils(config.getHttpConfig()).get(refreshTokenUrl(authToken.getRefreshToken())).getBody();
return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).data(getAuthToken(response)).build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String openId = this.getOpenId(authToken);
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
if (object.getIntValue("ret") != 0) {
throw new AuthException(object.getString("msg"));
}
String avatar = object.getString("figureurl_qq_2");
if (StringUtils.isEmpty(avatar)) {
avatar = object.getString("figureurl_qq_1");
}
String location = String.format("%s-%s", object.getString("province"), object.getString("city"));
return AuthUser.builder()
.rawUserInfo(object)
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(avatar)
.location(location)
.uuid(openId)
.gender(AuthUserGender.getRealGender(object.getString("gender")))
.token(authToken)
.source(source.toString())
.build();
}
/**
* 获取QQ用户的OpenId,支持自定义是否启用查询unionid的功能,如果启用查询unionid的功能,
* 那就需要开发者先通过邮件申请unionid功能,参考链接 {@see http://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D}
*
* @param authToken 通过{@link AuthQqRequest#getAccessToken(AuthCallback)}获取到的{@code authToken}
* @return openId
*/
private String getOpenId(AuthToken authToken) {
String response = new HttpUtils(config.getHttpConfig()).get(UrlBuilder.fromBaseUrl("https://graph.qq.com/oauth2.0/me")
.queryParam("access_token", authToken.getAccessToken())
.queryParam("unionid", config.isUnionId() ? 1 : 0)
.build()).getBody();
String removePrefix = response.replace("callback(", "");
String removeSuffix = removePrefix.replace(");", "");
String openId = removeSuffix.trim();
JSONObject object = JSONObject.parseObject(openId);
if (object.containsKey("error")) {
throw new AuthException(object.get("error") + ":" + object.get("error_description"));
}
authToken.setOpenId(object.getString("openid"));
if (object.containsKey("unionid")) {
authToken.setUnionId(object.getString("unionid"));
}
return StringUtils.isEmpty(authToken.getUnionId()) ? authToken.getOpenId() : authToken.getUnionId();
}
/**
* 返回获取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(String response) {
Map accessTokenObject = GlobalAuthUtils.parseStringToMap(response);
if (!accessTokenObject.containsKey("access_token") || accessTokenObject.containsKey("code")) {
throw new AuthException(accessTokenObject.get("msg"));
}
return AuthToken.builder()
.accessToken(accessTokenObject.get("access_token"))
.expireIn(Integer.parseInt(accessTokenObject.getOrDefault("expires_in", "0")))
.refreshToken(accessTokenObject.get("refresh_token"))
.build();
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("scope", this.getScopes(",", false, AuthScopeUtils.getDefaultScopes(AuthQqScope.values())))
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthRenrenRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xkcoding.http.util.UrlUtil;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.enums.scope.AuthRenrenScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.Objects;
import static me.zhyd.oauth.config.AuthDefaultSource.RENREN;
import static me.zhyd.oauth.enums.AuthResponseStatus.SUCCESS;
/**
* 人人登录
*
* @author hongwei.peng (pengisgood(at)gmail(dot)com)
* @since 1.9.0
*/
public class AuthRenrenRequest extends AuthDefaultRequest {
public AuthRenrenRequest(AuthConfig config) {
super(config, RENREN);
}
public AuthRenrenRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, RENREN, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
return this.getToken(accessTokenUrl(authCallback.getCode()));
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String response = doGetUserInfo(authToken);
JSONObject userObj = JSONObject.parseObject(response).getJSONObject("response");
return AuthUser.builder()
.rawUserInfo(userObj)
.uuid(userObj.getString("id"))
.avatar(getAvatarUrl(userObj))
.nickname(userObj.getString("name"))
.company(getCompany(userObj))
.gender(getGender(userObj))
.token(authToken)
.source(source.toString())
.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) {
String response = new HttpUtils(config.getHttpConfig()).post(url).getBody();
JSONObject jsonObject = JSONObject.parseObject(response);
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(UrlUtil.urlEncode(jsonObject.getString("access_token")))
.refreshToken(UrlUtil.urlEncode(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 用户授权后的token
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("userId", authToken.getOpenId())
.build();
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("scope", this.getScopes(",", false, AuthScopeUtils.getDefaultScopes(AuthRenrenScope.values())))
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthRequest.java
================================================
package me.zhyd.oauth.request;
import me.zhyd.oauth.enums.AuthResponseStatus;
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;
/**
* JustAuth {@code Request}公共接口,所有平台的{@code Request}都需要实现该接口
*
* {@link AuthRequest#authorize()}
* {@link AuthRequest#authorize(String)}
* {@link AuthRequest#login(AuthCallback)}
* {@link AuthRequest#revoke(AuthToken)}
* {@link AuthRequest#refresh(AuthToken)}
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.8
*/
public interface AuthRequest {
/**
* 返回授权url,可自行跳转页面
*
* 不建议使用该方式获取授权地址,不带{@code state}的授权地址,容易受到csrf攻击。
* 建议使用{@link AuthDefaultRequest#authorize(String)}方法生成授权地址,在回调方法中对{@code state}进行校验
*
* @return 返回授权地址
*/
@Deprecated
default String authorize() {
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
*/
default String authorize(String state) {
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
}
/**
* 获取access token
*
* @param authCallback 授权成功后的回调参数
* @return token
* @see AuthDefaultRequest#authorize()
* @see AuthDefaultRequest#authorize(String)
*/
AuthToken getAccessToken(AuthCallback authCallback);
/**
* 使用token换取用户信息
*
* @param authToken token信息
* @return 用户信息
* @see AuthDefaultRequest#getAccessToken(AuthCallback)
*/
AuthUser getUserInfo(AuthToken authToken);
/**
* 第三方登录
*
* @param authCallback 用于接收回调参数的实体
* @return 返回登录成功后的用户信息
*/
default AuthResponse login(AuthCallback authCallback) {
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
}
/**
* 撤销授权
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
default AuthResponse revoke(AuthToken authToken) {
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
default AuthResponse refresh(AuthToken authToken) {
throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED);
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthSlackRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONArray;
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.enums.scope.AuthSlackScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* slack登录, slack.com
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.0
*/
public class AuthSlackRequest extends AuthDefaultRequest {
public AuthSlackRequest(AuthConfig config) {
super(config, AuthDefaultSource.SLACK);
}
public AuthSlackRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.SLACK, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
HttpHeader header = new HttpHeader()
.add("Content-Type", "application/x-www-form-urlencoded");
String response = new HttpUtils(config.getHttpConfig())
.get(accessTokenUrl(authCallback.getCode()), null, header, false).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.scope(accessTokenObject.getString("scope"))
.tokenType(accessTokenObject.getString("token_type"))
.uid(accessTokenObject.getJSONObject("authed_user").getString("id"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
HttpHeader header = new HttpHeader()
.add("Content-Type", "application/x-www-form-urlencoded")
.add("Authorization", "Bearer ".concat(authToken.getAccessToken()));
String userInfo = new HttpUtils(config.getHttpConfig())
.get(userInfoUrl(authToken), null, header, false).getBody();
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
JSONObject user = object.getJSONObject("user");
JSONObject profile = user.getJSONObject("profile");
return AuthUser.builder()
.rawUserInfo(user)
.uuid(user.getString("id"))
.username(user.getString("name"))
.nickname(user.getString("real_name"))
.avatar(profile.getString("image_original"))
.email(profile.getString("email"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
@Override
public AuthResponse revoke(AuthToken authToken) {
HttpHeader header = new HttpHeader()
.add("Content-Type", "application/x-www-form-urlencoded")
.add("Authorization", "Bearer ".concat(authToken.getAccessToken()));
String userInfo = new HttpUtils(config.getHttpConfig())
.get(source.revoke(), null, header, false).getBody();
JSONObject object = JSONObject.parseObject(userInfo);
this.checkResponse(object);
// 返回1表示取消授权成功,否则失败
AuthResponseStatus status = object.getBooleanValue("revoked") ? AuthResponseStatus.SUCCESS : AuthResponseStatus.FAILURE;
return AuthResponse.builder().code(status.getCode()).msg(status.getMsg()).build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (!object.getBooleanValue("ok")) {
String errorMsg = object.getString("error");
if (object.containsKey("response_metadata")) {
JSONArray array = object.getJSONObject("response_metadata").getJSONArray("messages");
if (null != array && array.size() > 0) {
errorMsg += "; " + String.join(",", array.toArray(new String[0]));
}
}
throw new AuthException(errorMsg);
}
}
@Override
public String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("user", authToken.getUid())
.build();
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("client_id", config.getClientId())
.queryParam("state", getRealState(state))
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("scope", this.getScopes(",", true, AuthScopeUtils.getDefaultScopes(AuthSlackScope.values())))
.build();
}
@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("redirect_uri", config.getRedirectUri())
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthStackOverflowRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import com.xkcoding.http.constants.Constants;
import com.xkcoding.http.support.HttpHeader;
import com.xkcoding.http.util.MapUtil;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.enums.scope.AuthStackoverflowScope;
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.AuthScopeUtils;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.Map;
import static me.zhyd.oauth.config.AuthDefaultSource.STACK_OVERFLOW;
/**
* Stack Overflow登录
*
* @author hongwei.peng (pengisgood(at)gmail(dot)com)
* @since 1.9.0
*/
public class AuthStackOverflowRequest extends AuthDefaultRequest {
public AuthStackOverflowRequest(AuthConfig config) {
super(config, STACK_OVERFLOW);
}
public AuthStackOverflowRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, STACK_OVERFLOW, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String accessTokenUrl = accessTokenUrl(authCallback.getCode());
Map form = MapUtil.parseStringToMap(accessTokenUrl, false);
HttpHeader httpHeader = new HttpHeader();
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl, form, httpHeader, false).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires"))
.build();
}
@Override
public 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();
String response = new HttpUtils(config.getHttpConfig()).get(userInfoUrl).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
JSONObject userObj = object.getJSONArray("items").getJSONObject(0);
return AuthUser.builder()
.rawUserInfo(userObj)
.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(source.toString())
.build();
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("scope", this.getScopes(",", false, AuthScopeUtils.getDefaultScopes(AuthStackoverflowScope.values())))
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthTaobaoRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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;
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;
/**
* 淘宝登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.1.0
*/
public class AuthTaobaoRequest extends AuthDefaultRequest {
public AuthTaobaoRequest(AuthConfig config) {
super(config, AuthDefaultSource.TAOBAO);
}
public AuthTaobaoRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.TAOBAO, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
return AuthToken.builder().accessCode(authCallback.getCode()).build();
}
private AuthToken getAuthToken(JSONObject object) {
this.checkResponse(object);
return AuthToken.builder()
.accessToken(object.getString("access_token"))
.expireIn(object.getIntValue("expires_in"))
.tokenType(object.getString("token_type"))
.idToken(object.getString("id_token"))
.refreshToken(object.getString("refresh_token"))
.uid(object.getString("taobao_user_id"))
.openId(object.getString("taobao_open_uid"))
.build();
}
private void checkResponse(JSONObject object) {
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String response = doPostAuthorizationCode(authToken.getAccessCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
if (accessTokenObject.containsKey("error")) {
throw new AuthException(accessTokenObject.getString("error_description"));
}
authToken = this.getAuthToken(accessTokenObject);
String nick = GlobalAuthUtils.urlDecode(accessTokenObject.getString("taobao_user_nick"));
return AuthUser.builder()
.rawUserInfo(accessTokenObject)
.uuid(StringUtils.isEmpty(authToken.getUid()) ? authToken.getOpenId() : authToken.getUid())
.username(nick)
.nickname(nick)
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
String tokenUrl = refreshTokenUrl(oldToken.getRefreshToken());
String response = new HttpUtils(config.getHttpConfig()).post(tokenUrl).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(this.getAuthToken(accessTokenObject))
.build();
}
/**
* 返回带{@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("response_type", "code")
.queryParam("client_id", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("view", "web")
.queryParam("state", getRealState(state))
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthTeambitionRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.utils.HttpUtils;
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;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import java.util.HashMap;
import java.util.Map;
/**
* Teambition授权登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.9.0
*/
public class AuthTeambitionRequest extends AuthDefaultRequest {
public AuthTeambitionRequest(AuthConfig config) {
super(config, AuthDefaultSource.TEAMBITION);
}
public AuthTeambitionRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.TEAMBITION, authStateCache);
}
/**
* @param authCallback 回调返回的参数
* @return 所有信息
*/
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
Map form = new HashMap<>(7);
form.put("client_id", config.getClientId());
form.put("client_secret", config.getClientSecret());
form.put("code", authCallback.getCode());
form.put("grant_type", "code");
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, false).getBody();
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.refreshToken(accessTokenObject.getString("refresh_token"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Authorization", "OAuth2 " + accessToken);
String response = new HttpUtils(config.getHttpConfig())
.get(source.userInfo(), null, httpHeader, false).getBody();
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
authToken.setUid(object.getString("_id"));
return AuthUser.builder()
.rawUserInfo(object)
.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(source.toString())
.build();
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
String uid = oldToken.getUid();
String refreshToken = oldToken.getRefreshToken();
Map form = new HashMap<>(4);
form.put("_userId", uid);
form.put("refresh_token", refreshToken);
String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, false).getBody();
JSONObject refreshTokenObject = JSONObject.parseObject(response);
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"));
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthToutiaoRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.enums.AuthToutiaoErrorCode;
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;
/**
* 今日头条登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.6.0-beta
*/
public class AuthToutiaoRequest extends AuthDefaultRequest {
public AuthToutiaoRequest(AuthConfig config) {
super(config, AuthDefaultSource.TOUTIAO);
}
public AuthToutiaoRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.TOUTIAO, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doGetAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
this.checkResponse(accessTokenObject);
return AuthToken.builder()
.accessToken(accessTokenObject.getString("access_token"))
.expireIn(accessTokenObject.getIntValue("expires_in"))
.openId(accessTokenObject.getString("open_id"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String userResponse = doGetUserInfo(authToken);
JSONObject userProfile = JSONObject.parseObject(userResponse);
this.checkResponse(userProfile);
JSONObject user = userProfile.getJSONObject("data");
boolean isAnonymousUser = user.getIntValue("uid_type") == 14;
String anonymousUserName = "匿名用户";
return AuthUser.builder()
.rawUserInfo(user)
.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(source.toString())
.build();
}
/**
* 返回带{@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("response_type", "code")
.queryParam("client_key", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("auth_only", 1)
.queryParam("display", 0)
.queryParam("state", getRealState(state))
.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());
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthTwitterRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import com.xkcoding.http.constants.Constants;
import com.xkcoding.http.support.HttpHeader;
import com.xkcoding.http.util.MapUtil;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
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.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.HashMap;
import java.util.Map;
import static me.zhyd.oauth.config.AuthDefaultSource.TWITTER;
import static me.zhyd.oauth.utils.GlobalAuthUtils.generateTwitterSignature;
import static me.zhyd.oauth.utils.GlobalAuthUtils.urlEncode;
/**
* Twitter登录
*
* @author hongwei.peng (pengisgood(at)gmail(dot)com)
* @since 1.13.0
*/
public class AuthTwitterRequest extends AuthDefaultRequest {
private static final String PREAMBLE = "OAuth";
public AuthTwitterRequest(AuthConfig config) {
super(config, TWITTER);
}
public AuthTwitterRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, TWITTER, authStateCache);
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
@Override
public String authorize(String state) {
AuthToken token = this.getRequestToken();
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("oauth_token", token.getOauthToken())
.build();
}
/**
* Obtaining a request token
* https://developer.twitter.com/en/docs/twitter-for-websites/log-in-with-twitter/guides/implementing-sign-in-with-twitter
*
* @return request token
*/
public AuthToken getRequestToken() {
String baseUrl = "https://api.twitter.com/oauth/request_token";
Map oauthParams = buildOauthParams();
oauthParams.put("oauth_callback", config.getRedirectUri());
oauthParams.put("oauth_signature", generateTwitterSignature(oauthParams, "POST", baseUrl, config.getClientSecret(), null));
String header = buildHeader(oauthParams);
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Authorization", header);
httpHeader.add("User-Agent", "'JustAuth' HTTP Client Simple-Http");
String requestToken = new HttpUtils(config.getHttpConfig()).post(baseUrl, null, httpHeader).getBody();
Map res = MapUtil.parseStringToMap(requestToken, false);
return AuthToken.builder()
.oauthToken(res.get("oauth_token"))
.oauthTokenSecret(res.get("oauth_token_secret"))
.oauthCallbackConfirmed(Boolean.valueOf(res.get("oauth_callback_confirmed")))
.build();
}
/**
* Convert request token to access token
* https://developer.twitter.com/en/docs/twitter-for-websites/log-in-with-twitter/guides/implementing-sign-in-with-twitter
*
* @return access token
*/
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
Map oauthParams = buildOauthParams();
oauthParams.put("oauth_token", authCallback.getOauth_token());
oauthParams.put("oauth_verifier", authCallback.getOauth_verifier());
oauthParams.put("oauth_signature", generateTwitterSignature(oauthParams, "POST", source.accessToken(), config.getClientSecret(), authCallback
.getOauth_token()));
String header = buildHeader(oauthParams);
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Authorization", header);
httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded");
Map form = new HashMap<>(3);
form.put("oauth_verifier", authCallback.getOauth_verifier());
String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, httpHeader, false).getBody();
Map requestToken = MapUtil.parseStringToMap(response, false);
return AuthToken.builder()
.oauthToken(requestToken.get("oauth_token"))
.oauthTokenSecret(requestToken.get("oauth_token_secret"))
.userId(requestToken.get("user_id"))
.screenName(requestToken.get("screen_name"))
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
Map queryParams = new HashMap<>(5);
queryParams.put("include_entities", Boolean.toString(true));
queryParams.put("include_email", Boolean.toString(true));
Map oauthParams = buildOauthParams();
oauthParams.put("oauth_token", authToken.getOauthToken());
Map params = new HashMap<>(oauthParams);
params.putAll(queryParams);
oauthParams.put("oauth_signature", generateTwitterSignature(params, "GET", source.userInfo(), config.getClientSecret(), authToken.getOauthTokenSecret()));
String header = buildHeader(oauthParams);
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Authorization", header);
String response = new HttpUtils(config.getHttpConfig())
.get(userInfoUrl(authToken), null, httpHeader, false).getBody();
JSONObject userInfo = JSONObject.parseObject(response);
return AuthUser.builder()
.rawUserInfo(userInfo)
.uuid(userInfo.getString("id_str"))
.username(userInfo.getString("screen_name"))
.nickname(userInfo.getString("name"))
.remark(userInfo.getString("description"))
.avatar(userInfo.getString("profile_image_url_https"))
.blog(userInfo.getString("url"))
.location(userInfo.getString("location"))
.avatar(userInfo.getString("profile_image_url"))
.email(userInfo.getString("email"))
.source(source.toString())
.token(authToken)
.build();
}
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("include_entities", true)
.queryParam("include_email", true)
.build();
}
private Map buildOauthParams() {
Map params = new HashMap<>(12);
params.put("oauth_consumer_key", config.getClientId());
params.put("oauth_nonce", GlobalAuthUtils.generateNonce(32));
params.put("oauth_signature_method", "HMAC-SHA1");
params.put("oauth_timestamp", GlobalAuthUtils.getTimestamp());
params.put("oauth_version", "1.0");
return params;
}
private String buildHeader(Map oauthParams) {
final StringBuilder sb = new StringBuilder(PREAMBLE + " ");
for (Map.Entry param : oauthParams.entrySet()) {
sb.append(param.getKey()).append("=\"").append(urlEncode(param.getValue())).append('"').append(", ");
}
return sb.deleteCharAt(sb.length() - 2).toString();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseQrcodeRequest.java
================================================
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;
/**
*
* 企业微信二维码登录
*
*
* @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))
.queryParam("lang", config.getLang())
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseQrcodeV2Request.java
================================================
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.AuthResponseStatus;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.utils.GlobalAuthUtils;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
*
* 新版企业微信 Web 登录,参考 https://developer.work.weixin.qq.com/document/path/98152
*
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.16.7
*/
public class AuthWeChatEnterpriseQrcodeV2Request extends AbstractAuthWeChatEnterpriseRequest {
public AuthWeChatEnterpriseQrcodeV2Request(AuthConfig config) {
super(config, AuthDefaultSource.WECHAT_ENTERPRISE_V2);
}
public AuthWeChatEnterpriseQrcodeV2Request(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.WECHAT_ENTERPRISE_V2, authStateCache);
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("login_type", config.getLoginType())
// 登录类型为企业自建应用/服务商代开发应用时填企业 CorpID,第三方登录时填登录授权 SuiteID
.queryParam("appid", config.getClientId())
// 企业自建应用/服务商代开发应用 AgentID,当login_type=CorpApp时填写
.queryParam("agentid", config.getAgentId())
.queryParam("redirect_uri", GlobalAuthUtils.urlEncode(config.getRedirectUri()))
.queryParam("state", getRealState(state))
.queryParam("lang", config.getLang())
.build()
.concat("#wechat_redirect");
}
@Override
protected void checkConfig(AuthConfig config) {
super.checkConfig(config);
if ("CorpApp".equals(config.getLoginType()) && StringUtils.isEmpty(config.getAgentId())) {
throw new AuthException(AuthResponseStatus.ILLEGAL_WECHAT_AGENT_ID, source);
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseThirdQrcodeRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.exception.AuthException;
import me.zhyd.oauth.log.Log;
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.*;
/**
*
* 企业微信第三方二维码登录
*
*
* @author zhengjx
* @since 1.16.3
*/
public class AuthWeChatEnterpriseThirdQrcodeRequest extends AbstractAuthWeChatEnterpriseRequest {
public AuthWeChatEnterpriseThirdQrcodeRequest(AuthConfig config) {
super(config, AuthDefaultSource.WECHAT_ENTERPRISE_QRCODE_THIRD);
}
public AuthWeChatEnterpriseThirdQrcodeRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.WECHAT_ENTERPRISE_QRCODE_THIRD, authStateCache);
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("appid", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("state", getRealState(state))
.queryParam("usertype", config.getUsertype())
.build();
}
@Override
public AuthResponse login(AuthCallback authCallback) {
try {
if (!config.isIgnoreCheckState()) {
AuthChecker.checkState(authCallback.getState(), source, authStateCache);
}
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);
}
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
try {
String response = doGetAuthorizationCode(accessTokenUrl());
JSONObject object = this.checkResponse(response);
AuthToken authToken = AuthToken.builder()
.accessToken(object.getString("provider_access_token"))
.expireIn(object.getIntValue("expires_in"))
.code(authCallback.getCode())
.build();
return authToken;
} catch (Exception e) {
throw new AuthException("企业微信获取token失败", e);
}
}
@Override
protected String doGetAuthorizationCode(String code) {
JSONObject data = new JSONObject();
data.put("corpid", config.getClientId());
data.put("provider_secret", config.getClientSecret());
return new HttpUtils(config.getHttpConfig()).post(accessTokenUrl(code), data.toJSONString()).getBody();
}
/**
* 获取token的URL
*
* @return accessTokenUrl
*/
protected String accessTokenUrl() {
return UrlBuilder.fromBaseUrl(source.accessToken())
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
JSONObject response = this.checkResponse(doGetUserInfo(authToken));
return AuthUser.builder()
.rawUserInfo(response)
.build();
}
@Override
protected String doGetUserInfo(AuthToken authToken) {
JSONObject data = new JSONObject();
data.put("auth_code", authToken.getCode());
return new HttpUtils(config.getHttpConfig())
.post(userInfoUrl(authToken), data.toJSONString()).getBody();
}
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken()).
build();
}
private JSONObject checkResponse(String response) {
JSONObject object = JSONObject.parseObject(response);
if (object.containsKey("errcode") && object.getIntValue("errcode") != 0) {
throw new AuthException(object.getString("errmsg"), source);
}
return object;
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequest.java
================================================
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.GlobalAuthUtils;
import me.zhyd.oauth.utils.UrlBuilder;
/**
*
* 企业微信网页登录
*
*
* @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("agentid", config.getAgentId())
.queryParam("redirect_uri", GlobalAuthUtils.urlEncode(config.getRedirectUri()))
.queryParam("response_type", "code")
.queryParam("scope", this.getScopes(",", false, AuthScopeUtils.getDefaultScopes(AuthWeChatEnterpriseWebScope.values())))
.queryParam("state", getRealState(state).concat("#wechat_redirect"))
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthWeChatMpRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
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.enums.scope.AuthWechatMpScope;
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.*;
/**
* 微信公众平台登录
*
* @author yangkai.shen (https://xkcoding.com)
* @since 1.1.0
*/
public class AuthWeChatMpRequest extends AuthDefaultRequest {
public AuthWeChatMpRequest(AuthConfig config) {
super(config, AuthDefaultSource.WECHAT_MP);
}
public AuthWeChatMpRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.WECHAT_MP, authStateCache);
}
/**
* 微信的特殊性,此时返回的信息同时包含 openid 和 access_token
*
* @param authCallback 回调返回的参数
* @return 所有信息
*/
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
return this.getToken(accessTokenUrl(authCallback.getCode()));
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String openId = authToken.getOpenId();
String scope = authToken.getScope();
if (!StringUtils.isEmpty(scope) && !scope.contains("snsapi_userinfo")) {
return AuthUser.builder()
.rawUserInfo(JSONObject.parseObject(JSONObject.toJSONString(authToken)))
.uuid(openId)
.snapshotUser(authToken.isSnapshotUser())
.token(authToken)
.source(source.toString())
.build();
}
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
String location = String.format("%s-%s-%s", object.getString("country"), object.getString("province"), object.getString("city"));
if (object.containsKey("unionid")) {
authToken.setUnionId(object.getString("unionid"));
}
return AuthUser.builder()
.rawUserInfo(object)
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(object.getString("headimgurl"))
.location(location)
.uuid(openId)
.snapshotUser(authToken.isSnapshotUser())
.gender(AuthUserGender.getWechatRealGender(object.getString("sex")))
.token(authToken)
.source(source.toString())
.build();
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(this.getToken(refreshTokenUrl(oldToken.getRefreshToken())))
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("errcode")) {
throw new AuthException(object.getIntValue("errcode"), object.getString("errmsg"));
}
}
/**
* 获取token,适用于获取access_token和刷新token
*
* @param accessTokenUrl 实际请求token的地址
* @return token对象
*/
private AuthToken getToken(String accessTokenUrl) {
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl).getBody();
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"))
.openId(accessTokenObject.getString("openid"))
.scope(accessTokenObject.getString("scope"))
.snapshotUser(accessTokenObject.getIntValue("is_snapshotuser") == 1)
.build();
}
/**
* 返回带{@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("redirect_uri", GlobalAuthUtils.urlEncode(config.getRedirectUri()))
.queryParam("response_type", "code")
.queryParam("scope", this.getScopes(",", false, AuthScopeUtils.getDefaultScopes(AuthWechatMpScope.values())))
.queryParam("state", getRealState(state).concat("#wechat_redirect"))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("appid", config.getClientId())
.queryParam("secret", config.getClientSecret())
.queryParam("code", code)
.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("grant_type", "refresh_token")
.queryParam("refresh_token", refreshToken)
.build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthWeChatOpenRequest.java
================================================
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.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;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.utils.UrlBuilder;
/**
* 微信开放平台登录
*
* @author yangkai.shen (https://xkcoding.com)
* @since 1.1.0
*/
public class AuthWeChatOpenRequest extends AuthDefaultRequest {
public AuthWeChatOpenRequest(AuthConfig config) {
super(config, AuthDefaultSource.WECHAT_OPEN);
}
public AuthWeChatOpenRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.WECHAT_OPEN, authStateCache);
}
/**
* 微信的特殊性,此时返回的信息同时包含 openid 和 access_token
*
* @param authCallback 回调返回的参数
* @return 所有信息
*/
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
return this.getToken(accessTokenUrl(authCallback.getCode()));
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String openId = authToken.getOpenId();
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
String location = String.format("%s-%s-%s", object.getString("country"), object.getString("province"), object.getString("city"));
if (object.containsKey("unionid")) {
authToken.setUnionId(object.getString("unionid"));
}
return AuthUser.builder()
.rawUserInfo(object)
.username(object.getString("nickname"))
.nickname(object.getString("nickname"))
.avatar(object.getString("headimgurl"))
.location(location)
.uuid(openId)
.gender(AuthUserGender.getWechatRealGender(object.getString("sex")))
.token(authToken)
.source(source.toString())
.build();
}
@Override
public AuthResponse refresh(AuthToken oldToken) {
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(this.getToken(refreshTokenUrl(oldToken.getRefreshToken())))
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(JSONObject object) {
if (object.containsKey("errcode")) {
throw new AuthException(object.getIntValue("errcode"), object.getString("errmsg"));
}
}
/**
* 获取token,适用于获取access_token和刷新token
*
* @param accessTokenUrl 实际请求token的地址
* @return token对象
*/
private AuthToken getToken(String accessTokenUrl) {
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl).getBody();
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"))
.openId(accessTokenObject.getString("openid"))
.build();
}
/**
* 返回带{@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("response_type", "code")
.queryParam("appid", config.getClientId())
.queryParam("redirect_uri", config.getRedirectUri())
.queryParam("scope", "snsapi_login")
.queryParam("state", getRealState(state))
.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();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthWechatMiniProgramRequest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
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;
/**
* 微信小程序授权登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @author yudaocode
* @version 1.0.0
* @since 1.0.0
*/
public class AuthWechatMiniProgramRequest extends AuthDefaultRequest {
public AuthWechatMiniProgramRequest(AuthConfig config) {
super(config, AuthDefaultSource.WECHAT_MINI_PROGRAM);
}
public AuthWechatMiniProgramRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.WECHAT_MINI_PROGRAM, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
// 参见 https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html 文档
// 使用 code 获取对应的 openId、unionId 等字段
String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl(authCallback.getCode())).getBody();
JSCode2SessionResponse accessTokenObject = JSONObject.parseObject(response, JSCode2SessionResponse.class);
assert accessTokenObject != null;
checkResponse(accessTokenObject);
// 拼装结果
return AuthToken.builder()
.openId(accessTokenObject.getOpenid())
.unionId(accessTokenObject.getUnionId())
.accessToken(accessTokenObject.getSessionKey())
.build();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
// 参见 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html 文档
// 如果需要用户信息,需要在小程序调用函数后传给后端
return AuthUser.builder()
.username("")
.nickname("")
.avatar("")
.uuid(authToken.getOpenId())
.token(authToken)
.source(source.toString())
.build();
}
/**
* 检查响应内容是否正确
*
* @param response 请求响应内容
*/
private void checkResponse(JSCode2SessionResponse response) {
if (response.getErrorCode() != 0) {
throw new AuthException(response.getErrorCode(), response.getErrorMsg());
}
}
@Override
protected String accessTokenUrl(String code) {
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("appid", config.getClientId())
.queryParam("secret", config.getClientSecret())
.queryParam("js_code", code)
.queryParam("grant_type", "authorization_code")
.build();
}
@Data
@SuppressWarnings("SpellCheckingInspection")
private static class JSCode2SessionResponse {
@JSONField(name = "errcode")
private int errorCode;
@JSONField(name = "errmsg")
private String errorMsg;
@JSONField(name = "session_key")
private String sessionKey;
private String openid;
@JSONField(name = "unionid")
private String unionId;
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthWeiboRequest.java
================================================
package me.zhyd.oauth.request;
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.enums.scope.AuthWeiboScope;
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.*;
/**
* 微博登录
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
public class AuthWeiboRequest extends AuthDefaultRequest {
public AuthWeiboRequest(AuthConfig config) {
super(config, AuthDefaultSource.WEIBO);
}
public AuthWeiboRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthDefaultSource.WEIBO, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String response = doPostAuthorizationCode(authCallback.getCode());
JSONObject accessTokenObject = JSONObject.parseObject(response);
if (accessTokenObject.containsKey("error")) {
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();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String accessToken = authToken.getAccessToken();
String uid = authToken.getUid();
String oauthParam = String.format("uid=%s&access_token=%s", uid, accessToken);
HttpHeader httpHeader = new HttpHeader();
httpHeader.add("Authorization", "OAuth2 " + oauthParam);
httpHeader.add("API-RemoteIP", IpUtils.getLocalIp());
String userInfo = new HttpUtils(config.getHttpConfig())
.get(userInfoUrl(authToken), null, httpHeader, false).getBody();
JSONObject object = JSONObject.parseObject(userInfo);
if (object.containsKey("error")) {
throw new AuthException(object.getString("error"));
}
return AuthUser.builder()
.rawUserInfo(object)
.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(source.toString())
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken authToken
* @return 返回获取userInfo的url
*/
@Override
protected String userInfoUrl(AuthToken authToken) {
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.getAccessToken())
.queryParam("uid", authToken.getUid())
.build();
}
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state))
.queryParam("scope", this.getScopes(",", false, AuthScopeUtils.getDefaultScopes(AuthWeiboScope.values())))
.build();
}
@Override
public AuthResponse revoke(AuthToken authToken) {
String response = doGetRevoke(authToken);
JSONObject object = JSONObject.parseObject(response);
if (object.containsKey("error")) {
return AuthResponse.builder()
.code(AuthResponseStatus.FAILURE.getCode())
.msg(object.getString("error"))
.build();
}
// 返回 result = true 表示取消授权成功,否则失败
AuthResponseStatus status = object.getBooleanValue("result") ? AuthResponseStatus.SUCCESS : AuthResponseStatus.FAILURE;
return AuthResponse.builder().code(status.getCode()).msg(status.getMsg()).build();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/AuthXmlyRequest.java
================================================
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
public AuthToken getAccessToken(AuthCallback authCallback) {
Map map = new HashMap<>(9);
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).getBody();
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 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).getBody();
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"));
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/request/package-info.java
================================================
/**
* JustAuth核心组件,所有授权登录都是基于{@code request}实现
*/
package me.zhyd.oauth.request;
================================================
FILE: src/main/java/me/zhyd/oauth/utils/AuthChecker.java
================================================
package me.zhyd.oauth.utils;
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.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
/**
* 授权配置类的校验器
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.6.1-beta
*/
public class AuthChecker {
/**
* 是否支持第三方登录
*
* @param config config
* @param source source
* @return true or false
* @since 1.6.1-beta
*/
public static boolean isSupportedAuth(AuthConfig config, AuthSource source) {
boolean isSupported = StringUtils.isNotEmpty(config.getClientId())
&& StringUtils.isNotEmpty(config.getClientSecret());
if (isSupported && AuthDefaultSource.STACK_OVERFLOW == source) {
isSupported = StringUtils.isNotEmpty(config.getStackOverflowKey());
}
if (isSupported && AuthDefaultSource.WECHAT_ENTERPRISE == source) {
isSupported = StringUtils.isNotEmpty(config.getAgentId());
}
if (isSupported && (AuthDefaultSource.CODING == source || AuthDefaultSource.OKTA == source)) {
isSupported = StringUtils.isNotEmpty(config.getDomainPrefix());
}
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;
}
/**
* 检查配置合法性。针对部分平台, 对redirect uri有特定要求。一般来说redirect uri都是http://,而对于facebook平台, redirect uri 必须是https的链接
*
* @param config config
* @param source source
* @since 1.6.1-beta
*/
public static void checkConfig(AuthConfig config, AuthSource source) {
String redirectUri = config.getRedirectUri();
if (config.isIgnoreCheckRedirectUri()) {
return;
}
if (StringUtils.isEmpty(redirectUri)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
}
if (!GlobalAuthUtils.isHttpProtocol(redirectUri) && !GlobalAuthUtils.isHttpsProtocol(redirectUri)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
}
}
/**
* 校验回调传回的code
*
* {@code v1.10.0}版本中改为传入{@code source}和{@code callback},对于不同平台使用不同参数接受code的情况统一做处理
*
* @param source 当前授权平台
* @param callback 从第三方授权回调回来时传入的参数集合
* @since 1.8.0
*/
public static void checkCode(AuthSource source, AuthCallback callback) {
// 推特平台不支持回调 code 和 state
if (source == AuthDefaultSource.TWITTER) {
return;
}
String code = callback.getCode();
if (StringUtils.isEmpty(code) && source == AuthDefaultSource.HUAWEI) {
code = callback.getAuthorization_code();
}
if (StringUtils.isEmpty(code)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_CODE, source);
}
}
/**
* 校验回调传回的{@code state},为空或者不存在
*
* {@code state}不存在的情况只有两种:
* 1. {@code state}已使用,被正常清除
* 2. {@code state}为前端伪造,本身就不存在
*
* @param state {@code state}一定不为空
* @param source {@code source}当前授权平台
* @param authStateCache {@code authStateCache} state缓存实现
*/
public static void checkState(String state, AuthSource source, AuthStateCache authStateCache) {
// 推特平台不支持回调 code 和 state
if (source == AuthDefaultSource.TWITTER) {
return;
}
if (StringUtils.isEmpty(state) || !authStateCache.containsKey(state)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_STATUS, source);
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/utils/AuthScopeUtils.java
================================================
package me.zhyd.oauth.utils;
import me.zhyd.oauth.enums.scope.AuthScope;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* Scope 工具类,提供对 scope 类的统一操作
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.15.7
*/
public class AuthScopeUtils {
/**
* 获取 {@link me.zhyd.oauth.enums.scope.AuthScope} 数组中所有的被标记为 {@code default} 的 scope
*
* @param scopes scopes
* @return List
*/
public static List getDefaultScopes(AuthScope[] scopes) {
if (null == scopes || scopes.length == 0) {
return null;
}
return Arrays.stream(scopes)
.filter((AuthScope::isDefault))
.map(AuthScope::getScope)
.collect(Collectors.toList());
}
/**
* 从 {@link me.zhyd.oauth.enums.scope.AuthScope} 数组中获取实际的 scope 字符串
*
* @param scopes 可变参数,支持传任意 {@link me.zhyd.oauth.enums.scope.AuthScope}
* @return List
*/
public static List getScopes(AuthScope... scopes) {
if (null == scopes || scopes.length == 0) {
return null;
}
return Arrays.stream(scopes).map(AuthScope::getScope).collect(Collectors.toList());
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/utils/AuthStateUtils.java
================================================
package me.zhyd.oauth.utils;
/**
* AuthState工具类,默认只提供一个创建随机uuid的方法
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.9.3
*/
public class AuthStateUtils {
/**
* 生成随机state,采用https://github.com/lets-mica/mica的UUID工具
*
* @return 随机的state字符串
*/
public static String createState() {
return UuidUtils.getUUID();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/utils/Base64Utils.java
================================================
package me.zhyd.oauth.utils;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* Base64编码
*
* @author looly
* @since 3.2.0
*/
public class Base64Utils {
private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
/**
* 标准编码表
*/
private static final byte[] STANDARD_ENCODE_TABLE = { //
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', //
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', //
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', //
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', //
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', //
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', //
'w', 'x', 'y', 'z', '0', '1', '2', '3', //
'4', '5', '6', '7', '8', '9', '+', '/' //
};
/**
* URL安全的编码表,将 + 和 / 替换为 - 和 _
*/
private static final byte[] URL_SAFE_ENCODE_TABLE = { //
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', //
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', //
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', //
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', //
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', //
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', //
'w', 'x', 'y', 'z', '0', '1', '2', '3', //
'4', '5', '6', '7', '8', '9', '-', '_' //
};
// -------------------------------------------------------------------- encode
/**
* 编码为Base64,非URL安全的
*
* @param arr 被编码的数组
* @param lineSep 在76个char之后是CRLF还是EOF
* @return 编码后的bytes
*/
public static byte[] encode(byte[] arr, boolean lineSep) {
return encode(arr, lineSep, false);
}
/**
* 编码为Base64,URL安全的
*
* @param arr 被编码的数组
* @param lineSep 在76个char之后是CRLF还是EOF
* @return 编码后的bytes
* @since 3.0.6
*/
public static byte[] encodeUrlSafe(byte[] arr, boolean lineSep) {
return encode(arr, lineSep, true);
}
/**
* base64编码
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
*/
public static String encode(CharSequence source) {
return encode(source, DEFAULT_CHARSET);
}
/**
* base64编码,URL安全
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
* @since 3.0.6
*/
public static String encodeUrlSafe(CharSequence source) {
return encodeUrlSafe(source, DEFAULT_CHARSET);
}
/**
* base64编码
*
* @param source 被编码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
*/
public static String encode(CharSequence source, Charset charset) {
return encode(StringUtils.bytes(source, charset));
}
/**
* base64编码,URL安全的
*
* @param source 被编码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
* @since 3.0.6
*/
public static String encodeUrlSafe(CharSequence source, Charset charset) {
return encodeUrlSafe(StringUtils.bytes(source, charset));
}
/**
* base64编码
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
*/
public static String encode(byte[] source) {
return StringUtils.str(encode(source, false), DEFAULT_CHARSET);
}
/**
* base64编码,URL安全的
*
* @param source 被编码的base64字符串
* @return 被加密后的字符串
* @since 3.0.6
*/
public static String encodeUrlSafe(byte[] source) {
return StringUtils.str(encodeUrlSafe(source, false), DEFAULT_CHARSET);
}
/**
* 编码为Base64
* 如果isMultiLine为true,则每76个字符一个换行符,否则在一行显示
*
* @param arr 被编码的数组
* @param isMultiLine 在76个char之后是CRLF还是EOF
* @param isUrlSafe 是否使用URL安全字符,一般为false
* @return 编码后的bytes
*/
public static byte[] encode(byte[] arr, boolean isMultiLine, boolean isUrlSafe) {
if (null == arr) {
return null;
}
int len = arr.length;
if (len == 0) {
return new byte[0];
}
int evenlen = (len / 3) * 3;
int cnt = ((len - 1) / 3 + 1) << 2;
int destlen = cnt + (isMultiLine ? (cnt - 1) / 76 << 1 : 0);
byte[] dest = new byte[destlen];
byte[] encodeTable = isUrlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE;
for (int s = 0, d = 0, cc = 0; s < evenlen; ) {
int i = (arr[s++] & 0xff) << 16 | (arr[s++] & 0xff) << 8 | (arr[s++] & 0xff);
dest[d++] = encodeTable[(i >>> 18) & 0x3f];
dest[d++] = encodeTable[(i >>> 12) & 0x3f];
dest[d++] = encodeTable[(i >>> 6) & 0x3f];
dest[d++] = encodeTable[i & 0x3f];
if (isMultiLine && ++cc == 19 && d < destlen - 2) {
dest[d++] = '\r';
dest[d++] = '\n';
cc = 0;
}
}
int left = len - evenlen;// 剩余位数
if (left > 0) {
int i = ((arr[evenlen] & 0xff) << 10) | (left == 2 ? ((arr[len - 1] & 0xff) << 2) : 0);
dest[destlen - 4] = encodeTable[i >> 12];
dest[destlen - 3] = encodeTable[(i >>> 6) & 0x3f];
if (isUrlSafe) {
// 在URL Safe模式下,=为URL中的关键字符,不需要补充。空余的byte位要去掉。
int urlSafeLen = destlen - 2;
if (2 == left) {
dest[destlen - 2] = encodeTable[i & 0x3f];
urlSafeLen += 1;
}
byte[] urlSafeDest = new byte[urlSafeLen];
System.arraycopy(dest, 0, urlSafeDest, 0, urlSafeLen);
return urlSafeDest;
} else {
dest[destlen - 2] = (left == 2) ? encodeTable[i & 0x3f] : (byte) '=';
dest[destlen - 1] = '=';
}
}
return dest;
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/utils/GlobalAuthUtils.java
================================================
package me.zhyd.oauth.utils;
import com.alibaba.fastjson.JSON;
import me.zhyd.oauth.exception.AuthException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
/**
* 全局的工具类
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
public class GlobalAuthUtils {
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
private static final String HMAC_SHA1 = "HmacSHA1";
private static final String HMAC_SHA_256 = "HmacSHA256";
/**
* 生成钉钉请求的Signature
*
* @param secretKey 平台应用的授权密钥
* @param timestamp 时间戳
* @return Signature
*/
public static String generateDingTalkSignature(String secretKey, String timestamp) {
byte[] signData = sign(secretKey.getBytes(DEFAULT_ENCODING), timestamp.getBytes(DEFAULT_ENCODING), HMAC_SHA_256);
return urlEncode(new String(Base64Utils.encode(signData, false)));
}
/**
* 签名
*
* @param key key
* @param data data
* @param algorithm algorithm
* @return byte[]
*/
private static byte[] sign(byte[] key, byte[] data, String algorithm) {
try {
Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(key, algorithm));
return mac.doFinal(data);
} catch (NoSuchAlgorithmException ex) {
throw new AuthException("Unsupported algorithm: " + algorithm, ex);
} catch (InvalidKeyException ex) {
throw new AuthException("Invalid key: " + Arrays.toString(key), ex);
}
}
/**
* 编码
*
* @param value str
* @return encode str
*/
public static String urlEncode(String value) {
if (value == null) {
return "";
}
try {
String encoded = URLEncoder.encode(value, GlobalAuthUtils.DEFAULT_ENCODING.displayName());
return encoded.replace("+", "%20").replace("*", "%2A").replace("~", "%7E").replace("/", "%2F");
} catch (UnsupportedEncodingException e) {
throw new AuthException("Failed To Encode Uri", e);
}
}
/**
* 解码
*
* @param value str
* @return decode str
*/
public static String urlDecode(String value) {
if (value == null) {
return "";
}
try {
return URLDecoder.decode(value, GlobalAuthUtils.DEFAULT_ENCODING.displayName());
} catch (UnsupportedEncodingException e) {
throw new AuthException("Failed To Decode Uri", e);
}
}
/**
* string字符串转map,str格式为 {@code xxx=xxx&xxx=xxx}
*
* @param accessTokenStr 待转换的字符串
* @return map
*/
public static Map parseStringToMap(String accessTokenStr) {
Map res = null;
if (accessTokenStr.contains("&")) {
String[] fields = accessTokenStr.split("&");
res = new HashMap<>((int) (fields.length / 0.75 + 1));
for (String field : fields) {
if (field.contains("=")) {
String[] keyValue = field.split("=");
res.put(GlobalAuthUtils.urlDecode(keyValue[0]), keyValue.length == 2 ? GlobalAuthUtils.urlDecode(keyValue[1]) : null);
}
}
} else {
res = new HashMap<>(0);
}
return res;
}
/**
* map转字符串,转换后的字符串格式为 {@code xxx=xxx&xxx=xxx}
*
* @param params 待转换的map
* @param encode 是否转码
* @return str
*/
public static String parseMapToString(Map params, boolean encode) {
if (null == params || params.isEmpty()) {
return "";
}
List paramList = new ArrayList<>();
params.forEach((k, v) -> {
if (null == v) {
paramList.add(k + "=");
} else {
paramList.add(k + "=" + (encode ? urlEncode(v) : v));
}
});
return String.join("&", paramList);
}
/**
* 是否为http协议
*
* @param url 待验证的url
* @return true: http协议, false: 非http协议
*/
public static boolean isHttpProtocol(String url) {
if (StringUtils.isEmpty(url)) {
return false;
}
return url.startsWith("http://") || url.startsWith("http%3A%2F%2F");
}
/**
* 是否为https协议
*
* @param url 待验证的url
* @return true: https协议, false: 非https协议
*/
public static boolean isHttpsProtocol(String url) {
if (StringUtils.isEmpty(url)) {
return false;
}
return url.startsWith("https://") || url.startsWith("https%3A%2F%2F");
}
/**
* 是否为本地主机(域名)
*
* @param url 待验证的url
* @return true: 本地主机(域名), false: 非本地主机(域名)
*/
public static boolean isLocalHost(String url) {
return StringUtils.isEmpty(url) || url.contains("127.0.0.1") || url.contains("localhost");
}
/**
* 是否为https协议或本地主机(域名)
*
* @param url 待验证的url
* @return true: https协议或本地主机 false: 非https协议或本机主机
*/
public static boolean isHttpsProtocolOrLocalHost(String url) {
if (StringUtils.isEmpty(url)) {
return false;
}
return isHttpsProtocol(url) || isLocalHost(url);
}
/**
* Generate nonce with given length
*
* @param len length
* @return nonce string
*/
public static String generateNonce(int len) {
String s = "0123456789QWERTYUIOPLKJHGFDSAZXCVBNMqwertyuioplkjhgfdsazxcvbnm";
Random rng = new Random();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++) {
int index = rng.nextInt(62);
sb.append(s, index, index + 1);
}
return sb.toString();
}
/**
* Get current timestamp
*
* @return timestamp string
*/
public static String getTimestamp() {
return String.valueOf(System.currentTimeMillis() / 1000);
}
/**
* Generate Twitter signature
* https://developer.twitter.com/en/docs/basics/authentication/guides/creating-a-signature
*
* @param params parameters including: oauth headers, query params, body params
* @param method HTTP method
* @param baseUrl base url
* @param apiSecret api key secret can be found in the developer portal by viewing the app details page
* @param tokenSecret oauth token secret
* @return BASE64 encoded signature string
*/
public static String generateTwitterSignature(Map params, String method, String baseUrl, String apiSecret, String tokenSecret) {
TreeMap map = new TreeMap<>(params);
String str = parseMapToString(map, true);
String baseStr = method.toUpperCase() + "&" + urlEncode(baseUrl) + "&" + urlEncode(str);
String signKey = apiSecret + "&" + (StringUtils.isEmpty(tokenSecret) ? "" : tokenSecret);
byte[] signature = sign(signKey.getBytes(DEFAULT_ENCODING), baseStr.getBytes(DEFAULT_ENCODING), HMAC_SHA1);
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 params, String clientSecret) {
TreeMap 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
*
* 代码copy并修改自:https://coding.net/u/napos_openapi/p/eleme-openapi-java-sdk/git/blob/master/src/main/java/eleme/openapi/sdk/utils/SignatureUtil.java
*
* @param appKey 平台应用的授权key
* @param secret 平台应用的授权密钥
* @param timestamp 时间戳,单位秒。API服务端允许客户端请求最大时间误差为正负5分钟。
* @param action 饿了么请求的api方法
* @param token 用户授权的token
* @param parameters 加密参数
* @return Signature
*/
public static String generateElemeSignature(String appKey, String secret, long timestamp, String action, String token, Map parameters) {
final Map sorted = new TreeMap<>(parameters);
sorted.put("app_key", appKey);
sorted.put("timestamp", timestamp);
StringBuffer string = new StringBuffer();
for (Map.Entry entry : sorted.entrySet()) {
string.append(entry.getKey()).append("=").append(JSON.toJSONString(entry.getValue()));
}
String splice = String.format("%s%s%s%s", action, token, string, secret);
String calculatedSignature = md5(splice);
return calculatedSignature.toUpperCase();
}
/**
* MD5加密
*
* 代码copy并修改自:https://coding.net/u/napos_openapi/p/eleme-openapi-java-sdk/git/blob/master/src/main/java/eleme/openapi/sdk/utils/SignatureUtil.java
*
* @param str 待加密的字符串
* @return md5 str
*/
public static String md5(String str) {
MessageDigest md = null;
StringBuilder buffer = null;
try {
md = MessageDigest.getInstance("MD5");
md.update(str.getBytes(StandardCharsets.UTF_8));
byte[] byteData = md.digest();
buffer = new StringBuilder();
for (byte byteDatum : byteData) {
buffer.append(Integer.toString((byteDatum & 0xff) + 0x100, 16).substring(1));
}
} catch (Exception ignored) {
}
return null == buffer ? "" : buffer.toString();
}
/**
* 生成京东宙斯平台的签名字符串
* 宙斯签名规则过程如下:
* 将所有请求参数按照字母先后顺序排列,例如将access_token,app_key,method,timestamp,v 排序为access_token,app_key,method,timestamp,v
* 1.把所有参数名和参数值进行拼接,例如:access_tokenxxxapp_keyxxxmethodxxxxxxtimestampxxxxxxvx
* 2.把appSecret夹在字符串的两端,例如:appSecret+XXXX+appSecret
* 3.使用MD5进行加密,再转化成大写
* link: http://open.jd.com/home/home#/doc/common?listId=890
* link: https://github.com/pingjiang/jd-open-api-sdk-src/blob/master/src/main/java/com/jd/open/api/sdk/DefaultJdClient.java
*
* @param appSecret 京东应用密钥
* @param params 签名参数
* @return 签名后的字符串
* @since 1.15.0
*/
public static String generateJdSignature(String appSecret, Map params) {
Map treeMap = new TreeMap<>(params);
StringBuilder signBuilder = new StringBuilder(appSecret);
for (Map.Entry entry : treeMap.entrySet()) {
String name = entry.getKey();
String value = String.valueOf(entry.getValue());
if (StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) {
signBuilder.append(name).append(value);
}
}
signBuilder.append(appSecret);
return md5(signBuilder.toString()).toUpperCase();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/utils/HttpUtils.java
================================================
package me.zhyd.oauth.utils;
import com.xkcoding.http.HttpUtil;
import com.xkcoding.http.config.HttpConfig;
import com.xkcoding.http.support.HttpHeader;
import com.xkcoding.http.support.SimpleHttpResponse;
import me.zhyd.oauth.exception.AuthException;
import java.util.Map;
/**
* HttpUtil 工具,统一处理 http 请求,方便对 simple-http 做定制
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class HttpUtils {
private SimpleHttpResponse httpResponse;
public HttpUtils(HttpConfig config) {
HttpUtil.setConfig(config);
}
public HttpUtils() {
}
/**
* GET 请求
*
* @param url URL
* @return HttpUtils
*/
public HttpUtils get(String url) {
this.httpResponse = HttpUtil.get(url, null, null, false);
return this;
}
/**
* GET 请求
*
* @param url URL
* @param params 参数
* @param header 请求头
* @param encode 是否需要 url encode
* @return HttpUtils
*/
public HttpUtils get(String url, Map params, HttpHeader header, boolean encode) {
this.httpResponse = HttpUtil.get(url, params, header, encode);
return this;
}
/**
* POST 请求
*
* @param url URL
* @return HttpUtils
*/
public HttpUtils post(String url) {
this.httpResponse = HttpUtil.post(url);
return this;
}
/**
* POST 请求
*
* @param url URL
* @param data JSON 参数
* @return HttpUtils
*/
public HttpUtils post(String url, String data) {
this.httpResponse = HttpUtil.post(url, data);
return this;
}
/**
* POST 请求
*
* @param url URL
* @param data JSON 参数
* @param header 请求头
* @return HttpUtils
*/
public HttpUtils post(String url, String data, HttpHeader header) {
this.httpResponse = HttpUtil.post(url, data, header);
return this;
}
/**
* POST 请求
*
* @param url URL
* @param params form 参数
* @param encode 是否需要 url encode
* @return HttpUtils
*/
public HttpUtils post(String url, Map params, boolean encode) {
this.httpResponse = HttpUtil.post(url, params, encode);
return this;
}
/**
* POST 请求
*
* @param url URL
* @param params form 参数
* @param header 请求头
* @param encode 是否需要 url encode
* @return HttpUtils
*/
public HttpUtils post(String url, Map params, HttpHeader header, boolean encode) {
this.httpResponse = HttpUtil.post(url, params, header, encode);
return this;
}
private HttpUtils check() {
if (null == httpResponse) {
throw new AuthException("Invalid SimpleHttpResponse.");
}
if (!httpResponse.isSuccess()) {
throw new AuthException(httpResponse.getError());
}
return this;
}
public String getBody() {
return this.check().getHttpResponse().getBody();
}
public SimpleHttpResponse getHttpResponse() {
return httpResponse;
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/utils/IpUtils.java
================================================
package me.zhyd.oauth.utils;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* 获取IP的工具类
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
public class IpUtils {
/**
* 获取IP
*
* @return ip
*/
public static String getLocalIp() {
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
return null;
}
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/utils/PkceUtil.java
================================================
package me.zhyd.oauth.utils;
import java.nio.charset.StandardCharsets;
/**
* 该配置仅用于支持 PKCE 模式的平台,针对无服务应用,不推荐使用隐式授权,推荐使用 PKCE 模式
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.0.0
*/
public class PkceUtil {
public static String generateCodeVerifier() {
String randomStr = RandomUtil.randomString(50);
return Base64Utils.encodeUrlSafe(randomStr);
}
/**
* 适用于 OAuth 2.0 PKCE 增强协议
*
* @param codeChallengeMethod s256 / plain
* @param codeVerifier 客户端生产的校验码
* @return code challenge
*/
public static String generateCodeChallenge(String codeChallengeMethod, String codeVerifier) {
if ("S256".equalsIgnoreCase(codeChallengeMethod)) {
// https://tools.ietf.org/html/rfc7636#section-4.2
// code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
return newStringUsAscii(Base64Utils.encodeUrlSafe(Sha256.digest(codeVerifier), true));
} else {
return codeVerifier;
}
}
public static String newStringUsAscii(byte[] bytes) {
return new String(bytes, StandardCharsets.US_ASCII);
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/utils/RandomUtil.java
================================================
package me.zhyd.oauth.utils;
import java.util.concurrent.ThreadLocalRandom;
/**
* 生成随机字符串
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.16.0
*/
public class RandomUtil {
/**
* 用于随机选的字符和数字
*/
public static final String BASE_CHAR_NUMBER = "abcdefghijklmnopqrstuvwxyz0123456789";
/**
* 获得一个随机的字符串
*
* @param length 字符串的长度
* @return 指定长度的随机字符串
*/
public static String randomString(int length) {
final StringBuilder sb = new StringBuilder(length);
if (length < 1) {
length = 1;
}
int baseLength = BASE_CHAR_NUMBER.length();
for (int i = 0; i < length; i++) {
int number = ThreadLocalRandom.current().nextInt(baseLength);
sb.append(BASE_CHAR_NUMBER.charAt(number));
}
return sb.toString();
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/utils/Sha256.java
================================================
package me.zhyd.oauth.utils;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* SHA256 加密
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @since 1.16.0
*/
public class Sha256 {
public static byte[] digest(String str) {
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(str.getBytes(StandardCharsets.UTF_8));
return messageDigest.digest();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/utils/StringUtils.java
================================================
package me.zhyd.oauth.utils;
import java.nio.charset.Charset;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.0.0
*/
public class StringUtils {
public static boolean isEmpty(String str) {
return null == str || str.isEmpty();
}
public static boolean isNotEmpty(String str) {
return !isEmpty(str);
}
/**
* 如果给定字符串{@code str}中不包含{@code appendStr},则在{@code str}后追加{@code appendStr};
* 如果已包含{@code appendStr},则在{@code str}后追加{@code otherwise}
*
* @param str 给定的字符串
* @param appendStr 需要追加的内容
* @param otherwise 当{@code appendStr}不满足时追加到{@code str}后的内容
* @return 追加后的字符串
*/
public static String appendIfNotContain(String str, String appendStr, String otherwise) {
if (isEmpty(str) || isEmpty(appendStr)) {
return str;
}
if (str.contains(appendStr)) {
return str.concat(otherwise);
}
return str.concat(appendStr);
}
/**
* 编码字符串
*
* @param str 字符串
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台
* @return 编码后的字节码
*/
public static byte[] bytes(CharSequence str, Charset charset) {
if (str == null) {
return null;
}
if (null == charset) {
return str.toString().getBytes();
}
return str.toString().getBytes(charset);
}
/**
* 解码字节码
*
* @param data 字符串
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台
* @return 解码后的字符串
*/
public static String str(byte[] data, Charset charset) {
if (data == null) {
return null;
}
if (null == charset) {
return new String(data);
}
return new String(data, charset);
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/utils/UrlBuilder.java
================================================
package me.zhyd.oauth.utils;
import com.xkcoding.http.util.MapUtil;
import com.xkcoding.http.util.StringUtil;
import lombok.Setter;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
/**
*
* 构造URL
*
*
* @author yangkai.shen (https://xkcoding.com)
* @since 1.9.0
*/
@Setter
public class UrlBuilder {
private final Map params = new LinkedHashMap<>(7);
private String baseUrl;
private UrlBuilder() {
}
/**
* @param baseUrl 基础路径
* @return the new {@code UrlBuilder}
*/
public static UrlBuilder fromBaseUrl(String baseUrl) {
UrlBuilder builder = new UrlBuilder();
builder.setBaseUrl(baseUrl);
return builder;
}
/**
* 只读的参数Map
*
* @return unmodifiable Map
* @since 1.15.0
*/
public Map getReadOnlyParams() {
return Collections.unmodifiableMap(params);
}
/**
* 添加参数
*
* @param key 参数名称
* @param value 参数值
* @return this UrlBuilder
*/
public UrlBuilder queryParam(String key, Object value) {
if (StringUtil.isEmpty(key)) {
throw new RuntimeException("参数名不能为空");
}
String valueAsString = (value != null ? value.toString() : null);
this.params.put(key, valueAsString);
return this;
}
/**
* 构造url
*
* @return url
*/
public String build() {
return this.build(false);
}
/**
* 构造url
*
* @param encode 转码
* @return url
*/
public String build(boolean encode) {
if (MapUtil.isEmpty(this.params)) {
return this.baseUrl;
}
String baseUrl = StringUtils.appendIfNotContain(this.baseUrl, "?", "&");
String paramString = MapUtil.parseMapToString(this.params, encode);
return baseUrl + paramString;
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/utils/UuidUtils.java
================================================
package me.zhyd.oauth.utils;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ThreadLocalRandom;
/**
* 高性能的创建UUID的工具类,https://github.com/lets-mica/mica
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @since 1.9.3
*/
public class UuidUtils {
/**
* All possible chars for representing a number as a String
* copy from mica:https://github.com/lets-mica/mica/blob/master/mica-core/src/main/java/net/dreamlu/mica/core/utils/NumberUtil.java#L113
*/
private final static byte[] DIGITS = {
'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b',
'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z'
};
/**
* 生成uuid,采用 jdk 9 的形式,优化性能
* copy from mica:https://github.com/lets-mica/mica/blob/master/mica-core/src/main/java/net/dreamlu/mica/core/utils/StringUtil.java#L335
*
* 关于mica uuid生成方式的压测结果,可以参考:https://github.com/lets-mica/mica-jmh/wiki/uuid
*
* @return UUID
*/
public static String getUUID() {
ThreadLocalRandom random = ThreadLocalRandom.current();
long lsb = random.nextLong();
long msb = random.nextLong();
byte[] buf = new byte[32];
formatUnsignedLong(lsb, buf, 20, 12);
formatUnsignedLong(lsb >>> 48, buf, 16, 4);
formatUnsignedLong(msb, buf, 12, 4);
formatUnsignedLong(msb >>> 16, buf, 8, 4);
formatUnsignedLong(msb >>> 32, buf, 0, 8);
return new String(buf, StandardCharsets.UTF_8);
}
/**
* copy from mica:https://github.com/lets-mica/mica/blob/master/mica-core/src/main/java/net/dreamlu/mica/core/utils/StringUtil.java#L348
*/
private static void formatUnsignedLong(long val, byte[] buf, int offset, int len) {
int charPos = offset + len;
int radix = 1 << 4;
int mask = radix - 1;
do {
buf[--charPos] = DIGITS[((int) val) & mask];
val >>>= 4;
} while (charPos > offset);
}
}
================================================
FILE: src/main/java/me/zhyd/oauth/utils/package-info.java
================================================
/**
* 提供一些简单的工具和校验等
*/
package me.zhyd.oauth.utils;
================================================
FILE: src/test/java/me/zhyd/oauth/AuthRequestBuilderTest.java
================================================
package me.zhyd.oauth;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.config.AuthExtendSource;
import me.zhyd.oauth.request.*;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.junit.Assert;
import org.junit.Test;
public class AuthRequestBuilderTest {
/**
* 示例:一般场景下通过 AuthRequestBuilder 构建 AuthRequest
*/
@Test
public void build2() {
AuthRequest authRequest = AuthRequestBuilder.builder()
.source("github")
.authConfig(AuthConfig.builder()
.clientId("a")
.clientSecret("a")
.redirectUri("https://www.justauth.cn")
.build())
.build();
Assert.assertTrue(authRequest instanceof AuthGithubRequest);
System.out.println(authRequest.authorize(AuthStateUtils.createState()));
}
/**
* 示例:AuthConfig 需要动态获取的场景下通过 AuthRequestBuilder 构建 AuthRequest
*/
@Test
public void build() {
AuthRequest authRequest = AuthRequestBuilder.builder()
.source("gitee")
.authConfig((source) -> {
// 通过 source 动态获取 AuthConfig
// 此处可以灵活的从 sql 中取配置也可以从配置文件中取配置
return AuthConfig.builder()
.clientId(source)
.clientSecret(source)
.redirectUri("https://www.justauth.cn/" + source)
.build();
})
.build();
Assert.assertTrue(authRequest instanceof AuthGiteeRequest);
System.out.println(authRequest.authorize(AuthStateUtils.createState()));
}
/**
* 示例:自定义实现的 AuthRequest,通过 AuthRequestBuilder 构建 AuthRequest
*/
@Test
public void build3() {
AuthRequest authRequest = AuthRequestBuilder.builder()
// 关键点:将自定义的 AuthSource 配置上
.extendSource(AuthExtendSource.values())
.source("other")
.authConfig(AuthConfig.builder()
.clientId("a")
.clientSecret("a")
.redirectUri("https://www.justauth.cn")
.build())
.build();
Assert.assertTrue(authRequest instanceof AuthExtendRequest);
System.out.println(authRequest.authorize(AuthStateUtils.createState()));
}
/**
* 测试不同平台
*/
@Test
public void build4() {
AuthConfig config = AuthConfig.builder()
.clientId("a")
.clientSecret("a")
.redirectUri("https://www.justauth.cn")
.authServerId("asd")
.agentId("asd")
.domainPrefix("asd")
.stackOverflowKey("asd")
.deviceId("asd")
.clientOsType(3)
.kid("kid")
.teamId("teamid")
.ignoreCheckState(true)
.ignoreCheckRedirectUri(true)
.build();
for (AuthDefaultSource value : AuthDefaultSource.values()) {
switch (value) {
case TWITTER:
System.out.println(value.getTargetClass());
System.out.println("忽略 twitter");
continue;
case ALIPAY: {
// 单独给Alipay执行测试
AuthRequest authRequest = new AuthAlipayRequest(config, "asd");
System.out.println(value.getTargetClass());
System.out.println(authRequest.authorize(AuthStateUtils.createState()));
continue;
}
case WECHAT_MINI_PROGRAM: {
// 小程序不支持获取调用 authorize
AuthRequest authRequest = new AuthWechatMiniProgramRequest(config);
System.out.println(value.getTargetClass());
continue;
}
case QQ_MINI_PROGRAM: {
// 小程序不支持获取调用 authorize
AuthRequest authRequest = new AuthQQMiniProgramRequest(config);
System.out.println(value.getTargetClass());
continue;
}
default:
AuthRequest authRequest = AuthRequestBuilder.builder()
.source(value.getName())
.authConfig(config)
.build();
System.out.println(value.getTargetClass());
System.out.println(authRequest.authorize(AuthStateUtils.createState()));
}
}
}
}
================================================
FILE: src/test/java/me/zhyd/oauth/cache/AuthStateCacheTest.java
================================================
package me.zhyd.oauth.cache;
import org.junit.Assert;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
public class AuthStateCacheTest {
@Test
public void cache1() throws InterruptedException {
AuthDefaultStateCache.INSTANCE.cache("key", "value");
Assert.assertEquals(AuthDefaultStateCache.INSTANCE.get("key"), "value");
TimeUnit.MILLISECONDS.sleep(4);
Assert.assertEquals(AuthDefaultStateCache.INSTANCE.get("key"), "value");
}
@Test
public void cache2() throws InterruptedException {
AuthDefaultStateCache.INSTANCE.cache("key", "value", 10);
Assert.assertEquals(AuthDefaultStateCache.INSTANCE.get("key"), "value");
// 没过期
TimeUnit.MILLISECONDS.sleep(5);
Assert.assertEquals(AuthDefaultStateCache.INSTANCE.get("key"), "value");
// 过期
TimeUnit.MILLISECONDS.sleep(6);
Assert.assertNull(AuthDefaultStateCache.INSTANCE.get("key"));
}
}
================================================
FILE: src/test/java/me/zhyd/oauth/config/AuthExtendSource.java
================================================
package me.zhyd.oauth.config;
import me.zhyd.oauth.request.AuthDefaultRequest;
import me.zhyd.oauth.request.AuthExtendRequest;
/**
* 测试自定义实现{@link AuthSource}接口后的枚举类
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.12.0
*/
public enum AuthExtendSource implements AuthSource {
OTHER {
/**
* 授权的api
*
* @return url
*/
@Override
public String authorize() {
return "http://authorize";
}
/**
* 获取accessToken的api
*
* @return url
*/
@Override
public String accessToken() {
return "http://accessToken";
}
/**
* 获取用户信息的api
*
* @return url
*/
@Override
public String userInfo() {
return null;
}
/**
* 取消授权的api
*
* @return url
*/
@Override
public String revoke() {
return null;
}
/**
* 刷新授权的api
*
* @return url
*/
@Override
public String refresh() {
return null;
}
@Override
public Class extends AuthDefaultRequest> getTargetClass() {
return AuthExtendRequest.class;
}
}
}
================================================
FILE: src/test/java/me/zhyd/oauth/log/LogTest.java
================================================
package me.zhyd.oauth.log;
import org.junit.Test;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @date 2019/8/2 19:36
* @since 1.8
*/
public class LogTest {
public static void main(String[] args) {
// 测试正常打印
Log.debug("[1] This is a test.");
Log.debug("[1] This is a test.", new NullPointerException("npe"));
Log.warn("[1] This is a test.");
Log.warn("[1] This is a test.", new NullPointerException("npe"));
Log.error("[1] This is a test.");
Log.error("[1] This is a test.", new NullPointerException("npe"));
// 测试只打印 error级别的日志
Log.Config.level = Log.Level.ERROR;
Log.debug("[2] This is a test.");
Log.warn("[2] This is a test.");
Log.error("[2] This is a test.");
// 测试关闭日志
Log.Config.enable = false;
Log.debug("[3] This is a test.");
Log.warn("[3] This is a test.");
Log.error("[3] This is a test.");
}
/**
* 1000000: 23135ms
* 100000: 3016ms
* 10000: 328ms
* 1000: 26ms
*/
@Test
public void testByThread() {
long start = System.currentTimeMillis();
for (int i = 0; i < 1; i++) {
System.out.println(callMethodByThread());
}
long end = System.currentTimeMillis();
System.out.println((end - start) + "ms");
}
/**
* 1000000: 19058ms
* 100000: 2772ms
* 10000: 323ms
* 1000: 29ms
*/
@Test
public void testByThrowable() {
long end = System.currentTimeMillis();
for (int i = 0; i < 1; i++) {
System.out.println(callMethodByThrowable());
}
long end2 = System.currentTimeMillis();
System.out.println((end2 - end) + "ms");
}
@Test
public void testBySecurityManager() {
long end = System.currentTimeMillis();
for (int i = 0; i < 1; i++) {
System.out.println(callMethodBySecurityManager());
}
long end2 = System.currentTimeMillis();
System.out.println((end2 - end) + "ms");
}
private String callMethodByThread() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
System.out.println(stackTraceElement.getMethodName());
}
return stackTrace[2].getMethodName();
}
private String callMethodByThrowable() {
StackTraceElement[] stackTrace = (new Throwable()).getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
System.out.println(stackTraceElement.getMethodName());
}
return stackTrace[2].getMethodName();
}
private String callMethodBySecurityManager() {
return new SecurityManager() {
String getClassName() {
for (Class clazz : getClassContext()) {
System.out.println(clazz);
}
return getClassContext()[0].getName();
}
}.getClassName();
}
}
================================================
FILE: src/test/java/me/zhyd/oauth/model/AuthUserTest.java
================================================
package me.zhyd.oauth.model;
import com.alibaba.fastjson.JSON;
import me.zhyd.oauth.config.AuthDefaultSource;
import me.zhyd.oauth.config.AuthExtendSource;
import me.zhyd.oauth.config.AuthSource;
import org.junit.Assert;
import org.junit.Test;
public class AuthUserTest {
@Test
public void serialize() {
AuthUser user = AuthUser.builder()
.nickname("test")
.build();
String json = JSON.toJSONString(user);
Assert.assertEquals(json, "{\"nickname\":\"test\",\"snapshotUser\":false}");
}
@Test
public void deserialize() {
AuthUser user = AuthUser.builder()
.nickname("test")
.build();
String json = JSON.toJSONString(user);
AuthUser deserializeUser = JSON.parseObject(json, AuthUser.class);
Assert.assertEquals(deserializeUser.getNickname(), "test");
}
@Test
public void source() {
AuthSource source = AuthDefaultSource.HUAWEI;
AuthUser user = AuthUser.builder()
.source(source.toString())
.build();
Assert.assertEquals(user.getSource(), "HUAWEI");
source = AuthExtendSource.OTHER;
user = AuthUser.builder()
.source(source.toString())
.build();
Assert.assertEquals(user.getSource(), "OTHER");
source = AuthDefaultSource.HUAWEI;
Assert.assertEquals(source, AuthDefaultSource.HUAWEI);
source = AuthExtendSource.OTHER;
Assert.assertEquals(source, AuthExtendSource.OTHER);
}
}
================================================
FILE: src/test/java/me/zhyd/oauth/request/AuthExtendRequest.java
================================================
package me.zhyd.oauth.request;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthExtendSource;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
/**
* 测试用自定义扩展的第三方request
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.12.0
*/
public class AuthExtendRequest extends AuthDefaultRequest {
public AuthExtendRequest(AuthConfig config) {
super(config, AuthExtendSource.OTHER);
}
public AuthExtendRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthExtendSource.OTHER, authStateCache);
}
/**
* 获取access token
*
* @param authCallback 授权成功后的回调参数
* @return token
* @see AuthDefaultRequest#authorize()
* @see AuthDefaultRequest#authorize(String)
*/
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
return AuthToken.builder()
.openId("openId")
.expireIn(1000)
.idToken("idToken")
.scope("scope")
.refreshToken("refreshToken")
.accessToken("accessToken")
.code("code")
.build();
}
/**
* 使用token换取用户信息
*
* @param authToken token信息
* @return 用户信息
* @see AuthDefaultRequest#getAccessToken(AuthCallback)
*/
@Override
public AuthUser getUserInfo(AuthToken authToken) {
return AuthUser.builder()
.username("test")
.nickname("test")
.gender(AuthUserGender.MALE)
.token(authToken)
.source(this.source.toString())
.build();
}
/**
* 撤销授权
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
@Override
public AuthResponse revoke(AuthToken authToken) {
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.msg(AuthResponseStatus.SUCCESS.getMsg())
.build();
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
@Override
public AuthResponse refresh(AuthToken authToken) {
return AuthResponse.builder()
.code(AuthResponseStatus.SUCCESS.getCode())
.data(AuthToken.builder()
.openId("openId")
.expireIn(1000)
.idToken("idToken")
.scope("scope")
.refreshToken("refreshToken")
.accessToken("accessToken")
.code("code")
.build())
.build();
}
}
================================================
FILE: src/test/java/me/zhyd/oauth/request/AuthExtendRequestTest.java
================================================
package me.zhyd.oauth.request;
import com.alibaba.fastjson.JSON;
import me.zhyd.oauth.config.AuthConfig;
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.AuthStateUtils;
import org.junit.Assert;
import org.junit.Test;
/**
* 自定义扩展的第三方request的测试类,用于演示具体的用法
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @since 1.12.0
*/
public class AuthExtendRequestTest {
@Test
public void authorize() {
AuthRequest request = new AuthExtendRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("http://redirectUri")
.build());
String authorize = request.authorize(AuthStateUtils.createState());
System.out.println(authorize);
Assert.assertNotNull(authorize);
}
@Test
public void login() {
AuthRequest request = new AuthExtendRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("http://redirectUri")
.build());
String state = AuthStateUtils.createState();
request.authorize(state);
AuthCallback callback = AuthCallback.builder()
.code("code")
.state(state)
.build();
AuthResponse response = request.login(callback);
Assert.assertNotNull(response);
AuthUser user = response.getData();
Assert.assertNotNull(user);
System.out.println(JSON.toJSONString(user));
}
@Test
public void revoke() {
AuthRequest request = new AuthExtendRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("http://redirectUri")
.build());
AuthResponse response = request.revoke(AuthToken.builder().build());
Assert.assertNotNull(response);
System.out.println(JSON.toJSONString(response));
}
@Test
public void refresh() {
AuthRequest request = new AuthExtendRequest(AuthConfig.builder()
.clientId("clientId")
.clientSecret("clientSecret")
.redirectUri("http://redirectUri")
.build());
AuthResponse response = request.refresh(AuthToken.builder().build());
Assert.assertNotNull(response);
System.out.println(JSON.toJSONString(response.getData()));
}
}
================================================
FILE: src/test/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequestTest.java
================================================
package me.zhyd.oauth.request;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.junit.Test;
import static org.junit.Assert.*;
public class AuthWeChatEnterpriseWebRequestTest {
@Test
public void authorize() {
AuthRequest request = new AuthWeChatEnterpriseWebRequest(AuthConfig.builder()
.clientId("a")
.clientSecret("a")
.redirectUri("https://www.justauth.cn")
.build());
System.out.println(request.authorize(AuthStateUtils.createState()));
}
}
================================================
FILE: src/test/java/me/zhyd/oauth/request/AuthWeChatMpRequestTest.java
================================================
package me.zhyd.oauth.request;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.junit.Test;
public class AuthWeChatMpRequestTest {
@Test
public void authorize() {
AuthRequest request = new AuthWeChatMpRequest(AuthConfig.builder()
.clientId("a")
.clientSecret("a")
.redirectUri("https://www.justauth.cn")
.build());
System.out.println(request.authorize(AuthStateUtils.createState()));
}
}
================================================
FILE: src/test/java/me/zhyd/oauth/utils/GlobalAuthUtilsTest.java
================================================
package me.zhyd.oauth.utils;
import com.alibaba.fastjson.JSON;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import org.junit.Assert;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import static me.zhyd.oauth.config.AuthDefaultSource.TWITTER;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class GlobalAuthUtilsTest {
@Test
public void generateDingTalkSignature() {
assertEquals("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe%2FNis5lq9ik%3D", GlobalAuthUtils.generateDingTalkSignature("SHA-256", "1562325753000 "));
}
@Test
public void urlDecode() {
assertEquals("", GlobalAuthUtils.urlDecode(null));
assertEquals("https://www.foo.bar", GlobalAuthUtils.urlDecode("https://www.foo.bar"));
assertEquals("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe/Nis5lq9ik=", GlobalAuthUtils.urlDecode("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe%2FNis5lq9ik%3D"));
}
@Test
public void parseStringToMap() {
Map expected = new HashMap();
expected.put("bar", "baz");
assertEquals(expected, GlobalAuthUtils.parseStringToMap("foo&bar=baz"));
}
@Test
public void isHttpProtocol() {
Assert.assertFalse(GlobalAuthUtils.isHttpProtocol(""));
Assert.assertFalse(GlobalAuthUtils.isHttpProtocol("foo"));
Assert.assertTrue(GlobalAuthUtils.isHttpProtocol("http://www.foo.bar"));
}
@Test
public void isHttpsProtocol() {
Assert.assertFalse(GlobalAuthUtils.isHttpsProtocol(""));
Assert.assertFalse(GlobalAuthUtils.isHttpsProtocol("foo"));
Assert.assertTrue(GlobalAuthUtils.isHttpsProtocol("https://www.foo.bar"));
}
@Test
public void isLocalHost() {
Assert.assertFalse(GlobalAuthUtils.isLocalHost("foo"));
Assert.assertTrue(GlobalAuthUtils.isLocalHost(""));
Assert.assertTrue(GlobalAuthUtils.isLocalHost("127.0.0.1"));
Assert.assertTrue(GlobalAuthUtils.isLocalHost("localhost"));
}
@Test
public void testGenerateTwitterSignatureForRequestToken() {
AuthConfig config = AuthConfig.builder()
.clientId("HD0XLqzi5Wz0G08rh45Cg8mgh")
.clientSecret("0YX3RH2DnPiT77pgzLzFdfpMKX8ENLIWQKYQ7lG5TERuZNgXN5")
.redirectUri("https://codinglife.tech")
.build();
Map params = new HashMap<>();
params.put("oauth_consumer_key", config.getClientId());
params.put("oauth_nonce", "sTj7Ivg73u052eXstpoS1AWQCynuDEPN");
params.put("oauth_signature_method", "HMAC-SHA1");
params.put("oauth_timestamp", "1569750981");
params.put("oauth_callback", config.getRedirectUri());
params.put("oauth_version", "1.0");
String baseUrl = "https://api.twitter.com/oauth/request_token";
params.put("oauth_signature", GlobalAuthUtils.generateTwitterSignature(params, "POST", baseUrl, config.getClientSecret(), null));
params.forEach((k, v) -> params.put(k, "\"" + GlobalAuthUtils.urlEncode(v) + "\""));
String actual = "OAuth " + GlobalAuthUtils.parseMapToString(params, false).replaceAll("&", ", ");
assertEquals("OAuth oauth_nonce=\"sTj7Ivg73u052eXstpoS1AWQCynuDEPN\", oauth_signature=\"%2BL5Jq%2FTaKubge04cWw%2B4yfjFlaU%3D\", oauth_callback=\"https%3A%2F%2Fcodinglife.tech\", oauth_consumer_key=\"HD0XLqzi5Wz0G08rh45Cg8mgh\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"1569750981\", oauth_version=\"1.0\"", actual);
}
@Test
public void testGenerateTwitterSignatureForAccessToken() {
AuthConfig config = AuthConfig.builder()
.clientId("HD0XLqzi5Wz0G08rh45Cg8mgh")
.clientSecret("0YX3RH2DnPiT77pgzLzFdfpMKX8ENLIWQKYQ7lG5TERuZNgXN5")
.build();
AuthCallback authCallback = AuthCallback.builder()
.oauth_token("W_KLmAAAAAAAxq5LAAABbXxJeD0")
.oauth_verifier("lYou4gxfA6S5KioUa8VF8HCShzA2nSxp")
.build();
Map params = new HashMap<>();
params.put("oauth_consumer_key", config.getClientId());
params.put("oauth_nonce", "sTj7Ivg73u052eXstpoS1AWQCynuDEPN");
params.put("oauth_signature_method", "HMAC-SHA1");
params.put("oauth_timestamp", "1569751082");
params.put("oauth_token", authCallback.getOauth_token());
params.put("oauth_verifier", authCallback.getOauth_verifier());
params.put("oauth_version", "1.0");
params.put("oauth_signature", GlobalAuthUtils.generateTwitterSignature(params, "POST", TWITTER.accessToken(), config.getClientSecret(), authCallback
.getOauth_token()));
params.forEach((k, v) -> params.put(k, "\"" + GlobalAuthUtils.urlEncode(v) + "\""));
String actual = "OAuth " + GlobalAuthUtils.parseMapToString(params, false).replaceAll("&", ", ");
assertEquals("OAuth oauth_verifier=\"lYou4gxfA6S5KioUa8VF8HCShzA2nSxp\", oauth_nonce=\"sTj7Ivg73u052eXstpoS1AWQCynuDEPN\", oauth_signature=\"9i0lmWgvphtkl2KcCO9VyZ3K2%2F0%3D\", oauth_token=\"W_KLmAAAAAAAxq5LAAABbXxJeD0\", oauth_consumer_key=\"HD0XLqzi5Wz0G08rh45Cg8mgh\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"1569751082\", oauth_version=\"1.0\"", actual);
}
@Test
public void testGenerateTwitterSignatureForUserInfo() {
AuthConfig config = AuthConfig.builder()
.clientId("HD0XLqzi5Wz0G08rh45Cg8mgh")
.clientSecret("0YX3RH2DnPiT77pgzLzFdfpMKX8ENLIWQKYQ7lG5TERuZNgXN5")
.build();
AuthToken authToken = AuthToken.builder()
.oauthToken("1961977975-PcFQaCnpN9h9xqtqHwHlpGBXFrHJ9bOLy7OtGAL")
.oauthTokenSecret("ffyKe39GYYf8tAyhliSe3QmazpO65kZp5b49xOFX6wHho")
.userId("1961977975")
.screenName("pengisgood")
.build();
Map oauthParams = new HashMap<>();
oauthParams.put("oauth_consumer_key", config.getClientId());
oauthParams.put("oauth_nonce", "sTj7Ivg73u052eXstpoS1AWQCynuDEPN");
oauthParams.put("oauth_signature_method", "HMAC-SHA1");
oauthParams.put("oauth_timestamp", "1569751082");
oauthParams.put("oauth_token", authToken.getOauthToken());
oauthParams.put("oauth_version", "1.0");
Map queryParams = new HashMap<>();
queryParams.put("user_id", authToken.getUserId());
queryParams.put("screen_name", authToken.getScreenName());
queryParams.put("include_entities", Boolean.toString(true));
Map params = new HashMap<>(queryParams);
oauthParams.put("oauth_signature", GlobalAuthUtils.generateTwitterSignature(params, "GET", TWITTER.userInfo(), config.getClientSecret(), authToken
.getOauthTokenSecret()));
oauthParams.forEach((k, v) -> oauthParams.put(k, "\"" + GlobalAuthUtils.urlEncode(v) + "\""));
String actual = "OAuth " + GlobalAuthUtils.parseMapToString(oauthParams, false).replaceAll("&", ", ");
assertEquals("OAuth oauth_nonce=\"sTj7Ivg73u052eXstpoS1AWQCynuDEPN\", oauth_signature=\"OsqHjRmBf7syxlz8lB7MRdzqEjY%3D\", oauth_token=\"1961977975-PcFQaCnpN9h9xqtqHwHlpGBXFrHJ9bOLy7OtGAL\", oauth_consumer_key=\"HD0XLqzi5Wz0G08rh45Cg8mgh\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"1569751082\", oauth_version=\"1.0\"", actual);
}
@Test
public void md5() {
String str = "helloworld,iamjustauth";
String md5Str = GlobalAuthUtils.md5(str);
assertEquals("b0d923de4289b69976448cac718528b8", md5Str);
}
@Test
public void treemap() {
Map parameters = new HashMap<>();
parameters.put("user_id", "1");
parameters.put("screen_name", "222");
parameters.put("a", "222");
parameters.put("include_entities", Boolean.toString(true));
final Map sorted = new TreeMap<>(parameters);
assertEquals("{\"a\":\"222\",\"include_entities\":\"true\",\"screen_name\":\"222\",\"user_id\":\"1\"}", JSON.toJSONString(sorted));
}
@Test
public void urlEncode() {
assertEquals("", GlobalAuthUtils.urlEncode(null));
assertEquals("https%3A%2F%2Fwww.foo.bar", GlobalAuthUtils.urlEncode("https://www.foo.bar"));
assertEquals("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe%252FNis5lq9ik%253D", GlobalAuthUtils.urlEncode("mLTZEMqIlpAA3xtJ43KcRT0EDLwgSamFe%2FNis5lq9ik%3D"));
}
@Test
public void parseMapToString() {
Map parameters = new HashMap<>();
parameters.put("user_id", "1");
parameters.put("screen_name", "史上最全的第三方授权登录库");
parameters.put("include_entities", Boolean.toString(true));
assertEquals("user_id=1&screen_name=史上最全的第三方授权登录库&include_entities=true", GlobalAuthUtils.parseMapToString(parameters, false));
assertEquals("user_id=1&screen_name=%E5%8F%B2%E4%B8%8A%E6%9C%80%E5%85%A8%E7%9A%84%E7%AC%AC%E4%B8%89%E6%96%B9%E6%8E%88%E6%9D%83%E7%99%BB%E5%BD%95%E5%BA%93&include_entities=true", GlobalAuthUtils.parseMapToString(parameters, true));
assertEquals("", GlobalAuthUtils.parseMapToString(null, true));
}
@Test
public void generateNonce() {
assertEquals(10, GlobalAuthUtils.generateNonce(10).length());
}
@Test
public void getTimestamp() {
assertNotNull(GlobalAuthUtils.getTimestamp());
}
@Test
public void generateTwitterSignature() {
Map queryParams = new HashMap<>();
queryParams.put("name", "你好");
queryParams.put("gender", "male");
assertEquals("20FYnV2aZnxNQtp+I0tpMRTvcx0=", GlobalAuthUtils.generateTwitterSignature(queryParams, "GET", TWITTER.userInfo(), "xxxxx", "xxxxx"));
}
@Test
public void generateElemeSignature() {
Map parameters = new HashMap<>();
parameters.put("name", "你好");
parameters.put("gender", "male");
String appKey = "appKey";
String secret = "appKey";
long timestamp = 1233456789;
String action = "appKey";
String token = "appKey";
assertEquals("26FEB8BF7E84FED2619D9C5D97F421BD", GlobalAuthUtils.generateElemeSignature(appKey, secret, timestamp, action, token, parameters));
}
@Test
public void generateJdSignature() {
Map parameters = new HashMap<>();
parameters.put("name", "你好");
parameters.put("gender", "male");
String appSecret = "appKey";
assertEquals("FE04EC03BA8A619802CF309959C2B43F", GlobalAuthUtils.generateJdSignature(appSecret, parameters));
}
}
================================================
FILE: src/test/java/me/zhyd/oauth/utils/JsonPathTest.java
================================================
package me.zhyd.oauth.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* JsonPath用法测试
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
*/
public class JsonPathTest {
@Test
public void jsonPath() {
List