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


}


}

相关文章
|
8天前
|
Java 容器
如何在SpringBoot项目中使用过滤器和拦截器
过滤器和拦截器是日常开发中常用技术,用于对特定请求进行增强处理,如插入自定义代码以实现特定功能。过滤器在请求到达 `servlet` 前执行,而拦截器在请求到达 `servlet` 后执行。`SpringBoot` 中的拦截器依赖于 `SpringBoot` 容器,过滤器则由 `servlet` 提供。通过实现 `Filter` 接口并重写 `doFilter()` 方法可实现过滤器;通过实现 `HandlerInterceptor` 接口并重写相应方法可实现拦截器。两者的主要区别在于执行时机的不同,需根据具体场景选择使用。
如何在SpringBoot项目中使用过滤器和拦截器
|
11天前
|
XML 前端开发 Java
还不会SpringBoot项目模块分层?来这手把手教你
本文详细介绍了如何为SpringBoot项目创建模块并进行合理的分层设计。通过逐步演示,从创建项目到构建各功能模块,再到具体代码实现,手把手教你实现整洁的代码分层。主要内容包括:创建依赖层、主启动层、模块层及其子模块(如共通层、控制器层、数据持久层等),并通过实例演示了从前端请求到后台服务调用的实际流程。适合SpringBoot初学者及有一定经验但需优化项目结构的开发者参考。
49 2
还不会SpringBoot项目模块分层?来这手把手教你
|
11天前
|
小程序 前端开发 Java
SpringBoot+uniapp+uview打造H5+小程序+APP入门学习的聊天小项目
JavaDog Chat v1.0.0 是一款基于 SpringBoot、MybatisPlus 和 uniapp 的简易聊天软件,兼容 H5、小程序和 APP,提供丰富的注释和简洁代码,适合初学者。主要功能包括登录注册、消息发送、好友管理及群组交流。
28 0
SpringBoot+uniapp+uview打造H5+小程序+APP入门学习的聊天小项目
|
2天前
|
Java 关系型数据库 MySQL
创建一个SpringBoot项目,实现简单的CRUD功能和分页查询
【9月更文挑战第6天】该内容介绍如何使用 Spring Boot 实现具备 CRUD 功能及分页查询的项目。首先通过 Spring Initializr 创建项目并选择所需依赖;其次配置数据库连接,并创建实体类与数据访问层;接着构建服务层处理业务逻辑;最后创建控制器处理 HTTP 请求。分页查询可通过添加 URL 参数实现。
|
11天前
|
小程序 前端开发 JavaScript
【项目实战】SpringBoot+uniapp+uview2打造一个企业黑红名单吐槽小程序
【避坑宝】是一款企业黑红名单吐槽小程序,旨在帮助打工人群体辨别企业优劣。该平台采用SpringBoot+MybatisPlus+uniapp+uview2等技术栈构建,具备丰富的注释与简洁的代码结构,非常适合实战练习与学习。通过小程序搜索“避坑宝”即可体验。
30 0
【项目实战】SpringBoot+uniapp+uview2打造一个企业黑红名单吐槽小程序
|
11天前
|
JavaScript 前端开发 小程序
【项目实战】SpringBoot+vue+iview打造一个极简个人博客系统
这是一个基于 SpringBoot+MybatisPlus+Vue+Iview 技术栈构建的个人极简博客系统,适合初学者实战练习。项目包含文章分类、撰写文章、标签管理和用户管理等功能,代码简洁并配有详细注释,易于上手。此外,该项目也可作为毕业设计的基础进行二次开发。
39 0
【项目实战】SpringBoot+vue+iview打造一个极简个人博客系统
|
10天前
|
Java 数据库连接 数据库
告别繁琐 SQL!Hibernate 入门指南带你轻松玩转 ORM,解锁高效数据库操作新姿势
【8月更文挑战第31天】Hibernate 是一款流行的 Java 持久层框架,简化了对象关系映射(ORM)过程,使开发者能以面向对象的方式进行数据持久化操作而无需直接编写 SQL 语句。本文提供 Hibernate 入门指南,介绍核心概念及示例代码,涵盖依赖引入、配置文件设置、实体类定义、工具类构建及基本 CRUD 操作。通过学习,你将掌握使用 Hibernate 简化数据持久化的技巧,为实际项目应用打下基础。
29 0
|
10天前
|
Java 前端开发 Spring
技术融合新潮流!Vaadin携手Spring Boot、React、Angular,引领Web开发变革,你准备好了吗?
【8月更文挑战第31天】本文探讨了Vaadin与Spring Boot、React及Angular等主流技术栈的最佳融合实践。Vaadin作为现代Java Web框架,与其他技术栈结合能更好地满足复杂应用需求。文中通过示例代码展示了如何在Spring Boot项目中集成Vaadin,以及如何在Vaadin项目中使用React和Angular组件,充分发挥各技术栈的优势,提升开发效率和用户体验。开发者可根据具体需求选择合适的技术组合。
22 0
|
12天前
|
缓存 Java 数据库连接
Spring Boot 资源文件属性配置,紧跟技术热点,为你的应用注入灵动活力!
【8月更文挑战第29天】在Spring Boot开发中,资源文件属性配置至关重要,它让开发者能灵活定制应用行为而不改动代码,极大提升了可维护性和扩展性。Spring Boot支持多种配置文件类型,如`application.properties`和`application.yml`,分别位于项目的resources目录下。`.properties`文件采用键值对形式,而`yml`文件则具有更清晰的层次结构,适合复杂配置。此外,Spring Boot还支持占位符引用和其他外部来源的属性值,便于不同环境下覆盖默认配置。通过合理配置,应用能快速适应各种环境与需求变化。
24 0
|
2月前
|
监控 负载均衡 Java
深入理解Spring Cloud中的服务网关
深入理解Spring Cloud中的服务网关
下一篇
DDNS