技术笔记:springboot项目使用拦截器实现一个简单的网关请求透传

简介: 技术笔记:springboot项目使用拦截器实现一个简单的网关请求透传

需求:做一个网关将外网的请求做一个转化(添加上签名,格式化等操作),然后将请求转发到内网的网关soul-gateway上


1. 思路:采用拦截器拦截掉请求(我这里是拦截固定前缀的请求),然后做一个请求的转化,最后在拦截器中做response返回


1.1 定义的拦截器 继承 handlerInterceptor接口


package com.tyyd.dyhdzzxxxt.gateway.interceptor;


import com.tyyd.dyhdzzxxxt.gateway.common.AcwsException;


import com.tyyd.dyhdzzxxxt.gateway.config.SoulConfig;


import lombok.extern.slf4j.Slf4j;


import org.springframework.beans.factory.annotation.Autowired;


import org.springframework.http.ResponseEntity;


import org.springframework.stereotype.Component;


import org.springframework.web.servlet.HandlerInterceptor;


import javax.servlet.http.HttpServletRequest;


import javax.servlet.http.HttpServletResponse;


import java.io.IOException;


import java.io.PrintWriter;


@Component


@Slf4j


public class AllRequestHandler implements HandlerInterceptor {


//内网网关的配置 主要是内网地址和端口


@Autowired


SoulConfig soulConfig;


//路由处理请求参数的处理类


@Autowired


RoutingDelegate routingDelegate;


@Override


public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)


throws Exception {


if (log.isDebugEnabled()) {


long logstart = System.currentTimeMillis();


request.setAttribute("logstart", logstart);


}


String URL = "" + soulConfig.getHost() + ":" + soulConfig.getPort();


ResponseEntity responseEntity = routingDelegate.redirect(request, response, URL, "/api/v1");


if (responseEntity == null) {


throw new AcwsException("请求结果异常");


}


response.setStatus(responseEntity.getStatusCodeValue());


response.setCharacterEncoding("UTF-8");


response.setContentType("application/json; charset=utf-8");


PrintWriter out = null;


try {


out = response.getWriter();


out.append(responseEntity.getBody());


} catch (IOException e) {


e.printStackTrace();


} finally {


if (out != null) {


out.close();


}


}


if (log.isDebugEnabled()) {


long logstart = (long) request.getAttribute("logstart");


log.debug("代理请求耗时:{}ms", System.currentTimeMillis() - logstart);


}


return false;


}


}


1.2注入定义的拦截器配置 继承webMvcConfigurer接口


package com.tyyd.dyhdzzxxxt.gateway.interceptor;


import lombok.extern.slf4j.Slf4j;


import org.springframework.beans.factory.annotation.Autowired;


import org.springframework.context.annotation.Configuration;


import org.springframework.web.servlet.config.annotation.InterceptorRegistry;


import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration


@Slf4j


public class IntercepterConfig implements WebMvcConfigurer {


//注入自定义的


@Autowired


private AllRequestHandler addInterceptor;


/


配置拦截路径



@param registry


/


@Override


public void addInterceptors(InterceptorRegistry registry) {


//配置拦截的路径


registry.addInterceptor(addInterceptor).addPathPatterns("/api/v1/");


}


}


1.3 处理请求参数的处理类


package com.tyyd.dyhdzzxxxt.gateway.interceptor;


import com.tyyd.dyhdzzxxxt.gateway.config.SoulAppConfig;


import com.tyyd.dyhdzzxxxt.gateway.config.SoulConfig;


import com.tyyd.dyhdzzxxxt.gateway.domain.AppKeyAndAppSecretEntity;


import com.tyyd.dyhdzzxxxt.gateway.util.SignUtils;


import lombok.extern.slf4j.Slf4j;


import org.springframework.beans.factory.annotation.Autowired;


import org.springframework.http.;


import org.springframework.stereotype.Component;


import org.springframework.util.MultiValueMap;


import org.springframework.util.StreamUtils;


import org.springframework.util.StringUtils;


import org.springframework.web.client.RestTemplate;


import javax.servlet.http.HttpServletRequest;


import javax.servlet.http.HttpServletResponse;


import java.io.IOException;


import java.io.InputStream;


import java.net.URI;


import java.net.URISyntaxException;


import java.util.Collections;


import java.util.List;


import java.util.Map;


@Component


@Slf4j


public class RoutingDelegate {


//内网网关soulgateway的配置 IP地址和端口等


@Autowired


SoulConfig soulConfig;


//考虑分布式部署 每个当前的网关都会有单独的 appkey和appsecret 这里配置的就是appkey和appsecret


@Autowired


SoulAppConfig soulAppConfig;


@Autowired


RestTemplate restTemplate;


public ResponseEntity redirect(HttpServletRequest request, HttpServletResponse response, String routeUrl, String prefix) {


try {


//构造url


String redirectUrl = createRedictUrl(request, routeUrl, prefix);


//构造请求头和请求体


RequestEntity requestEntity = createRequestEntity(request, redirectUrl,prefix);


//转发请求


return route(requestEntity);


} catch (Exception e) {


log.error("请求失败", e);


return new ResponseEntity("请求失败", HttpStatus.INTERNAL_SERVER_ERROR);


}


}


//构造url和路径参数


private String createRedictUrl(HttpServletRequest request, String routeUrl, String prefix) {


String queryString = request.getQueryString();


String url = request.getRequestURI().replaceAll(prefix, "");


return routeUrl + url + (queryString != null ? "?" + queryString : "");


}


//构造请求头和请求体


private RequestEntity createRequestEntity(HttpServletRequest request, String url, String prefix) throws URISyntaxException, //代码效果参考:http://www.jhylw.com.cn/380435929.html

IOException {

String method = request.getMethod();


HttpMethod httpMethod = HttpMethod.resolve(method);


MultiValueMap headers = parseRequestHeader(request, prefix);


byte【】 body = parseRequestBody(request);


return new RequestEntity(body, headers, httpMethod, new URI(url));


}


private ResponseEntity route(RequestEntity requestEntity) {


log.debug("代理访问:{},参数:{}",requestEntity.getUrl(),requestEntity);


ResponseEntity result = restTemplate.exchange(requestEntity, String.class);


log.debug("代理访问:{},结果:{}",requestEntity.getUrl(),result.getBody());


return result;


}


private byte【】 parseRequestBody(HttpServletRequest request) throws IOException {


InputStream inputStream = request.getInputStream();


return //代码效果参考:http://www.jhylw.com.cn/431231091.html

StreamUtils.copyToByteArray(inputStream);

}


private MultiValueMap parseRequestHeader(HttpServletRequest request, String prefix) {


HttpHeaders headers = new HttpHeaders();


List headerNames = Collections.list(request.getHeaderNames());


for (String headerName : headerNames) {


List headerValues = Collections.list(request.getHeaders(headerName));


for (String headerValue : headerValues) {


headers.add(headerName, headerValue);


}


}


String sysId = request.getHeader("sysId");


if(!StringUtils.hasText(sysId)){


sysId = request.getParameter("sysId");


}


    //根据传递过来的sysId来选择不同的appkey可secret


AppKeyAndAppSecretEntity appKeyAndAppSecretEntity = soulAppConfig.getAppKeyAndAppSecretEntity(Integer.valueOf(sysId));


String url = request.getRequestURI().replaceAll(prefix, "");


//构造签名


Map signMap = SignUtils.getInstance().sign(appKeyAndAppSecretEntity, url);


headers.add("sign", signMap.get("sign"));


headers.add("timestamp", signMap.get("timestamp"));


headers.add("version", signMap.get("version"));


headers.add("path", url);


headers.add("appKey", appKeyAndAppSecretEntity.getAppKey());


headers.add("group", soulConfig.getGroup());


headers.add("tag", soulConfig.getTag());


return headers;


}


}


签名工具类Signutils:


package com.tyyd.dyhdzzxxxt.gateway.util;


import com.tyyd.dyhdzzxxxt.gateway.domain.AppKeyAndAppSecretEntity;


import org.springframework.util.DigestUtils;


import java.util.;


import java.util.stream.Collectors;


public final class SignUtils {


private static final SignUtils SIGN_UTILS = new SignUtils();


private SignUtils() {


}


public static SignUtils getInstance() {


return SIGN_UTILS;


}


public static String generateSign(final String signKey, final Map params) {


List storedKeys = Arrays.stream(params.keySet()


.toArray(new String【】{}))


.sorted(Comparator.naturalOrder())


.collect(Collectors.toList());


final String sign = storedKeys.stream()


.filter(key -> !Objects.equals(key, "sign"))


.map(key -> String.join("", key, params.get(key)))


.collect(Collectors.joining()).trim()


.concat(signKey);


return DigestUtils.md5DigestAsHex(sign.getBytes()).toUpperCase();


}


public Map sign(AppKeyAndAppSecretEntity appKeyAndAppSecretEntity , String url){


long now = System.currentTimeMillis();


HashMap params = new HashMap();


//这里要和内网的保持规则相同 如果要改请同时改 否则可能会导致签名校验不通过


//params.put("timestamp", String.valueOf(new Date()));


params.put("timestamp", String.valueOf(now));


params.put("path", url);


params.put("version", "1.0.0");


String sign = generateSign(appKeyAndAppSecretEntity.getAppSecret(), params);


params.put("sign",sign);


return params;


}


}

相关文章
|
5月前
|
XML JSON Java
【SpringBoot(三)】从请求到响应再到视图解析与模板引擎,本文带你领悟SpringBoot请求接收全流程!
Springboot专栏第三章,从请求的接收到视图解析,再到thymeleaf模板引擎的使用! 本文带你领悟SpringBoot请求接收到渲染的使用全流程!
420 3
|
8月前
|
JSON 分布式计算 大数据
springboot项目集成大数据第三方dolphinscheduler调度器
springboot项目集成大数据第三方dolphinscheduler调度器
491 3
|
8月前
|
Java 关系型数据库 数据库连接
Spring Boot项目集成MyBatis Plus操作PostgreSQL全解析
集成 Spring Boot、PostgreSQL 和 MyBatis Plus 的步骤与 MyBatis 类似,只不过在 MyBatis Plus 中提供了更多的便利功能,如自动生成 SQL、分页查询、Wrapper 查询等。
793 3
|
8月前
|
Java 关系型数据库 MySQL
springboot项目集成dolphinscheduler调度器 实现datax数据同步任务
springboot项目集成dolphinscheduler调度器 实现datax数据同步任务
787 2
|
8月前
|
分布式计算 Java 大数据
springboot项目集成dolphinscheduler调度器 可拖拽spark任务管理
springboot项目集成dolphinscheduler调度器 可拖拽spark任务管理
450 2
|
8月前
|
Java 测试技术 Spring
简单学Spring Boot | 博客项目的测试
本内容介绍了基于Spring Boot的博客项目测试实践,重点在于通过测试驱动开发(TDD)优化服务层代码,提升代码质量和功能可靠性。案例详细展示了如何为PostService类编写测试用例、运行测试并根据反馈优化功能代码,包括两次优化过程。通过TDD流程,确保每项功能经过严格验证,增强代码可维护性与系统稳定性。
325 0
|
8月前
|
存储 Java 数据库连接
简单学Spring Boot | 博客项目的三层架构重构
本案例通过采用三层架构(数据访问层、业务逻辑层、表现层)重构项目,解决了集中式开发导致的代码臃肿问题。各层职责清晰,结合依赖注入实现解耦,提升了系统的可维护性、可测试性和可扩展性,为后续接入真实数据库奠定基础。
622 0
|
分布式计算 大数据 Java
springboot项目集成大数据第三方dolphinscheduler调度器 执行/停止任务
springboot项目集成大数据第三方dolphinscheduler调度器 执行/停止任务
191 0
|
9月前
|
网络协议 Java
在SpringBoot项目中使用Netty实现远程调用
本文介绍了使用Netty解决网络连接性能问题的方法,重点讲解了Netty的NIO特性及其在SpringBoot中的应用。Netty作为高效的NIO框架,支持非阻塞IO,能通过单线程管理多个客户端连接,简化TCP/UDP套接字服务器开发。文章详细展示了Netty在SpringBoot中实现远程调用的过程,包括服务端与客户端代码实现、依赖配置及测试验证。通过示例代码,如`NettyServer`、`NettyClientUtil`等,清晰说明了Netty的工作原理和实际应用,解决了半包等问题,并提供了完整的测试结果。
900 3
|
分布式计算 Java 大数据
springboot项目集成dolphinscheduler调度器 项目管理
springboot项目集成dolphinscheduler调度器 项目管理
241 0

热门文章

最新文章