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 个解决方案

AllenJiang
中间件研发,关注微信公众号 : 小哈学Java, 回复"666", 即可免费领取10G学习&面试资料

spring security 默认是开启了 csrf 防护的,而 editor.md 中使用 iframe 开启的表单上传是没有回传 csrf 相关参数,你需要在上传图片的表单中添加 csrf 相关参数,以便后台验证通过。

官方文档:https://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html#csrf-include-csrf-token-in-action

你可以在 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 给后台,即可认证通过!