Spring Security: безопасная конечная точка на основе полномочий клиента

В настоящее время я использую OAuth2 от Spring Security для реализации авторизации по нескольким микросервисам. Наш AuthService выполняет всю аутентификацию с помощью токенов OAuth2 и т. Д. И может создавать пользователей.

Рассмотрим два клиента: клиент A и клиент B.

Клиент A имеет полномочия: CREATE_USER, CREATE_POST Клиент B имеет полномочия: READ_USER

(Да, мы могли бы использовать область вместо этого, но это всего лишь пример!)

Цель:

Только клиенту A, обладающему полномочием CREATE_USER , должно быть разрешено создать пользователя. Пользователи создаются путем публикации в /users .

Проблема:

Проблема в том, что когда я отправляю запрос POST в конечную точку / users с базовым заголовком проверки подлинности для клиента A, полномочия CREATE_USER не найдены, потому что запрос попадает в AnonymousAuthenticationFilter ROLE_ANONYMOUS и единственный найденный ROLE_ANONYMOUSROLE_ANONYMOUS и я получаю следующее:

 10:38:34.852 [http-nio-9999-exec-1] DEBUG osswaiFilterSecurityInterceptor - Secure object: FilterInvocation: URL: /users; Attributes: [#oauth2.throwOnError(#oauth2.hasAuthority('CREATE_USER))] 10:38:34.852 [http-nio-9999-exec-1] DEBUG osswaiFilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS 10:38:34.854 [http-nio-9999-exec-1] DEBUG ossaccess.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@a63e3e8, returned: -1 10:38:34.856 [http-nio-9999-exec-1] DEBUG osswaExceptionTranslationFilter - Access is denied (user is anonymous); redirecting to authentication entry point org.springframework.security.access.AccessDeniedException: Access is denied 

Одним из невероятно хакерских решений было бы зарегистрировать настраиваемый фильтр безопасности, который читает основной заголовок auth и проверяет, что имя клиента равно клиенту A, но это не будет работать для третьего клиента Client C, который также имеет полномочия CREATE_VIEWER , так как название, а не полномочия проверяются здесь.

 // UsersController.kt @PostMapping("/users") @ResponseStatus(HttpStatus.OK) @ResponseBody fun createUser(): String { return "Created user!" } 

Конфигурация клиента

 override fun configure(clients: ClientDetailsServiceConfigurer?) { clients!!.inMemory() .withClient("ClientA") .scopes("all") .authorities("CREATE_USER", "CREATE_POST") .authorizedGrantTypes("refresh_token", "password") .and() .withClient("ClientB") .scopes("all") .authorities("READ_USER") .authorizedGrantTypes("refresh_token", "password") } 

WebSecurityConfigurerAdaptor impl

 override fun configure(http: HttpSecurity) { http.requestMatchers().antMatchers("/oauth/authorize", "/oauth/confirm_access") .and() .authorizeRequests() .antMatchers("/users").access("hasAuthority('CREATE_USER')") .anyRequest().authenticated() .and() .csrf().disable() } override fun configure(auth: AuthenticationManagerBuilder) { auth.authenticationProvider(authenticationProvider()) } @Bean open fun authenticationProvider(): DaoAuthenticationProvider { val authProvider = DaoAuthenticationProvider() authProvider.setUserDetailsService(userCredentialService) authProvider.setPasswordEncoder(passwordEncoderService) return authProvider }