Markdown 编辑器 editor.md 上传图片到后台服务,后台接口报 403 错误
我在 editor.md 中开启了上传图片功能:
$(function() {
editormd("editormd1", {
width: "100%",
height: 600,
syncScrolling: "single",
path: "/plugins/editor-md/lib/", // 依赖的 lib 目录
imageUpload : true,
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
imageUploadURL : "/file/editorMdImg/upload" // 依赖的 lib 目录
});
});
后台也定义了 /file/editorMdImg/upload
接口,代码如下:
@RestController
@RequestMapping("/file")
@Slf4j
public class FileController extends BaseController {
@Autowired
private FileBusiness fileBusiness;
/**
* 文件上传
*
* @return
*/
@PostMapping("/editorMdImg/upload")
public EditorMdUploadImageResponse editorMdImageUpload(@RequestParam(value = "editormd-image-file") MultipartFile file) {
return fileBusiness.editorMdImageUpload(file);
}
}
security 配置:
/**
* http 权限控制
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 资源访问控制
http.authorizeRequests()
.antMatchers("/user/login").permitAll()
.antMatchers("/home/**").permitAll()
.antMatchers("/tag/**").permitAll()
// .antMatchers("/question/**").permitAll()
// .antMatchers("/static/**").permitAll()
.antMatchers(HttpMethod.POST, "/question").hasRole("USER")
.antMatchers(HttpMethod.POST, "/question/edit").hasRole("USER")
.antMatchers(HttpMethod.POST, "/answer/edit").hasRole("USER")
.antMatchers(HttpMethod.POST,"/file/editorMdImg/upload").hasRole("USER")
.and()
.formLogin()
.loginPage("/user/login") // 配置角色登录处理入口
.loginProcessingUrl("/login")
.defaultSuccessUrl("/home") // 设置默认登录后 forword url
.and()
.headers()
.frameOptions()
.disable();
}
我的整个环境如下:
- 1.后台 springboot + security
- 2.前端 thymeleaf
1 个解决方案
spring security 默认是开启了 csrf 防护的,而 editor.md 中使用 iframe 开启的表单上传是没有回传 csrf 相关参数,你需要在上传图片的表单中添加 csrf 相关参数,以便后台验证通过。
你可以在 editor.md 的显示页面添加隐藏域,以便点击上传图片时,获取 csrf 相关参数:
<input type="hidden" th:value="${_csrf.parameterName}" id="parameterName">
<input type="hidden" th:value="${_csrf.token}" id="token">
然后,你需要在 editor.md
引入的插件 imag-dialog.js
中的代码中添加 csrf 参数:
// get the token
var parameterName = $('#parameterName').val();
var token = $('#token').val();
var dialogContent = ( (settings.imageUpload) ? "<form action=\"" + action + "&" + parameterName + "=" + token +"\" target=\"" + iframeName + "\" method=\"post\" enctype=\"multipart/form-data\" class=\"" + classPrefix + "form\">" : "<div class=\"" + classPrefix + "form\">" ) +
( (settings.imageUpload) ? "<iframe name=\"" + iframeName + "\" id=\"" + iframeName + "\" guid=\"" + guid + "\"></iframe>" : "" ) +
"<label>" + imageLang.url + "</label>" +
"<input type=\"text\" data-url />" + (function(){
return (settings.imageUpload) ? "<div class=\"" + classPrefix + "file-input\">" +
"<input type=\"file\" name=\"" + classPrefix + "image-file\" accept=\"image/*\" />" +
"<input type=\"submit\" value=\"" + imageLang.uploadButton + "\" />" +
"</div>" : "";
})() +
"<br/>" +
"<label>" + imageLang.alt + "</label>" +
"<input type=\"text\" value=\"" + selection + "\" data-alt />" +
"<br/>" +
"<label>" + imageLang.link + "</label>" +
"<input type=\"text\" value=\"http://\" data-link />" +
"<br/>" +
( (settings.imageUpload) ? "</form>" : "</div>");
在其 <form>
表单的 action 中回传 csrf token
给后台,即可认证通过!