原创 Springboot实战案例锦集 Spring全家桶实战案例源码 2024年08月24日 11:30 新疆 标题已修改

Spring全家桶实战案例源码

spring, springboot, springcloud 案例开发详解

559篇原创内容

公众号

环境:SpringBoot3.2.5



1. 简介

修改请求Body内容的需求源于多种场景,其中最重要的是数据预处理和安全性考虑。在Web应用中,客户端发送的请求数据可能不符合后端服务的直接处理要求,如格式不匹配、不文明用语、数据不完整或包含敏感信息。通过修改请求Body,可以在数据到达Controller之前进行必要的格式化、验证、脱敏等处理,确保数据的准确性和安全性。同时这促进了松散耦合,大大减少了开发工作量。

接下来我将通过2种方式来实现如何在请求到底Controller之前进行请求body的修改。

2. 实战案例

2.1 准备接口

需要先准备一个后端接口,用来接收请求body内容。

public class Article {  private String title ;  private String content ;  // getters, setters}// 接口@RestController@RequestMapping("/modifybody")public class ModifyBodyController {
  @PostMapping  public Article save(@RequestBody Article article) {    return article ;  }}

接口定义非常简单,并没有任何的其它处理。接下来将基于该接口实现在不修改Controller接口的前提下,修改请求中title或者是content中的特殊字符。

2.2 基于Filter实现

过滤器是解决在任何 Servlet 容器中运行的应用程序的这些通用问题的最佳选择。下图是过滤器的工作原理图:

图片

理解了Filter的工作原理后,接下来定义一个Filter,该Filter作用是用来转义 HTTP 请求正文中的所有 HTML 字符,以防止 XSS 攻击。

过滤器定义

@Component@Order(1)public static class EscapeHtmlFilter implements Filter {  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)      throws IOException, ServletException {    filterChain.doFilter(new EscapeHtmlRequestWrapper((HttpServletRequest) servletRequest), servletResponse);  }}

注意,这里我们重新包装了HttpServletRequest对象,否则将会出现Request Body只能读取一次的情况。

public static class EscapeHtmlRequestWrapper extends HttpServletRequestWrapper {
  private String body = null;  public EscapeHtmlRequestWrapper(HttpServletRequest request) throws IOException {    super(request);    this.body = HtmlUtils.escapeHtml(request);  }  public ServletInputStream getInputStream() throws IOException {    final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());    ServletInputStream servletInputStream = new ServletInputStream() {      @Override      public int read() throws IOException {        return byteArrayInputStream.read();      }      // 这里还有其它的方法,默认即可    };    return servletInputStream;  }  public BufferedReader getReader() {    try {      return new BufferedReader(new InputStreamReader(this.getInputStream()));    } catch (IOException e) {}    return null;  }}

以上就完成了关键的代码,接下来就可以进行测试了

图片

正确的将<script>标签删除了。

2.3 通过AOP实现

通过RequestBodyAdvice 接口和注解 @RestControllerAdvice可用于所有 REST 控制器。利用它们在 HTTP 请求到达控制器之前转义 HTML 字符:

@RestControllerAdvicepublic class EscapeHtmlBodyAdvice implements RequestBodyAdvice {  public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,      Class<? extends HttpMessageConverter<?>> converterType) throws IOException {    InputStream is = inputMessage.getBody() ;    return new HttpInputMessage() {      public InputStream getBody() throws IOException {        return new ByteArrayInputStream(HtmlUtils.escapeHtml(is).getBytes(StandardCharsets.UTF_8)) ;      }      public HttpHeaders getHeaders() {        return inputMessage.getHeaders() ;      }    } ;  }
  public boolean supports(MethodParameter methodParameter, Type targetType,      Class<? extends HttpMessageConverter<?>> converterType) {    return true ;  }
  public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,      Class<? extends HttpMessageConverter<?>> converterType) {    return body ;  }
  public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,      Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {    return body ;  }}

注:这并非真的AOP技术(并没有产生代理),只是在读取数据之前执行了该操作。

如果有必要你可以在上面的supports放中进行条件判断,只有符合条件的才进行处理。

以上是正确的2种方式实现修改Request Body内容。是否还有其它技术实现呢?拦截器是可以呢?接下来我们通过拦截器的方式进行尝试修改内容。

2.4 使用拦截器

拦截器可以拦截传入的 HTTP 请求,并在控制器处理这些请求之前对其进行处理。拦截器有多种用途,如身份验证、授权、日志记录和缓存。此外,拦截器是 Spring MVC 框架的特有功能,它们可以访问 Spring ApplicationContext,如下是拦截器的工作原理:

图片

DispatcherServlet 会将 HTTP 请求转发给拦截器。此外,在处理之后,拦截器可以将请求转发给控制器或拒绝该请求。

public static class EscapeHtmlRequestInterceptor implements HandlerInterceptor {  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)      throws Exception {    EscapeHtmlRequestWrapper htmlEscapeRequestWrapper = new EscapeHtmlRequestWrapper(request);    // 其实写到这你就应该想到这里的EscapeHtmlRequestWrapper 是如何传递下去的呢?    // 这里的返回值仅仅是boolean类型是否要继续请求而已。    // 这里我们姑且像下面这样调用父类的方法吧    return HandlerInterceptor.super.preHandle(htmlEscapeRequestWrapper, response, handler) ;  }}// 注册拦截器@Componentpublic class WebMvcConfiguration implements WebMvcConfigurer {  public void addInterceptors(InterceptorRegistry registry) {    registry.addInterceptor(new EscapeHtmlRequestInterceptor()).addPathPatterns("/**");  }}

测试请求

图片

控制台抛出了错误,客户端返回了400状态码。

这也就说明了,通过拦截器是无法实现修改Request Body内容的。

以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏

推荐文章

Jackson才是王!SpringBoot优雅的控制JSON数据

高性能缓存神器Caffeine

建议收藏!SpringBoot项目实战开发技巧

基于SpringBoot支持任意文件在线预览

放弃ThreadLocal!TTL真香

别不信@PathVariable你真不会用

优雅!SpringBoot通过函数式编程模型声明Restful API接口

新一代web框架WebFlux到底要不要学?

想成为高手?SpringBoot这些事件你必须掌握

优雅实现API接口开关:让你的应用更可控

全网最详细的OpenFeign讲解,肯定有你不知道的

优化技巧:如何加快Spring项目启动速度

你了解Spring AOP的这个技能点吗?有什么应用场景?

如何利用ElasticSearch动态创建索引并批量保存数据,轻松提升工作效率

深入ReactiveFeign:反应式远程接口调用的最佳实践

图片

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐