【问题标题】:Spring Security - @PreAuthorize returns 404Spring Security - @PreAuthorize 返回 404
【发布时间】:2020-08-23 23:53:54
【问题描述】:

我目前在使用 Spring Method Security 时遇到了一个奇怪的问题,@PreAuthorize("hasRole('MODERATOR')")

如果用户尝试访问需要“MODERATOR”角色的控制器,则返回资源,一切正常(如果用户实际上具有该角色)。但是,如果用户没有这个角色,服务器会返回 404 - Not Found。这很奇怪,因为我预计服务器会返回其他内容,也许是 403 Forbidden?知道为什么会发生这种情况吗?这是我的安全配置:

@EnableWebSecurity
@Order(2)
public class WebSecurity extends WebSecurityConfigurerAdapter {

    private final UserDetailsService userDetailsService;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    public WebSecurity(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
        super();
        this.userDetailsService = userDetailsService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .antMatcher("/api/**")
                .cors()
                .and()
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilter(new JWTAuthenticationFilter(authenticationManager()))
                .addFilter(new JWTAuthorizationFilter(authenticationManager()))
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); 
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
    }

    @Bean 
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.applyPermitDefaultValues();
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }      

}

和我的控制器:

@GetMapping
@PreAuthorize("hasRole('MODERATOR')")
public List<ApplicationUser> getAllUsers(HttpServletRequest request) {
    try (final ConnectionResource connectionResource = connectionFactory.create(); final UserDAO dao = new UserDAO()) {
        dao.setEm(connectionResource.em);
        return dao.getAllUsers();
    } catch (Exception ex) {
        Logger.getLogger(UserController.class.getName()).log(Level.SEVERE, "unable to get all users", ex);
        return null;
    }
}

谢谢!

【问题讨论】:

  • 您确认它不是“真正的 404”吗? (例如,通过放弃安全性并“浏览”same 资源/url)
  • 是的。如果没有 PreAuthorize,则会找到资源端点。
  • ..我发现this ..并假设一些错误配置: 1. 怀疑:cors() 没有记录与antMatcher() 一起使用(而是使用registerCorsConfiguration 的参数功能)..实际上:您如何进行身份验证?

标签: java spring security


【解决方案1】:

可以借助注解 @EnableGlobalMethodSecurity(prePostEnabled=true) 启用全局方法安全性。这个和@Preauthorize 的组合将为您的控制器创建一个新代理,它会丢失请求映射(在您的情况下为 GetMapping),这将导致 404 异常。

要处理这个问题,您可以使用@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)

Annotated Controllers in Spring documentation

A related Github Issue

【讨论】:

    【解决方案2】:

    我们需要为使用@PreAuthorize 注解启用全局方法安全性。例如https://dzone.com/articles/securing-spring-data-rest-with-preauthorize

    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled=true)
    @Order(2)
    public class WebSecurity extends WebSecurityConfigurerAdapter {
    ..............
    }
    

    【讨论】:

      【解决方案3】:

      以下是我的代码:-

       @Override
      protected void configure(HttpSecurity http) throws Exception{
          http.authorizeRequests()
          .antMatchers(HttpMethod.POST,"/api/2.0/login/**").permitAll()
          .anyRequest().authenticated()
          .and().exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint())
          .and()  
          .addFilterBefore()
      }
      
      
      @Bean
      public AuthenticationEntryPoint unauthorizedEntryPoint() {
          return new RestAuthenticationEntryPoint();
      }
      
      
      public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint{
      private static Logger logger = Logger.getLogger(RestAuthenticationEntryPoint.class);
      
      public RestAuthenticationEntryPoint() {
          super();
      }
      
      @Override
      public void commence(HttpServletRequest request, HttpServletResponse response,
              AuthenticationException authException) throws IOException, ServletException {
          logger.info("Inside Rest Authentication entry Points");
          String error="{ \"status\":\"FAILURE\",\"error\":{\"code\":\"401\",\"message\":\""+authException.getMessage()+"\"} }";
          HttpServletResponse httpResponse = (HttpServletResponse) response;
          httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
          httpResponse.setContentType("application/json");
      
          if(authException instanceof BadCredentialsException){
              httpResponse.getOutputStream().println("{ \"Bad credential\": \"" + authException.getMessage() + "\" }");
          }
          if(authException instanceof AuthenticationCredentialsNotFoundException){
              logger.info("Inside AuthenticationCredentialsNotFoundException");
              error="{ \"status\":\"FAILURE\",\"error\":{\"code\":\""+SecurityExceptions.TOKEN_EXPIRED+"\",\"message\":\""+SecurityExceptions.TOKEN_EXPIRED_MESSAGE+"\"} }";
          }
          httpResponse.getOutputStream().println(error);
      }
      

      }

      【讨论】:

        猜你喜欢
        • 2019-09-18
        • 1970-01-01
        • 2016-01-14
        • 1970-01-01
        • 2020-10-15
        • 2013-10-13
        • 2021-09-12
        • 2017-09-12
        • 2015-07-30
        相关资源
        最近更新 更多