如何拒绝访问没有 PreAuthorize 注释的控制器?

问题描述 投票:0回答:1

我这里有 Spring Boot Web 应用程序。

升级为以下设置:

  • Java 17
  • 春季启动 3.0.4
  • 春季安全 6.0.2
  • Jetty 11 服务器

当我启动应用程序时,我在控制台上看到这一行:

使用 Spring Boot v3.0.4、Spring v6.0.6 运行

我在使用 Spring Security 配置时遇到问题。

application.properties
我有这样的说法:

spring.security.filter.dispatcher-types=request,async,error,forward,include

我正在关注这个 Spring Boot 迁移指南:

接下来是安全配置 Java 文件。

@Configuration
@Order(SecurityProperties.DEFAULT_FILTER_ORDER)
@EnableMethodSecurity(securedEnabled = true, prePostEnabled = true)
@EnableWebSecurity
public class SecConfiguration {

    private final HouseUserDetailsService customUserDetailsService;
    private final HouseShiftConfiguration dashShiftConfig;

    private final DBI dbi;
    private final AuditLogService auditLogService;

    @Autowired
    private CustomSecurityconfiguration customSecurityConfiguration;

    @Autowired
    public SecConfiguration(HouseUserDetailsService customUserDetailsService, DBI dbi, HouseShiftConfiguration dashShiftConfig, AuditLogService auditLogService) {
        this.customUserDetailsService = customUserDetailsService;
        this.dbi = dbi;
        this.dashShiftConfig = dashShiftConfig;
        this.auditLogService = auditLogService;
    }

    @Bean
    public HouseRedirectAuthenticationSuccessHandler houseRedirectAuthenticationSuccessHandler() {
        return new HouseRedirectAuthenticationSuccessHandler(dbi);
    }
  
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfiguration) throws Exception {
       return authConfiguration.getAuthenticationManager();
    }
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
        requestCache.setMatchingRequestParameterName("continue");

        CsrfTokenRequestAttributeHandler csrfRequestHandler = new CsrfTokenRequestAttributeHandler();
        csrfRequestHandler.setCsrfRequestAttributeName("_csrf");// set the name of the attribute the CsrfToken will be populated on

        XorCsrfTokenRequestAttributeHandler xorCsrfRequestHandler = new XorCsrfTokenRequestAttributeHandler();
        xorCsrfRequestHandler.setCsrfRequestAttributeName("_csrf");// set the name of the attribute the CsrfToken will be populated on
        
        //csrf white list, when you don't want to use csrf, put your url here.
        String[] csrfWhiteList = {
                "/emailSender/**"
        };

        http
            .csrf((csrf) -> csrf
                .csrfTokenRequestHandler(csrfRequestHandler)
            )
            .csrf((xorCsrf) -> xorCsrf
                .csrfTokenRequestHandler(xorCsrfRequestHandler)
            )
            .csrf()
                .ignoringRequestMatchers(csrfWhiteList) 
                .and() 
            .rememberMe()
                .rememberMeServices(dashShiftConfig.tokenBasedRememberMeServicesCore())
                .and()
            .authorizeHttpRequests(auth -> auth
                .dispatcherTypeMatchers(DispatcherType.FORWARD/*, DispatcherType.INCLUDE, DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST*/).permitAll()
                .requestMatchers("/accessDenied", "/security_check", "/img/*", "/js/*", "/css/*", "/?*", "/", "/login*").permitAll()
                .anyRequest().denyAll())           
            .formLogin()
                .loginPage("/")
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/security_check")
                .failureUrl("/?r=fail")
                .failureHandler(HouseAuthFailureHandler())
                .successHandler(HouseRedirectAuthenticationSuccessHandler())
                .and()    
            .logout()
                .logoutUrl("/logout")
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessHandler(customLogoutHandler())
                .deleteCookies("JSESSIONID","rm")
                .invalidateHttpSession(true)
                .and()
            .sessionManagement()
                .invalidSessionUrl("/?r=invalidSessionUrlLogin")
                .and()
            .exceptionHandling()
                .accessDeniedHandler(accessDeniedHandler());

        http
            .headers()
                .frameOptions().disable()
                .addHeaderWriter(new StaticHeadersWriter("X-FRAME-OPTIONS", "ALLOW-FROM https://www.secure-service.com"));

        http
            .headers()
                .xssProtection(xssProtection -> xssProtection.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK))
                .contentSecurityPolicy("form-action 'self'");

        http
            .sessionManagement((sessions) -> sessions
                .requireExplicitAuthenticationStrategy(false)
            )
            .sessionManagement()
                .maximumSessions(1)
                .sessionRegistry(sessionRegistry())
                .expiredUrl("/?r=sessionExpiredDuplicateLogin");
      
        return http.build();
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
         
        authProvider.setUserDetailsService(this.customUserDetailsService);
        authProvider.setPasswordEncoder(customSecurityConfiguration.passwordEncoder());
     
        return authProvider;
    }
}

导游是这样说的:

建议 Spring Security 保护所有调度类型。因此,在 6.0 中,Spring Security 更改了此默认值。 所以,最后,更改您的授权规则以过滤所有调度程序类型。

当我保持这样的配置时:

.authorizeHttpRequests(auth -> auth
    .dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
    .requestMatchers("/accessDenied", "/security_check", "/img/*", "/js/*", "/css/*", "/?*", "/", "/login*").permitAll()
    .anyRequest().denyAll())

我无法访问具有

@PreAuthorize()
规则的 Spring 控制器,我得到 Access Denied.

所以,我想在这里实现的是:

  • 只允许访问那些具有
    @PreAuthorize()
    约束集的Spring控制器,
  • 如果控制器没有
    @PreAuthorize()
    约束,拒绝访问它,
  • 通过从外部(例如,从 Plesk)调用它们来允许访问某些控制器(例如 CRON),
  • 了解每种调度程序类型如何控制 Web 应用程序:
    request,async,error,forward,include

------------- 2023 年 3 月 15 日更新 --------------

spring-boot spring-security authorization access-denied java-17
1个回答
0
投票

参考你的问题,Spring Security分两步处理授权:

  1. SecurityFilterChain
    Spring 将检查这个特定的过滤器链是否通常负责请求。因此,它将首先检查一个可选的指定
    http.securityMatchers(matchers -> ...)
    ,您可以使用它来预过滤一些通用路由。比方说,您想要一个仅用于
    api
    调用的 FilterChain,这是选择所有这些调用并将它们拖到此过滤器链中的方法。现在你在过滤器链中,Spring 将寻找许可。 在这里你必须明确允许过滤器链中的每条路由。您可以使用通配符概括路由,但无法忽略显式授予此权限。默认情况下,Spring Security 6 将对所有未设置的路由应用
    denyAll()
    。这是从 5.x 到 6.x 的更新中所做的众多更改之一

  2. 只有当第一步的检查成功并且路由被允许时,您才可以选择使用方法安全性来细化安全性。除非过滤器链在第一步中找到有效权限,否则不会计算像

    PreAuthorize
    PostAuthorize
    和其他注释。第二步完全是可选的,但第一步是强制性的。

所以这是行不通的,因为您根本不授予任何业务路线的许可:

.authorizeHttpRequests(auth -> auth
    .dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
    .requestMatchers("/accessDenied", "/security_check", "/img/*", "/js/*", "/css/*", "/?*", "/", "/login*").permitAll()
    .anyRequest().denyAll()

你必须像这样设置它:

.authorizeHttpRequests(auth -> auth
    .dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
    .requestMatchers("/accessDenied", "/security_check", "/img/*", "/js/*", "/css/*", "/?*", "/", "/login*").permitAll()
    .requestMatchers("/routes_to_restrict_completely_depending_on_method_security/**").permitAll()
    .requestMatchers("/routes_to_restrict_additionally_by_method_security/**")..authenticated()
    .anyRequest().denyAll()

记住以上几点,您的要求可能不会像您想象的那样工作:

如果控制器没有@PreAuthorize()约束,拒绝访问它,

可能有一种方法可以实现这种行为,当您首先允许专用路线(或一般所有路线)然后在最后一步中,

AuthorizationFilter
将发挥作用。但老实说,我自己还没有想出如何用 Spring Security 6 做到这一点。

© www.soinside.com 2019 - 2024. All rights reserved.