(一)基本配置使用
下载插件
https://github.com/fex-team/ueditor/releases
后端依赖
<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配置服务器统一请求接口路径
// 服务器统一请求接口路径
, serverUrl: "/admin/ueditor/config"
自定义文件上传路径
<!-- 编辑器源码文件 -->
<!-- 实例化编辑器 -->
<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>
外部文件参考
后台接口
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());
}
}
}
(二)UEditor富文本编辑器解决CSRF拦截问题
后端开启了CSRF拦截,使用的安全框架是SpringSecurity,但是就自己了解到UEditor并没有提供自定义方法设置头部,但是对于CSRF又不能放行,于是自己就根据源码进行了修改。
根据CSRF规则需要在头部提供一个Token保证权限通过,然后在观察了UEditor发现其中涉及了三个地方的图片上传,而且每个都是使用的不同组件方法
1、单图片上传(表单提交)
2、多图片上传(Ajax)
3、拖拽图片上传(Ajax)
由于涉及到很多的地方都需要拿到CSRF_TOEKN,这里为了方便直接将该Token放在了localStorage中,后面的Token都将从这里获取
<script>
localStorage.setItem("X-CSRF-TOKEN", "[[${_csrf.token}]]");
</script>
【单图片上传】
单图片的上传处理逻辑在ueditor.all.js中
通过观察代码可以发现,这里使用的是Form表单的方式提交文件,针对这个问题我使用了俩种方式来解决的,第一种是在原来的表单中添加hidden类型的空间添加csrf隐藏域解决,第二种方式将源码的逻辑注释掉了,自己使用AJAX重写然后增加了Header,并在header添加CSRF属性,俩个方式都是可以的,这里使用更为简单的表单传值方式。
在原来的HTML中加入下面这一行代码,传递csrf_token
' <input type="hidden" id="csrf_token" name="_csrf" value="' + localStorage.getItem("X-CSRF-TOKEN") + '" > ' +
注意事项:
这里隐藏控件的name值很重要,别写错了,我看网上写的各种各样的都是一样,我不清楚别的对不对,但是如果你使用的是SpringSecurity的话,只有俩种方式
1、请求头带有X-CSRF-TOKEN属性
2、请求参数带有_csrf属性
在SpringSecurity源码中
org.springframework.security.web.csrf.CsrfFilter.doFilterInternal()其中会做如下取值判断处理
注意别写错了,我看网上很多csrf_token是不行的,单图片的上传这样修改就可以了,如果你想带其他Token,还是把这个修改成AJAX的方式上传。
【多图片上传】
多图上传对对应的组件是dialogs/images/images.js组件,使用的是Ajax提交方式
找到这个注册事件方法,然后添加携带csrf头部即可
header['X-CSRF-TOKEN'] = localStorage.getItem("X-CSRF-TOKEN");
【拖拽图片上传】
一般时候我们会直接将本地文件夹的图片拖到富文本编辑器中或者通过CTRL+V的方式上传,这个也需要单独修改源码
主要的处理逻辑在下面的组件中
添加下面这行代码即可
xhr.setRequestHeader('X-CSRF-TOKEN',localStorage.getItem("X-CSRF-TOKEN"));