springgateway实现接口数据加密传输
在一些对安全要求很高的系统,需要对数据进行加密传输,我们可以采用在网关加密解密数据传输提高系统安全性,加密解密算法使用rsa(相对性能影响较小),java代码如下import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.asymmetric.KeyType; import cn.hutool.crypto.asymmetric.RSA; import com.google.common.base.Joiner; import io.netty.buffer.ByteBufAllocator; import lombok.extern.slf4j.Slf4j; import org.apache.commons.compress.utils.Lists; import org.apache.commons.lang3.StringUtils; import org.reactivestreams.Publisher; import org.springframework.cloud.context.config.annotation.RefreshScope; 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.core.io.buffer.NettyDataBufferFactory; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequestDecorator; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpResponseDecorator; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.net.URI; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR; /** * @ClassName AppFilter * @Description TODO * @Author ljq * @Date 2022/12/13 16:16 * @Version 1.0 */ @Slf4j @RefreshScope @Component public class AppFilter implements GlobalFilter, Ordered { private static Joiner joiner = Joiner.on(""); @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { //如果存在appId 走应用认证接口 ServerHttpRequest request = exchange.getRequest(); String appId = request.getHeaders().getFirst("appId"); if(StringUtils.isNotBlank(appId)){ if (exchange.getRequest().getMethod().equals(HttpMethod.POST)) { String bodyStr = getBodyStr(exchange.getRequest().getBody()); log.info("加密原始数据"+bodyStr); //重新封装请求 因为请求体内容已被消费,需要重新写入 URI uri = request.getURI(); request = request.mutate().uri(uri).build(); //这块需要掉用微服务进行应用权限校验 并返回应用公私钥 这里使用hutu工具包 对数据进行rsa加解密 start String privateKey = "30820155020100300d06092a864886f70d01010105000482013f3082013b020100024100b9c7d70130df32ff4f41f79390fc085439e917a632b2707c0d3d4c8da0cedc22e55d66ddbe59287b16aa63ff3a98d23e3938da0ceabac4167204e09a3a0cd88f020301000102401f5ae821ce52cd73a3b7d98631612832b6f76d4362a9152d0abafed1a4836549c042700fc26b750a460c999c9a0107aec4cdfecde897606dcc56bb653aee1a89022100f773cf5f947e4eb442b8a2ba42929a557e2b6c48869347296fe6e5a5c7e12513022100c032ac9e7c2ca7ca817bdd8626fe25f427188c10bc42206fd3605764f767ba15022100f0653ded29219bec5b756c016f736523f132d63b8f21bd5c702deca4258e80a9022043e3625fd4c2bd3de580c81db3b63fd7bedb87d5fd796a15b5d728e78c104285022100879796fca38aadf54a0dd90959d1eef452fffe504d73dbaff887f5089e36cd19"; String publicKey = "305c300d06092a864886f70d0101010500034b003048024100b9c7d70130df32ff4f41f79390fc085439e917a632b2707c0d3d4c8da0cedc22e55d66ddbe59287b16aa63ff3a98d23e3938da0ceabac4167204e09a3a0cd88f0203010001"; RSA rsa = SecureUtil.rsa(privateKey,publicKey); bodyStr = rsa.decryptStr(bodyStr, KeyType.PrivateKey); log.info("解密数据:"+bodyStr); //这块需要掉用微服务进行应用权限校验 并返回应用公私钥 这里使用hutu工具包 对数据进行rsa加解密 end //将解密数据封装向下传递 DataBuffer bodyDataBuffer = stringBuffer(bodyStr); Flux bodyFlux = Flux.just(bodyDataBuffer); request = new ServerHttpRequestDecorator(request) { @Override public Flux getBody() { return bodyFlux; } }; //可以选择对返回体也进行加密 ResBodyEncryptDecorator responseDecorator = new ResBodyEncryptDecorator(exchange.getResponse(),exchange.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR),rsa); ServerHttpRequest newRequest = request.mutate().build(); return chain.filter(exchange.mutate().request(newRequest).response(responseDecorator).build()); }else{ //可以将get请求也设置特定的参数读取加解密 可以照着post请求做 return AppIdInvalid(exchange); } }else{ return AppIdInvalid(exchange); } } public static class ResBodyEncryptDecorator extends ServerHttpResponseDecorator{ private DataBufferFactory bufferFactory; private String contentType; private RSA rsa; public ResBodyEncryptDecorator(ServerHttpResponse delegate,String contentType,RSA rsa) { super(delegate); this.contentType = contentType; this.bufferFactory = delegate.bufferFactory(); this.rsa = rsa; } @Override public Mono writeWith(Publisher<? extends DataBuffer> body) { log.info("得到响应体"); if (getStatusCode().equals(HttpStatus.OK) && body instanceof Flux) { // 获取响应 ContentType // 记录 JSON 格式数据的响应体 if (!StringUtils.isEmpty(contentType) && contentType.contains(MediaType.APPLICATION_JSON_VALUE)) { Flux<? extends DataBuffer> fluxBody = Flux.from(body); // 解决返回体分段传输 return super.writeWith(fluxBody.buffer().map(dataBuffers -> { List list = Lists.newArrayList(); dataBuffers.forEach(dataBuffer -> { byte[] content = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(content); DataBufferUtils.release(dataBuffer); list.add(new String(content, Charset.forName("UTF-8"))); }); String responseData = joiner.join(list); log.info("得到响应体内容:{}", responseData.replaceAll(" ","").replaceAll(" ","")); responseData = rsa.encryptBase64(responseData, KeyType.PublicKey); return bufferFactory.wrap(responseData.getBytes()); })); } } return super.writeWith(body); } } /** * token 无效,消息返回 * @param exchange * @return */ private Mono AppIdInvalid(ServerWebExchange exchange) { ServerHttpResponse response = exchange.getResponse(); byte[] bits = "{"result":"-1","message":"应用不存在","data":null}".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)); } /** * 获取请求体内容 * @param body * @return */ private String getBodyStr(Flux body) { AtomicReference bodyRef = new AtomicReference<>(); body.subscribe(buffer -> { CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer()); DataBufferUtils.release(buffer); bodyRef.set(charBuffer.toString()); log.info(charBuffer.toString()); }); //获取request body return bodyRef.get(); } private DataBuffer stringBuffer(String value) { byte[] bytes = value.getBytes(StandardCharsets.UTF_8); NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT); DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length); buffer.write(bytes); return buffer; } public static void main(String[] args) { String privateKey = "30820155020100300d06092a864886f70d01010105000482013f3082013b020100024100b9c7d70130df32ff4f41f79390fc085439e917a632b2707c0d3d4c8da0cedc22e55d66ddbe59287b16aa63ff3a98d23e3938da0ceabac4167204e09a3a0cd88f020301000102401f5ae821ce52cd73a3b7d98631612832b6f76d4362a9152d0abafed1a4836549c042700fc26b750a460c999c9a0107aec4cdfecde897606dcc56bb653aee1a89022100f773cf5f947e4eb442b8a2ba42929a557e2b6c48869347296fe6e5a5c7e12513022100c032ac9e7c2ca7ca817bdd8626fe25f427188c10bc42206fd3605764f767ba15022100f0653ded29219bec5b756c016f736523f132d63b8f21bd5c702deca4258e80a9022043e3625fd4c2bd3de580c81db3b63fd7bedb87d5fd796a15b5d728e78c104285022100879796fca38aadf54a0dd90959d1eef452fffe504d73dbaff887f5089e36cd19"; String publicKey = "305c300d06092a864886f70d0101010500034b003048024100b9c7d70130df32ff4f41f79390fc085439e917a632b2707c0d3d4c8da0cedc22e55d66ddbe59287b16aa63ff3a98d23e3938da0ceabac4167204e09a3a0cd88f0203010001"; RSA rsa = SecureUtil.rsa(privateKey,publicKey); String data = "{ " + " "current":1, " + " "size":20, " + " "param":{ " + " "afterSale":"Y", " + " "commitTimeStart":"2011-11-11 00:00:00" " + " } " + "}"; String encrypt = rsa.encryptBase64(data, KeyType.PrivateKey); String decypt = rsa.decryptStr(encrypt, KeyType.PublicKey); System.out.println(encrypt); System.out.println(decypt); encrypt = rsa.encryptBase64(data, KeyType.PublicKey); decypt = rsa.decryptStr(encrypt, KeyType.PrivateKey); System.out.println(encrypt); System.out.println(decypt); } @Override public int getOrder() { return -2; } }
遇到的问题:
1 有可能无法获取到请求体里面的内容可以参考 https://zhuanlan.zhihu.com/p/471402045
效果:
postman请求
服务器日志
放弃高薪执教机会,王治郅回归军旅拿月薪,军衔惊人,收入感人王治郅,中国男篮三大移动长城之一,虽然曾经有过一段不愉快经历,但他仍然为国家队立下了汗马功劳,2016年,王治郅正式宣布退役,他没有开启退休生活,而是转换身份继续为中国篮球的发展做
签运有多重要?韩国队晋级,抽签撞大运,日本瑞典队眼红不已签运有多重要?有人欢喜有人忧。北京时间10月5日,成都世乒赛团体赛男子八分之一决赛中,首先是韩国对战波兰,30大比分顺利晋级。不少人调侃韩国队此次的签运真的是太好了,避开了死亡半区
网曝多名双面人港星!杨千嬅被列为第一,吴镇宇被指脾气古怪双面人这个称谓听起来并不是很友善,但的的确确让人感到反感,尤其是在纷杂的娱乐圈中,人前人后两个模样,这样的艺人好像尤为多。早前,因为网友偶遇钟嘉欣,从而引发网友对她的负面评价,连带
常吃发酵食品,或有助于减少体内炎症,医生建议6种食物可多吃在我们传承了几千年的饮食文化中,发酵食品是不可或缺的。自古以来,我们制作发酵食品的技术越来越成熟,制作出来的食品也越来越多样唯美,例如经常吃的酸奶泡菜各种发酵酒,都是发酵食品。食物
常喝咖啡,到底好不好?医生这5类人最好不要碰,希望你能听劝说到咖啡,大家也并不陌生,咖啡从西方传入东方就成了小资品味的象征,与中国的茶一样,咖啡也是世界上比较受欢迎的饮料之一。但是在我们的日常生活中,许多人也对咖啡持怀疑态度,一些人认为长
纹身师诉WWE2K侵权胜诉奥顿身上纹身未授权近日,纹身师凯瑟琳亚历山大诉2KSports及其母公司TakeTwo侵权案胜诉。凯瑟琳于2018年提起诉讼,声称她为摔跤巨星兰迪奥顿设计的纹身未经授权在WWE2K16WWE2K17
退休医生表明多休息多运动对身体好,试着坚持2件小事或会长寿俗话说得好,生老病死人之常情,人的长度是有固定点的,而宽度没有大家可以通过有限的时间去做无限的事情。古代有很多王侯将相会追求长生不老,得永生,但没有一个人能成功,就连秦始皇也不例外
台东海域,岛内与日本爆发冲突,三艘船对峙10个小时台媒日本舰船骚扰台船只对峙10小时文君剑近年来,台湾省的蔡当局为了讨好美国和日本,可以说是花样百出,各种献媚,甚至连莱猪(瘦肉精猪肉)和核食(日本核灾区食品)也都照单全收了。然而最
修炼之开悟开悟。色即是空,空即是色,心无挂碍,这什么意思呢,各位,悟字是心和吾组合而成的,意思是用自己的心去领悟,色即是空,是让你把自己的心清空,清除一切杂念,杂思,什么也不要想,以一种空空
情牵紫木槿有人说,爱上一个人,便爱上了他所在的一座城。而爱上一种花,不单单是因为这种花的美丽,更是因为这种花在你的生命中留下了芬芳的印记。很多时候,物是我们情感的载体。我对紫木槿情有独钟。木
一夜秋雨感觉夏天的闷热刚刚褪去,转眼之间就到了九月。这个宁静的小山村秋天总是多雨的。田野里的玉米洋芋豆子等都开始收割了,也是一个收获的季节。此时这里便是多雨的天气,有时候雨会下上一周。辛劳