这是我的代码:
安全配置
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
internal class WebAuthorizationConfig(
private val userDetailsManager: ReactiveUserDetailsManager,
@Qualifier("customPasswordEncoder") private val passwordEncoder: PasswordEncoder
) {
@Bean
fun configureSecurity(
http: ServerHttpSecurity,
requestValidationFilter: RequestValidationFilter,
): SecurityWebFilterChain {
http
.csrf { csrf -> csrf.disable() }
.addFilterBefore(validEndPointFilter, SecurityWebFiltersOrder.HTTP_BASIC)
.httpBasic { httpBasic ->
httpBasic.authenticationEntryPoint(customAuthenticationEntryPoint())
}
.authorizeExchange { authorize ->
authorize
.anyExchange().hasAuthority("WRITE")
}
.authenticationManager(customAuthenticationManager())
.securityContextRepository(customSecurityContextRepository())
return http.build()
}
@Bean
fun customAuthenticationManager(): ReactiveAuthenticationManager {
return CustomAuthenticationManager(userDetailsManager, passwordEncoder)
}
@Bean
fun customAuthenticationEntryPoint(): CustomAuthenticationEntryPoint {
return CustomAuthenticationEntryPoint()
}
@Bean
fun customSecurityContextRepository(): ServerSecurityContextRepository {
return CustomSecurityContextRepository()
}
}
控制器
@RestController
internal class UserController(
@Autowired
private val userService: UserServiceImpl,
) {
@GetMapping("/users/{uid}")
final suspend fun getUser(
@PathVariable("uid") uidString: String
): ResponseEntity<UserDTO> {
// validate user ID
val uid = userService.validateUserId(uidString)
// get from database inside this co-routine
return withContext(Dispatchers.IO) {
ResponseEntity.ok(userService.getUser(uid))
}
}
}
服务
@Service
internal class UserGetServiceDelegate(
@Autowired
private val userRepo: UserRepositoryImpl,
private val userDTOEntityMapMapper: UserDTOEntityMapMapperImpl,
private val userMapKeyRenamer: UserMapKeyRenamerImpl,
) : UserGetService {
@PreAuthorize("denyAll()")
final override suspend fun getUser(uid: ObjectId): UserDTO {
//** SIGNIFICANT DATABASE STEP **//
// attempt to get user from database
val userEntity = userRepo.getUser(uid)
?: throw ErrorManager.getException("service-user-does-not-exist")
// return retrieved user
return userDTOEntityMapMapper.fromEntity(userEntity)
}
}
无论我做什么,它仍然授予对服务的访问权限,并返回结果,尽管服务中有 @PreAuthorize("denyAll()")?
很多年前就有很多帖子抱怨这一点。有些人建议添加此注释@EnableReactiveMethodSecurity(已经完成)。有人建议删除final(但是kotlin,如果删除可见性修饰符,它默认是final,当我选择open或sealed时,它们会变灰,我的IDE说它们是多余的)
但他们都没有帮助。
有什么想法吗?
提前致谢
我采用了您的示例的一部分,并使用 Spring boot 最新的 WebFlux、Kotlin 和 Java 21 以最低的安全性进行了尝试。所以不确定这是否会回答您的问题。我发现对于用
final
注释的方法使用 @PreAuthorize
与不使用它是获得 200 或 403 返回的区别。不知道为什么会这样,但这就是我尝试过的。
我使用的示例代码(为了简洁起见,将其全部放在一个 Kotlin 文件中):
package com.example.demo
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.config.web.server.invoke
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService
import org.springframework.security.core.userdetails.ReactiveUserDetailsService
import org.springframework.security.core.userdetails.User
import org.springframework.security.web.server.SecurityWebFilterChain
import org.springframework.stereotype.Service
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController
import reactor.core.publisher.Mono
@SpringBootApplication
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity(useAuthorizationManager = true)
class WebAuthorizationConfig {
@Bean
fun userDetailsService(): ReactiveUserDetailsService {
val userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build()
return MapReactiveUserDetailsService(userDetails)
}
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
formLogin { }
httpBasic { }
}
}
}
@RestController
class UserController(private val userService: UserService) {
@GetMapping("/users/{uid}")
final fun getUser(@PathVariable("uid") uid: String): Mono<String> {
return userService.getUser(uid)
}
}
@Service
class UserService {
@PreAuthorize("denyAll()")
// Notice no final keyword on this function - get expected 403. But if final keyword added, I get a 200.
fun getUser(uid: String): Mono<String> {
return Mono.just("user")
}
}
测试
curl -u "user:user" http://localhost:8080/users/1 -v