me.zhyd.oauth.utils.GlobalAuthUtils Maven / Gradle / Ivy
Show all versions of JustAuth Show documentation
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();
}
}