针对前面的网关渠道RSA加密,有些页面和接口需要不过滤;所以做了以下不过滤指定路径相关处理配置:
yml配置文件application.yml
agilepay:
gateway:
shouldSkipUrls:
- /gatherpay/doc.html
- /gatherpay/passive/callback
- /gatherpay/static/webjars/**
- /gatherpay/resources/webjars/
- /gatherpay/resources/
- /gatherpay/swagger-ui.html
- /gatherpay/webjars/**
- /gatherpay/favicon.ico
- /gatherpay/swagger-resources
- /gatherpay/v3/api-docs
配置文件:NotAuthUrlProperties
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.LinkedHashSet;
@Data
@Component
@ConfigurationProperties("agilepay.gateway")
public class NotAuthUrlProperties {
private LinkedHashSet<String> shouldSkipUrls;
}
全局前置过滤器和后置过滤器中加入for循环判断
@Component
@Slf4j
public class WrapperRequestGlobalFilter implements GlobalFilter, Ordered {//, Ordered
@Autowired
private RsaKeyMapper rsaKeyMapper;
@Resource
private NotAuthUrlProperties notAuthUrlProperties;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String currentUrl = exchange.getRequest().getURI().getPath();
//1:不需要认证的url,不过滤放行的url
if (shouldSkip(currentUrl)) {
log.info("==========已跳过url{}=====", currentUrl);
return chain.filter(exchange);
}
log.info("===========网关请求解密开始==================");
ServerRequest serverRequest = new DefaultServerRequest(exchange);
//获取渠道id
String channelId = exchange.getRequest().getHeaders().getFirst("channelId");
if (channelId != null) {
//查看是否存在
RsaKey rsaKeyTable = new LambdaQueryChainWrapper<>(rsaKeyMapper)
.eq(RsaKey::getChannelId, channelId).one();
if (rsaKeyTable != null) {
Mono<String> modifiedBody = serverRequest.bodyToMono(String.class)
.flatMap(body -> {
//加密String转json对象
JSONObject jsonObject = JSONObject.parseObject(body);
String data = jsonObject.getString("data");//获取加密数据值
//获取渠道对应支付私钥(其他项目传来的是用渠道对应支付公钥加密值)
String privateKeyPay = rsaKeyTable.getRsaPrivatePayKey();
log.info(privateKeyPay + "私钥-------------");
try {
//私钥解密
String sp = EncryptUtil.decryptByPrivateKey(data, privateKeyPay);
// 解密后的值json字符串
String newBody = sp;
log.info(newBody + "------------解密的值---------------------");
//TODO 获取body内容去干你想做的事
return Mono.just(newBody);
} catch (Exception e) {
e.printStackTrace();
//只要有异常就返回方法不允许
response.setStatusCode(HttpStatus.METHOD_NOT_ALLOWED);
//禁止访问
return Mono.empty();
}
});
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
headers.remove(HttpHeaders.CONTENT_LENGTH);
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
return bodyInserter.insert(outputMessage, new BodyInserterContext())
.then(Mono.defer(() -> {
ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(
exchange.getRequest()) {
@Override
public HttpHeaders getHeaders() {
long contentLength = headers.getContentLength();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
if (contentLength > 0) {
httpHeaders.setContentLength(contentLength);
} else {
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
}
return httpHeaders;
}
@Override
public Flux<DataBuffer> getBody() {
return outputMessage.getBody();
}
};
return chain.filter(exchange.mutate().request(decorator).build());
}));
} else {
//如果rsaKeyTable==null 也不准访问
return exchange.getResponse().setComplete();
}
} else {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
//禁止访问
return exchange.getResponse().setComplete();
}
// }
}
@Override
public int getOrder() {
return 0;
}
/**
* 方法实现说明:不需要过滤的路径
* <p>
* // * @param currentUrl 当前请求路径
*/
private boolean shouldSkip(String currentUrl) {
PathMatcher pathMatcher = new AntPathMatcher();
for (String skipPath : notAuthUrlProperties.getShouldSkipUrls()) {
if (pathMatcher.match(skipPath, currentUrl)) {
return true;
}
}
return false;
}
}
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.szzz.pay.agilepaygateway.properties.NotAuthUrlProperties;
import com.szzz.pay.agilepaygateway.rsakey.entity.RsaKey;
import com.szzz.pay.agilepaygateway.rsakey.mapper.RsaKeyMapper;
import com.szzz.pay.agilepaygateway.utils.EncryptUtil;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
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.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.nio.charset.Charset;
@Component
@Slf4j
public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered {
@Autowired
private RsaKeyMapper rsaKeyMapper;
@Resource
private NotAuthUrlProperties notAuthUrlProperties;
@Override
public int getOrder() {
// -1 is response write filter, must be called before that
return -1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String currentUrl = exchange.getRequest().getURI().getPath();
//1:不需要认证的url
if (shouldSkip(currentUrl)) {
log.info("=====返回已跳过url{}===", currentUrl);
return chain.filter(exchange);
}
log.info("===============过滤返回值加密开始=====================");
log.info(exchange.getRequest().getPath().toString() + "----------------------------------");
ServerHttpResponse originalResponse = exchange.getResponse();
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.map(dataBuffer -> {
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
//释放掉内存
DataBufferUtils.release(dataBuffer);
String s = new String(content, Charset.forName("UTF-8"));
String channelId = exchange.getRequest().getHeaders().getFirst("channelId");
RsaKey rsaKeyTable = new LambdaQueryChainWrapper<>(rsaKeyMapper)
.eq(RsaKey::getChannelId, channelId).one();
//通过渠道id获取对应渠道的公钥
String channelPublicKey = rsaKeyTable.getRsaPublicKey();
//渠道私钥他们对应渠道自己有留存,不需要传||渠道对应的公私钥和支付项目的公私钥他们渠道那边都可以查到?根据渠道id,所以不需要传给他们,他们自己获取即可,我们传渠道公钥加密后的值就可以,他们那边用对应渠道私钥进行解密
log.info("过滤返回值明文:" + s);
String pubKeyEncrypt = "";
try {
pubKeyEncrypt = EncryptUtil.encryptByPublicKey(s, channelPublicKey);
} catch (Exception e) {
e.printStackTrace();
}
log.info("过滤返回值公钥加密:" + pubKeyEncrypt);
//TODO,s就是response的值,想修改、查看就随意而为了
byte[] uppedContent = pubKeyEncrypt.getBytes();//new String(content, Charset.forName("UTF-8")).getBytes();
return bufferFactory.wrap(uppedContent);
}));
}
return super.writeWith(body);
}
};
return chain.filter(exchange.mutate().response(decoratedResponse).build());
// }
}
/**
* 方法实现说明:不需要过滤的路径
* <p>
* // * @param currentUrl 当前请求路径
*/
private boolean shouldSkip(String currentUrl) {
PathMatcher pathMatcher = new AntPathMatcher();
log.info("===========需要跳过的地址==========:" + notAuthUrlProperties.getShouldSkipUrls());
for (String skipPath : notAuthUrlProperties.getShouldSkipUrls()) {
if (pathMatcher.match(skipPath, currentUrl)) {
return true;
}
}
return false;
}
}
在过滤器中家的编码如下
String currentUrl = exchange.getRequest().getURI().getPath();
//1:不需要认证的url,不过滤放行的url
if (shouldSkip(currentUrl)) {
log.info("==========已跳过url{}=====", currentUrl);
return chain.filter(exchange);
}
/**
* 方法实现说明:不需要过滤的路径
* <p>
* // * @param currentUrl 当前请求路径
*/
private boolean shouldSkip(String currentUrl) {
PathMatcher pathMatcher = new AntPathMatcher();
for (String skipPath : notAuthUrlProperties.getShouldSkipUrls()) {
if (pathMatcher.match(skipPath, currentUrl)) {
return true;
}
}
return false;
}
}