• 通过SpringCloudGateway中的GlobalFilter实现鉴权过滤


    1.pom.xml中加入gateway jar包

    1. <dependency>
    2. <groupId>org.springframework.cloudgroupId>
    3. <artifactId>spring-cloud-starter-gatewayartifactId>
    4. dependency>

    2.创建权限过滤器 SecurityFilter

    1. /**
    2. * 鉴权过滤
    3. *
    4. **/
    5. @Slf4j
    6. @Component
    7. public class SecurityFilter implements GlobalFilter, Ordered {
    8. @Resource
    9. private MerchantAppApi merchantAppApi;
    10. /**
    11. * 签名算法
    12. */
    13. private final static String SIGN_RULE = "HmacSHA256";
    14. /**
    15. * 通过sdkKey做鉴权过滤
    16. *
    17. **/
    18. @Override
    19. public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    20. // 获取头信息
    21. HttpHeaders headers = exchange.getRequest().getHeaders();
    22. // 头信息 客户端签名
    23. String sign = headers.getFirst("sign");
    24. // 头信息 客户端timeStamp
    25. String timeStamp = headers.getFirst("timeStamp");
    26. // 头信息 账户key
    27. String sdkKey = headers.getFirst("sdkKey");
    28. //获取body str
    29. String bodyStr = RequestBodyUtil.resolveBodyFromRequest(exchange.getRequest());
    30. // 去空格
    31. bodyStr = StringUtils.replace(bodyStr, " ", "");
    32. String authDesc = "鉴权异常..";
    33. try {
    34. // rpc 账户信息
    35. GetMerchantAppResp merchantApp = merchantAppApi.getMerchantApp(new MerchantAppReq().setSdkKey(sdkKey)).getData();
    36. if (merchantApp == null) {
    37. authDesc = "账户不存在..";
    38. throw new IllegalAccessException("账户不存在..");
    39. }
    40. if (!merchantApp.getStatus()) {
    41. authDesc = "账户被禁用..";
    42. throw new IllegalAccessException("账户被禁用..");
    43. }
    44. // 验证签名
    45. String signStr = SecurityFilter.genSign(timeStamp, sdkKey, merchantApp.getSdkSecret(), bodyStr);
    46. if (!StringUtils.equals(sign, signStr)) {
    47. authDesc = "签名验证失败..";
    48. throw new IllegalAccessException("签名验证失败..");
    49. }
    50. } catch (Exception e) {
    51. log.error(String.format("=== %s ===, timeStampStr=%s, sdkKey=%s, requestBody=%s",
    52. authDesc, timeStamp, sdkKey, bodyStr), e);
    53. ServerHttpResponse response = exchange.getResponse();
    54. ApiResp apiResp = ApiResp.authFail(authDesc);
    55. byte[] bits = JacksonUtil.toJsonString(apiResp).getBytes(StandardCharsets.UTF_8);
    56. DataBuffer buffer = response.bufferFactory().wrap(bits);
    57. // 200
    58. response.setStatusCode(HttpStatus.OK);
    59. //指定编码,否则在浏览器中会中文乱码
    60. response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
    61. return response.writeWith(Mono.just(buffer));
    62. }
    63. return chain.filter(exchange);
    64. }
    65. /**
    66. * 根据请求时间戳 以及请求body 生产签名
    67. *
    68. * @param timeStamp 时间戳
    69. * @param bodyStr 实体类
    70. * @return 签名
    71. */
    72. private static String genSign(String timeStamp, String sdkKey, String sdkSecret, String bodyStr) {
    73. String sign = "";
    74. try {
    75. String signResource = timeStamp + sdkKey + bodyStr;
    76. Mac mac = Mac.getInstance(SIGN_RULE);
    77. mac.init(new SecretKeySpec(sdkSecret.getBytes(StandardCharsets.UTF_8), SIGN_RULE));
    78. byte[] signatureBytes = mac.doFinal(signResource.getBytes(StandardCharsets.UTF_8));
    79. String hexStr = Hex.encodeHexString(signatureBytes);
    80. sign = Base64.encodeBase64String(hexStr.getBytes());
    81. } catch (Exception e) {
    82. log.error("=== 生成签名失败 ===, timeStampStr={}, sdkKey={}, sdkSecret={}, requestBody={}", timeStamp, sdkKey, sdkSecret, bodyStr);
    83. }
    84. return sign;
    85. }
    86. /**
    87. * 过滤顺序
    88. *
    89. * @return 排序
    90. */
    91. @Override
    92. public int getOrder() {
    93. return 5;
    94. }
    95. }

    3.封装工具类 RequestBodyUtil

    1. /**
    2. * 操作request body的工具
    3. *
    4. */
    5. public class RequestBodyUtil {
    6. private final static Pattern P = Pattern.compile("\\s*|\t|\r|\n");
    7. /**
    8. * 读取body内容
    9. *
    10. * @param serverHttpRequest 请求对象
    11. * @return body
    12. */
    13. public static String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {
    14. //获取请求体
    15. Flux body = serverHttpRequest.getBody();
    16. StringBuilder sb = new StringBuilder();
    17. body.subscribe(buffer -> {
    18. byte[] bytes = new byte[buffer.readableByteCount()];
    19. buffer.read(bytes);
    20. String bodyString = new String(bytes, StandardCharsets.UTF_8);
    21. sb.append(bodyString);
    22. });
    23. return formatStr(sb.toString());
    24. }
    25. /**
    26. * 去掉空格,换行和制表符
    27. *
    28. * @param str 待优化字符串
    29. * @return 格式化后的str
    30. */
    31. private static String formatStr(String str) {
    32. if (str != null && str.length() > 0) {
    33. Matcher m = P.matcher(str);
    34. return m.replaceAll("");
    35. }
    36. return str;
    37. }
    38. }

  • 相关阅读:
    仅手机大小!极空间T2随身数据魔盒发布:既是NAS 又是U盘
    vscode编写verilog的插件【对齐、自动生成testbench文件】
    国密是什么意思?属于商密还是普密?
    一行行的代码解密马尔可夫链
    Linux——监控GPU集群显存并自动运行python训练脚本
    Maven的常用命令
    nginx(七十九)nginx与tls/ssl续
    从0开始搭建Web自动化测试框架全网最牛最全教程
    异步回调
    Flink学习4 - 富函数 + 数据重分区操作 + sink 操作(kafka、redis、jdbc)
  • 原文地址:https://blog.csdn.net/weixin_67727883/article/details/139322010