Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring security - creating 403 Access denied custom response

Tags:

I have a spring boot rest api with jwt authentication. The problem is i cannot get rid of default 403 Access Denied rest response which looks like this:

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

I created custom AccessDeniedHandler:

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

and added it to WebConfig class

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

Despite this i'm still getting the default Access Denied response. When debugging i realized that the handle method from custom handler isn't even called. What is the case here?

like image 544
kubi Avatar asked Jan 17 '18 16:01

kubi


People also ask

How does spring boot handle Access Denied exception?

Access Denied Handler. Using an access denied handler instead of a page has the advantage that we can define custom logic to be executed before redirecting to the 403 page. For this, we need to create a class that implements the AccessDeniedHandler interface and overrides the handle() method.

How do I return a 403 Spring boot?

Use this: response. setStatus(403) . Save this answer.

How do I fix a 403 postman error?

The simple answer is; “You need to be given the correct access”. Without being given the correct access you'd technically be hacking the server, as it is specifically set up to restrict said access.


2 Answers

I think i resolved the problem. Instead of creating an implementation of AccessDeniedHandler i had to create a custom AuthenticationEntryPoint and set it in exception handling.

WebConfig now looks like this:

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

and the 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());     } } 

Now everything works as i wanted.

like image 98
kubi Avatar answered Oct 02 '22 18:10

kubi


I have the same problem & tried to resolve as per the right answer, but it doesn't solve the issue. The best way to handle this is to implement custom access denied handler. AuthenticationEntryPoint implementation is best to handle 401, UNAUTHORIZED access and AccessDeniedHandler implementation is there for 403, FORBIDDEN access.

Override AccessDeniedHandler's method in your implementation class as:

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

And add this custom access denied handler in your security config like this:

.exceptionHandling()      .authenticationEntryPoint(authenticationEntryPoint()) .accessDeniedHandler(accessDeniedHandler()) 
like image 35
Amit Samuel Avatar answered Oct 02 '22 18:10

Amit Samuel