You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Spring Boot从主机公开静态文件并规避Spring Security认证

问题描述

部署后端服务的主机上存储了一些PDF文件,需要公开提供,同时支持客户端通过开发的模块动态替换旧文件。为避免接口服务的性能损耗,选择配置静态资源路径,在application.properties中添加了:

spring.web.resources.static-locations=file:/home/spidey/sopon3/rda-aof/

现在可通过my-devdomain.com/public-data.pdf访问文件,但受Spring Security限制,访问需要认证。已在安全配置中尝试放行/rda-aof/**路径,但未生效,且不能直接放行所有端点,寻求解决方案及其他可行实现方法。

安全配置代码如下:

package ca.p6son.spring.config.security;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import java.util.Arrays;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    Environment env;
    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    @Autowired
    private UserDetailsService jwtUserDetailsService;
    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        logger.info("AuthManager init: " + super.authenticationManagerBean().toString());
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        boolean isProd = (Arrays.stream(env.getActiveProfiles()).findFirst().orElse(null)).equals("prod");

        http
                .authorizeRequests()
                    .antMatchers("/api/v1/geojson/atm",
                            isProd ? "/api/v1/invalid/path1" : "/swagger-ui/**",
                            isProd ? "/api/v1/invalid/path2" : "/v3/api-docs/**",
                            "/api/v1/account/resend_otp",
                            "/api/v1/resource/**",
                            "/api/v1/geojson/branches",
                            "/api/v1/guides",
                            "/api/v1/faq",
                            "/actuator/**",
                            "/api/v1/products",
                            "/api/v1/version_validate",
                            "/api/v1/security_guides",
                            "/api/v1/biometric_challenge", // To enable
                            "/api/v1/bio-override", 
                            "/api/v1/dao/bio-override", 
                            "/api/v1/push-notification", 
                            "/api/v1/deals_and_offers",
                            "/api/v1/city_list",
                            "/api/v1/users",
                            "/api/v1/cleanup/*",
                            "/api/v1/account/forgot_username/*",
                            "/api/v1/account/forgot_password/*",
                            "/api/v1/auth/**",
                            "/api/v1/validate/pin",
                            "/api/v1/test",
                            "/api/v1/cleanup",
                            "/inter-module/**",
                            "/rda-aof/**",
                            "/api/v1/verify-email-link",
                            "/api/v1/robo/**",
                            "/api/v1/terms_and_conditions").permitAll()
                    .anyRequest().authenticated()
                .and()
                    .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .and()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .headers(headers -> {
                    headers.addHeaderWriter((request, response) -> {
                        String requestURI = request.getRequestURI();
                        if (!(requestURI.startsWith("/api/v1/resource/alerts") || requestURI.startsWith("/api/v1/verify-email-link"))) {
                            response.addHeader("Referrer-Policy", "strict-origin-when-cross-origin");
                            response.addHeader("Content-Security-Policy", "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self'; base-uri 'self'; form-action 'self'");
                            response.addHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
                            response.addHeader("Permissions-Policy", "geolocation=(self), microphone=(), camera=()");
                        }
                    });
                })
                .cors().and().csrf().disable();

        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }
}
解决方案

问题根源

你配置的静态资源路径file:/home/spidey/sopon3/rda-aof/被Spring默认映射到根路径(即/),所以文件访问路径是/public-data.pdf,但Security中放行的是/rda-aof/**,两者路径不匹配,导致放行规则无效。

方案1:修正静态资源映射前缀,匹配Security规则

修改application.properties,添加静态资源访问前缀配置:

spring.web.resources.static-locations=file:/home/spidey/sopon3/rda-aof/
spring.mvc.static-path-pattern=/rda-aof/**

修改后文件访问路径变为my-devdomain.com/rda-aof/public-data.pdf,与Security中/rda-aof/**的放行规则完全匹配,无需修改Security代码即可生效。

方案2:直接放行当前文件访问路径

如果不想修改访问路径,直接在Security的antMatchers中添加PDF文件的放行规则:
在permitAll()的路径列表中加入"/*.pdf"(放行所有根路径下的PDF文件),或精确到单个文件"/public-data.pdf"。修改后的片段示例:

.antMatchers(
    // 原有路径...
    "/rda-aof/**",
    "/*.pdf", // 新增:放行根路径下所有PDF
    "/api/v1/verify-email-link",
    // 其他路径...
).permitAll()

方案3:自定义静态资源映射(更灵活)

通过WebMvcConfigurer手动配置静态资源映射,同时确保Security放行对应路径,这种方式便于后续扩展:

  1. 在配置类中添加WebMvcConfigurer Bean:
@Bean
public WebMvcConfigurer staticResourceConfigurer() {
    return new WebMvcConfigurerAdapter() {
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            // 配置访问前缀为/rda-aof/**,映射到本地文件路径
            registry.addResourceHandler("/rda-aof/**")
                    .addResourceLocations("file:/home/spidey/sopon3/rda-aof/");
        }
    };
}
  1. 确保Security配置中/rda-aof/**已在permitAll()列表中(当前配置已包含),之后即可通过my-devdomain.com/rda-aof/public-data.pdf访问文件,且支持动态替换文件。

额外检查

确认JwtRequestFilter没有绕过Security授权规则,只要antMatchers配置了permitAll,Spring Security会在过滤器执行前放行这些请求,无需修改过滤器逻辑。

内容的提问来源于stack exchange,提问作者Hussain Ahmed Siddiqui

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 08:28:12