1.pom.xml中加入gateway jar包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
2.创建权限过滤器 SecurityFilter
/**
* 鉴权过滤
*
**/
@Slf4j
@Component
public class SecurityFilter implements GlobalFilter, Ordered {
@Resource
private MerchantAppApi merchantAppApi;
/**
* 签名算法
*/
private final static String SIGN_RULE = "HmacSHA256";
/**
* 通过sdkKey做鉴权过滤
*
**/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取头信息
HttpHeaders headers = exchange.getRequest().getHeaders();
// 头信息 客户端签名
String sign = headers.getFirst("sign");
// 头信息 客户端timeStamp
String timeStamp = headers.getFirst("timeStamp");
// 头信息 账户key
String sdkKey = headers.getFirst("sdkKey");
//获取body str
String bodyStr = RequestBodyUtil.resolveBodyFromRequest(exchange.getRequest());
// 去空格
bodyStr = StringUtils.replace(bodyStr, " ", "");
String authDesc = "鉴权异常..";
try {
// rpc 账户信息
GetMerchantAppResp merchantApp = merchantAppApi.getMerchantApp(new MerchantAppReq().setSdkKey(sdkKey)).getData();
if (merchantApp == null) {
authDesc = "账户不存在..";
throw new IllegalAccessException("账户不存在..");
}
if (!merchantApp.getStatus()) {
authDesc = "账户被禁用..";
throw new IllegalAccessException("账户被禁用..");
}
// 验证签名
String signStr = SecurityFilter.genSign(timeStamp, sdkKey, merchantApp.getSdkSecret(), bodyStr);
if (!StringUtils.equals(sign, signStr)) {
authDesc = "签名验证失败..";
throw new IllegalAccessException("签名验证失败..");
}
} catch (Exception e) {
log.error(String.format("=== %s ===, timeStampStr=%s, sdkKey=%s, requestBody=%s",
authDesc, timeStamp, sdkKey, bodyStr), e);
ServerHttpResponse response = exchange.getResponse();
ApiResp apiResp = ApiResp.authFail(authDesc);
byte[] bits = JacksonUtil.toJsonString(apiResp).getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bits);
// 200
response.setStatusCode(HttpStatus.OK);
//指定编码,否则在浏览器中会中文乱码
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
return response.writeWith(Mono.just(buffer));
}
return chain.filter(exchange);
}
/**
* 根据请求时间戳 以及请求body 生产签名
*
* @param timeStamp 时间戳
* @param bodyStr 实体类
* @return 签名
*/
private static String genSign(String timeStamp, String sdkKey, String sdkSecret, String bodyStr) {
String sign = "";
try {
String signResource = timeStamp + sdkKey + bodyStr;
Mac mac = Mac.getInstance(SIGN_RULE);
mac.init(new SecretKeySpec(sdkSecret.getBytes(StandardCharsets.UTF_8), SIGN_RULE));
byte[] signatureBytes = mac.doFinal(signResource.getBytes(StandardCharsets.UTF_8));
String hexStr = Hex.encodeHexString(signatureBytes);
sign = Base64.encodeBase64String(hexStr.getBytes());
} catch (Exception e) {
log.error("=== 生成签名失败 ===, timeStampStr={}, sdkKey={}, sdkSecret={}, requestBody={}", timeStamp, sdkKey, sdkSecret, bodyStr);
}
return sign;
}
/**
* 过滤顺序
*
* @return 排序
*/
@Override
public int getOrder() {
return 5;
}
}
3.封装工具类 RequestBodyUtil
/**
* 操作request body的工具
*
*/
public class RequestBodyUtil {
private final static Pattern P = Pattern.compile("\\s*|\t|\r|\n");
/**
* 读取body内容
*
* @param serverHttpRequest 请求对象
* @return body
*/
public static String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {
//获取请求体
Flux<DataBuffer> body = serverHttpRequest.getBody();
StringBuilder sb = new StringBuilder();
body.subscribe(buffer -> {
byte[] bytes = new byte[buffer.readableByteCount()];
buffer.read(bytes);
String bodyString = new String(bytes, StandardCharsets.UTF_8);
sb.append(bodyString);
});
return formatStr(sb.toString());
}
/**
* 去掉空格,换行和制表符
*
* @param str 待优化字符串
* @return 格式化后的str
*/
private static String formatStr(String str) {
if (str != null && str.length() > 0) {
Matcher m = P.matcher(str);
return m.replaceAll("");
}
return str;
}
}