简体   繁体   中英

Spring Security - @PreAuthorize returns 404

I"m currently having a strange issue when I use Spring Method Security, @PreAuthorize("hasRole('MODERATOR')")

If the user tries to access a controller, which requires the role of "MODERATOR", then the resource is returned, and everything is fine (if the user, in fact, has that role). However, if the user does not have this role, the server returns 404 - Not Found. This is odd, since I expect the server would have returned something else, perhaps 403 Forbidden? Any idea why this would happen? Here is my Security Configuration:

@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;
    }      

}

and my controller:

@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;
    }
}

Thank you!

Global method security can be enabled with the help of annotation @EnableGlobalMethodSecurity(prePostEnabled=true) . The combination of this and @Preauthorize will create a new proxy for your controller and it will loose the Request mapping (GetMapping in your case) which will result in 404 Exception.

To handle this you can use @EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)

Annotated Controllers in Spring documentation

A related Github Issue

We need to enable global Method Security for using @PreAuthorize annotation. For eg https://dzone.com/articles/securing-spring-data-rest-with-preauthorize

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

Below is my code of doing this:-

 @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);
}

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM