SpringBoot integra el editor de texto enriquecido UEditor y resuelve problemas de CSRF

(1) Configuración y uso básicos

Descarga el complemento
https://github.com/fex-team/ueditor/releases

Inserte la descripción de la imagen aquí
Dependencia de backend

 <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20160810</version>
        </dependency>
        <dependency>
            <groupId>com.gitee.qdbp.thirdparty</groupId>
            <artifactId>ueditor</artifactId>
            <version>1.4.3.3</version>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.9</version>
        </dependency>

ueditor.config.js ruta de la interfaz de solicitud unificada del servidor de configuración


        // 服务器统一请求接口路径
        , serverUrl:   "/admin/ueditor/config"

Ruta de carga de archivos personalizada

<!-- 编辑器源码文件 -->
<!-- 实例化编辑器 -->
<script type="text/javascript">
    let ue = UE.getEditor('container');
    UE.Editor.prototype._bkGetActionUrl = UE.Editor.prototype.getActionUrl;
    UE.Editor.prototype.getActionUrl = function (action) {
     
     
        if (action === 'uploadimage' || action === 'uploadscrawl' || action === 'uploadimage') {
     
     
            return '/admin/ueditor/upload';
        } else if (action === 'uploadvideo') {
     
     
            return '/admin/ueditor/upload';
        } else {
     
     
            return this._bkGetActionUrl.call(this, action);
        }
    }
</script>

Referencia de archivo externo

Inserte la descripción de la imagen aquí
Interfaz de backend

import cn.hutool.core.text.StrBuilder;
import com.aisino.common.config.client.FastDfsClient;
import com.aisino.common.entity.OperatorFile;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;


/***
 *
 *  UEditor富文本编辑器处理器
 *
 * @author ZhangYu
 * @date 2021/3/4
 */
@RestController
@RequestMapping("/admin/ueditor")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UEditorServerRestController {
    
    

    private final Logger logger = LoggerFactory.getLogger(UEditorServerRestController.class);

    private final FastDfsClient fastDFSClient;

    private static final String CONFIG_JSON_PATH = "static/js/ueditor/config.json";


    /***
     *  UEditor获取config.json配置信息
     * @author ZhangYu
     * @date 2021/3/4
     */
    @RequestMapping(value = "/config")
    public String config() throws IOException {
    
    
        ClassPathResource resource = new ClassPathResource(CONFIG_JSON_PATH);
        JSONObject jsonObject = new JSONObject();
        InputStream is = null;
        try {
    
    
            is = new FileInputStream(resource.getFile());
            StrBuilder strBuilder = new StrBuilder();
            byte[] bytes = new byte[1024];
            int length;
            while (-1 != (length = is.read(bytes))) {
    
    
                strBuilder.append(new String(bytes, 0, length, StandardCharsets.UTF_8));
            }
            String result = strBuilder.toString().replaceAll("/\\*(.|[\\r\\n])*?\\*/", "");
            jsonObject = JSON.parseObject(result);
        } catch (IOException e) {
    
    
            logger.error("UEditor读取config.json配置文件错误");
        } finally {
    
    
            assert is != null;
            is.close();
        }
        return jsonObject.toJSONString();
    }


    /***
     *  UEditor文件上传接口
     * @param files 文件列表
     * @param response 输出流
     * @date 2021/3/4
     */
    @PostMapping("/upload")
    public void upload(@NotNull @RequestParam("upfile") MultipartFile[] files, HttpServletResponse response) throws IOException {
    
    
        for (MultipartFile file : files) {
    
    
            //上传文件至文件服务器
            OperatorFile operatorFile = fastDFSClient.uploadFile(file);
            String url = operatorFile.getUrl();
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("state", "SUCCESS");
            jsonObject.put("url", url);
            jsonObject.put("title", "");
            jsonObject.put("original", "");
            //输出结果回显UEditor文本编辑器
            response.getWriter().write(jsonObject.toJSONString());
        }
    }
}

(2) El editor de texto enriquecido de UEditor resuelve el problema de interceptación de CSRF

后端开启了CSRF拦截,使用的安全框架是SpringSecurity,但是就自己了解到UEditor并没有提供自定义方法设置头部,但是对于CSRF又不能放行,于是自己就根据源码进行了修改。

De acuerdo con las reglas de CSRF, debe proporcionar un Token en el encabezado para asegurarse de que se pasa el permiso. Luego, después de observar UEditor, se encuentra que hay tres lugares involucrados en la carga de imágenes, y cada uno de ellos usa componentes diferentes .
1. imagen única carga (envío del formulario)
2, imagen múltiple de carga (Ajax)
3. Arrastrar y soltar la carga de imágenes (Ajax)

Dado que CSRF_TOEKN debe obtenerse en muchos lugares, el Token se coloca directamente en localStorage para mayor comodidad, y los siguientes Tokens se obtendrán desde aquí

<script>
    localStorage.setItem("X-CSRF-TOKEN", "[[${_csrf.token}]]");
</script>
【Carga de una sola imagen】

La lógica de procesamiento de carga de una sola imagen está en ueditor.all.js
Inserte la descripción de la imagen aquí

Al observar el código, podemos encontrar que el formulario se usa para enviar el archivo. Para este problema, utilicé dos formas de resolver el problema. La primera es agregar un espacio oculto en el formulario original para agregar un campo oculto csrf. La segunda forma es comentar la lógica del código fuente, usar AJAX para reescribirlo y luego agregar el encabezado y agregar el atributo CSRF al encabezado. Ambos métodos son posibles. Aquí usamos una forma más simple para pasar valores.

Agregue la siguiente línea de código al HTML original y pase csrf_token

 ' <input type="hidden"   id="csrf_token"   name="_csrf"   value="' + localStorage.getItem("X-CSRF-TOKEN") + '" > ' +

Inserte la descripción de la imagen aquí
Nota:
El valor del nombre del control oculto aquí es muy importante. No se equivoque. Veo que todo tipo de cosas escritas en Internet son iguales. No sé si las demás tienen razón. Pero si usted es con Spring Security, solo hay dos. Formas
1. Solicitar encabezado con el atributo X-CSRF-TOKEN
2. Solicitar parámetros con el atributo _csrf

En el código fuente de Spring Security,
org.springframework.security.web.csrf.CsrfFilter.doFilterInternal () hará el siguiente procesamiento de juicio de valor

Inserte la descripción de la imagen aquí

Inserte la descripción de la imagen aquí

Tenga cuidado de no equivocarse. Creo que muchos csrf_tokens en Internet no son aceptables. La carga de una sola imagen se puede modificar. Si desea traer otros tokens, debe modificar esto para cargar en AJAX.

Inserte la descripción de la imagen aquí

【Carga de múltiples imágenes】

El componente correspondiente para la carga de múltiples imágenes es el componente dialogs / images / images.js, que utiliza el método de envío Ajax
Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí

Busque este método de evento de registro y luego agregue el encabezado que lleva csrf

header['X-CSRF-TOKEN'] = localStorage.getItem("X-CSRF-TOKEN");

Inserte la descripción de la imagen aquí

[Arrastra y suelta imágenes para subir]

Generalmente, arrastraremos directamente las imágenes en la carpeta local al editor de texto enriquecido o las cargaremos con CTRL + V. Esto también requiere una modificación separada del código fuente.

La lógica de procesamiento principal está en los siguientes componentes

Inserte la descripción de la imagen aquí

Simplemente agregue la siguiente línea de código

 xhr.setRequestHeader('X-CSRF-TOKEN',localStorage.getItem("X-CSRF-TOKEN"));

Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/Octopus21/article/details/114536939
Recomendado
Clasificación