-1

I'm working on a spring-boot3 project and added spring-security 6 for authorization process. Since it's working with Authorization token in header, I would like to let in with no Authorization token, such as /health or /signup

First, I wrote SecurityConfig

@Configuration
@EnableWebSecurity
@Order(1)
class SecurityConfiguration(private val jwtFilter : JwtFilter) {
    @Bean
    fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http.csrf().disable().
        authorizeHttpRequests { authorize ->
            authorize
                .requestMatchers("/health").permitAll()
                .anyRequest().authenticated()
        }

        http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter::class.java)
        return http.build()
    }
}

And here is filter;

@Component
class JwtFilter : Filter {
    override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
        val httpRequest = request as HttpServletRequest
        val httpResponse = response as HttpServletResponse
        val jwt = httpRequest.getHeader("Authorization")?.removePrefix("Bearer ")
        if(jwt != null){
            val extractedToken = verifyJwtWithLambda(jwt)
            val isValid : Boolean? = extractedToken["isValid"] as? Boolean
            if(isValid!!){
                val attributes = extractedToken["attributes"] as? Map<*, *>
                val subject = attributes?.get("userId") as? String
                val authentication = UsernamePasswordAuthenticationToken(subject, null, emptyList())
                SecurityContextHolder.getContext().authentication = authentication
                chain.doFilter(request, response)
            }
            else {
                httpResponse.status = HttpServletResponse.SC_UNAUTHORIZED
            }
        }
        else {
                httpResponse.status = HttpServletResponse.SC_UNAUTHORIZED
        }
    }

Now, when I send get request to /health, it returns 401. But when I send request with Authorization token, it returns 200.

What I have tried so far?

  • Added WebSecurityCustomizer to ignore, did not work.

  • Tried to .anonymous instead of .permitAll, did not work.

  • Added logic control to JwtFilter to check ServletPath for defined urls and then chain.doFilter, it works but I do not think it is usable.

  • Spring Security permitAll not allowing anonymous access , Tried this topic

  • I tried to change addFilterBefore like addFilterAfter, did not work.

  • If I would change httpResponse.status = HttpServletResponse.SC_UNAUTHORIZED part in second Else block with chain.doFilter(), it works as expected but returns 403 instead of 401.

Thank you a lot.

nerdicsapo
  • 427
  • 5
  • 16

1 Answers1

1

How about using OncePerRequestFilter instead and overriding the shouldNotFilter method and adding any endpoints to be excluded. I have added an example for a single url but you can maintain a list and exclude those endpoints as well.

@Component
class JwtFilter : OncePerRequestFilter {
    override fun doFilterInternal(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
        val httpRequest = request as HttpServletRequest
        val httpResponse = response as HttpServletResponse
        val jwt = httpRequest.getHeader("Authorization")?.removePrefix("Bearer ")
        if (jwt != null) {
            val extractedToken = verifyJwtWithLambda(jwt)
            val isValid: Boolean? = extractedToken["isValid"] as? Boolean
            if (isValid!!) {
                val attributes = extractedToken["attributes"] as? Map<*, *>
                val subject = attributes?.get("userId") as? String
                val authentication = UsernamePasswordAuthenticationToken(subject, null, emptyList())
                SecurityContextHolder.getContext().authentication = authentication
                chain.doFilter(request, response)
            } else {
                httpResponse.status = HttpServletResponse.SC_UNAUTHORIZED
            }
        } else {
            httpResponse.status = HttpServletResponse.SC_UNAUTHORIZED
        }
    }

    override fun shouldNotFilter(request: HttpServletRequest): Boolean {
        val url = request.requestURL;
        return "/health".equals(url);
    }
}

Reference:

Extends OncePerRequestFilter bypass specific URL

https://www.baeldung.com/spring-exclude-filter

cant-code
  • 66
  • 3
  • 1
    Sorry, I have modified the answer to change doFilter to doFilterInternal. The OncePerRequestFilter, as the name suggest, runs only once per a request lifecycle whereas the normal Filter can run multiple times if lets say a Servlet passes on the request to another servlet which might run your custom filter again which might not be an ideal situation. I am not sure if that answers your question. Source: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/OncePerRequestFilter.html https://www.baeldung.com/spring-onceperrequestfilter – cant-code Aug 17 '23 at 14:53