技术笔记: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;


}


}

相关文章
|
4月前
|
人工智能 负载均衡 API
长连接网关技术专题(十二):大模型时代多模型AI网关的架构设计与实现
随着 AI 技术快速发展,业务对 AI 能力的渴求日益增长。当 AI 服务面对处理大规模请求和高并发流量时,AI 网关从中扮演着至关重要的角色。AI 服务通常涉及大量的计算任务和设备资源占用,此时需要一个 AI 网关负责协调这些请求来确保系统的稳定性与高效性。因此,与传统微服务架构类似,我们将相关 API 管理的功能(如流量控制、用户鉴权、配额计费、负载均衡、API 路由等)集中放置在 AI 网关层,可以降低系统整体复杂度并提升可维护性。 本文要分享的是B站在大模型时代基于多模型AI的网关架构设计和实践总结,希望能带给你启发。
340 4
|
2月前
|
Java 数据库连接 API
Java 8 + 特性及 Spring Boot 与 Hibernate 等最新技术的实操内容详解
本内容涵盖Java 8+核心语法、Spring Boot与Hibernate实操,按考试考点分类整理,含技术详解与代码示例,助力掌握最新Java技术与应用。
107 2
|
5月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
394 70
|
5月前
|
缓存 安全 Java
深入解析HTTP请求方法:Spring Boot实战与最佳实践
这篇博客结合了HTTP规范、Spring Boot实现和实际工程经验,通过代码示例、对比表格和架构图等方式,系统性地讲解了不同HTTP方法的应用场景和最佳实践。
549 5
|
8月前
|
Java 应用服务中间件 Spring
SpringBoot 响应请求是串行还是并行?
Spring Boot 在默认情况下通过 Servlet 容器的线程池实现并行处理 HTTP 请求。通过适当的线程池配置,可以进一步优化并发性能。此外,Spring Boot 提供了异步处理机制(如使用 `@Async` 注解)和反应式编程模型(Spring WebFlux),使得应用能够处理更高的并发负载。在具体项目中,可以根据需求选择合适的处理模型,以充分利用 Spring Boot 的并发处理能力。
244 21
|
9月前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
425 1
|
11月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
350 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
10月前
|
JavaScript 前端开发 Java
SpringBoot项目的html页面使用axios进行get post请求
SpringBoot项目的html页面使用axios进行get post请求
168 2
|
10月前
|
JavaScript 前端开发 Java
SpringBoot项目的html页面使用axios进行get post请求
SpringBoot项目的html页面使用axios进行get post请求
132 0
|
12天前
|
前端开发 安全 Java
基于springboot+vue开发的会议预约管理系统
一个完整的会议预约管理系统,包含前端用户界面、管理后台和后端API服务。 ### 后端 - **框架**: Spring Boot 2.7.18 - **数据库**: MySQL 5.6+ - **ORM**: MyBatis Plus 3.5.3.1 - **安全**: Spring Security + JWT - **Java版本**: Java 11 ### 前端 - **框架**: Vue 3.3.4 - **UI组件**: Element Plus 2.3.8 - **构建工具**: Vite 4.4.5 - **状态管理**: Pinia 2.1.6 - **HTTP客户端
104 4
基于springboot+vue开发的会议预约管理系统