♻️ 抽取 HTTP,具体实现交给使用者,解耦 hutool-http
This commit is contained in:
@@ -47,16 +47,16 @@ public class AuthChecker {
|
||||
*/
|
||||
public static void checkConfig(AuthConfig config, AuthSource source) {
|
||||
String redirectUri = config.getRedirectUri();
|
||||
if (!GlobalAuthUtil.isHttpProtocol(redirectUri) && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) {
|
||||
if (!GlobalAuthUtils.isHttpProtocol(redirectUri) && !GlobalAuthUtils.isHttpsProtocol(redirectUri)) {
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
|
||||
}
|
||||
// facebook的回调地址必须为https的链接
|
||||
if (AuthDefaultSource.FACEBOOK == source && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) {
|
||||
if (AuthDefaultSource.FACEBOOK == source && !GlobalAuthUtils.isHttpsProtocol(redirectUri)) {
|
||||
// Facebook's redirect uri must use the HTTPS protocol
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
|
||||
}
|
||||
// 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1
|
||||
if (AuthDefaultSource.ALIPAY == source && GlobalAuthUtil.isLocalHost(redirectUri)) {
|
||||
if (AuthDefaultSource.ALIPAY == source && GlobalAuthUtils.isLocalHost(redirectUri)) {
|
||||
// The redirect uri of alipay is forbidden to use localhost or 127.0.0.1
|
||||
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
|
||||
}
|
||||
|
||||
197
src/main/java/me/zhyd/oauth/utils/Base64Utils.java
Normal file
197
src/main/java/me/zhyd/oauth/utils/Base64Utils.java
Normal file
@@ -0,0 +1,197 @@
|
||||
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<br>
|
||||
* 如果isMultiLine为<code>true</code>,则每76个字符一个换行符,否则在一行显示
|
||||
*
|
||||
* @param arr 被编码的数组
|
||||
* @param isMultiLine 在76个char之后是CRLF还是EOF
|
||||
* @param isUrlSafe 是否使用URL安全字符,一般为<code>false</code>
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
|
||||
@@ -17,13 +13,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.TreeMap;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 全局的工具类
|
||||
@@ -31,7 +21,7 @@ import java.util.TreeMap;
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class GlobalAuthUtil {
|
||||
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";
|
||||
@@ -45,7 +35,7 @@ public class GlobalAuthUtil {
|
||||
*/
|
||||
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(Base64.encode(signData, false)));
|
||||
return urlEncode(new String(Base64Utils.encode(signData, false)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,7 +69,7 @@ public class GlobalAuthUtil {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
String encoded = URLEncoder.encode(value, GlobalAuthUtil.DEFAULT_ENCODING.displayName());
|
||||
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);
|
||||
@@ -98,7 +88,7 @@ public class GlobalAuthUtil {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
return URLDecoder.decode(value, GlobalAuthUtil.DEFAULT_ENCODING.displayName());
|
||||
return URLDecoder.decode(value, GlobalAuthUtils.DEFAULT_ENCODING.displayName());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AuthException("Failed To Decode Uri", e);
|
||||
}
|
||||
@@ -111,13 +101,13 @@ public class GlobalAuthUtil {
|
||||
* @return map
|
||||
*/
|
||||
public static Map<String, String> parseStringToMap(String accessTokenStr) {
|
||||
Map<String, String> res = new HashMap<>();
|
||||
Map<String, String> res = new HashMap<>(6);
|
||||
if (accessTokenStr.contains("&")) {
|
||||
String[] fields = accessTokenStr.split("&");
|
||||
for (String field : fields) {
|
||||
if (field.contains("=")) {
|
||||
String[] keyValue = field.split("=");
|
||||
res.put(GlobalAuthUtil.urlDecode(keyValue[0]), keyValue.length == 2 ? GlobalAuthUtil.urlDecode(keyValue[1]) : null);
|
||||
res.put(GlobalAuthUtils.urlDecode(keyValue[0]), keyValue.length == 2 ? GlobalAuthUtils.urlDecode(keyValue[1]) : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,29 +121,16 @@ public class GlobalAuthUtil {
|
||||
* @param encode 是否转码
|
||||
* @return str
|
||||
*/
|
||||
public static String parseMapToString(Map<String, Object> params, boolean encode) {
|
||||
public static String parseMapToString(Map<String, String> params, boolean encode) {
|
||||
List<String> paramList = new ArrayList<>();
|
||||
params.forEach((k, v) -> {
|
||||
if (ObjectUtil.isNull(v)) {
|
||||
if (null == v) {
|
||||
paramList.add(k + "=");
|
||||
} else {
|
||||
String valueString = v.toString();
|
||||
paramList.add(k + "=" + (encode ? urlEncode(valueString) : valueString));
|
||||
paramList.add(k + "=" + (encode ? urlEncode(v) : v));
|
||||
}
|
||||
});
|
||||
return CollUtil.join(paramList, "&");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将url的参数列表转换成map
|
||||
*
|
||||
* @param url 待转换的url
|
||||
* @return map
|
||||
*/
|
||||
public static Map<String, Object> parseQueryToMap(String url) {
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
HttpUtil.decodeParamMap(url, "UTF-8").forEach(paramMap::put);
|
||||
return paramMap;
|
||||
return String.join("&", paramList);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,9 +207,9 @@ public class GlobalAuthUtil {
|
||||
* @param tokenSecret oauth token secret
|
||||
* @return BASE64 encoded signature string
|
||||
*/
|
||||
public static String generateTwitterSignature(Map<String, Object> params, String method, String baseUrl, String apiSecret, String tokenSecret) {
|
||||
TreeMap<String, Object> map = new TreeMap<>();
|
||||
for (Map.Entry<String, Object> e : params.entrySet()) {
|
||||
public static String generateTwitterSignature(Map<String, String> params, String method, String baseUrl, String apiSecret, String tokenSecret) {
|
||||
TreeMap<String, String> map = new TreeMap<>();
|
||||
for (Map.Entry<String, String> e : params.entrySet()) {
|
||||
map.put(urlEncode(e.getKey()), e.getValue());
|
||||
}
|
||||
String str = parseMapToString(map, true);
|
||||
@@ -240,7 +217,7 @@ public class GlobalAuthUtil {
|
||||
String signKey = apiSecret + "&" + (StringUtils.isEmpty(tokenSecret) ? "" : tokenSecret);
|
||||
byte[] signature = sign(signKey.getBytes(DEFAULT_ENCODING), baseStr.getBytes(DEFAULT_ENCODING), HMAC_SHA1);
|
||||
|
||||
return new String(Base64.encode(signature, false));
|
||||
return new String(Base64Utils.encode(signature, false));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1,7 +1,6 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
|
||||
@@ -36,4 +35,40 @@ public class StringUtils {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package me.zhyd.oauth.utils;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.xkcoding.http.util.MapUtil;
|
||||
import com.xkcoding.http.util.StringUtil;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -19,7 +18,7 @@ import java.util.Map;
|
||||
@Setter
|
||||
public class UrlBuilder {
|
||||
|
||||
private final Map<String, Object> params = new LinkedHashMap<>(7);
|
||||
private final Map<String, String> params = new LinkedHashMap<>(7);
|
||||
private String baseUrl;
|
||||
|
||||
private UrlBuilder() {
|
||||
@@ -44,8 +43,9 @@ public class UrlBuilder {
|
||||
* @return this UrlBuilder
|
||||
*/
|
||||
public UrlBuilder queryParam(String key, Object value) {
|
||||
Assert.notBlank(key, "参数名不能为空");
|
||||
|
||||
if (StringUtil.isEmpty(key)) {
|
||||
throw new RuntimeException("参数名不能为空");
|
||||
}
|
||||
String valueAsString = (value != null ? value.toString() : null);
|
||||
this.params.put(key, valueAsString);
|
||||
|
||||
@@ -72,7 +72,7 @@ public class UrlBuilder {
|
||||
return this.baseUrl;
|
||||
}
|
||||
String baseUrl = StringUtils.appendIfNotContain(this.baseUrl, "?", "&");
|
||||
String paramString = GlobalAuthUtil.parseMapToString(this.params, encode);
|
||||
String paramString = MapUtil.parseMapToString(this.params, encode);
|
||||
return baseUrl + paramString;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user