简体   繁体   中英

CORS exception with Spring Data Rest + Spring Security when posting via '/login' path but other paths are fine

Spring Boot 2.7.0 I faced CORS exception that triggered XMLHttpRequestError when trying to post via the '/login' path, other paths such as '/api/v1/members' are fine.

I followed the below guide, https://github.com/amigoscode/spring-boot-security-course/tree/8-JTW

I am currently using SpringDataRest with configuration in RepositoryRestConfigurer as follow

@Component
class AppRepositoryRestConfigurer : RepositoryRestConfigurer { 
    override fun configureRepositoryRestConfiguration(config: RepositoryRestConfiguration?, cors: CorsRegistry?) {
        cors?.addMapping("/**")?.allowedOriginPatterns("http://localhost:[*]")
    }
}

Where else do I need to add CORS mapping in order to fulfil the post request via web. Right now posting via mobile app(iOS + Android) is ok with the Flutter framework, but Flutter Web is not ok.

I think the issue might be related to the OncePerRequestFilter? Below is my code from my WebSecurityConfigurerAdapter

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class AppWebSecurityConfigurerAdapter(
    private val passwordEncoder: PasswordEncoder,
    private val appUserDetailsService: AppUserDetailsService,
    private val jwtConfiguration: JwtConfiguration,
    private val secretKey: SecretKey,
    private val repository: MemberRepository
) : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity?) { 
        http {
            csrf {
                disable()
            }
            sessionManagement {
                sessionCreationPolicy = SessionCreationPolicy.STATELESS
            }
            addFilterAt<UsernamePasswordAuthenticationFilter>(
                JwtUsernameAndPasswordAuthenticationFilter(
                    authenticationManager(),
                    jwtConfiguration,
                    secretKey,
                    repository
                )
            )
            addFilterAfter<JwtUsernameAndPasswordAuthenticationFilter>(JwtTokenVerifier(jwtConfiguration, secretKey))
            authorizeRequests {
                authorize(anyRequest, permitAll)
            }
        }
    }

    override fun configure(auth: AuthenticationManagerBuilder?) {
        auth?.authenticationProvider(daoAuthenticationProvider())
    }

    @Bean
    fun daoAuthenticationProvider() =
        DaoAuthenticationProvider().apply {
            setPasswordEncoder(passwordEncoder)
            setUserDetailsService(appUserDetailsService)
        }
}

My code for UsernameAndPasswordAuthenticationFilter

class JwtUsernameAndPasswordAuthenticationFilter(
    authenticationManager: AuthenticationManager,
    private val configuration: JwtConfiguration,
    private val secretKey: SecretKey,
    private val repository: MemberRepository
) : UsernamePasswordAuthenticationFilter(authenticationManager) {

    private val objectMapper = jacksonObjectMapper().apply {
        registerModule(JavaTimeModule())
        disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
    }

    override fun attemptAuthentication(request: HttpServletRequest?, response: HttpServletResponse?): Authentication {
        val authenticationRequest: UsernameAndPasswordAuthenticationRequest? =
            request?.inputStream?.let { jacksonObjectMapper().readValue(it) }
        return authenticationManager.authenticate(
            UsernamePasswordAuthenticationToken(authenticationRequest?.username, authenticationRequest?.password)
        )
    }
 
    override fun successfulAuthentication(
        request: HttpServletRequest?,
        response: HttpServletResponse?,
        chain: FilterChain?,
        authResult: Authentication?
    ) {
        val token = Jwts.builder()
            .setSubject(authResult?.name)
            .claim("authorities", authResult?.authorities)
            .setIssuedAt(Date())
            .setExpiration(java.sql.Date.valueOf(LocalDate.now().plusDays(configuration.daysToExpire)))
            .signWith(secretKey)
            .compact()
        val body = objectMapper.writeValueAsString(authResult?.name?.let(repository::findByEmail))
        response?.apply {
            addHeader(configuration.authorizationHeader, "${configuration.tokenPrefix} $token") 
            addHeader(HttpHeaders.CONTENT_TYPE, "application/json;charset=utf-8")
            writer.write(body)
        }
    }
}

App.kt


@SpringBootApplication
@ConfigurationPropertiesScan
class Application

fun main(args: Array<String>) {
    runApplication<Application>(*args)
}

Much help is appreciated:)

I figured out the issue. This relates to the cors configuration in Spring Security. If you use web, the browser changed the POST request into an OPTIONS request, therefore blocked by the default CORS configuration because there default does not allow OPTION requests

override fun configure(http: HttpSecurity?) { 
        http {
            csrf {
                disable()
            }
            cors {} //Enable CORS here
            sessionManagement {
                sessionCreationPolicy = SessionCreationPolicy.STATELESS
            }
            addFilterAt<UsernamePasswordAuthenticationFilter>(
                JwtUsernameAndPasswordAuthenticationFilter(
                    authenticationManager(),
                    jwtConfiguration,
                    secretKey,
                    repository
                )
            )
            addFilterAfter<JwtUsernameAndPasswordAuthenticationFilter>(JwtTokenVerifier(jwtConfiguration, secretKey))
            authorizeRequests {
                authorize(anyRequest, permitAll)
            }
        }
    }

Configure your CORS here

@Bean
fun corsConfigurationSource(): CorsConfigurationSource {
    val configuration = CorsConfiguration()
    configuration.allowedOrigins = listOf("https://example.com")
    configuration.allowedMethods = listOf("*") //Allow all http methods
    val source = UrlBasedCorsConfigurationSource()
    source.registerCorsConfiguration("/**", configuration)
    return source
}

This is no longer needed after implementing CORS in Spring Security

@Component
class AppRepositoryRestConfigurer : RepositoryRestConfigurer { 
    override fun configureRepositoryRestConfiguration(config: RepositoryRestConfiguration?, cors: CorsRegistry?) {
        cors?.addMapping("/**")?.allowedOriginPatterns("http://localhost:[*]")
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM