富文本编辑器格式丢失

本文档记录了一位开发者在使用若依开源框架的“通知公告”功能时遇到的问题:富文本内容保存后格式丢失。经过前端和后端的排查,发现是自定义的XssFilter导致的,该过滤器在过滤XSS攻击时修改了请求参数。解决方案是在application.yaml中配置excludes,将富文本相关URL排除在外,以保持富文本格式不变。
摘要由CSDN通过智能技术生成

一、环境说明

项目中使用的是若依开源框架的 “ 通知公告 ” 功能。
富文本编辑器是 Summernote :Summernote 是一个简单的基于 Bootstrap 的轻量级富文本编辑器 summernote 官方文档

二、问题描述

编辑富文本内容后,点击保存,富文本格式部分丢失!(包括


下面是ajax请求和controller部分代码

        function submitHandler() {
            if ($.validate.form()) {
                var sHTML = $('.summernote').summernote('code');
                var formData = new FormData();
                formData.append('newsId', $('#newsId').val());
                formData.append('newsTitle', $('#newsTitle').val());
                formData.append('newsContent', sHTML);
                formData.append('newsContentText', removeElement(sHTML));
                formData.append('newsTime', $('#newsTime').val());
                formData.append('file', $('#image')[0].files[0]);
                $.ajax({
                    url: prefix + "/edit",
                    type: "post",
                    cache: false,
                    dataType: "json",
                    data: formData,
                    processData: false,
                    contentType: false,
                    success: function (result) {
                        $.operate.successCallback(result);
                    }
                })
            }
        }
	@PostMapping("/edit")
    @ResponseBody
    public AjaxResult editSave(SysNews sysNews)
    {
        uploadImage(sysNews, file);
        return toAjax(sysNewsService.updateSysNews(sysNews));
    }

三、排查

1. 查询前端提交内容

打开浏览器调试工具,以Chrome浏览器为例,按下F12,选中Network,发送请求后,选中对应url,查看request header及body,重点观察Content-Type及Form Data。以下截取部分内容

Accept: application/json, text/javascript, */*; q=0.01
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Content-Length: 5201
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarywD5RCXzp4HD55AKK
Cookie: JSESSIONID=9e6c34ce-b491-470c-93e8-fbef5d8bb6be

newsContent: <b>测试</b><span style="background-color: rgb(255, 0, 0);">测试内容</span>

由于需要传文件到后台,因此要确保Content-Type类型为multipart/form-data;Form Data中的文本内容此时仍包含,说明前端传值没问题。

2. 查询后端接受到原始值

后台代码打断点,找到执行靠前的filter,查看request.getParameter(“newsContent”)值,发现取值正常,此时已完全排除前端传值问题,确定后端程序导致。实际测试中在editSave方法此处,增加ServletRequest req参数,仍可正常获取值;使用@RequestParam(value = “newsContent”, required = false) String newsContente同样获取不到值。

    @PostMapping("/edit")
    @ResponseBody
    public AjaxResult editSave(ServletRequest req, SysNews sysNews)
    {
        System.out.println(req.getParameter("newsContent"));
        uploadImage(sysNews, file);
        return toAjax(sysNewsService.updateSysNews(sysNews)); 
    }

3. 源码分析

以@RequestParam注解为切入口,查看从request对象中提取值时,内容为什么会变掉!
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
->
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal 留意一下org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getDefaultArgumentResolvers
->
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod 核心代码 invocableMethod.invokeAndHandle(webRequest, mavContainer);
->
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle 核心代码Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
->
org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
->
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver#resolveName
->
org.springframework.web.context.request.WebRequest#getParameterValues 注意此处值已经变掉了,进一步查看源码发现调用的是自定义HttpServletRequestWrapper com.ruoyi.common.xss.XssHttpServletRequestWrapper#getParameterValues,该方法在com.ruoyi.common.xss.XssFilter#doFilter中被调用。

package com.ruoyi.common.xss;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.common.utils.StringUtils;

/**
 * 防止XSS攻击的过滤器
 * 
 * @author ruoyi
 */
public class XssFilter implements Filter
{
    /**
     * 排除链接
     */
    public List<String> excludes = new ArrayList<>();

    /**
     * xss过滤开关
     */
    public boolean enabled = false;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {
        String tempExcludes = filterConfig.getInitParameter("excludes");
        String tempEnabled = filterConfig.getInitParameter("enabled");
        if (StringUtils.isNotEmpty(tempExcludes))
        {
            String[] url = tempExcludes.split(",");
            for (int i = 0; url != null && i < url.length; i++)
            {
                excludes.add(url[i]);
            }
        }
        if (StringUtils.isNotEmpty(tempEnabled))
        {
            enabled = Boolean.valueOf(tempEnabled);
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException
    {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        if (handleExcludeURL(req, resp))
        {
            chain.doFilter(request, response);
            return;
        }
        XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
        chain.doFilter(xssRequest, response);
    }

    private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response)
    {
        if (!enabled)
        {
            return true;
        }
        if (excludes == null || excludes.isEmpty())
        {
            return false;
        }
        String url = request.getServletPath();
        for (String pattern : excludes)
        {
            Pattern p = Pattern.compile("^" + pattern);
            Matcher m = p.matcher(url);
            if (m.find())
            {
                return true;
            }
        }
        return false;
    }

    @Override
    public void destroy()
    {

    }
}
package com.ruoyi.common.xss;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import com.ruoyi.common.utils.html.EscapeUtil;

/**
 * XSS过滤处理
 * 
 * @author ruoyi
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
{
    /**
     * @param request
     */
    public XssHttpServletRequestWrapper(HttpServletRequest request)
    {
        super(request);
    }

    @Override
    public String[] getParameterValues(String name)
    {
        String[] values = super.getParameterValues(name);
        if (values != null)
        {
            int length = values.length;
            String[] escapseValues = new String[length];
            for (int i = 0; i < length; i++)
            {
                // 防xss攻击和过滤前后空格
                escapseValues[i] = EscapeUtil.clean(values[i]).trim();
            }
            return escapseValues;
        }
        return super.getParameterValues(name);
    }
}

四、结果办法

在 application.yaml 配置文件中,增加以下内容,使用 excludes 排查富文本 url

  # 防止XSS攻击
  xss: 
  # 过滤开关
  enabled: true
  # 排除链接(多个用逗号分隔)
  excludes: /system/notice/*,/system/news/*
  # 匹配链接
  urlPatterns: /system/*,/monitor/*,/tool/*

好事定律:每件事最后都会是好事,如果不是好事,说明还没到最后。

h5移动端富文本编辑器是一种用于在移动设备上创建、编辑和排版富文本内容的工具。它通过简单而强大的用户界面,使用户能够以直观的方式对文本进行格式化和排版,同时支持插入图片、链接、表格和其他富媒体元素。 相比传统的文本编辑器,h5移动端富文本编辑器具有以下优点: 1. 响应式设计:移动设备的屏幕空间有限,h5移动端富文本编辑器能够根据屏幕大小动态调整布局和功能,以适应不同设备的需求。 2. 触摸友好:h5移动端富文本编辑器采用触摸屏操作,支持手指滑动、捏合和缩放等手势,使得编辑过程更加流畅自然。 3. 自动保存:编辑器可以自动保存用户的编辑内容,防止意外关闭或断网等情况下的数据丢失,提供更好的用户体验。 4. 多样化的样式和工具:编辑器提供丰富的文本样式和排版选项,如字体、颜色、加粗、斜体等,还支持插入图片、链接、表格等富媒体元素,使用户能够创建出更具表现力的文档。 5. 兼容性强:h5移动端富文本编辑器具有良好的兼容性,可以在主流的移动端浏览器中运行,不需要安装额外的插件或应用程序,方便用户在不同设备上进行编辑和调整。 综上所述,h5移动端富文本编辑器是一种功能强大、操作简便的工具,适用于移动设备上创建和编辑富文本内容,能够满足用户对于文本排版和富媒体元素插入的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值