在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)); } }
相关文章
暂无评论...