Статьи

Использование сервера авторизации UAA OAuth2 — клиент и ресурс

В предыдущем посте я рассказал о том, как вызвать сервер авторизации 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.