
18 changed files with 2113 additions and 57 deletions
@ -0,0 +1,223 @@ |
|||
package com.yxt.ss.gateway.api; |
|||
|
|||
import com.fasterxml.jackson.core.type.TypeReference; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import com.yxt.ss.gateway.api.authutils.*; |
|||
import com.yxt.ss.gateway.api.utils.AppKeyConfig; |
|||
import com.yxt.ss.gateway.api.utils.ResultBean; |
|||
import com.yxt.ss.gateway.api.utils.SignatureUtil; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.cloud.gateway.filter.GatewayFilterChain; |
|||
import org.springframework.cloud.gateway.filter.GlobalFilter; |
|||
import org.springframework.core.Ordered; |
|||
import org.springframework.core.io.buffer.DataBuffer; |
|||
import org.springframework.core.io.buffer.DataBufferUtils; |
|||
import org.springframework.http.HttpMethod; |
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.http.MediaType; |
|||
import org.springframework.http.server.reactive.ServerHttpRequest; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.util.MultiValueMap; |
|||
import org.springframework.web.server.ServerWebExchange; |
|||
import reactor.core.publisher.Mono; |
|||
|
|||
import java.io.IOException; |
|||
import java.io.UnsupportedEncodingException; |
|||
import java.net.URLDecoder; |
|||
import java.nio.CharBuffer; |
|||
import java.nio.charset.StandardCharsets; |
|||
import java.time.Instant; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* @author dimengzhe |
|||
* @description 网关鉴权 |
|||
*/ |
|||
@Component |
|||
public class AuthFilter implements GlobalFilter, Ordered { |
|||
|
|||
private static final Logger log = LoggerFactory.getLogger(AuthFilter.class); |
|||
|
|||
@Autowired |
|||
private IgnoreWhiteProperties ignoreWhite; |
|||
@Autowired |
|||
private AppKeyConfig appKeyConfig; |
|||
|
|||
public String getSecret(String appKey) { |
|||
return appKeyConfig.getKeys().get(appKey); |
|||
} |
|||
|
|||
@Override |
|||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { |
|||
String url = exchange.getRequest().getURI().getPath(); |
|||
// 1. URI 白名单过滤:如果请求路径在白名单中,直接放行
|
|||
if (isWhitelisted(url)) { |
|||
return chain.filter(exchange); |
|||
} |
|||
|
|||
// 2. 提取请求参数并进行验证
|
|||
return extractParameters(exchange) |
|||
.flatMap(parameters -> { |
|||
// 校验请求参数
|
|||
ResultBean validationResult = validate(parameters); |
|||
// 校验失败,返回 401 Unauthorized 错误响应
|
|||
if (!validationResult.getSuccess()) { |
|||
return setUnauthorizedResponse(exchange, validationResult.getMsg()); |
|||
} |
|||
|
|||
// 3. 如果需要,可以从参数中提取信息并添加到请求头
|
|||
ServerHttpRequest mutableReq = exchange.getRequest().mutate() |
|||
.header(CacheConstants._APP, parameters.get("_app")) |
|||
.build(); |
|||
ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build(); |
|||
// 4. 继续执行后续过滤器链
|
|||
return chain.filter(mutableExchange); |
|||
}); |
|||
} |
|||
|
|||
// 提取请求参数方法:根据请求类型 (GET 或 POST) 提取参数
|
|||
private Mono<Map<String, String>> extractParameters(ServerWebExchange exchange) { |
|||
HttpMethod method = exchange.getRequest().getMethod(); |
|||
// 1. 如果是 GET 请求,从 URL 查询参数中提取
|
|||
if (method == HttpMethod.GET) { |
|||
return Mono.just(exchange.getRequest().getQueryParams().toSingleValueMap()); |
|||
} |
|||
// 如果是 POST、PUT 或 DELETE 请求,从请求体中提取参数
|
|||
if (exchange.getRequest().getMethod() == HttpMethod.POST || |
|||
exchange.getRequest().getMethod() == HttpMethod.PUT || |
|||
exchange.getRequest().getMethod() == HttpMethod.DELETE) { |
|||
|
|||
String contentType = exchange.getRequest().getHeaders().getContentType().toString(); |
|||
if (contentType.contains("application/json")) { |
|||
return exchange.getRequest().getBody() |
|||
.collectList() |
|||
.map(dataBuffers -> { |
|||
// 将 DataBuffer 转换为字符串
|
|||
StringBuilder bodyBuilder = new StringBuilder(); |
|||
dataBuffers.forEach(dataBuffer -> { |
|||
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer()); |
|||
bodyBuilder.append(charBuffer); |
|||
}); |
|||
|
|||
// 解析 JSON 请求体
|
|||
try { |
|||
return parseJsonBody(bodyBuilder.toString()); |
|||
} catch (IOException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return new HashMap<String, String>(); // 如果解析失败,返回空 Map
|
|||
}); |
|||
} else if (contentType.contains("application/x-www-form-urlencoded")) { |
|||
// 处理 x-www-form-urlencoded 格式
|
|||
return exchange.getRequest().getBody() |
|||
.collectList() |
|||
.map(dataBuffers -> { |
|||
StringBuilder bodyBuilder = new StringBuilder(); |
|||
dataBuffers.forEach(dataBuffer -> { |
|||
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer()); |
|||
bodyBuilder.append(charBuffer); |
|||
}); |
|||
try { |
|||
return parseFormBody(bodyBuilder.toString()); |
|||
} catch (UnsupportedEncodingException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return new HashMap<String, String>(); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
// 其他情况返回空 Map
|
|||
return Mono.just(new HashMap<>()); |
|||
} |
|||
|
|||
//解析 JSON 请求体的方法
|
|||
private Map<String, String> parseJsonBody(String body) throws IOException { |
|||
ObjectMapper objectMapper = new ObjectMapper(); |
|||
return objectMapper.readValue(body, Map.class); |
|||
} |
|||
|
|||
// 辅助方法:解析 form-data 格式字符串
|
|||
private Map<String, String> parseFormBody(String body) throws UnsupportedEncodingException { |
|||
Map<String, String> parameters = new HashMap<>(); |
|||
// 按照 & 分割每一对键值
|
|||
String[] pairs = body.split("&"); |
|||
for (String pair : pairs) { |
|||
String[] keyValue = pair.split("="); |
|||
if (keyValue.length == 2) { |
|||
// 解码键和值
|
|||
parameters.put(URLDecoder.decode(keyValue[0], "UTF-8"), |
|||
URLDecoder.decode(keyValue[1], "UTF-8")); |
|||
} |
|||
} |
|||
return parameters; |
|||
} |
|||
|
|||
// 设置未授权响应方法
|
|||
private Mono<Void> setUnauthorizedResponse(ServerWebExchange exchange, String message) { |
|||
// 设置响应状态码为 401 Unauthorized
|
|||
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); |
|||
// 设置响应内容类型为 JSON
|
|||
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON); |
|||
// 创建返回的错误信息
|
|||
DataBuffer buffer = exchange.getResponse().bufferFactory() |
|||
.wrap(("{\"error\":\"" + message + "\"}").getBytes(StandardCharsets.UTF_8)); |
|||
return exchange.getResponse().writeWith(Mono.just(buffer)); |
|||
} |
|||
|
|||
private boolean isWhitelisted(String url) { |
|||
return StringUtils.matches(url, ignoreWhite.getWhites()) || |
|||
StringUtils.matchesTwo(url, ignoreWhite.getWhitesTwo()); |
|||
} |
|||
|
|||
@Override |
|||
public int getOrder() { |
|||
return 0; |
|||
} |
|||
|
|||
ResultBean validate(Map<String, String> data) { |
|||
ResultBean rb = ResultBean.fireFail(); |
|||
// 解析参数
|
|||
String app = data.get("_app"); |
|||
if (org.springframework.util.StringUtils.isEmpty(app)) { |
|||
return rb.setMsg("_app参数缺失或无效"); |
|||
} |
|||
// 获取 secret 值
|
|||
String secret = getSecret(app); |
|||
if (org.springframework.util.StringUtils.isEmpty(secret)) { |
|||
return rb.setMsg("_app参数不正确"); |
|||
} |
|||
// 校验时间戳 _t 参数
|
|||
String timestampStr = data.get("_t"); |
|||
if (org.springframework.util.StringUtils.isEmpty(timestampStr)) { |
|||
return rb.setMsg("_t参数缺失"); |
|||
} |
|||
long timestamp; |
|||
try { |
|||
timestamp = Long.parseLong(timestampStr); |
|||
} catch (NumberFormatException e) { |
|||
return rb.setMsg("_t参数格式不正确"); |
|||
} |
|||
// 时间范围校验
|
|||
long currentTimestamp = Instant.now().getEpochSecond(); |
|||
long timeDifference = Math.abs(currentTimestamp - timestamp); |
|||
final int ALLOWED_TIME_DIFF = 300; // 最大允许时间偏差(秒)
|
|||
if (timeDifference > ALLOWED_TIME_DIFF) { |
|||
return rb.setMsg("时间已超过5分钟,时间失效"); |
|||
} |
|||
|
|||
// 签名验证
|
|||
ResultBean<Boolean> resultBean = SignatureUtil.validateSignature(data, secret); |
|||
if (!resultBean.getSuccess()) { |
|||
return rb.setMsg(resultBean.getMsg()); |
|||
} |
|||
|
|||
return rb.success(); |
|||
|
|||
|
|||
} |
|||
|
|||
} |
@ -0,0 +1,25 @@ |
|||
package com.yxt.ss.gateway.api.authutils; |
|||
|
|||
/** |
|||
* @author dimengzhe |
|||
* @description 缓存的key 常量 |
|||
*/ |
|||
|
|||
public class CacheConstants { |
|||
|
|||
/** |
|||
* 令牌自定义标识 |
|||
*/ |
|||
public static final String HEADER = "token"; |
|||
|
|||
/** |
|||
* 令牌前缀 |
|||
*/ |
|||
public static final String TOKEN_PREFIX = "Bearer "; |
|||
|
|||
/** |
|||
* 用户名字段 |
|||
*/ |
|||
public static final String DETAILS_USERNAME = "userName"; |
|||
public static final String _APP = "_app"; |
|||
} |
@ -0,0 +1,90 @@ |
|||
package com.yxt.ss.gateway.api.authutils; |
|||
|
|||
import java.nio.charset.Charset; |
|||
import java.nio.charset.StandardCharsets; |
|||
|
|||
/** |
|||
* @author dimengzhe |
|||
* @description |
|||
*/ |
|||
|
|||
public class CharsetKit { |
|||
|
|||
/** |
|||
* ISO-8859-1 |
|||
*/ |
|||
public static final String ISO_8859_1 = "ISO-8859-1"; |
|||
/** |
|||
* UTF-8 |
|||
*/ |
|||
public static final String UTF_8 = "UTF-8"; |
|||
/** |
|||
* GBK |
|||
*/ |
|||
public static final String GBK = "GBK"; |
|||
|
|||
/** |
|||
* ISO-8859-1 |
|||
*/ |
|||
public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); |
|||
/** |
|||
* UTF-8 |
|||
*/ |
|||
public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); |
|||
/** |
|||
* GBK |
|||
*/ |
|||
public static final Charset CHARSET_GBK = Charset.forName(GBK); |
|||
|
|||
/** |
|||
* 转换为Charset对象 |
|||
* |
|||
* @param charset 字符集,为空则返回默认字符集 |
|||
* @return Charset |
|||
*/ |
|||
public static Charset charset(String charset) { |
|||
return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); |
|||
} |
|||
|
|||
/** |
|||
* 转换字符串的字符集编码 |
|||
* |
|||
* @param source 字符串 |
|||
* @param srcCharset 源字符集,默认ISO-8859-1 |
|||
* @param destCharset 目标字符集,默认UTF-8 |
|||
* @return 转换后的字符集 |
|||
*/ |
|||
public static String convert(String source, String srcCharset, String destCharset) { |
|||
return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); |
|||
} |
|||
|
|||
/** |
|||
* 转换字符串的字符集编码 |
|||
* |
|||
* @param source 字符串 |
|||
* @param srcCharset 源字符集,默认ISO-8859-1 |
|||
* @param destCharset 目标字符集,默认UTF-8 |
|||
* @return 转换后的字符集 |
|||
*/ |
|||
public static String convert(String source, Charset srcCharset, Charset destCharset) { |
|||
if (null == srcCharset) { |
|||
srcCharset = StandardCharsets.ISO_8859_1; |
|||
} |
|||
|
|||
if (null == destCharset) { |
|||
destCharset = StandardCharsets.UTF_8; |
|||
} |
|||
|
|||
if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) { |
|||
return source; |
|||
} |
|||
return new String(source.getBytes(srcCharset), destCharset); |
|||
} |
|||
|
|||
/** |
|||
* @return 系统字符集编码 |
|||
*/ |
|||
public static String systemCharset() { |
|||
return Charset.defaultCharset().name(); |
|||
} |
|||
} |
@ -0,0 +1,849 @@ |
|||
package com.yxt.ss.gateway.api.authutils; |
|||
|
|||
import java.math.BigDecimal; |
|||
import java.math.BigInteger; |
|||
import java.nio.ByteBuffer; |
|||
import java.nio.charset.Charset; |
|||
import java.text.NumberFormat; |
|||
import java.util.Set; |
|||
|
|||
/** |
|||
* @author dimengzhe |
|||
* @description |
|||
*/ |
|||
|
|||
public class Convert { |
|||
|
|||
/** |
|||
* 转换为字符串<br> |
|||
* 如果给定的值为null,或者转换失败,返回默认值<br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @param defaultValue 转换错误时的默认值 |
|||
* @return 结果 |
|||
*/ |
|||
public static String toStr(Object value, String defaultValue) { |
|||
if (null == value) { |
|||
return defaultValue; |
|||
} |
|||
if (value instanceof String) { |
|||
return (String) value; |
|||
} |
|||
return value.toString(); |
|||
} |
|||
|
|||
/** |
|||
* 转换为字符串<br> |
|||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static String toStr(Object value) { |
|||
return toStr(value, null); |
|||
} |
|||
|
|||
/** |
|||
* 转换为字符<br> |
|||
* 如果给定的值为null,或者转换失败,返回默认值<br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @param defaultValue 转换错误时的默认值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Character toChar(Object value, Character defaultValue) { |
|||
if (null == value) { |
|||
return defaultValue; |
|||
} |
|||
if (value instanceof Character) { |
|||
return (Character) value; |
|||
} |
|||
|
|||
final String valueStr = toStr(value, null); |
|||
return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); |
|||
} |
|||
|
|||
/** |
|||
* 转换为字符<br> |
|||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Character toChar(Object value) { |
|||
return toChar(value, null); |
|||
} |
|||
|
|||
/** |
|||
* 转换为byte<br> |
|||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @param defaultValue 转换错误时的默认值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Byte toByte(Object value, Byte defaultValue) { |
|||
if (value == null) { |
|||
return defaultValue; |
|||
} |
|||
if (value instanceof Byte) { |
|||
return (Byte) value; |
|||
} |
|||
if (value instanceof Number) { |
|||
return ((Number) value).byteValue(); |
|||
} |
|||
final String valueStr = toStr(value, null); |
|||
if (StringUtils.isEmpty(valueStr)) { |
|||
return defaultValue; |
|||
} |
|||
try { |
|||
return Byte.parseByte(valueStr); |
|||
} catch (Exception e) { |
|||
return defaultValue; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 转换为byte<br> |
|||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Byte toByte(Object value) { |
|||
return toByte(value, null); |
|||
} |
|||
|
|||
/** |
|||
* 转换为Short<br> |
|||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @param defaultValue 转换错误时的默认值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Short toShort(Object value, Short defaultValue) { |
|||
if (value == null) { |
|||
return defaultValue; |
|||
} |
|||
if (value instanceof Short) { |
|||
return (Short) value; |
|||
} |
|||
if (value instanceof Number) { |
|||
return ((Number) value).shortValue(); |
|||
} |
|||
final String valueStr = toStr(value, null); |
|||
if (StringUtils.isEmpty(valueStr)) { |
|||
return defaultValue; |
|||
} |
|||
try { |
|||
return Short.parseShort(valueStr.trim()); |
|||
} catch (Exception e) { |
|||
return defaultValue; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 转换为Short<br> |
|||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Short toShort(Object value) { |
|||
return toShort(value, null); |
|||
} |
|||
|
|||
/** |
|||
* 转换为Number<br> |
|||
* 如果给定的值为空,或者转换失败,返回默认值<br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @param defaultValue 转换错误时的默认值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Number toNumber(Object value, Number defaultValue) { |
|||
if (value == null) { |
|||
return defaultValue; |
|||
} |
|||
if (value instanceof Number) { |
|||
return (Number) value; |
|||
} |
|||
final String valueStr = toStr(value, null); |
|||
if (StringUtils.isEmpty(valueStr)) { |
|||
return defaultValue; |
|||
} |
|||
try { |
|||
return NumberFormat.getInstance().parse(valueStr); |
|||
} catch (Exception e) { |
|||
return defaultValue; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 转换为Number<br> |
|||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Number toNumber(Object value) { |
|||
return toNumber(value, null); |
|||
} |
|||
|
|||
/** |
|||
* 转换为int<br> |
|||
* 如果给定的值为空,或者转换失败,返回默认值<br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @param defaultValue 转换错误时的默认值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Integer toInt(Object value, Integer defaultValue) { |
|||
if (value == null) { |
|||
return defaultValue; |
|||
} |
|||
if (value instanceof Integer) { |
|||
return (Integer) value; |
|||
} |
|||
if (value instanceof Number) { |
|||
return ((Number) value).intValue(); |
|||
} |
|||
final String valueStr = toStr(value, null); |
|||
if (StringUtils.isEmpty(valueStr)) { |
|||
return defaultValue; |
|||
} |
|||
try { |
|||
return Integer.parseInt(valueStr.trim()); |
|||
} catch (Exception e) { |
|||
return defaultValue; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 转换为int<br> |
|||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Integer toInt(Object value) { |
|||
return toInt(value, null); |
|||
} |
|||
|
|||
/** |
|||
* 转换为Integer数组<br> |
|||
* |
|||
* @param str 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Integer[] toIntArray(String str) { |
|||
return toIntArray(",", str); |
|||
} |
|||
|
|||
/** |
|||
* 转换为Long数组<br> |
|||
* |
|||
* @param str 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Long[] toLongArray(String str) { |
|||
return toLongArray(",", str); |
|||
} |
|||
|
|||
/** |
|||
* 转换为Integer数组<br> |
|||
* |
|||
* @param split 分隔符 |
|||
* @param split 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Integer[] toIntArray(String split, String str) { |
|||
if (StringUtils.isEmpty(str)) { |
|||
return new Integer[]{}; |
|||
} |
|||
String[] arr = str.split(split); |
|||
final Integer[] ints = new Integer[arr.length]; |
|||
for (int i = 0; i < arr.length; i++) { |
|||
final Integer v = toInt(arr[i], 0); |
|||
ints[i] = v; |
|||
} |
|||
return ints; |
|||
} |
|||
|
|||
/** |
|||
* 转换为Long数组<br> |
|||
* |
|||
* @param split 分隔符 |
|||
* @param str 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Long[] toLongArray(String split, String str) { |
|||
if (StringUtils.isEmpty(str)) { |
|||
return new Long[]{}; |
|||
} |
|||
String[] arr = str.split(split); |
|||
final Long[] longs = new Long[arr.length]; |
|||
for (int i = 0; i < arr.length; i++) { |
|||
final Long v = toLong(arr[i], null); |
|||
longs[i] = v; |
|||
} |
|||
return longs; |
|||
} |
|||
|
|||
/** |
|||
* 转换为String数组<br> |
|||
* |
|||
* @param str 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static String[] toStrArray(String str) { |
|||
return toStrArray(",", str); |
|||
} |
|||
|
|||
/** |
|||
* 转换为String数组<br> |
|||
* |
|||
* @param split 分隔符 |
|||
* @param split 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static String[] toStrArray(String split, String str) { |
|||
return str.split(split); |
|||
} |
|||
|
|||
/** |
|||
* 转换为long<br> |
|||
* 如果给定的值为空,或者转换失败,返回默认值<br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @param defaultValue 转换错误时的默认值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Long toLong(Object value, Long defaultValue) { |
|||
if (value == null) { |
|||
return defaultValue; |
|||
} |
|||
if (value instanceof Long) { |
|||
return (Long) value; |
|||
} |
|||
if (value instanceof Number) { |
|||
return ((Number) value).longValue(); |
|||
} |
|||
final String valueStr = toStr(value, null); |
|||
if (StringUtils.isEmpty(valueStr)) { |
|||
return defaultValue; |
|||
} |
|||
try { |
|||
// 支持科学计数法
|
|||
return new BigDecimal(valueStr.trim()).longValue(); |
|||
} catch (Exception e) { |
|||
return defaultValue; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 转换为long<br> |
|||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Long toLong(Object value) { |
|||
return toLong(value, null); |
|||
} |
|||
|
|||
/** |
|||
* 转换为double<br> |
|||
* 如果给定的值为空,或者转换失败,返回默认值<br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @param defaultValue 转换错误时的默认值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Double toDouble(Object value, Double defaultValue) { |
|||
if (value == null) { |
|||
return defaultValue; |
|||
} |
|||
if (value instanceof Double) { |
|||
return (Double) value; |
|||
} |
|||
if (value instanceof Number) { |
|||
return ((Number) value).doubleValue(); |
|||
} |
|||
final String valueStr = toStr(value, null); |
|||
if (StringUtils.isEmpty(valueStr)) { |
|||
return defaultValue; |
|||
} |
|||
try { |
|||
// 支持科学计数法
|
|||
return new BigDecimal(valueStr.trim()).doubleValue(); |
|||
} catch (Exception e) { |
|||
return defaultValue; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 转换为double<br> |
|||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Double toDouble(Object value) { |
|||
return toDouble(value, null); |
|||
} |
|||
|
|||
/** |
|||
* 转换为Float<br> |
|||
* 如果给定的值为空,或者转换失败,返回默认值<br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @param defaultValue 转换错误时的默认值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Float toFloat(Object value, Float defaultValue) { |
|||
if (value == null) { |
|||
return defaultValue; |
|||
} |
|||
if (value instanceof Float) { |
|||
return (Float) value; |
|||
} |
|||
if (value instanceof Number) { |
|||
return ((Number) value).floatValue(); |
|||
} |
|||
final String valueStr = toStr(value, null); |
|||
if (StringUtils.isEmpty(valueStr)) { |
|||
return defaultValue; |
|||
} |
|||
try { |
|||
return Float.parseFloat(valueStr.trim()); |
|||
} catch (Exception e) { |
|||
return defaultValue; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 转换为Float<br> |
|||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Float toFloat(Object value) { |
|||
return toFloat(value, null); |
|||
} |
|||
|
|||
/** |
|||
* 转换为boolean<br> |
|||
* String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @param defaultValue 转换错误时的默认值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Boolean toBool(Object value, Boolean defaultValue) { |
|||
if (value == null) { |
|||
return defaultValue; |
|||
} |
|||
if (value instanceof Boolean) { |
|||
return (Boolean) value; |
|||
} |
|||
String valueStr = toStr(value, null); |
|||
if (StringUtils.isEmpty(valueStr)) { |
|||
return defaultValue; |
|||
} |
|||
valueStr = valueStr.trim().toLowerCase(); |
|||
switch (valueStr) { |
|||
case "true": |
|||
return true; |
|||
case "false": |
|||
return false; |
|||
case "yes": |
|||
return true; |
|||
case "ok": |
|||
return true; |
|||
case "no": |
|||
return false; |
|||
case "1": |
|||
return true; |
|||
case "0": |
|||
return false; |
|||
default: |
|||
return defaultValue; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 转换为boolean<br> |
|||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static Boolean toBool(Object value) { |
|||
return toBool(value, null); |
|||
} |
|||
|
|||
/** |
|||
* 转换为Enum对象<br> |
|||
* 如果给定的值为空,或者转换失败,返回默认值<br> |
|||
* |
|||
* @param clazz Enum的Class |
|||
* @param value 值 |
|||
* @param defaultValue 默认值 |
|||
* @return Enum |
|||
*/ |
|||
public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue) { |
|||
if (value == null) { |
|||
return defaultValue; |
|||
} |
|||
if (clazz.isAssignableFrom(value.getClass())) { |
|||
@SuppressWarnings("unchecked") |
|||
E myE = (E) value; |
|||
return myE; |
|||
} |
|||
final String valueStr = toStr(value, null); |
|||
if (StringUtils.isEmpty(valueStr)) { |
|||
return defaultValue; |
|||
} |
|||
try { |
|||
return Enum.valueOf(clazz, valueStr); |
|||
} catch (Exception e) { |
|||
return defaultValue; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 转换为Enum对象<br> |
|||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> |
|||
* |
|||
* @param clazz Enum的Class |
|||
* @param value 值 |
|||
* @return Enum |
|||
*/ |
|||
public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value) { |
|||
return toEnum(clazz, value, null); |
|||
} |
|||
|
|||
/** |
|||
* 转换为BigInteger<br> |
|||
* 如果给定的值为空,或者转换失败,返回默认值<br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @param defaultValue 转换错误时的默认值 |
|||
* @return 结果 |
|||
*/ |
|||
public static BigInteger toBigInteger(Object value, BigInteger defaultValue) { |
|||
if (value == null) { |
|||
return defaultValue; |
|||
} |
|||
if (value instanceof BigInteger) { |
|||
return (BigInteger) value; |
|||
} |
|||
if (value instanceof Long) { |
|||
return BigInteger.valueOf((Long) value); |
|||
} |
|||
final String valueStr = toStr(value, null); |
|||
if (StringUtils.isEmpty(valueStr)) { |
|||
return defaultValue; |
|||
} |
|||
try { |
|||
return new BigInteger(valueStr); |
|||
} catch (Exception e) { |
|||
return defaultValue; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 转换为BigInteger<br> |
|||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static BigInteger toBigInteger(Object value) { |
|||
return toBigInteger(value, null); |
|||
} |
|||
|
|||
/** |
|||
* 转换为BigDecimal<br> |
|||
* 如果给定的值为空,或者转换失败,返回默认值<br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @param defaultValue 转换错误时的默认值 |
|||
* @return 结果 |
|||
*/ |
|||
public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) { |
|||
if (value == null) { |
|||
return defaultValue; |
|||
} |
|||
if (value instanceof BigDecimal) { |
|||
return (BigDecimal) value; |
|||
} |
|||
if (value instanceof Long) { |
|||
return new BigDecimal((Long) value); |
|||
} |
|||
if (value instanceof Double) { |
|||
return new BigDecimal((Double) value); |
|||
} |
|||
if (value instanceof Integer) { |
|||
return new BigDecimal((Integer) value); |
|||
} |
|||
final String valueStr = toStr(value, null); |
|||
if (StringUtils.isEmpty(valueStr)) { |
|||
return defaultValue; |
|||
} |
|||
try { |
|||
return new BigDecimal(valueStr); |
|||
} catch (Exception e) { |
|||
return defaultValue; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 转换为BigDecimal<br> |
|||
* 如果给定的值为空,或者转换失败,返回默认值<br> |
|||
* 转换失败不会报错 |
|||
* |
|||
* @param value 被转换的值 |
|||
* @return 结果 |
|||
*/ |
|||
public static BigDecimal toBigDecimal(Object value) { |
|||
return toBigDecimal(value, null); |
|||
} |
|||
|
|||
/** |
|||
* 将对象转为字符串<br> |
|||
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 |
|||
* |
|||
* @param obj 对象 |
|||
* @return 字符串 |
|||
*/ |
|||
public static String utf8Str(Object obj) { |
|||
return str(obj, CharsetKit.CHARSET_UTF_8); |
|||
} |
|||
|
|||
/** |
|||
* 将对象转为字符串<br> |
|||
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 |
|||
* |
|||
* @param obj 对象 |
|||
* @param charsetName 字符集 |
|||
* @return 字符串 |
|||
*/ |
|||
public static String str(Object obj, String charsetName) { |
|||
return str(obj, Charset.forName(charsetName)); |
|||
} |
|||
|
|||
/** |
|||
* 将对象转为字符串<br> |
|||
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 |
|||
* |
|||
* @param obj 对象 |
|||
* @param charset 字符集 |
|||
* @return 字符串 |
|||
*/ |
|||
public static String str(Object obj, Charset charset) { |
|||
if (null == obj) { |
|||
return null; |
|||
} |
|||
|
|||
if (obj instanceof String) { |
|||
return (String) obj; |
|||
} else if (obj instanceof byte[] || obj instanceof Byte[]) { |
|||
return str((Byte[]) obj, charset); |
|||
} else if (obj instanceof ByteBuffer) { |
|||
return str((ByteBuffer) obj, charset); |
|||
} |
|||
return obj.toString(); |
|||
} |
|||
|
|||
/** |
|||
* 将byte数组转为字符串 |
|||
* |
|||
* @param bytes byte数组 |
|||
* @param charset 字符集 |
|||
* @return 字符串 |
|||
*/ |
|||
public static String str(byte[] bytes, String charset) { |
|||
return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(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); |
|||
} |
|||
|
|||
/** |
|||
* 将编码的byteBuffer数据转换为字符串 |
|||
* |
|||
* @param data 数据 |
|||
* @param charset 字符集,如果为空使用当前系统字符集 |
|||
* @return 字符串 |
|||
*/ |
|||
public static String str(ByteBuffer data, String charset) { |
|||
if (data == null) { |
|||
return null; |
|||
} |
|||
|
|||
return str(data, Charset.forName(charset)); |
|||
} |
|||
|
|||
/** |
|||
* 将编码的byteBuffer数据转换为字符串 |
|||
* |
|||
* @param data 数据 |
|||
* @param charset 字符集,如果为空使用当前系统字符集 |
|||
* @return 字符串 |
|||
*/ |
|||
public static String str(ByteBuffer data, Charset charset) { |
|||
if (null == charset) { |
|||
charset = Charset.defaultCharset(); |
|||
} |
|||
return charset.decode(data).toString(); |
|||
} |
|||
|
|||
// ----------------------------------------------------------------------- 全角半角转换
|
|||
|
|||
/** |
|||
* 半角转全角 |
|||
* |
|||
* @param input String. |
|||
* @return 全角字符串. |
|||
*/ |
|||
public static String toSBC(String input) { |
|||
return toSBC(input, null); |
|||
} |
|||
|
|||
/** |
|||
* 半角转全角 |
|||
* |
|||
* @param input String |
|||
* @param notConvertSet 不替换的字符集合 |
|||
* @return 全角字符串. |
|||
*/ |
|||
public static String toSBC(String input, Set<Character> notConvertSet) { |
|||
char c[] = input.toCharArray(); |
|||
for (int i = 0; i < c.length; i++) { |
|||
if (null != notConvertSet && notConvertSet.contains(c[i])) { |
|||
// 跳过不替换的字符
|
|||
continue; |
|||
} |
|||
|
|||
if (c[i] == ' ') { |
|||
c[i] = '\u3000'; |
|||
} else if (c[i] < '\177') { |
|||
c[i] = (char) (c[i] + 65248); |
|||
|
|||
} |
|||
} |
|||
return new String(c); |
|||
} |
|||
|
|||
/** |
|||
* 全角转半角 |
|||
* |
|||
* @param input String. |
|||
* @return 半角字符串 |
|||
*/ |
|||
public static String toDBC(String input) { |
|||
return toDBC(input, null); |
|||
} |
|||
|
|||
/** |
|||
* 替换全角为半角 |
|||
* |
|||
* @param text 文本 |
|||
* @param notConvertSet 不替换的字符集合 |
|||
* @return 替换后的字符 |
|||
*/ |
|||
public static String toDBC(String text, Set<Character> notConvertSet) { |
|||
char c[] = text.toCharArray(); |
|||
for (int i = 0; i < c.length; i++) { |
|||
if (null != notConvertSet && notConvertSet.contains(c[i])) { |
|||
// 跳过不替换的字符
|
|||
continue; |
|||
} |
|||
|
|||
if (c[i] == '\u3000') { |
|||
c[i] = ' '; |
|||
} else if (c[i] > '\uFF00' && c[i] < '\uFF5F') { |
|||
c[i] = (char) (c[i] - 65248); |
|||
} |
|||
} |
|||
String returnString = new String(c); |
|||
|
|||
return returnString; |
|||
} |
|||
|
|||
/** |
|||
* 数字金额大写转换 先写个完整的然后将如零拾替换成零 |
|||
* |
|||
* @param n 数字 |
|||
* @return 中文大写数字 |
|||
*/ |
|||
public static String digitUppercase(double n) { |
|||
String[] fraction = {"角", "分"}; |
|||
String[] digit = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"}; |
|||
String[][] unit = {{"元", "万", "亿"}, {"", "拾", "佰", "仟"}}; |
|||
|
|||
String head = n < 0 ? "负" : ""; |
|||
n = Math.abs(n); |
|||
|
|||
String s = ""; |
|||
for (int i = 0; i < fraction.length; i++) { |
|||
s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); |
|||
} |
|||
if (s.length() < 1) { |
|||
s = "整"; |
|||
} |
|||
int integerPart = (int) Math.floor(n); |
|||
|
|||
for (int i = 0; i < unit[0].length && integerPart > 0; i++) { |
|||
String p = ""; |
|||
for (int j = 0; j < unit[1].length && n > 0; j++) { |
|||
p = digit[integerPart % 10] + unit[1][j] + p; |
|||
integerPart = integerPart / 10; |
|||
} |
|||
s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; |
|||
} |
|||
return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); |
|||
} |
|||
} |
@ -0,0 +1,41 @@ |
|||
package com.yxt.ss.gateway.api.authutils; |
|||
|
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.cloud.context.config.annotation.RefreshScope; |
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author dimengzhe |
|||
* @description 放行白名单配置 |
|||
*/ |
|||
@Configuration |
|||
@RefreshScope |
|||
@ConfigurationProperties(prefix = "ignore") |
|||
public class IgnoreWhiteProperties { |
|||
|
|||
/** |
|||
* 放行白名单配置,网关不校验此处的白名单 |
|||
*/ |
|||
private List<String> whites = new ArrayList<>(); |
|||
|
|||
public List<String> getWhites() { |
|||
return whites; |
|||
} |
|||
|
|||
public void setWhites(List<String> whites) { |
|||
this.whites = whites; |
|||
} |
|||
|
|||
private List<String> whitesTwo = new ArrayList<>(); |
|||
|
|||
public List<String> getWhitesTwo() { |
|||
return whitesTwo; |
|||
} |
|||
|
|||
public void setWhitesTwo(List<String> whitesTwo) { |
|||
this.whitesTwo = whitesTwo; |
|||
} |
|||
} |
@ -0,0 +1,75 @@ |
|||
package com.yxt.ss.gateway.api.authutils; |
|||
|
|||
/** |
|||
* @author dimengzhe |
|||
* @description |
|||
*/ |
|||
|
|||
public class StrFormatter { |
|||
|
|||
public static final String EMPTY_JSON = "{}"; |
|||
public static final char C_BACKSLASH = '\\'; |
|||
public static final char C_DELIM_START = '{'; |
|||
public static final char C_DELIM_END = '}'; |
|||
|
|||
/** |
|||
* 格式化字符串<br> |
|||
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br> |
|||
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br> |
|||
* 例:<br> |
|||
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br> |
|||
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br> |
|||
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br> |
|||
* |
|||
* @param strPattern 字符串模板 |
|||
* @param argArray 参数列表 |
|||
* @return 结果 |
|||
*/ |
|||
public static String format(final String strPattern, final Object... argArray) { |
|||
if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) { |
|||
return strPattern; |
|||
} |
|||
final int strPatternLength = strPattern.length(); |
|||
|
|||
// 初始化定义好的长度以获得更好的性能
|
|||
StringBuilder sbuf = new StringBuilder(strPatternLength + 50); |
|||
|
|||
int handledPosition = 0; |
|||
int delimIndex;// 占位符所在位置
|
|||
for (int argIndex = 0; argIndex < argArray.length; argIndex++) { |
|||
delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); |
|||
if (delimIndex == -1) { |
|||
if (handledPosition == 0) { |
|||
return strPattern; |
|||
} else { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果
|
|||
sbuf.append(strPattern, handledPosition, strPatternLength); |
|||
return sbuf.toString(); |
|||
} |
|||
} else { |
|||
if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) { |
|||
if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) { |
|||
// 转义符之前还有一个转义符,占位符依旧有效
|
|||
sbuf.append(strPattern, handledPosition, delimIndex - 1); |
|||
sbuf.append(Convert.utf8Str(argArray[argIndex])); |
|||
handledPosition = delimIndex + 2; |
|||
} else { |
|||
// 占位符被转义
|
|||
argIndex--; |
|||
sbuf.append(strPattern, handledPosition, delimIndex - 1); |
|||
sbuf.append(C_DELIM_START); |
|||
handledPosition = delimIndex + 1; |
|||
} |
|||
} else { |
|||
// 正常占位符
|
|||
sbuf.append(strPattern, handledPosition, delimIndex); |
|||
sbuf.append(Convert.utf8Str(argArray[argIndex])); |
|||
handledPosition = delimIndex + 2; |
|||
} |
|||
} |
|||
} |
|||
// 加入最后一个占位符后所有的字符
|
|||
sbuf.append(strPattern, handledPosition, strPattern.length()); |
|||
|
|||
return sbuf.toString(); |
|||
} |
|||
} |
@ -0,0 +1,525 @@ |
|||
package com.yxt.ss.gateway.api.authutils; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* @author dimengzhe |
|||
* @description |
|||
*/ |
|||
|
|||
public class StringUtils extends org.apache.commons.lang3.StringUtils { |
|||
|
|||
/** |
|||
* 空字符串 |
|||
*/ |
|||
private static final String NULLSTR = ""; |
|||
|
|||
/** |
|||
* 下划线 |
|||
*/ |
|||
private static final char SEPARATOR = '_'; |
|||
|
|||
/** |
|||
* 星号 |
|||
*/ |
|||
private static final String START = "*"; |
|||
|
|||
/** |
|||
* 获取参数不为空值 |
|||
* |
|||
* @param value defaultValue 要判断的value |
|||
* @return value 返回值 |
|||
*/ |
|||
public static <T> T nvl(T value, T defaultValue) { |
|||
return value != null ? value : defaultValue; |
|||
} |
|||
|
|||
/** |
|||
* * 判断一个Collection是否为空, 包含List,Set,Queue |
|||
* |
|||
* @param coll 要判断的Collection |
|||
* @return true:为空 false:非空 |
|||
*/ |
|||
public static boolean isEmpty(Collection<?> coll) { |
|||
return isNull(coll) || coll.isEmpty(); |
|||
} |
|||
|
|||
/** |
|||
* * 判断一个Collection是否非空,包含List,Set,Queue |
|||
* |
|||
* @param coll 要判断的Collection |
|||
* @return true:非空 false:空 |
|||
*/ |
|||
public static boolean isNotEmpty(Collection<?> coll) { |
|||
return !isEmpty(coll); |
|||
} |
|||
|
|||
/** |
|||
* * 判断一个对象数组是否为空 |
|||
* |
|||
* @param objects 要判断的对象数组 |
|||
* * @return true:为空 false:非空 |
|||
*/ |
|||
public static boolean isEmpty(Object[] objects) { |
|||
return isNull(objects) || (objects.length == 0); |
|||
} |
|||
|
|||
/** |
|||
* * 判断一个对象数组是否非空 |
|||
* |
|||
* @param objects 要判断的对象数组 |
|||
* @return true:非空 false:空 |
|||
*/ |
|||
public static boolean isNotEmpty(Object[] objects) { |
|||
return !isEmpty(objects); |
|||
} |
|||
|
|||
/** |
|||
* * 判断一个Map是否为空 |
|||
* |
|||
* @param map 要判断的Map |
|||
* @return true:为空 false:非空 |
|||
*/ |
|||
public static boolean isEmpty(Map<?, ?> map) { |
|||
return isNull(map) || map.isEmpty(); |
|||
} |
|||
|
|||
/** |
|||
* * 判断一个Map是否为空 |
|||
* |
|||
* @param map 要判断的Map |
|||
* @return true:非空 false:空 |
|||
*/ |
|||
public static boolean isNotEmpty(Map<?, ?> map) { |
|||
return !isEmpty(map); |
|||
} |
|||
|
|||
/** |
|||
* * 判断一个字符串是否为空串 |
|||
* |
|||
* @param str String |
|||
* @return true:为空 false:非空 |
|||
*/ |
|||
public static boolean isEmpty(String str) { |
|||
return isNull(str) || NULLSTR.equals(str.trim()); |
|||
} |
|||
|
|||
/** |
|||
* * 判断一个字符串是否为非空串 |
|||
* |
|||
* @param str String |
|||
* @return true:非空串 false:空串 |
|||
*/ |
|||
public static boolean isNotEmpty(String str) { |
|||
return !isEmpty(str); |
|||
} |
|||
|
|||
/** |
|||
* * 判断一个对象是否为空 |
|||
* |
|||
* @param object Object |
|||
* @return true:为空 false:非空 |
|||
*/ |
|||
public static boolean isNull(Object object) { |
|||
return object == null; |
|||
} |
|||
|
|||
/** |
|||
* * 判断一个对象是否非空 |
|||
* |
|||
* @param object Object |
|||
* @return true:非空 false:空 |
|||
*/ |
|||
public static boolean isNotNull(Object object) { |
|||
return !isNull(object); |
|||
} |
|||
|
|||
/** |
|||
* * 判断一个对象是否是数组类型(Java基本型别的数组) |
|||
* |
|||
* @param object 对象 |
|||
* @return true:是数组 false:不是数组 |
|||
*/ |
|||
public static boolean isArray(Object object) { |
|||
return isNotNull(object) && object.getClass().isArray(); |
|||
} |
|||
|
|||
/** |
|||
* 去空格 |
|||
*/ |
|||
public static String trim(String str) { |
|||
return (str == null ? "" : str.trim()); |
|||
} |
|||
|
|||
/** |
|||
* 截取字符串 |
|||
* |
|||
* @param str 字符串 |
|||
* @param start 开始 |
|||
* @return 结果 |
|||
*/ |
|||
public static String substring(final String str, int start) { |
|||
if (str == null) { |
|||
return NULLSTR; |
|||
} |
|||
|
|||
if (start < 0) { |
|||
start = str.length() + start; |
|||
} |
|||
|
|||
if (start < 0) { |
|||
start = 0; |
|||
} |
|||
if (start > str.length()) { |
|||
return NULLSTR; |
|||
} |
|||
|
|||
return str.substring(start); |
|||
} |
|||
|
|||
/** |
|||
* 截取字符串 |
|||
* |
|||
* @param str 字符串 |
|||
* @param start 开始 |
|||
* @param end 结束 |
|||
* @return 结果 |
|||
*/ |
|||
public static String substring(final String str, int start, int end) { |
|||
if (str == null) { |
|||
return NULLSTR; |
|||
} |
|||
|
|||
if (end < 0) { |
|||
end = str.length() + end; |
|||
} |
|||
if (start < 0) { |
|||
start = str.length() + start; |
|||
} |
|||
|
|||
if (end > str.length()) { |
|||
end = str.length(); |
|||
} |
|||
|
|||
if (start > end) { |
|||
return NULLSTR; |
|||
} |
|||
|
|||
if (start < 0) { |
|||
start = 0; |
|||
} |
|||
if (end < 0) { |
|||
end = 0; |
|||
} |
|||
|
|||
return str.substring(start, end); |
|||
} |
|||
|
|||
/** |
|||
* 格式化文本, {} 表示占位符<br> |
|||
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br> |
|||
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br> |
|||
* 例:<br> |
|||
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br> |
|||
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br> |
|||
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br> |
|||
* |
|||
* @param template 文本模板,被替换的部分用 {} 表示 |
|||
* @param params 参数值 |
|||
* @return 格式化后的文本 |
|||
*/ |
|||
public static String format(String template, Object... params) { |
|||
if (isEmpty(params) || isEmpty(template)) { |
|||
return template; |
|||
} |
|||
return StrFormatter.format(template, params); |
|||
} |
|||
|
|||
/** |
|||
* 下划线转驼峰命名 |
|||
*/ |
|||
public static String toUnderScoreCase(String str) { |
|||
if (str == null) { |
|||
return null; |
|||
} |
|||
StringBuilder sb = new StringBuilder(); |
|||
// 前置字符是否大写
|
|||
boolean preCharIsUpperCase = true; |
|||
// 当前字符是否大写
|
|||
boolean curreCharIsUpperCase = true; |
|||
// 下一字符是否大写
|
|||
boolean nexteCharIsUpperCase = true; |
|||
for (int i = 0; i < str.length(); i++) { |
|||
char c = str.charAt(i); |
|||
if (i > 0) { |
|||
preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); |
|||
} else { |
|||
preCharIsUpperCase = false; |
|||
} |
|||
|
|||
curreCharIsUpperCase = Character.isUpperCase(c); |
|||
|
|||
if (i < (str.length() - 1)) { |
|||
nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); |
|||
} |
|||
|
|||
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) { |
|||
sb.append(SEPARATOR); |
|||
} else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) { |
|||
sb.append(SEPARATOR); |
|||
} |
|||
sb.append(Character.toLowerCase(c)); |
|||
} |
|||
|
|||
return sb.toString(); |
|||
} |
|||
|
|||
/** |
|||
* 是否包含字符串 |
|||
* |
|||
* @param str 验证字符串 |
|||
* @param strs 字符串组 |
|||
* @return 包含返回true |
|||
*/ |
|||
public static boolean inStringIgnoreCase(String str, String... strs) { |
|||
if (str != null && strs != null) { |
|||
for (String s : strs) { |
|||
if (str.equalsIgnoreCase(trim(s))) { |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld |
|||
* |
|||
* @param name 转换前的下划线大写方式命名的字符串 |
|||
* @return 转换后的驼峰式命名的字符串 |
|||
*/ |
|||
public static String convertToCamelCase(String name) { |
|||
StringBuilder result = new StringBuilder(); |
|||
// 快速检查
|
|||
if (name == null || name.isEmpty()) { |
|||
// 没必要转换
|
|||
return ""; |
|||
} else if (!name.contains("_")) { |
|||
// 不含下划线,仅将首字母大写
|
|||
return name.substring(0, 1).toUpperCase() + name.substring(1); |
|||
} |
|||
// 用下划线将原始字符串分割
|
|||
String[] camels = name.split("_"); |
|||
for (String camel : camels) { |
|||
// 跳过原始字符串中开头、结尾的下换线或双重下划线
|
|||
if (camel.isEmpty()) { |
|||
continue; |
|||
} |
|||
// 首字母大写
|
|||
result.append(camel.substring(0, 1).toUpperCase()); |
|||
result.append(camel.substring(1).toLowerCase()); |
|||
} |
|||
return result.toString(); |
|||
} |
|||
|
|||
/** |
|||
* 驼峰式命名法 例如:user_name->userName |
|||
*/ |
|||
public static String toCamelCase(String s) { |
|||
if (s == null) { |
|||
return null; |
|||
} |
|||
s = s.toLowerCase(); |
|||
StringBuilder sb = new StringBuilder(s.length()); |
|||
boolean upperCase = false; |
|||
for (int i = 0; i < s.length(); i++) { |
|||
char c = s.charAt(i); |
|||
|
|||
if (c == SEPARATOR) { |
|||
upperCase = true; |
|||
} else if (upperCase) { |
|||
sb.append(Character.toUpperCase(c)); |
|||
upperCase = false; |
|||
} else { |
|||
sb.append(c); |
|||
} |
|||
} |
|||
return sb.toString(); |
|||
} |
|||
|
|||
/** |
|||
* 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 |
|||
* |
|||
* @param str 指定字符串 |
|||
* @param strs 需要检查的字符串数组 |
|||
* @return 是否匹配 |
|||
*/ |
|||
public static boolean matchesTwo(String str, List<String> strs) { |
|||
if (isEmpty(str) || isEmpty(strs)) { |
|||
return false; |
|||
} |
|||
for (String testStr : strs) { |
|||
if (matchesTwo(str, testStr)) { |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
public static boolean matches(String str, List<String> strs) { |
|||
if (isEmpty(str) || isEmpty(strs)) { |
|||
return false; |
|||
} |
|||
for (String testStr : strs) { |
|||
if (matches(str, testStr)) { |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 查找指定字符串是否匹配指定字符串数组中的任意一个字符串 |
|||
* |
|||
* @param str 指定字符串 |
|||
* @param strs 需要检查的字符串数组 |
|||
* @return 是否匹配 |
|||
*/ |
|||
public static boolean matches(String str, String... strs) { |
|||
if (isEmpty(str) || isEmpty(strs)) { |
|||
return false; |
|||
} |
|||
for (String testStr : strs) { |
|||
if (matches(str, testStr)) { |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
public static boolean matches(String str, String pattern) { |
|||
if (isEmpty(pattern) || isEmpty(str)) { |
|||
return false; |
|||
} |
|||
|
|||
pattern = pattern.replaceAll("\\s*", ""); // 替换空格
|
|||
int beginOffset = 0; // pattern截取开始位置
|
|||
int formerStarOffset = -1; // 前星号的偏移位置
|
|||
int latterStarOffset = -1; // 后星号的偏移位置
|
|||
|
|||
String remainingURI = str; |
|||
String prefixPattern = ""; |
|||
String suffixPattern = ""; |
|||
|
|||
boolean result = false; |
|||
do { |
|||
formerStarOffset = indexOf(pattern, START, beginOffset); |
|||
prefixPattern = substring(pattern, beginOffset, formerStarOffset > -1 ? formerStarOffset : pattern.length()); |
|||
|
|||
// 匹配前缀Pattern
|
|||
result = remainingURI.equals(prefixPattern); |
|||
// 已经没有星号,直接返回
|
|||
if (formerStarOffset == -1) { |
|||
return result; |
|||
} |
|||
|
|||
// 匹配失败,直接返回
|
|||
if (!result) { |
|||
return false; |
|||
} |
|||
if (!isEmpty(prefixPattern)) { |
|||
remainingURI = substringAfter(str, prefixPattern); |
|||
} |
|||
|
|||
// 匹配后缀Pattern
|
|||
latterStarOffset = indexOf(pattern, START, formerStarOffset + 1); |
|||
suffixPattern = substring(pattern, formerStarOffset + 1, latterStarOffset > -1 ? latterStarOffset : pattern.length()); |
|||
|
|||
result = remainingURI.equals(suffixPattern); |
|||
// 匹配失败,直接返回
|
|||
if (!result) { |
|||
return false; |
|||
} |
|||
if (!isEmpty(suffixPattern)) { |
|||
remainingURI = substringAfter(str, suffixPattern); |
|||
} |
|||
|
|||
// 移动指针
|
|||
beginOffset = latterStarOffset + 1; |
|||
|
|||
} |
|||
while (!isEmpty(suffixPattern) && !isEmpty(remainingURI)); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/** |
|||
* 查找指定字符串是否匹配 |
|||
* |
|||
* @param str 指定字符串 |
|||
* @param pattern 需要检查的字符串 |
|||
* @return 是否匹配 |
|||
*/ |
|||
public static boolean matchesTwo(String str, String pattern) { |
|||
if (isEmpty(pattern) || isEmpty(str)) { |
|||
return false; |
|||
} |
|||
|
|||
pattern = pattern.replaceAll("\\s*", ""); // 替换空格
|
|||
int beginOffset = 0; // pattern截取开始位置
|
|||
int formerStarOffset = -1; // 前星号的偏移位置
|
|||
int latterStarOffset = -1; // 后星号的偏移位置
|
|||
|
|||
String remainingURI = str; |
|||
String prefixPattern = ""; |
|||
String suffixPattern = ""; |
|||
|
|||
boolean result = false; |
|||
do { |
|||
formerStarOffset = indexOf(pattern, START, beginOffset); |
|||
prefixPattern = substring(pattern, beginOffset, formerStarOffset > -1 ? formerStarOffset : pattern.length()); |
|||
|
|||
// 匹配前缀Pattern
|
|||
result = remainingURI.contains(prefixPattern); |
|||
// 已经没有星号,直接返回
|
|||
if (formerStarOffset == -1) { |
|||
return result; |
|||
} |
|||
|
|||
// 匹配失败,直接返回
|
|||
if (!result) { |
|||
return false; |
|||
} |
|||
if (!isEmpty(prefixPattern)) { |
|||
remainingURI = substringAfter(str, prefixPattern); |
|||
} |
|||
|
|||
// 匹配后缀Pattern
|
|||
latterStarOffset = indexOf(pattern, START, formerStarOffset + 1); |
|||
suffixPattern = substring(pattern, formerStarOffset + 1, latterStarOffset > -1 ? latterStarOffset : pattern.length()); |
|||
|
|||
result = remainingURI.contains(suffixPattern); |
|||
// 匹配失败,直接返回
|
|||
if (!result) { |
|||
return false; |
|||
} |
|||
if (!isEmpty(suffixPattern)) { |
|||
remainingURI = substringAfter(str, suffixPattern); |
|||
} |
|||
|
|||
// 移动指针
|
|||
beginOffset = latterStarOffset + 1; |
|||
|
|||
} |
|||
while (!isEmpty(suffixPattern) && !isEmpty(remainingURI)); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
@SuppressWarnings("unchecked") |
|||
public static <T> T cast(Object obj) { |
|||
return (T) obj; |
|||
} |
|||
} |
@ -0,0 +1,128 @@ |
|||
package com.yxt.ss.gateway.api.rest; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import com.yxt.ss.gateway.api.authutils.StringUtils; |
|||
import com.yxt.ss.gateway.api.service.ClientService; |
|||
import com.yxt.ss.gateway.api.utils.AppKeyConfig; |
|||
import com.yxt.ss.gateway.api.utils.ResultBean; |
|||
import com.yxt.ss.gateway.api.utils.SignatureQuery; |
|||
import okhttp3.*; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.web.bind.annotation.PostMapping; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
import java.io.IOException; |
|||
import java.io.UnsupportedEncodingException; |
|||
import java.security.NoSuchAlgorithmException; |
|||
import java.util.Map; |
|||
import java.util.TreeMap; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
/** |
|||
* @description: |
|||
* @author: dimengzhe |
|||
* @date: 2024/12/10 |
|||
**/ |
|||
@RestController |
|||
@RequestMapping("/ApiTestRest") |
|||
public class ApiTestRest { |
|||
|
|||
@Autowired |
|||
private ClientService clientService; |
|||
|
|||
|
|||
//appkey
|
|||
static final String APPKEY = "appKey4"; |
|||
static final String SECRET = "secret"; |
|||
|
|||
//开发端,生成签名并调用服务器端验证签名、appKey等值。
|
|||
@PostMapping("/getSign") |
|||
ResultBean getSign(SignatureQuery query) { |
|||
ResultBean<String> rb = ResultBean.fireFail(); |
|||
try { |
|||
Map<String, String> formData = query.getParameters(); |
|||
//使用treeMap排序
|
|||
Map<String, String> tree = new TreeMap<>(formData); |
|||
tree.put("_app", APPKEY); |
|||
tree.put("_t", String.valueOf(System.currentTimeMillis() / 1000)); |
|||
tree.put("_s", ""); |
|||
// 生成签名
|
|||
String sign = clientService.generateSignature(tree, SECRET); |
|||
//添加签名值map
|
|||
tree.put("_sign", sign); |
|||
//发起请求
|
|||
ResultBean resultBean = client(tree); |
|||
if (!resultBean.getSuccess()) { |
|||
return rb.setMsg(resultBean.getMsg()); |
|||
} |
|||
//通过验证继续调用接口
|
|||
|
|||
|
|||
return rb.success(); |
|||
} catch (UnsupportedEncodingException e) { |
|||
return rb.setMsg("Unsupported encoding: " + e.getMessage()); |
|||
} catch (NoSuchAlgorithmException e) { |
|||
return rb.setMsg("Algorithm not found: " + e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
//发起请求验证签名等
|
|||
public ResultBean client(Map<String, String> data) { |
|||
ResultBean rb = ResultBean.fireFail(); |
|||
OkHttpClient client = new OkHttpClient.Builder() |
|||
.connectTimeout(10, TimeUnit.SECONDS) |
|||
.writeTimeout(10, TimeUnit.SECONDS) |
|||
.readTimeout(30, TimeUnit.SECONDS) |
|||
.build(); |
|||
|
|||
try { |
|||
// 构建URL
|
|||
String endPoint = "http://127.0.0.1:9999"; |
|||
String path = "/signature/validate"; |
|||
|
|||
// 创建FormData
|
|||
FormBody.Builder formBuilder = new FormBody.Builder(); |
|||
for (Map.Entry<String, String> entry : data.entrySet()) { |
|||
formBuilder.add(entry.getKey(), entry.getValue()); |
|||
} |
|||
RequestBody formBody = formBuilder.build(); |
|||
|
|||
// 构建POST请求
|
|||
String url = endPoint + path; |
|||
System.out.println("Request URL: " + url); |
|||
System.out.println("Request Data: " + data); |
|||
|
|||
Request request = new Request.Builder() |
|||
.url(url) |
|||
.post(formBody) |
|||
.build(); |
|||
|
|||
// 发送请求
|
|||
try (Response response = client.newCall(request).execute()) { |
|||
String responseBody = response.body().string(); |
|||
// 使用 Jackson 解析 JSON 响应
|
|||
ObjectMapper objectMapper = new ObjectMapper(); |
|||
JsonNode jsonNode = objectMapper.readTree(responseBody); |
|||
String success = jsonNode.path("success").asText(); |
|||
String msg = jsonNode.path("msg").asText(); |
|||
if ("false".equals(success)) { |
|||
return rb.setMsg(msg); |
|||
} |
|||
if (response.isSuccessful()) { |
|||
System.out.println("Response: " + response.body().string()); |
|||
} else { |
|||
System.err.println("Request failed: " + response.message()); |
|||
} |
|||
|
|||
|
|||
} |
|||
} catch (IOException e) { |
|||
System.err.println("Network error: " + e.getMessage()); |
|||
} catch (Exception e) { |
|||
System.err.println("Unexpected error: " + e.getMessage()); |
|||
} |
|||
return rb.success(); |
|||
} |
|||
} |
@ -0,0 +1,29 @@ |
|||
package com.yxt.ss.gateway.api.utils; |
|||
|
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.web.server.WebFilter; |
|||
|
|||
/** |
|||
* @description: |
|||
* @author: dimengzhe |
|||
* @date: 2024/12/11 |
|||
**/ |
|||
@Configuration |
|||
public class WebFluxLoggingConfig { |
|||
|
|||
@Bean |
|||
public WebFilter loggingFilter() { |
|||
return (exchange, chain) -> { |
|||
// 打印请求信息
|
|||
System.out.println("Request: " + exchange.getRequest().getURI()); |
|||
|
|||
// 继续处理请求
|
|||
return chain.filter(exchange) |
|||
.doOnTerminate(() -> { |
|||
// 打印响应信息
|
|||
System.out.println("Response: " + exchange.getResponse().getStatusCode()); |
|||
}); |
|||
}; |
|||
} |
|||
} |
@ -1,4 +1,4 @@ |
|||
app: |
|||
appkey: |
|||
keys: |
|||
appKey1: secret1 |
|||
appKey2: secret2 |
|||
|
Loading…
Reference in new issue