В предыдущем посте я рассказал о том, как вызвать сервер авторизации OAuth2 с помощью UAA- проекта Cloud Foundry и заполнить его некоторыми участниками процесса авторизации OAuth2.
Я обнаружил, что эта статья на сайте Digital Ocean отлично справляется с описанием потока кода авторизации OAuth2, поэтому вместо того, чтобы перефразировать то, что задействовано в этом потоке, я непосредственно перейду к реализации этого потока с помощью Spring Boot / Spring Security.
На следующей диаграмме, вдохновленной приведенной здесь, показан поток высокого уровня в типе предоставления кода авторизации:
У меня будет два приложения — сервер ресурсов, представляющий некоторые ресурсы пользователя, и клиентское приложение, которое хочет получить доступ к этим ресурсам от имени пользователя. Сам сервер авторизации может быть запущен, как описано в предыдущем сообщении в блоге .
Остальная часть поста может быть легко прослежена вместе с кодом, доступным в моем репозитории github здесь
Сервер авторизации
Сервер Cloud Foundry UAA можно легко запустить с помощью шагов, описанных в моем предыдущем сообщении в блоге . После запуска следующие команды uaac можно использовать для ввода различных учетных данных, необходимых для запуска образца.
Эти сценарии создадут учетные данные клиента для клиентского приложения и добавят пользователя с именем «user1» с областью «resource.read» и «resource.write».
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
# Login as a canned client uaac token client get admin -s adminsecret # Add a client credential with client_id of client1 and client_secret of client1 uaac client add client1 \ --name client1 \ --scope resource.read,resource.write \ -s client1 \ --authorized_grant_types authorization_code,refresh_token,client_credentials \ --authorities uaa.resource # Another client credential resource1/resource1 uaac client add resource1 \ --name resource1 \ -s resource1 \ --authorized_grant_types client_credentials \ --authorities uaa.resource # Add a user called user1/user1 uaac user add user1 -p user1 --emails user1 @user1 .com # Add two scopes resource.read, resource.write uaac group add resource.read uaac group add resource.write # Assign user1 both resource.read, resource.write scopes.. uaac member add resource.read user1 uaac member add resource.write user1 |
Ресурсный сервер
Сервер ресурсов предоставляет несколько конечных точек, выраженных с помощью Spring MVC и защищенных с помощью Spring Security, следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
@RestController public class GreetingsController { @PreAuthorize ( "#oauth2.hasScope('resource.read')" ) @RequestMapping (method = RequestMethod.GET, value = "/secured/read" ) @ResponseBody public String read(Authentication authentication) { return String.format( "Read Called: Hello %s" , authentication.getCredentials()); } @PreAuthorize ( "#oauth2.hasScope('resource.write')" ) @RequestMapping (method = RequestMethod.GET, value = "/secured/write" ) @ResponseBody public String write(Authentication authentication) { return String.format( "Write Called: Hello %s" , authentication.getCredentials()); } } |
Доступны две конечные точки uri — «/ secured / read», авторизованный для области «resource.read» и «/ secured / write», авторизованный для области «resource.write»
Конфигурация, которая защищает эти конечные точки и помечает приложение как сервер ресурсов, следующая:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
@Configuration @EnableResourceServer @EnableWebSecurity @EnableGlobalMethodSecurity (securedEnabled = true , prePostEnabled = true ) public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.resourceId( "resource" ); } @Override public void configure(HttpSecurity http) throws Exception { http .antMatcher( "/secured/**" ) .authorizeRequests() .anyRequest().authenticated(); } } |
Эта конфигурация вместе со свойствами, описывающими способ проверки токена, — это все, что требуется для запуска сервера ресурсов.
клиент
Конфигурация клиента для OAuth2 с использованием Spring Security OAuth2 также довольно проста: аннотация @ EnableAuth2SSO включает всю необходимую конфигурацию для подключения пружинных фильтров безопасности для потоков OAuth2:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
@EnableOAuth2Sso @Configuration public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter { @Override public void configure(WebSecurity web) throws Exception { super .configure(web); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); //@formatter:off http.authorizeRequests() .antMatchers( "/secured/**" ) .authenticated() .antMatchers( "/" ) .permitAll() .anyRequest() .authenticated(); //@formatter:on } } |
Чтобы вызвать нисходящую систему, клиент должен передать токен OAuth в качестве заголовка в нисходящих вызовах, это делается путем перехвата специализированного RestTemplate, называемого OAuth2RestTemplate, который может получить токен доступа из контекста и передать его в нисходящем направлении, как только он подключен безопасный нисходящий вызов выглядит так:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
public class DownstreamServiceHandler { private final OAuth2RestTemplate oAuth2RestTemplate; private final String resourceUrl; public DownstreamServiceHandler(OAuth2RestTemplate oAuth2RestTemplate, String resourceUrl) { this .oAuth2RestTemplate = oAuth2RestTemplate; this .resourceUrl = resourceUrl; } public String callRead() { return callDownstream(String.format( "%s/secured/read" , resourceUrl)); } public String callWrite() { return callDownstream(String.format( "%s/secured/write" , resourceUrl)); } public String callInvalidScope() { return callDownstream(String.format( "%s/secured/invalid" , resourceUrl)); } private String callDownstream(String uri) { try { ResponseEntity<String> responseEntity = this .oAuth2RestTemplate.getForEntity(uri, String. class ); return responseEntity.getBody(); } catch (HttpStatusCodeException statusCodeException) { return statusCodeException.getResponseBodyAsString(); } } } |
демонстрация
Клиент и сервер ресурсов могут быть запущены с использованием приведенных здесь инструкций. Как только все системы будут запущены, доступ к клиенту предоставит пользователю страницу, которая выглядит следующим образом:
Доступ к защищенной странице приведет к тому, что страница авторизации будет представлена сервером авторизации:
Клиент запрашивает у пользователя область «resource.read» и «resource.write», пользователю предлагается авторизовать следующие области:
Предполагая, что пользователь авторизовал «resource.read», но не «resource.write», токен будет представлен пользователю:
На этом этапе, если запрашивается нисходящий ресурс, который требует области действия «resource.read», он должен быть получен:
И если нисходящий ресурс запрашивается с областью, которую пользователь не авторизовал — «resource.write» в этом случае:
Ссылка
- Большая часть кода основана на образцах приложений Cloud Foundry UAA, доступных здесь — https://github.com/pivotal-cf/identity-sample-apps
- Код в посте находится здесь : https://github.com/bijukunjummen/oauth-uaa-sample
Ссылка: | Использование сервера авторизации UAA OAuth2 — клиента и ресурса от нашего партнера по JCG Бижу Кунджуммена в блоге all and sundry. |