Search code examples
springkotlinspring-security

HTTP 401 on AccessDeniedException


I have the following security config and JwtFilter:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class SecurityConfig(
    val tokenService: TokenService,
) : WebSecurityConfigurerAdapter(), WebMvcConfigurer {

    override fun configure(http: HttpSecurity) {
        http
            .csrf().disable()
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .addFilterAt(
                JwtFilter(authenticationManager(), tokenService),
                UsernamePasswordAuthenticationFilter::class.java
            )
    }
}

class JwtFilter(
    authManager: AuthenticationManager,
    private val tokenService: TokenService,
) : BasicAuthenticationFilter(authManager) {

    @Throws(IOException::class, ServletException::class)
    override fun doFilterInternal(
        req: HttpServletRequest,
        res: HttpServletResponse,
        chain: FilterChain
    ) {
        val header = req.getHeader("Authorization")
        if (header == null || !header.startsWith("Bearer")) {
            chain.doFilter(req, res)
            return
        }
        tokenService.extractUser(header)?.also {
            SecurityContextHolder.getContext().authentication = it
        }
        chain.doFilter(req, res)
    }
}

TokenService#extractUser throws an AccessDeniedException in case of JWT parsing errors. I can't find a way to map it to HTTP 401. Can it be done?

I have tried using HttpSecurity#exceptionHandling() as well as adding an @ExceptionHandler inside a @ControllerAdvice, but I am always getting HTTP 403.


Solution

  • Thank to this thread I managed to solve it by adding the following two lines to my WebSecurityConfigurerAdapter#configure:

    .exceptionHandling()
    .authenticationEntryPoint(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))