使用 Spring 安全注释保护考虑动态参数的 Spring Webflux 控制器
Secure a Spring Webflux controller considering dynamic parameters with Spring Security annotations
我有一个带有一些控制器的应用程序,这些控制器需要根据请求的资源 ID 进行访问控制,检查 Spring 安全用户身份验证角色。目前我已经创建了一个函数来检查这个条件返回一个 Mono<True>
如果它是好的(这样我可以平面化它)或者一个空的 Mono (并且还设置一个 403 状态代码)否则:
@RestController
@RequestMapping("/api/v1/clients/{clientId}/departments/{departmentId}/users")
class UserRestController(private val userService: UserService) {
@GetMapping
fun getAll(principal: Principal, response: ServerHttpResponse,
@PathVariable clientId: String, @PathVariable departmentId: String): Flux<Users> {
return checkDepartmentViewPermissions(principal, response, clientId, departmentId)
.flatMap {
userService.getAll(clientId, departmentId)
}
}
...
}
fun checkDepartmentViewPermissions(principal: Principal, response: ServerHttpResponse,
clientId: String, departmentId: String): Mono<Boolean> {
val authentication = principal as MyAuthentication
authentication.authorities.contains(SimpleGrantedAuthority("${clientId}:${departmentId}")).toMono()
.filter {
it == true
}.switchIfEmpty {
response.statusCode = HttpStatus.FORBIDDEN
Mono.empty()
}
}
如上所示,请求的格式为 /api/v1/clients/{clientId}/departments/{departmentId}/users
,其中 clientId
和 departmentId
是动态路径变量。
checkDepartmentViewPermission
方法访问 Authentication
角色,其中用户将拥有一个列表,例如 (client1:department1, client1:department2, client2:department1
)。因此,URL /api/v1/clients/client1/departments/department1/users
可以很好地满足这些权限。
尽管我的工作有效,但如果可能的话,我想使用更具声明性的方式来处理这个问题,理想情况下是基于 Spring 安全注释并考虑到我需要访问 PathVariable 参数,类似(我正在编造):
@RestController
@RequestMapping("/api/v1/clients/{clientId}/departments/{departmentId}/users")
class UserRestController(private val userService: UserService) {
@PreAuthorize("#{principal.roles.contains(clientId:departmentId)}")
@GetMapping
fun getAll(principal: Principal, response: ServerHttpResponse,
@PathVariable clientId: String, @PathVariable departmentId: String): Flux<Users> {
return userService.getAll(clientId, departmentId)
}
...
}
Spring 安全是否支持执行此操作的方法?
如果没有,你能提出一些实现它的想法吗?
首先,感谢 Thomas 为我指明了正确的方向,我还没有意识到 we can invoke a Spring bean from the security web expressions。这样,就不再需要注入主体,因为 Authentication
对象将传递给 bean。
控制器:
@PreAuthorize("@permissionChecker.hasDepartmentViewPermissions(authentication, #clientId, #departmentId)")
@GetMapping
fun getAll(@PathVariable clientId: String, @PathVariable departmentId: String): Flux<Users> {
return userService.getAll(clientId, departmentId)
}
PermissionChecker bean:
class PermissionChecker {
fun hasDepartmentViewPermissions(authentication: Authentication, clientId: String, projectId: String): Boolean {
...
}
我有一个带有一些控制器的应用程序,这些控制器需要根据请求的资源 ID 进行访问控制,检查 Spring 安全用户身份验证角色。目前我已经创建了一个函数来检查这个条件返回一个 Mono<True>
如果它是好的(这样我可以平面化它)或者一个空的 Mono (并且还设置一个 403 状态代码)否则:
@RestController
@RequestMapping("/api/v1/clients/{clientId}/departments/{departmentId}/users")
class UserRestController(private val userService: UserService) {
@GetMapping
fun getAll(principal: Principal, response: ServerHttpResponse,
@PathVariable clientId: String, @PathVariable departmentId: String): Flux<Users> {
return checkDepartmentViewPermissions(principal, response, clientId, departmentId)
.flatMap {
userService.getAll(clientId, departmentId)
}
}
...
}
fun checkDepartmentViewPermissions(principal: Principal, response: ServerHttpResponse,
clientId: String, departmentId: String): Mono<Boolean> {
val authentication = principal as MyAuthentication
authentication.authorities.contains(SimpleGrantedAuthority("${clientId}:${departmentId}")).toMono()
.filter {
it == true
}.switchIfEmpty {
response.statusCode = HttpStatus.FORBIDDEN
Mono.empty()
}
}
如上所示,请求的格式为 /api/v1/clients/{clientId}/departments/{departmentId}/users
,其中 clientId
和 departmentId
是动态路径变量。
checkDepartmentViewPermission
方法访问 Authentication
角色,其中用户将拥有一个列表,例如 (client1:department1, client1:department2, client2:department1
)。因此,URL /api/v1/clients/client1/departments/department1/users
可以很好地满足这些权限。
尽管我的工作有效,但如果可能的话,我想使用更具声明性的方式来处理这个问题,理想情况下是基于 Spring 安全注释并考虑到我需要访问 PathVariable 参数,类似(我正在编造):
@RestController
@RequestMapping("/api/v1/clients/{clientId}/departments/{departmentId}/users")
class UserRestController(private val userService: UserService) {
@PreAuthorize("#{principal.roles.contains(clientId:departmentId)}")
@GetMapping
fun getAll(principal: Principal, response: ServerHttpResponse,
@PathVariable clientId: String, @PathVariable departmentId: String): Flux<Users> {
return userService.getAll(clientId, departmentId)
}
...
}
Spring 安全是否支持执行此操作的方法? 如果没有,你能提出一些实现它的想法吗?
首先,感谢 Thomas 为我指明了正确的方向,我还没有意识到 we can invoke a Spring bean from the security web expressions。这样,就不再需要注入主体,因为 Authentication
对象将传递给 bean。
控制器:
@PreAuthorize("@permissionChecker.hasDepartmentViewPermissions(authentication, #clientId, #departmentId)")
@GetMapping
fun getAll(@PathVariable clientId: String, @PathVariable departmentId: String): Flux<Users> {
return userService.getAll(clientId, departmentId)
}
PermissionChecker bean:
class PermissionChecker {
fun hasDepartmentViewPermissions(authentication: Authentication, clientId: String, projectId: String): Boolean {
...
}