spring-cloud-gateway 功能-接口签名

spring-cloud-gateway 功能-接口签名

基于webflux

场景

一般防止接口被大量请求,会使用接口签名的技术,如时间戳,随机数,加盐

签名方式

将请求参数key根据ASCCI排序,以 key=val&key=val形式拼接 为 str, 然后将 str进行 salt 拼接

str.concat(salt)为 s2, 然后对 s2进行 md5 即为 签名

HttpSignFilter

public class HttpSignFilter implements GlobalFilter, Ordered {

    private SignService signService;
    private static final String HEAD_SIGN = "sign";

    public HttpSignFilter(SignService signService) {
        this.signService = signService;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (!exchange.getRequest().getHeaders().containsKey(HEAD_SIGN)) {
            log.error("# lack of header[sign]: {}", exchange.getRequest().getPath().value());
            throw new GatewaySignException(ErrorCode.GATEWAY_SIGN_ERROR.getMessage());
        }
        RequestParamContext context = RequestContextUtil.getRequestParamContext(exchange);
        RequestContextUtil.logRequest(context, log);
        SignInfo signInfo = signService.genSin(exchange);
        String requestSign = exchange.getRequest().getHeaders().get(HEAD_SIGN).get(0);
        log.info("params and sign: {}, request-sign: {}", JsonUtil.getInstance().toJsonString(signInfo), requestSign);
        if (!verifySign(signInfo.getSign(), requestSign)) {
            log.info("> HttpSignFilter sign error");
            return Mono.error(new GatewaySignException(ErrorCode.GATEWAY_SIGN_ERROR.getMessage()));
        }
        return chain.filter(exchange);
    }

    private boolean verifySign(String sign, String requestSign) {
        return sign.equals(requestSign);
    }

    @Override
    public int getOrder() {
        return HTTPSIGNFILTER_ORDER;
    }
}

SignService

public interface SignService {
    SignInfo genSin(ServerWebExchange exchange);

    String getSecret(ServerWebExchange exchange);
}



public abstract class AbstractSignService implements SignService {


    protected static final String AND = "&";
    protected static final String EQ = "=";

    public Map<String, String> extractParams(RequestParamContext context) {
        Map<String, String> sortedMap = new TreeMap<>();
        if (CollectionUtils.isEmpty(context.getQueryParams())) {
            Map<String, String> collect = context.getQueryParams().entrySet().stream()
                    .filter(entry -> StrUtil.isNotBlank(entry.getValue()))
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
            sortedMap.putAll(collect);
        }

        if (StrUtil.isNotBlank(context.getBody()) && StrUtil.isNotBlank(context.getContentType())) {
            if (context.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
                //{a:b,c:d}
                // TODO: 2023/7/11 处理数组?嵌套型?
                if (context.getBody().startsWith("{")) {
                    Map<String, Object> map = JsonUtil.getInstance().parseObject(context.getBody(), Map.class);
                    for (Map.Entry<String, Object> entry : map.entrySet()) {
                        String val = Objects.toString(entry.getValue(), null);
                        if (Objects.isNull(val)) {
                            continue;
                        }
                        sortedMap.putIfAbsent(entry.getKey(), val);
                    }
                }
            } else if (context.getContentType().equals(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
                // a=b&c=d
                String[] split = context.getBody().split(AND);
                for (String s : split) {
                    String[] arr = s.split(EQ);
                    if (StrUtil.isBlank(arr[1])) {
                        continue;
                    }
                    sortedMap.putIfAbsent(arr[0], arr[1]);
                }
            }
        }
        return sortedMap;
    }

}


public class DefaultSignServiceImpl extends AbstractSignService {

    private String secret = "default_secret";

    @Override
    public SignInfo genSin(ServerWebExchange exchange) {
        RequestParamContext context = RequestContextUtil.getRequestParamContext(exchange);
        Map<String, String> sortedMap = extractParams(context);
        SignInfo signInfo = new SignInfo();
        signInfo.setData(sortedMap);
        String sign = generateSign(getSecret(exchange), sortedMap);
        signInfo.setSign(sign);
        return signInfo;
    }

    @Override
    public String getSecret(ServerWebExchange exchange) {
        return secret;
    }


    public String generateSign(final String signKey, final Map<String, String> params) {
        final String sign = params.keySet().stream()
                .map(key -> String.join(EQ, key, params.get(key)))
                .collect(Collectors.joining(AND)).trim().concat(signKey);
        return DigestUtils.md5DigestAsHex(sign.getBytes()).toUpperCase();
    }
}

其他

@Data
public class SignInfo {
    //签名排序之后的参数
    private Map<String,String> data;
    //服务器端生成的签名
    private String sign;
}

public class GatewaySignException extends RuntimeException{

    public GatewaySignException(String message) {
        super(message);
    }

    public GatewaySignException(String message, Throwable cause) {
        super(message, cause);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值