Spring Security身份验证响应中缺少Set-cookie头

k97glaaz  于 12个月前  发布在  Spring
关注(0)|答案(1)|浏览(213)

我正在创建一个Sping Boot 应用程序,并计划通过API请求和响应sessionId执行身份验证。
WebSecurityConfig.kt

@Configuration
@EnableWebSecurity
class WebSecurityConfig {
    @Bean
    fun filterChain(http: HttpSecurity, authenticationManager: AuthenticationManager): SecurityFilterChain {
        http {
            authorizeHttpRequests {
                authorize("/api/login", permitAll)
                authorize(anyRequest, authenticated)
                HttpMethod.PUT
                HttpMethod.DELETE
            }
            csrf { disable() }
        }

        http.addFilter(ApiUsernamePasswordAuthenticationFilter(authenticationManager))
        return http.build()
    }

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

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

字符串
UserDetailServiceImpl.kt

@Service
@Transactional
class UserDetailServiceImpl(
    private val userRepository: UserRepository,
) : UserDetailsService {
    override fun loadUserByUsername(username: String): UserDetails {
        // Created custom UserRepository to fetch user data from database
        val loginUser = userRepository.findByUsername(username)
            ?: throw UsernameNotFoundException("User not found")

        return LoginUserDetails(loginUser)
    }
}


ApiUsernamePasswordAuthenticationFilter.kt

data class LoginRequestParams(
    val username: String,
    val password: String,
)

class ApiUsernamePasswordAuthenticationFilter(authenticationManager: AuthenticationManager) :
    UsernamePasswordAuthenticationFilter(authenticationManager) {
        private val objectMapper = jacksonObjectMapper()

        init {
            setRequiresAuthenticationRequestMatcher(AntPathRequestMatcher("/api/login", "POST"))

            this.setAuthenticationSuccessHandler { _, response, _ ->
                response.status = HttpServletResponse.SC_OK
            }
            this.setAuthenticationFailureHandler { _, response, _ ->
                response.status = HttpServletResponse.SC_UNAUTHORIZED
            }
        }

        override fun attemptAuthentication(request: HttpServletRequest, response: HttpServletResponse): Authentication {
            val loginRequestParams = objectMapper.readValue(request.inputStream, LoginRequestParams::class.java)
            val authenticationToken =
                UsernamePasswordAuthenticationToken(loginRequestParams.username, loginRequestParams.password)
            return authenticationManager.authenticate(authenticationToken)
        }
    }


现在登录请求正确地得到200,但是没有返回set-cookie头,所以我不能保持登录状态。

curl -X POST -H "Content-Type: application/json" -d '{"username": "lipsum", "password": "example"}' http://localhost:8080/api/login -i -c cookie.txt
HTTP/1.1 200
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Length: 0
Date: Thu, 23 Nov 2023 09:29:51 GMT


我认为Spring Security会自动生成jsessionid,我想使用它,但是需要更多的设置吗?

piah890a

piah890a1#

我的例子是通过REST API执行身份验证,在这种情况下,我需要使用SecurityContextRepository显式地保存用户信息。
https://spring.pleiades.io/spring-security/reference/servlet/authentication/passwords/#publish-authentication-manager-bean
而且正如Toerktumbery所说,我退出了自定义过滤器,而是通过向配置中添加AuthenticationManager bean来使用另一个控制器。
WebSecurityConfig.kt

@Configuration
@EnableWebSecurity
class WebSecurityConfig {
    @Bean
    fun filterChain(http: HttpSecurity, authenticationManager: AuthenticationManager): SecurityFilterChain {
        http {
            authorizeHttpRequests {
                authorize("/api/login", permitAll)
                authorize(anyRequest, authenticated)
                HttpMethod.PUT
                HttpMethod.DELETE
            }
            csrf { disable() }
        }

        return http.build()
    }

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

    @Bean
    fun authenticationManager(
        userDetailsService: UserDetailsService,
        passwordEncoder: PasswordEncoder,
    ): AuthenticationManager {
        val authenticationProvider = DaoAuthenticationProvider()
        authenticationProvider.setUserDetailsService(userDetailsService)
        authenticationProvider.setPasswordEncoder(passwordEncoder)

        return ProviderManager(authenticationProvider)
    }
}

字符串
LoginController.kt

data class LoginRequestParams(
    val username: String,
    val password: String,
)

@RestController
class LoginController(private val authenticationManager: AuthenticationManager) {
    private val securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy()
    private val securityContextRepository: SecurityContextRepository = HttpSessionSecurityContextRepository()

    @PostMapping("/api/login")
    fun login(
        @RequestBody loginRequestParams: LoginRequestParams,
        request: HttpServletRequest,
        response: HttpServletResponse,
    ) {
        val authenticationToken =
            UsernamePasswordAuthenticationToken(loginRequestParams.username, loginRequestParams.password)
        val authentication = authenticationManager.authenticate(authenticationToken)

        val securityContext = securityContextHolderStrategy.createEmptyContext()
        securityContext.authentication = authentication
        securityContextHolderStrategy.context = securityContext
        securityContextRepository.saveContext(securityContext, request, response)
    }
}


UserDetailServiceImpl.kt相同,ApiUsernamePasswordAuthenticationFilter.kt被移除。
现在服务器响应Set-Cookie头。

curl -X POST -H "Content-Type: application/json" -d '{"username": "lipsum", "password": "example"}' http://localhost:8080/api/login -i -c cookie.txt
HTTP/1.1 200
Set-Cookie: JSESSIONID=<cookie value>; Path=/; HttpOnly
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Length: 0
Date: Sat, 25 Nov 2023 10:45:44 GMT

相关问题