1.pom.xml中加入gateway jar包
- <dependency>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-starter-gatewayartifactId>
- 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
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
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;
- }
-
- }