在Spring Boot和Spring Security应用程序中提供静态Web资源

98

我正在尝试开发Spring Boot Web应用程序,并使用Spring Security Java配置对其进行安全保护。

根据Spring博客中的建议,将我的静态Web资源放置在“src/main/resources/public”中后,我能够获取静态资源。也就是说,在浏览器中输入https://localhost/test.html可以提供HTML内容。

问题

启用Spring Security后,访问静态资源URL需要进行身份验证。

我相关的Spring Security Java配置如下:

@Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http.
            authorizeRequests()
                .antMatchers("/","/public/**", "/resources/**","/resources/public/**")
                    .permitAll()
                .antMatchers("/google_oauth2_login").anonymous()
                    .anyRequest().authenticated()
                .and()
                .formLogin()
                    .loginPage("/")
                    .loginProcessingUrl("/login")
                    .defaultSuccessUrl("/home")
                    .and()
                    .csrf().disable()
                    .logout()
                        .logoutSuccessUrl("/")
                        .logoutUrl("/logout") // POST only
                .and()
                    .requiresChannel()
                    .anyRequest().requiresSecure()
                .and()
                    .addFilterAfter(oAuth2ClientContextFilter(),ExceptionTranslationFilter.class)
                    .addFilterAfter(googleOAuth2Filter(),OAuth2ClientContextFilter.class)
                .userDetailsService(userService);
        // @formatter:on
    }

我该如何配置 antMatchers 来允许放置在 src/main/resources/public 内的静态资源?

请参见https://dev59.com/d2Af5IYBdhLWcg3w52E_#24179151。 - Samuli Pahaoja
请注意,您可能需要在静态内容(包括任何默认/自定义错误页面)中添加内容安全头以防止点击劫持等攻击。具体操作请参考此链接 - Janaka Bandara
11个回答

123

需要注意以下几点:

  • Ant匹配器会根据请求路径匹配,而不是文件系统中资源的路径。
  • 放置在 src/main/resources/public 目录下的资源将从应用程序的根目录下提供服务。例如 src/main/resources/public/hello.jpg 将被从 http://localhost:8080/hello.jpg 访问。

这就是你当前匹配器配置不允许访问静态资源的原因。要使 /resources/** 生效,您需要将资源放置在 src/main/resources/public/resources 中并通过 http://localhost:8080/resources/your-resource 进行访问。

由于你正在使用Spring Boot,你可能希望考虑使用其默认值而不是添加额外的配置。Spring Boot 默认情况下允许访问 /css/**/js/**/images/**/**/favicon.ico。例如,如果有一个名为 src/main/resources/public/images/hello.jpg 的文件,则无需添加任何额外的配置即可在 http://localhost:8080/images/hello.jpg 访问它,无需登录。您可以在web方法安全测试中看到此示例,其中允许访问Bootstrap CSS文件而无需任何特殊配置。


1
我已经克隆了Spring Boot示例库并运行了示例(Web方法安全示例)。但它没有起作用。http://localhost:8080/css/bootstrap.min.css被重定向到登录页面。 它的实现与描述的解决方案不同。静态文件的路径为:src/main/resources/static/css/ - Florin Grozea
4
如果您正在使用Spring Boot 2,请参考Thomas Lang的答案。 - Wim Deblauwe
静态文件或CSS JS应该放在src/main/resources/public文件夹中,这个public文件夹是关键。谢谢。 - asifaftab87
我认为这很必要:http.authorizeRequests().antMatchers("/css/**").permitAll() - Maayan Hope
1
我使用了 web.ignoring().antMatchers("/static/**"); 来访问静态资源,但现在 Spring Security 在登录后一直重定向到 CSS 并显示 404 页面,而不是回到主页。只有刷新后才会显示主页。我没有使用 Spring Boot,只是使用了 Spring MVC 和 Spring Security,并使用 @EnableWebSecurity 注解来激活它。 - DiegLuthor
@Andy Wilkinson,您能否重新上传您在答案中提到的“Web方法安全示例”链接?先行致谢。 - NikolaS

37
  @Override
      public void configure(WebSecurity web) throws Exception {
        web
          .ignoring()
             .antMatchers("/resources/**"); // #3
      }

忽略任何以“/resources/”开头的请求。这类似于在使用XML命名空间配置时配置http@security=none。


2
对我也不起作用。虽然我正在从API加载我的静态HTML,并引用我的一个静态文件/resources/css/main.css。由Rest API呈现的HTML页面工作正常。但是,静态CSS没有工作。 - Ranger Way

34

这可能是关于Spring Boot 2的答案和问题。 似乎在Spring Boot 2与Spring Security结合使用时,如果您使用扩展自{{individual security mechanism}}的个人安全机制,则默认情况下保护所有内容(即每个路由/antmatcher)。

WebSecurityConfigurerAdapter

如果您不使用个别的安全机制,一切都和以前一样吗?
在旧版Spring Boot(1.5及以下版本)中,正如Andy Wilkinson在他上面的回答中所述,像public/**或static/**这样的地方默认是允许的。
因此,总结这个问题/答案——如果您正在使用Spring Boot 2和Spring Security,并且有一个个别的安全机制,您必须专门允许访问任何路由上放置的静态内容。就像这样:
@Configuration
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {

private final ThdAuthenticationProvider thdAuthenticationProvider;

private final ThdAuthenticationDetails thdAuthenticationDetails;

/**
 * Overloaded constructor.
 * Builds up the needed dependencies.
 *
 * @param thdAuthenticationProvider a given authentication provider
 * @param thdAuthenticationDetails  given authentication details
 */
@Autowired
public SpringSecurityConfiguration(@NonNull ThdAuthenticationProvider thdAuthenticationProvider,
                                   @NonNull ThdAuthenticationDetails thdAuthenticationDetails) {
    this.thdAuthenticationProvider = thdAuthenticationProvider;
    this.thdAuthenticationDetails = thdAuthenticationDetails;
}

/**
 * Creates the AuthenticationManager with the given values.
 *
 * @param auth the AuthenticationManagerBuilder
 */
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {

    auth.authenticationProvider(thdAuthenticationProvider);
}

/**
 * Configures the http Security.
 *
 * @param http HttpSecurity
 * @throws Exception a given exception
 */
@Override
protected void configure(HttpSecurity http) throws Exception {

    http.authorizeRequests()
            .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
            .antMatchers("/management/**").hasAnyAuthority(Role.Role_Engineer.getValue(),
            Role.Role_Admin.getValue())
            .antMatchers("/settings/**").hasAnyAuthority(Role.Role_Engineer.getValue(),
            Role.Role_Admin.getValue())

            .anyRequest()
            .fullyAuthenticated()
            .and()
            .formLogin()
            .authenticationDetailsSource(thdAuthenticationDetails)
            .loginPage("/login").permitAll()
            .defaultSuccessUrl("/bundle/index", true)
            .failureUrl("/denied")
            .and()
            .logout()
            .invalidateHttpSession(true)
            .logoutSuccessUrl("/login")
            .logoutUrl("/logout")
            .and()
            .exceptionHandling()
            .accessDeniedHandler(new CustomAccessDeniedHandler());
}

}

请注意这行代码,它是新添加的:
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()

如果您使用的是Spring Boot 1.5及以下版本,则不需要显式允许这些位置(静态/公共/WebJars等)。这里是官方说明,新安全框架与旧版本相比有哪些变化:Spring Boot 2.0 M4中的安全变更。希望这能帮助到某些人。谢谢!祝您愉快!

3
我可以确认,对于我来说(Spring Boot 2.0.3),添加额外的一行修复了它。 - Wim Deblauwe
2
额外的代码行确实有很大帮助,但我需要再添加几行才能使其正常工作。引导版本为2.0.6。(1) .antMatchers("/", "/callback", "/login**", "/webjars/", "/error", "/static/").permitAll()和(2) registry.addResourceHandler("/static/").addResourceLocations("classpath:/static/"); 在WebMvcConfigurer.addResourceHandlers()下。 - Rites
非常感谢您! - Jazerix
1
@Thomas,我也希望这对我有用,但不幸的是这并没有起作用(Spring Boot 2.7)。我尝试了许多方法,如antMatchers和放置到公共目录以及您建议的方法。我能够访问其他URL,自定义登录页面没有CSS。基本上,在添加自定义登录页面后,我的所有静态内容都无法访问。我是否遗漏了什么或在Spring Boot 2.7中需要做其他事情? - Amit Shil

26

在经过20多个小时的研究后,这里是最终解决方案。

步骤1。 将“MvcConfig.java”添加到您的项目中。

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry
                .addResourceHandler("/resources/**")
                .addResourceLocations("/resources/");
    }
}

第二步。configure(WebSecurity web)覆盖添加到您的SecurityConfig类中。

@Override
    public void configure(WebSecurity web) throws Exception {
        web
                .ignoring()
                .antMatchers("/resources/**");
    }

步骤3。 将所有静态资源放置在webapp/resources/..


2
你能解释一下你在做什么以及为什么吗? "步骤1":添加静态资源处理。 "步骤2":删除静态资源处理。 - loshad vtapkah
2
如果有人使用XML配置,则在步骤1中,您可以在dispatcher-servlet.xml中使用此行 <mvc:resources mapping="/resources/**" location="/resources/" />,而无需创建新的Java配置类。 - gdrt

9
如果您正在使用Webjars,您需要在configure方法中添加以下内容:http.authorizeRequests().antMatchers("/webjars/**").permitAll(); 请确保这是第一条语句。例如:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/webjars/**").permitAll();
        http.authorizeRequests().anyRequest().authenticated();
         http.formLogin()
         .loginPage("/login")
         .failureUrl("/login?error")
         .usernameParameter("email")
         .permitAll()
         .and()
         .logout()
         .logoutUrl("/logout")
         .deleteCookies("remember-me")
         .logoutSuccessUrl("/")
         .permitAll()
         .and()
         .rememberMe();
    }

您还需要这个才能启用webjars:
@Configuration
    public class MvcConfig extends WebMvcConfigurerAdapter {
        ...
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
                registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        }
        ...
    }

1
WebMvcConfigurerAdapter已经被弃用,因此您可以使用WebMvcConfigurationSupport。 - PratikShah

8
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        String[] resources = new String[]{
                "/", "/home","/pictureCheckCode","/include/**",
                "/css/**","/icons/**","/images/**","/js/**","/layer/**"
        };

        http.authorizeRequests()
                .antMatchers(resources).permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout().logoutUrl("/404")
                .permitAll();
        super.configure(http);
    }
}

调用 super.configure 不会启用基本身份验证吗? - Peter Chaula

4

我的Spring Boot应用程序也遇到了同样的问题,所以我想与大家分享我的解决方案。我只是简单地配置了antMatchers以适应特定类型的文件。在我的情况下,这只是js文件和js.map文件。以下是代码:

   @Configuration
   @EnableWebSecurity
   public class SecurityConfig extends WebSecurityConfigurerAdapter {

   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http.authorizeRequests()
      .antMatchers("/index.html", "/", "/home", 
       "/login","/favicon.ico","/*.js","/*.js.map").permitAll()
      .anyRequest().authenticated().and().csrf().disable();
   }
  }

有趣的是,我发现在antMatcher中像"resources/myStyle.css"这样的资源路径对我完全没有用。如果您在资源文件夹中有一个文件夹,只需将其添加到antMatcher中,如"/myFolder/myFille.js"*,它应该可以正常工作。


对于需要大量资源的人:http.authorizeRequests().antMatchers(HttpMethod.GET, "/", "/index.html", "/favicon.ico", "//*.js", "//.js.map", "/**/.css", "/assets/images/.png", "/assets/images/.jpg", "/assets/images/.jpeg", "/assets/images/.gif", "//*.ttf", "//.json", "/**/.woff", "//*.woff2", "//.eot", "/**/.svg").permitAll() 如果你想知道为什么有双星号。使用 ** 表示允许任何具有该扩展名的文件。还要注意 HTTPMETHOD.GET。将 /assets/images 替换为您自己的文件夹。否则只需放置 /*.jpg。 - Merv

3
在最新的Spring Security 6中,WebSecurityConfigurerAdapter已被弃用。
应该声明一个WebSecurityCustomizer bean代替。
 @Bean
 public WebSecurityCustomizer ignoringCustomizer() {
     return (web) -> web.ignoring().requestMatchers("...");
 }

3

这适用于Spring Security 6.0.*版本。

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {


        http
                .csrf()
                .disable()
                .authorizeHttpRequests()
                .requestMatchers(
                        "/home/**",
                        "/login/**",
                        "/account/starter/**",
                        "/register/**",
                        "/plugins/**",
                        "/dist/**",
                        "/js/**",
                        "/**/favicon.ico").permitAll()
                .and()
                .httpBasic()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        return http.build();
    }


 
                        "/plugins/**",
                        "/dist/**",
                        "/js/**",

...它们位于资源文件夹中(resources/)

plugins、dist、js - 这些是带有资源的目录名称。


1
另一个Spring Security 6的示例,遵循文档建议“优先使用permitAll而不是忽略”,并使用PathRequest创建静态资源请求匹配器:
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http
                .securityMatcher("/**")
                .authorizeHttpRequests(
                        authorizationManagerRequestMatcherRegistry ->
                                authorizationManagerRequestMatcherRegistry
                                        .requestMatchers(
                                                PathRequest
                                                        .toStaticResources()
                                                        .atCommonLocations())
                                        .permitAll()
                                        .requestMatchers("/**")
                                        .fullyAuthenticated()
                )
                
                ...
                
                ;

        return http.build();
    }

我有一个几乎和这个一样的代码。但是它不起作用。我的代码是.....requestMatchers("/css/**").permitAll().anyRequest().authenticated()。当我访问http://localhost:8080/css/myCss.css时,我得到了"No Mapping for GET /css/myCss.css"的错误提示。 - Nick Wills
myCss.css位于resources/static/css/myCss.css下。 - Nick Wills
@NickWills "No Mapping for GET..."错误与Spring Security配置无关。 - Tyutyutyu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接