Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
符节科技
Jap
提交
132a7f43
Jap
项目概览
符节科技
/
Jap
9 个月 前同步成功
通知
90
Star
3
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
Jap
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
132a7f43
编写于
3月 13, 2021
作者:
智布道
👁
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
🥚
添加 amazon 平台
上级
c79b97a0
变更
7
隐藏空白更改
内联
并排
Showing
7 changed file
with
348 addition
and
0 deletion
+348
-0
src/main/java/me/zhyd/oauth/config/AuthConfig.java
src/main/java/me/zhyd/oauth/config/AuthConfig.java
+7
-0
src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java
src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java
+27
-0
src/main/java/me/zhyd/oauth/enums/scope/AuthAmazonScope.java
src/main/java/me/zhyd/oauth/enums/scope/AuthAmazonScope.java
+28
-0
src/main/java/me/zhyd/oauth/request/AuthAmazonRequest.java
src/main/java/me/zhyd/oauth/request/AuthAmazonRequest.java
+182
-0
src/main/java/me/zhyd/oauth/utils/PkceUtil.java
src/main/java/me/zhyd/oauth/utils/PkceUtil.java
+39
-0
src/main/java/me/zhyd/oauth/utils/RandomUtil.java
src/main/java/me/zhyd/oauth/utils/RandomUtil.java
+38
-0
src/main/java/me/zhyd/oauth/utils/Sha256.java
src/main/java/me/zhyd/oauth/utils/Sha256.java
+27
-0
未找到文件。
src/main/java/me/zhyd/oauth/config/AuthConfig.java
浏览文件 @
132a7f43
...
@@ -131,4 +131,11 @@ public class AuthConfig {
...
@@ -131,4 +131,11 @@ public class AuthConfig {
* @since 1.15.9
* @since 1.15.9
*/
*/
private
String
packId
;
private
String
packId
;
/**
* 是否开启 PKCE 模式,该配置仅用于支持 PKCE 模式的平台,针对无服务应用,不推荐使用隐式授权,推荐使用 PKCE 模式
*
* @since 1.16.0
*/
private
boolean
pkce
;
}
}
src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java
浏览文件 @
132a7f43
...
@@ -832,5 +832,32 @@ public enum AuthDefaultSource implements AuthSource {
...
@@ -832,5 +832,32 @@ public enum AuthDefaultSource implements AuthSource {
public
String
refresh
()
{
public
String
refresh
()
{
return
"https://oauth.aliyun.com/v1/token"
;
return
"https://oauth.aliyun.com/v1/token"
;
}
}
},
/**
* 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"
;
}
}
}
}
}
src/main/java/me/zhyd/oauth/enums/scope/AuthAmazonScope.java
0 → 100644
浏览文件 @
132a7f43
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
;
}
src/main/java/me/zhyd/oauth/request/AuthAmazonRequest.java
0 → 100644
浏览文件 @
132a7f43
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
)
{
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"
,
getRealState
(
state
));
if
(
config
.
isPkce
())
{
String
cacheKey
=
this
.
source
.
getName
().
concat
(
":code_verifier:"
).
concat
(
config
.
getClientId
());
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
protected
AuthToken
getAccessToken
(
AuthCallback
authCallback
)
{
Map
<
String
,
String
>
form
=
new
HashMap
<>(
8
);
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
(
config
.
getClientId
());
String
codeVerifier
=
this
.
authStateCache
.
get
(
cacheKey
);
form
.
put
(
"code_verifier"
,
codeVerifier
);
}
return
getToken
(
form
,
this
.
source
.
accessToken
());
}
@Override
public
AuthResponse
refresh
(
AuthToken
authToken
)
{
Map
<
String
,
String
>
form
=
new
HashMap
<>(
6
);
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
<
String
,
String
>
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
);
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
protected
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
);
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
));
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
();
}
}
src/main/java/me/zhyd/oauth/utils/PkceUtil.java
0 → 100644
浏览文件 @
132a7f43
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
);
}
}
src/main/java/me/zhyd/oauth/utils/RandomUtil.java
0 → 100644
浏览文件 @
132a7f43
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
();
}
}
src/main/java/me/zhyd/oauth/utils/Sha256.java
0 → 100644
浏览文件 @
132a7f43
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
;
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录