Spring security - creating 403 Access denied custom response
Solution 1
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.
Solution 2
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())
Solution 3
Try this
@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().accessDeniedPage("/view/notAuth")
.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();
}
And make this configuration class for view page
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
@Configuration
public class ViewRegistryConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/view/notAuth").setViewName("notAuth");
}
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
// TODO Auto-generated method stub
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
// TODO Auto-generated method stub
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
// TODO Auto-generated method stub
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
// TODO Auto-generated method stub
}
@Override
public void addFormatters(FormatterRegistry registry) {
// TODO Auto-generated method stub
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// TODO Auto-generated method stub
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// TODO Auto-generated method stub
}
@Override
public void addCorsMappings(CorsRegistry registry) {
// TODO Auto-generated method stub
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
// TODO Auto-generated method stub
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
// TODO Auto-generated method stub
}
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
// TODO Auto-generated method stub
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// TODO Auto-generated method stub
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
// TODO Auto-generated method stub
}
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
// TODO Auto-generated method stub
}
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
// TODO Auto-generated method stub
}
@Override
public Validator getValidator() {
// TODO Auto-generated method stub
return null;
}
@Override
public MessageCodesResolver getMessageCodesResolver() {
// TODO Auto-generated method stub
return null;
}
}
Related videos on Youtube
kubi
Updated on January 28, 2021Comments
-
kubi over 3 years
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?-
chaoluo over 6 yearsSee here stackoverflow.com/a/43122832/1842482
-
kubi over 6 yearsI already resolved this problem. Look at answers. Thanks for response though.
-
Roman T over 3 yearsHad the same problem with the Reactive stack and for me introducing
AccessDeniedHandler
worked out. Thank you.
-
-
OO7 about 3 yearsThanks for sharing CustomAuthenticationEntryPoint :)
-
Burak over 2 yearsWhen will the authentication end? I mean, is there any default time to hold the authentication credentials?