webentwicklung-frage-antwort-db.com.de

Frühlingssicherheit - 403 Zugriff verweigert benutzerdefinierte Antwort

Ich habe eine Spring Boot Rest-API mit JWT-Authentifizierung. Das Problem ist, dass ich die Standardantwort 403 Access Denied rest nicht loswerden kann, die wie folgt aussieht: 

{
    "timestamp": 1516206966541,
    "status": 403,
    "error": "Forbidden",
    "message": "Access Denied",
    "path": "/api/items/2"
}

Ich habe einen benutzerdefinierten AccessDeniedHandler erstellt:

public class CustomAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest req,
                       HttpServletResponse res,
                       AccessDeniedException accessDeniedException) throws IOException, ServletException {



        ObjectMapper mapper = new ObjectMapper();
        res.setContentType("application/json;charset=UTF-8");
        res.setStatus(403);
        res.getWriter().write(mapper.writeValueAsString(new JsonResponse()
                .add("timestamp", System.currentTimeMillis())
                .add("status", 403)
                .add("message", "Access denied")));
    }
}

und fügte es zur WebConfig-Klasse hinzu

@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {

    private UserDetailsService userDetailsService;
    private BCryptPasswordEncoder bCryptPasswordEncoder;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.NEVER)
                .and()
                    .csrf().disable()
                    .authorizeRequests()
                    .antMatchers(HttpMethod.POST, REGISTER_URL).permitAll()
                    .anyRequest().authenticated()
                .and()
                    .exceptionHandling().accessDeniedHandler(accessDeniedHandler())
                .and()
                    .addFilter(new JWTAuthenticationFilter(authenticationManager(), tokenProvider()))
                    .addFilter(new JWTAuthorizationFilter(authenticationManager(), tokenProvider()));

    }

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

    @Bean
    public TokenProvider tokenProvider(){
        return new TokenProvider();
    }

    @Bean
    public AccessDeniedHandler accessDeniedHandler(){
        return new CustomAccessDeniedHandler();
    }
}

Trotzdem bekomme ich immer noch die standardmäßige Antwort "Zugriff verweigert". Beim Debuggen wurde mir klar, dass die handle-Methode vom benutzerdefinierten Handler nicht einmal aufgerufen wird. Was ist hier der Fall?

8
dudinii

Ich glaube, ich habe das Problem gelöst. Statt eine Implementierung von AccessDeniedHandler zu erstellen, musste ich einen benutzerdefinierten AuthenticationEntryPoint erstellen und in der Ausnahmebehandlung festlegen.

WebConfig sieht jetzt so aus:

@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {

    private UserDetailsService userDetailsService;
    private BCryptPasswordEncoder bCryptPasswordEncoder;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                    .csrf().disable()
                    .authorizeRequests()
                    .antMatchers(HttpMethod.POST, REGISTER_URL).permitAll()
                    .anyRequest().authenticated()
                .and()
                    .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
                .and()
                    .addFilter(new JWTAuthenticationFilter(authenticationManager(), tokenProvider()))
                    .addFilter(new JWTAuthorizationFilter(authenticationManager(), tokenProvider()));

    }

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

    @Bean
    public TokenProvider tokenProvider(){
        return new TokenProvider();
    }

    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint(){
        return new CustomAuthenticationEntryPoint();
    }
}

und der CustomAuthenticationEntryPoint:

public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest req, HttpServletResponse res, AuthenticationException authException) throws IOException, ServletException {
        res.setContentType("application/json;charset=UTF-8");
        res.setStatus(403);
        res.getWriter().write(JsonBuilder //my util class for creating json strings
                .put("timestamp", DateGenerator.getDate())
                .put("status", 403)
                .put("message", "Access denied")
                .build());
    }
}

Jetzt funktioniert alles wie ich wollte.

0
dudinii

Ich habe das gleiche Problem und habe versucht, es gemäß der richtigen Antwort zu lösen, aber es löst das Problem nicht. Der beste Weg, dies zu handhaben, ist die Implementierung eines benutzerdefinierten Zugriffsverweigerungshandlers. Die AuthenticationEntryPoint-Implementierung sollte am besten mit 401, UNAUTHORIZED-Zugriff und AccessDeniedHandler-Implementierung für 403, FORBIDDEN-Zugriff behandelt werden. 

Überschreiben Sie die Methode von AccessDeniedHandler in Ihrer Implementierungsklasse wie folgt:

@Override
public void handle(HttpServletRequest request, HttpServletResponse response, 
AccessDeniedException accessDeniedException) throws IOException, ServletException {
    response.getWriter().write("Access Denied... Forbidden");
}

Fügen Sie diesen benutzerdefinierten Zugriff-verweigerten Handler wie folgt in Ihre Sicherheitskonfiguration ein:

.exceptionHandling()     
.authenticationEntryPoint(authenticationEntryPoint())
.accessDeniedHandler(accessDeniedHandler())
2
Amit Samuel

Hier ist eine minimale Sicherheitskonfiguration, die zeigt, dass ein benutzerdefiniertes AccessDeniedHandler in Szenarios mit Zugriff verweigert (403) aufgerufen wird:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/css/**", "/index").permitAll()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .and()
            .formLogin()
                .and()
            .exceptionHandling()
                .accessDeniedHandler((request, response, accessDeniedException) -> {
                    AccessDeniedHandler defaultAccessDeniedHandler = new AccessDeniedHandlerImpl();
                    defaultAccessDeniedHandler.handle(request, response, accessDeniedException);
                });
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
                .withUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER"))
                .withUser(User.withDefaultPasswordEncoder().username("admin").password("password").roles("ADMIN"));
    }
}

Schritte zum Reproduzieren:

  1. Einloggen mit user/password
  2. Versuchen Sie, auf http://localhost:8080/user/index - Zugriff zuzugreifen
  3. Versuchen Sie, auf http://localhost:8080/admin/index zuzugreifen - der Zugriff wurde verweigert, und das benutzerdefinierte AccessDeniedHandler wird aufgerufen 
0
Joe Grandja