org.aoju.bus.oauth.provider.AmazonProvider Maven / Gradle / Ivy
package org.aoju.bus.oauth.provider;
import com.alibaba.fastjson.JSONObject;
import org.aoju.bus.cache.metric.ExtendCache;
import org.aoju.bus.core.codec.Base64;
import org.aoju.bus.core.exception.AuthorizedException;
import org.aoju.bus.core.lang.*;
import org.aoju.bus.core.toolkit.RandomKit;
import org.aoju.bus.core.toolkit.UriKit;
import org.aoju.bus.http.Httpx;
import org.aoju.bus.oauth.Builder;
import org.aoju.bus.oauth.Context;
import org.aoju.bus.oauth.Registry;
import org.aoju.bus.oauth.magic.AccToken;
import org.aoju.bus.oauth.magic.Callback;
import org.aoju.bus.oauth.magic.Message;
import org.aoju.bus.oauth.magic.Property;
import org.aoju.bus.oauth.metric.OauthScope;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class AmazonProvider extends AbstractProvider {
public AmazonProvider(Context context) {
super(context, Registry.AMAZON);
}
public AmazonProvider(Context context, ExtendCache extendCache) {
super(context, Registry.AMAZON, extendCache);
}
public static String generateCodeVerifier() {
String randomStr = RandomKit.randomString(50);
return Base64.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)) {
return new String(Base64.encode(digest(codeVerifier), true, true), Charset.US_ASCII);
} else {
return codeVerifier;
}
}
public static byte[] digest(String text) {
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance(Algorithm.SHA256.getValue());
messageDigest.update(text.getBytes(Charset.UTF_8));
return messageDigest.digest();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
/**
* 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) {
Builder builder = Builder.fromUrl(source.authorize())
.queryParam("client_id", context.getAppKey())
.queryParam("scope", this.getScopes(" ", true, getScopes(false, OauthScope.Amazon.values())))
.queryParam("redirect_uri", context.getRedirectUri())
.queryParam("response_type", "code")
.queryParam("state", getRealState(state));
if (context.isPkce()) {
String cacheKey = this.source.getName().concat(":code_verifier:").concat(context.getAppKey());
String codeVerifier = generateCodeVerifier();
String codeChallengeMethod = "S256";
String codeChallenge = generateCodeChallenge(codeChallengeMethod, codeVerifier);
builder.queryParam("code_challenge", codeChallenge)
.queryParam("code_challenge_method", codeChallengeMethod);
// 缓存 codeVerifier 十分钟
this.extendCache.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 AccToken getAccessToken(Callback callback) {
Map form = new HashMap<>(8);
form.put("grant_type", "authorization_code");
form.put("code", callback.getCode());
form.put("redirect_uri", context.getRedirectUri());
form.put("client_id", context.getAppKey());
form.put("client_secret", context.getAppSecret());
if (context.isPkce()) {
String cacheKey = this.source.getName().concat(":code_verifier:").concat(context.getAppKey());
String codeVerifier = (String) this.extendCache.get(cacheKey);
form.put("code_verifier", codeVerifier);
}
return getToken(form, this.source.accessToken());
}
@Override
public Message refresh(AccToken authToken) {
Map form = new HashMap<>(6);
form.put("grant_type", "refresh_token");
form.put("refresh_token", authToken.getRefreshToken());
form.put("client_id", context.getAppKey());
form.put("client_secret", context.getAppSecret());
return Message.builder()
.errcode(Builder.ErrorCode.SUCCESS.getCode())
.data(getToken(form, this.source.refresh()))
.build();
}
/**
* https://developer.amazon.com/zh/docs/login-with-amazon/obtain-customer-profile.html#call-profile-endpoint
*
* @param accToken token信息
* @return AuthUser
*/
@Override
protected Property getUserInfo(AccToken accToken) {
String accessToken = accToken.getAccessToken();
this.checkToken(accessToken);
Map header = new HashMap<>();
header.put(Header.HOST, "api.amazon.com");
header.put(Header.AUTHORIZATION, "bearer " + accessToken);
String userInfo = Httpx.get(this.source.userInfo(), new HashMap<>(0), header);
JSONObject jsonObject = JSONObject.parseObject(userInfo);
this.checkResponse(jsonObject);
return Property.builder()
.rawJson(jsonObject)
.uuid(jsonObject.getString("user_id"))
.username(jsonObject.getString("name"))
.nickname(jsonObject.getString("name"))
.email(jsonObject.getString("email"))
.gender(Normal.Gender.UNKNOWN)
.source(source.toString())
.token(accToken)
.build();
}
@Override
protected String userInfoUrl(AccToken authToken) {
return Builder.fromUrl(source.userInfo())
.queryParam("user_id", authToken.getUserId())
.queryParam("screen_name", authToken.getScreenName())
.queryParam("include_entities", true)
.build();
}
private void checkToken(String accessToken) {
String tokenInfo = Httpx.get("https://api.amazon.com/auth/o2/tokeninfo?access_token=" + UriKit.encode(accessToken));
JSONObject jsonObject = JSONObject.parseObject(tokenInfo);
if (!context.getAppKey().equals(jsonObject.getString("aud"))) {
throw new AuthorizedException(Builder.ErrorCode.ILLEGAL_TOKEN.getMsg());
}
}
private AccToken getToken(Map param, String url) {
Map header = new HashMap<>();
header.put(Header.HOST, "api.amazon.com");
header.put(Header.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED + ";charset=UTF-8");
String response = Httpx.post(url, param, header);
JSONObject jsonObject = JSONObject.parseObject(response);
this.checkResponse(jsonObject);
return AccToken.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 AuthorizedException(jsonObject.getString("error_description").concat(" ") + jsonObject.getString("error_description"));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy