0

I am trying to secure a simple webservice with an AuthController providing a login and refresh endpoint. Hence auth/** should be open and all other apis should only be accessible with a valid token. The auth endpoints work, however I have some troubles with the proper security configuration. I implemented the following JwtTokenFilter:

class JwtTokenFilter() : OncePerRequestFilter() {

    @Autowired
    private lateinit var jwtTokenService: JwtTokenService

    @Autowired
    private lateinit var refreshTokenService: RefreshTokenService

    @Autowired
    private lateinit var userDetailService: UserDetailService

    override fun doFilterInternal(
        request: HttpServletRequest,
        response: HttpServletResponse,
        filterChain: FilterChain
    ) {
        val jwt = jwtTokenService.getTokenFromAuthHeader(request)
        jwt?.let { token ->
            try {
                val claims = jwtTokenService.getClaimsFromToken(token)
                val userDetails = userDetailService.loadUserByUsername(claims.subject)
                val authentication = UsernamePasswordAuthenticationToken(userDetails, null, userDetails.authorities)
                authentication.details = WebAuthenticationDetailsSource().buildDetails(request)
                SecurityContextHolder.getContext().authentication = authentication
                refreshTokenService.updateRefreshToken(claims.subject)
            } catch (ex: Exception) {
                logger.warn("JWT-Token <$token> invalid token")
            }
        }
        logger.trace("API request to <${request.requestURI}> with token <${!jwt.isNullOrEmpty()}>")

        filterChain.doFilter(request, response)
    }
}

The corresponding WebSecurity class:

@Configuration
@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
class WebSecurityConfig() {

    @Bean
    fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http.authorizeHttpRequests()
            .antMatchers("/auth/**").permitAll()
            .anyRequest().authenticated()
        http.csrf().disable()
        http.addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter::class.java)

        return http.build()
    }

    @Bean
    fun jwtTokenFilter(): JwtTokenFilter {
        return JwtTokenFilter()
    }
    @Bean
    fun authenticationManager(authenticationConfiguration: AuthenticationConfiguration): AuthenticationManager? {
        return authenticationConfiguration.authenticationManager
    }

    @Bean
    fun passwordEncoder(): PasswordEncoder {
        return BCryptPasswordEncoder()
    }
}

After adding the JwtTokenFilter to the security config I get connection-refused errors when I try to post to "auth/login". I also noticed that I am unable to debug into filterChain after adding the JwtTokenFilter bean to the config, so I guess there is some error in the filter implementation or registration?

Update to WebSecurityConfig:

@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
@EnableWebSecurity(debug = true)
@Configuration
class WebSecurityConfig(
    private val jwtTokenService: JwtTokenService,
    private val refreshTokenService: RefreshTokenService,
    private val userDetailService: UserDetailService
) {

    @Bean
    fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http
            .csrf().disable()

        http.sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)

        http.authorizeHttpRequests()
            .antMatchers("/auth/**").permitAll()

        http.authorizeHttpRequests()
            .anyRequest().authenticated()

        //http.apply(MyCustomDsl.customDsl(jwtTokenService, userDetailService, refreshTokenService))
        http.addFilterBefore(JwtTokenFilter(jwtTokenService, userDetailService, refreshTokenService), UsernamePasswordAuthenticationFilter::class.java)
        return http.build()
    }


    @Bean
    fun authenticationManager(authenticationConfiguration: AuthenticationConfiguration): AuthenticationManager? {
        return authenticationConfiguration.authenticationManager
    }

    @Bean
    fun passwordEncoder(): PasswordEncoder {
        return BCryptPasswordEncoder()
    }

/*    class MyCustomDsl(
        private val jwtTokenService: JwtTokenService,
        private val userDetailService: UserDetailService,
        private val refreshTokenService: RefreshTokenService,
        ) : AbstractHttpConfigurer<MyCustomDsl, HttpSecurity>() {

        companion object {
            fun customDsl(jwtTokenService: JwtTokenService,userDetailService: UserDetailService, refreshTokenService: RefreshTokenService): MyCustomDsl {
                return MyCustomDsl(jwtTokenService, userDetailService, refreshTokenService)
            }
        }

        override fun configure(http: HttpSecurity) {
            http.addFilterBefore(JwtTokenFilter(jwtTokenService, userDetailService, refreshTokenService), UsernamePasswordAuthenticationFilter::class.java)
        }
    }*/
}
Christian
  • 821
  • 1
  • 11
  • 26
  • Thanks for the hint! I tried to add the custom dsl as suggested in the thread (see the edit), not sure if I did it correctly. However, I now see that there is a problem within the JwtTokenFilter: lateinit property jwtTokenService has not been initialized. Any suggestions here? I am puzzled. – Christian Jul 08 '22 at 14:25
  • It's likely related to using `@Autowired` instead of constructor injection in the filter. Check out [this question](https://stackoverflow.com/questions/40620000/spring-autowire-on-properties-vs-constructor) for more details. – Eleftheria Stein-Kousathana Jul 08 '22 at 14:33
  • Thank you, I updated my post again. It works now with just `http.addFilterBefore(..)`and it also works when using the (currently commented out) `http.apply(MyCustomDsl..)` approach. Is there any thing I am missing or why should I prefer the latter? It seems pretty excessive in comparison? – Christian Jul 08 '22 at 14:58

0 Answers0