Защищайте контроллер Spring Webflux с учетом динамических параметров с помощью аннотаций Spring Security - PullRequest
0 голосов
/ 19 сентября 2019

У меня есть приложение с некоторыми контроллерами, которые требуют контроля доступа на основе запрошенных идентификаторов ресурсов, сравнивая их с ролями аутентификации пользователя Spring Security.На данный момент я создал функцию, которая проверяет это условие, возвращая Mono<True>, если оно в порядке (чтобы я мог отобразить его), или пустое моно (а также устанавливая код состояния 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 Securityаннотации и с учетом того, что мне нужно получить доступ к параметрам 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 Security способ сделать это?Если нет, не могли бы вы предложить какие-либо идеи для его достижения?

1 Ответ

1 голос
/ 20 сентября 2019

Прежде всего, спасибо Томасу за то, что он указал мне правильное направление, я не понял, , что мы можем вызвать bean-компонент Spring из выражений безопасности веб-сайта .Таким образом, больше не нужно вводить принципал, поскольку объект Authentication будет передан в bean-компонент.

Controller :

   @PreAuthorize("@permissionChecker.hasDepartmentViewPermissions(authentication, #clientId, #departmentId)")
    @GetMapping
    fun getAll(@PathVariable clientId: String, @PathVariable departmentId: String): Flux<Users> {
        return userService.getAll(clientId, departmentId)
    }

Bean-компонент PermissionChecker:

class PermissionChecker {

    fun hasDepartmentViewPermissions(authentication: Authentication, clientId: String, projectId: String): Boolean {
        ...
    }
...