在SpringCloud微服务框架下,可以通过网关gateway来进行统一的接口请求拦截,这里我主要用来做接口数据的加解密传输,这里使用了RSA非对称加密算法。(后面会附上完整代码)
首先先定义一个FilterConfig,实现GlobalFilter和Ordered两个接口

主要是实现filter拦截方法

rsaFilter函数的实现:
整个拦截过程做了两件事:1、解密get请求参数,也就是url中的参数,2、解密body体中的请求参数,也就是post请求参数,这里前后端约定好了使用json传输。
注:这里不会直接修改请求,而创建了一个新的请求然后分发下去,如果有多个filter也是一样。
最终效果:
如果是get请求,后端收到的格式统一为:https://xxx?param=xxxxxxx
如果是post请求,后端收到的请求体格式统一为:{param: "xxxxxxx"}
GatewayFilterConfig:
import com.alibaba.fastjson.JSONObject;
import com.huaihai.common.utils.RSA.*;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.lang.reflect.Field;
import java.net.URI;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.stream.Collectors;
/**
* @author yeguodong
* @date 2022/3/28
*/
@Configuration
@Component
public class GatewayFilterConfig implements GlobalFilter, Ordered {
private static final String LOGIN_PATH = "/User/login";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain){
// 验证token,如果是登录或者非后台不校验Token
// String requestUrl = exchange.getRequest().getPath().value();
// String userType = exchange.getRequest().getHeaders().getFirst(UserConstant.USER_TYPE);
// AntPathMatcher pathMatcher = new AntPathMatcher();
// if (!pathMatcher.match(LOGIN_PATH, requestUrl) && "admin".equalsIgnoreCase(userType)) {
// String token = exchange.getRequest().getHeaders().getFirst(UserConstant.TOKEN);
// Claims claim = TokenUtils.getClaim(token);
// if (StringUtils.isBlank(token) || claim == null) {
// return FilterUtils.invalidToken(exchange);
// }
// }
return rsaFilter(exchange, chain);
// 测试用,不拦截
// return chain.filter(exchange);
}
/**
* 拦截请求,rsa解密参数
* @param exchange
* @param chain
* @return
*/
private Mono rsaFilter(ServerWebExchange exchange, GatewayFilterChain chain) {
MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
if(!(MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)
|| MediaType.APPLICATION_JSON_UTF8.isCompatibleWith(mediaType))) {
return chain.filter(exchange);
}
try {
updateRequestParam(exchange);
} catch (Exception e) {
e.printStackTrace();
return FilterUtils.invalidUrl(exchange);
}
ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
Mono<String> modifiedBody = serverRequest.bodyToMono(String.class)
.flatMap(body -> {
JSONObject jsonObject = JSONObject.parseObject(body);
String param = "";
try {
param = RSAUtils.decrypt(jsonObject.getString("param"), RSAConstant.PRIVATE_KEY);
} catch (Exception e) {
}
return Mono.just(param);
});
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());
}));
}
/**
* 修改前端传的Get参数
*/
private void updateRequestParam(ServerWebExchange exchange)
throws NoSuchFieldException, IllegalAccessException, NoSuchPaddingException, NoSuchAlgorithmException,
IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidKeySpecException {
ServerHttpRequest request = exchange.getRequest();
URI uri = request.getURI();
String query = uri.getQuery();
String[] split;
if (StringUtils.isNotBlank(query)
&& query.contains("param")
&& (split = query.split("=")).length > 0) {
String param = RSAUtils.decrypt(split[1], RSAConstant.PRIVATE_KEY);
Field targetQuery = uri.getClass().getDeclaredField("query");
targetQuery.setAccessible(true);
targetQuery.set(uri, getParams(param));
}
}
private String getParams(String query) {
JSONObject jsonObject = JSONObject.parseObject(query);
return jsonObject.entrySet()
.stream()
.map(e -> e.getKey() + "=" + e.getValue())
.collect(Collectors.joining("&"));
}
@Override
public int getOrder() {
return Integer.MIN_VALUE;
}
}
GatewayContext:
import org.springframework.util.MultiValueMap;
/**
* @author yeguodong
* @date 2022/4/6
*/
public class GatewayContext {
public static final String CACHE_GATEWAY_CONTEXT = "cacheGatewayContext";
/**
* cache json body
*/
private String cacheBody;
/**
* cache formdata
*/
private MultiValueMap<String, String> formData;
/**
* cache reqeust path
*/
private String path;
public String getCacheBody() {
return cacheBody;
}
public void setCacheBody(String cacheBody) {
this.cacheBody = cacheBody;
}
public MultiValueMap<String, String> getFormData() {
return formData;
}
public void setFormData(MultiValueMap<String, String> formData) {
this.formData = formData;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
RSAUtils:
import org.apache.commons.codec.binary.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
/**
* @author yeguodong
* @date 2022/3/31
*/
public class RSAUtils {
public static final String PUBLIC_KEY = "public_key";
public static final String PRIVATE_KEY = "private_key";
public static String encrypt(String str, String publicKey)
throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException,
InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
//base64编码的公钥
byte[] decoded = Base64.decodeBase64(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return Base64.encodeBase64String(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
}
public static String decrypt(String str, String privateKey)
throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException,
InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
//64位解码加密后的字符串
byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));
//base64编码的私钥
byte[] decoded = Base64.decodeBase64(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, priKey);
return new String(cipher.doFinal(inputByte));
}
private static Map<String, String> generateRasKey() {
Map<String, String> rs = new HashMap<>();
try {
// KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
KeyPairGenerator keyPairGen = null;
keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(1024, new SecureRandom());
// 生成一个密钥对,保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
// 得到私钥 公钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
// 得到私钥字符串
String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
// 将公钥和私钥保存到Map
rs.put(PUBLIC_KEY, publicKeyString);
rs.put(PRIVATE_KEY, privateKeyString);
} catch (Exception e) {
}
return rs;
}
public static void main(String[] args) {
System.out.println(generateRasKey());
}
}
FilterUtils:
import com.alibaba.fastjson.JSONObject;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
/**
* @author yeguodong
* @date 2022/4/1
*/
public class FilterUtils {
private FilterUtils() {
}
public static Mono<Void> invalidToken(ServerWebExchange exchange) {
JSONObject json = new JSONObject();
json.put("code", HttpStatus.UNAUTHORIZED.value());
json.put("msg", "无效的token");
return buildReturnMono(json, exchange);
}
public static Mono<Void> invalidUrl(ServerWebExchange exchange){
JSONObject json = new JSONObject();
json.put("code", HttpStatus.BAD_REQUEST.value());
json.put("msg", "无效的请求");
return buildReturnMono(json, exchange);
}
public static Mono<Void> buildReturnMono(JSONObject json, ServerWebExchange exchange) {
ServerHttpResponse response = exchange.getResponse();
byte[] bits = json.toJSONString().getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bits);
response.setStatusCode(HttpStatus.UNAUTHORIZED);
//指定编码,否则在浏览器中会中文乱码
response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
return response.writeWith(Mono.just(buffer));
}
}

相关文章
暂无评论...
