当前位置: 首页 > 知识库问答 >
问题:

尽管配置完整,但无法使用Spring Security将文件上传到Spring MVC

狄鸿禧
2023-03-14

我正在尝试上载。pdf文件,带有jQuery AJAX到Spring MVC 5,Spring Security 5后端在Tomcat上运行,根据Spring配置面临多个问题

注:

文件上传应该可以在没有身份验证的情况下使用

标记:

<div id="upload-modal" class="modal">
    <div class="modal-content">
        <h4>Upload</h4>
        <form action="#" enctype="multipart/form-data">
            <div class="file-field input-field">
                <div class="btn">
                    <span>View...</span>
                    <input type="file" name="file" accept="application/pdf">
                </div>
                <div class="file-path-wrapper">
                    <label>
                        <input class="file-path validate" type="text">
                    </label>
                </div>
            </div>
        </form>
    </div>
    <div class="modal-footer">
        <a href="#" class="modal-close waves-effect waves-green btn-flat">Cancel</a>
        <a href="#" id="upload-bttn" class="waves-effect waves-light btn-flat btn">Upload</a>
    </div>
</div>

所有请求的csrf标头:

$(document).ready(function () {
    var token = $("meta[name='_csrf']").attr("content");
    var header = $("meta[name='_csrf_header']").attr("content");

    $(document).ajaxSend(function (e, xhr, options) {
        xhr.setRequestHeader(header, token);
    });
});

使用jQuery AJAX上传:

$("#upload-bttn").click(function () {
    var $uploadModal = $("#upload-modal");
    const fileName = $uploadModal.find(".file-path").val();
    const extension = fileName.substr(fileName.lastIndexOf(".") + 1);
    if (extension === "pdf") {
        $.ajax({
            url: "/upload",
            type: "POST",
            data: new FormData($uploadModal.find("form").get(0)),
            processData: false,
            contentType: false,
            success: function () {
                console.log("success")
            },
            error: function () {
                console.log("error")
            }
        });
    } else {
        M.toast({html: 'Selected file is not .pdf'});
    }
});

一般配置如下所示。根据具体情况进行修改

安全初始化:

public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {

    public SecurityInitializer() {
        super(SecurityContext.class);
    }

    @Override
    protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
        insertFilters(servletContext, new MultipartFilter());
    }

}

应用程序初始化:

public class ApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) {

        servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));
        servletContext.getSessionCookieConfig().setHttpOnly(true);
        servletContext.getSessionCookieConfig().setSecure(true);

        AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
        dispatcherServlet.register(WebAppContext.class);

        ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(dispatcherServlet));
        servlet.addMapping("/");
        servlet.setLoadOnStartup(1);
    }
}

Common sMultipartResolverbean定义

@Bean
public CommonsMultipartResolver multipartResolver(
        @Value("${max.upload.size}") Integer maxNumber,
        @Value("${max.size}") Integer maxSize) {

    CommonsMultipartResolver resolver = new CommonsMultipartResolver();
    resolver.setMaxUploadSize(1024 * maxSize * maxNumber);
    resolver.setMaxUploadSizePerFile(maxSize);
    resolver.setMaxInMemorySize(maxSize);
    resolver.setDefaultEncoding("UTF-8");
    try {
        resolver.setUploadTempDir(new FileSystemResource(System.getProperty("java.io.tmpdir")));
    } catch (IOException e) {
        e.printStackTrace();
    }
    return resolver;
}

我记得当bean应该显式地命名为“MultipartResolver”时,有一个奇怪的Spring行为。我用上面的配置尝试了@Bean@Bean(“multipartResolver”),得到了相同的结果(尽管根据方法名称,上面的Bean被命名为“multipartResolver”)

结果:

错误500-无法处理部件,因为未提供多部件配置

  • 已删除CommonsMultipartResolver

标准ServletMultipartResolver bean定义:

@Bean
public StandardServletMultipartResolver multipartResolver() {
    return new StandardServletMultipartResolver();
}

更新了ApplicationInitializer:

@Override
public void onStartup(ServletContext servletContext) {
    ...
    servlet.setMultipartConfig(new MultipartConfigElement(
            System.getProperty("java.io.tmpdir")
    ));
}

根据Spring文档:

确保在Spring Security筛选器之前指定MultipartFilter。在Spring Security过滤器之后指定MultipartFilter意味着没有调用MultipartFilter的授权,这意味着任何人都可以在服务器上放置临时文件。但是,只有授权用户才能提交由您的应用程序处理的文件

因为我需要允许未经身份验证的用户上载我在SecurityInitializer中尝试之前和之后的文件,结果如下

@Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
    insertFilters(servletContext, new MultipartFilter());
}

@Override
protected void afterSpringSecurityFilterChain(ServletContext servletContext) {
    insertFilters(servletContext, new MultipartFilter());
}

结果:

错误403

  • 我在配置中错过了什么?
  • 最好使用CommonMultipartResolver,因为它允许使用Spring属性驱动它
  • Spring Security上下文设置有问题
  • 有一个allowCasualMultipartParsing=“true”选项(未测试),我不想将其作为特定于Tomcat的选项
  • 禁用Spring Security后,一切正常
  • http。authorizeRequests()。antMatchers(“/**”)。permitAll() 仍然是唯一的安全上下文配置,因此不要认为它的安全上下文配置有问题
  • 在springsecurityfilterchain(ServletContext-ServletContext)之前的MultipartFilter中显式设置多部分解析器bean名称,但仍然没有运气
  • 将csrf令牌添加到请求标头并不能同时用于这两种情况
  • 意识到我错过了SecurityInitializer构造函数中额外的WebAppContext类。现在错误500消失了,但案例1出现了403。日志显示,尽管我像上面那样将其添加到标题中,但我的csrf令牌无效
  • 试图使用csrf令牌提交表单,包括隐藏输入

共有1个答案

林昱
2023-03-14

经过两天的挣扎:

构造函数应同时包含安全和应用程序上下文配置类

public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {

    public SecurityInitializer() {
        super(SecurityContext.class, WebAppContext.class);
    }

}

应用程序上下文(WebAppContext)应包含多部分解析器bean定义

@Bean
public CommonsMultipartResolver multipartResolver(
        @Value("${max.upload.size}") Integer maxNumber,
        @Value("${max.size}") Integer maxSize) {

    CommonsMultipartResolver resolver = new CommonsMultipartResolver();
    resolver.setMaxUploadSize(1024 * maxSize * maxNumber);
    resolver.setMaxUploadSizePerFile(maxSize);
    resolver.setMaxInMemorySize(maxSize);
    resolver.setDefaultEncoding("UTF-8");
    try {
        resolver.setUploadTempDir(new FileSystemResource(System.getProperty("java.io.tmpdir")));
    } catch (IOException e) {
        e.printStackTrace();
    }
    return resolver;
}

在我的情况下,在应用程序初始化之后,Spring中的令牌因某种原因为空,因此当Spring将客户端请求头中的令牌与CsrfFilter中的null进行比较时,Spring返回错误403。我在安全上下文中以以下方式配置了csrf:

@Override
protected void configure(HttpSecurity http) throws Exception {
    ...
    http.csrf().csrfTokenRepository(new CookieCsrfTokenRepository());
    ...    
}

现在csrf令牌在cookie中传递,第一个服务器响应浏览器,存储库生成并缓存一个令牌以与来自客户端的令牌进行比较,因此比较成功通过

此处,CookieCsrfTokenRepository也可以声明为CookieCsrfTokenRepository。如果您想从cookie中获取令牌并将其设置到csrf头,请使用httponlyfalse(),但我选择了上面的元标记方法

 类似资料:
  • 我使用下面的代码上传一个文本文件到服务器,它可以工作。 但当我从服务器下载文本文件时,我无法打开它,并发现文本文件中添加了其他消息(粗体文本)。 --RzBVXI2AHuDiIU5UHz-A1jZrpEg6a0JY内容处理:表单数据;name=“userfile”;filename=“test.txt”内容类型:应用程序/八位字节流内容传输编码:二进制 所容纳之物 --RzBVXI2AHuDiIU

  • 我已经迁移了所有内容并将应用程序部署到新服务器。当我尝试运行它时,我得到以下异常: 尝试使用提供程序URL获取初始上下文时发生通信故障:“corbaloc:iiop:127.0.0.1:2809”。确保URL中的任何引导地址信息正确,并且目标名称服务器正在运行。没有端口规范的引导地址默认为端口2809。除了不正确的引导地址或名称服务器不可用之外,可能的原因包括网络环境和工作站网络配置。 现在,在完

  • 本文向大家介绍SpringMVC 文件上传配置,多文件上传,使用的MultipartFile的实例,包括了SpringMVC 文件上传配置,多文件上传,使用的MultipartFile的实例的使用技巧和注意事项,需要的朋友参考一下 基本的SpringMVC的搭建在我的上一篇文章里已经写过了,这篇文章主要说明一下如何使用SpringMVC进行表单上的文件上传以及多个文件同时上传的步骤 文件上传项目的

  • 我试图在AWSS3中上传错误文件,但它显示错误,如“您试图访问的存储桶必须使用指定的endpoint寻址。请将所有未来的请求发送到此endpoint:”test9011960909。s3。亚马逊。com“ 我还指定了“区域”= 当我指定时,它正在工作 但我想把文件上传到主bucket的子文件夹中 我已经应用了AWS S3批准的答案:您试图访问的桶必须使用指定的终结点进行寻址 但仍然得到同样的错误,

  • 在托管域上添加mariadb jdbc驱动程序时遇到问题,profile=full-->出现以下错误 我的当前配置 这是我使用的文件jar mariadb-java-client-2.7.1.jar

  • 远程:对mqshaikh8/amigo.git的权限被拒绝给kshaikh99。致命:无法访问“https://github.com/mqshaikh8/amigo.git/”:请求的URL返回错误:403 git remote--V origin https://github.com/mqshaikh8/amigo.git(fetch)origin https://github.com/mqsh