Search code examples
springkotlinspring-securityconcurrency

How to save header's value into Spring Security context considering concurrency safety


I have to use a header value for some utils logic in my kotlin spring application. I found a way to get header value for each request, but I realize that this way is not so good because of concurrency.

It is how I get and save the header value:

class HeaderInterceptor : HandlerInterceptor {
    @Throws(Exception::class)
    override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
        val anonymousUserId = request.getHeader("anonymous_user_id")
        headerHolder = HeaderHolder(anonymousUserId)
        return true
    }

    companion object {
        private var headerHolder: HeaderHolder = HeaderHolder()
        fun getHeaders(): HeaderHolder = headerHolder
    }
}

@Configuration
class HeaderInterceptorConfig : WebMvcConfigurer {
    override fun addInterceptors(registry: InterceptorRegistry) {
        registry.addInterceptor(anonymousUserIdInterceptor())
    }

    @Bean
    fun anonymousUserIdInterceptor(): HeaderInterceptor {
        return HeaderInterceptor()
    }
}

data class HeaderHolder(
    val anonymousUserId: String? = null
)

To get the header's value I can just call HeaderInterceptor.getHeaders(). But it is totally bad idea because of importance of concurrency safety.

It seems like the ideal solution would be to somehow put the header's value into the spring security context. Then I could access it simply by calling:

class SecurityUtils private constructor() {
    companion object {
        fun getAnonymousUserId(): String? = SecurityContextHolder.getContext()... // an example way to access a stored value
    }
}

Since i will need to use the header's value from including in utility classes in static methods, I would like to be able to access it without bean injection.

Maybe someone has already encountered something similar and was able to solve this problem? I would be grateful for any tips


Solution

  • The solution to my problem turned out to be quite simple. To get the header's value it is enough to refer to RequestContextHolder. Since there is also asynchronously executed code, I added a check for RequestContextHolder.getRequestAttributes() existence.

    private fun getValueFromHeader(headerName: String): String? {
        return if (RequestContextHolder.getRequestAttributes() == null) {
            return null
        } else if (RequestContextHolder.currentRequestAttributes() is ServletRequestAttributes) {
            (RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes)
                .request.getHeader(headerName)
        } else null
    }
    

    This method can also be static.