Извлечение принципала и полномочий с помощью Spring Security OAuth

Извлечение принципала и полномочий с помощью Spring Security OAuth

1. обзор

В этом руководстве мы покажем, как создать приложение, которое делегирует аутентификацию пользователя третьему лицу, а также настраиваемому серверу авторизации, используя Spring Boot и Spring Security OAuth.

Такжеwe’ll demonstrate how to extract both Principal and Authorities using Spring’s PrincipalExtractor and AuthoritiesExtractor interfaces.

Для введения в Spring Security OAuth2 обратитесь к статьямthese.

2. Maven Зависимости

Для начала нам нужно добавить зависимостьspring-security-oauth2-autoconfigure к нашемуpom.xml:


    org.springframework.security.oauth.boot
    spring-security-oauth2-autoconfigure
    2.0.1.RELEASE

3. OAuth-аутентификация с использованием Github

Затем давайте создадим конфигурацию безопасности нашего приложения:

@Configuration
@EnableOAuth2Sso
public class SecurityConfig
  extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http)
      throws Exception {

        http.antMatcher("/**")
          .authorizeRequests()
          .antMatchers("/login**")
          .permitAll()
          .anyRequest()
          .authenticated()
          .and()
          .formLogin().disable();
    }
}

Короче говоря, мы говорим, что любой может получить доступ к конечной точке/login и что для всех остальных конечных точек потребуется аутентификация пользователя.

Мы также аннотировали наш класс конфигурации с помощью@EnableOAuthSso, который преобразует наше приложение в клиент OAuth и создает необходимые компоненты для того, чтобы оно работало как таковое.

Хотя Spring по умолчанию создает для нас большинство компонентов, нам все же нужно настроить некоторые свойства:

security.oauth2.client.client-id=89a7c4facbb3434d599d
security.oauth2.client.client-secret=9b3b08e4a340bd20e866787e4645b54f73d74b6a
security.oauth2.client.access-token-uri=https://github.com/login/oauth/access_token
security.oauth2.client.user-authorization-uri=https://github.com/login/oauth/authorize
security.oauth2.client.scope=read:user,user:email
security.oauth2.resource.user-info-uri=https://api.github.com/user

Вместо того, чтобы заниматься управлением учетными записями пользователей, мы делегируем их третьей стороне, в данном случае Github, что позволяет нам сосредоточиться на логике нашего приложения.

4. Извлечение принципала и властей

При работе в качестве клиента OAuth и аутентификации пользователей с помощью третьей стороны необходимо учитывать три этапа:

  1. Аутентификация пользователя - пользователь аутентифицируется с третьей стороной

  2. Авторизация пользователя - следует за аутентификацией, когда пользователь разрешает нашему приложению выполнять определенные операции от его имени; вот где приходитscopes

  3. Получение данных пользователя. Используйте полученный токен OAuth для получения данных пользователя.

После получения данных пользователяSpring is able to automatically create the user’s Principal and Authorities.

Хотя это может быть приемлемым, чаще всего мы оказываемся в сценарии, где мы хотим иметь полный контроль над ними.

Для этогоSpring gives us two interfaces we can use to override its default behavior:

  • PrincipalExtractor - Интерфейс, который мы можем использовать для предоставления нашей пользовательской логики для извлеченияPrincipal

  • AuthoritiesExtractor - аналогичноPrincipalExtractor, но вместо этого используется для настройки извлеченияAuthorities

По умолчаниюSpring provides two components – FixedPrincipalExtractor and FixedAuthoritiesExtractor  реализуют эти интерфейсы и имеют заранее определенную стратегию их создания для нас.

4.1. Настройка аутентификации Github

В нашем случае мы знаем, как выглядят данные пользователя Githublike, и что мы можем использовать для их адаптации в соответствии с нашими потребностями.

Таким образом, чтобы переопределить компоненты Spring по умолчанию, нам просто нужно создать дваBeans, которые также реализуют эти интерфейсы.

ДляPrincipalнашего приложения мы просто собираемся использовать имя пользователя Github:

public class GithubPrincipalExtractor
  implements PrincipalExtractor {

    @Override
    public Object extractPrincipal(Map map) {
        return map.get("login");
    }
}

В зависимости от подписки на Github нашего пользователя - бесплатной или иной - мы дадим ему право доступаGITHUB_USER_SUBSCRIBED илиGITHUB_USER_FREE:

public class GithubAuthoritiesExtractor
  implements AuthoritiesExtractor {
    List GITHUB_FREE_AUTHORITIES
     = AuthorityUtils.commaSeparatedStringToAuthorityList(
     "GITHUB_USER,GITHUB_USER_FREE");
    List GITHUB_SUBSCRIBED_AUTHORITIES
     = AuthorityUtils.commaSeparatedStringToAuthorityList(
     "GITHUB_USER,GITHUB_USER_SUBSCRIBED");

    @Override
    public List extractAuthorities
      (Map map) {

        if (Objects.nonNull(map.get("plan"))) {
            if (!((LinkedHashMap) map.get("plan"))
              .get("name")
              .equals("free")) {
                return GITHUB_SUBSCRIBED_AUTHORITIES;
            }
        }
        return GITHUB_FREE_AUTHORITIES;
    }
}

Затем нам также нужно создать компоненты с помощью этих классов:

@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // ...

    @Bean
    public PrincipalExtractor githubPrincipalExtractor() {
        return new GithubPrincipalExtractor();
    }

    @Bean
    public AuthoritiesExtractor githubAuthoritiesExtractor() {
        return new GithubAuthoritiesExtractor();
    }
}

4.2. Использование пользовательского сервера авторизации

Мы также можем использовать наш собственный Сервер авторизации для наших пользователей - вместо того, чтобы полагаться на третьих лиц.

Несмотря на то, что мы решили использовать сервер авторизации, компоненты, которые нам нужны для настройки какPrincipal, так иAuthorities, остаются прежними: aPrincipalExtractor иAuthoritiesExtractor.

Нам просто нужноbe aware of the data returned by the user-info-uri endpoint и использовать его по своему усмотрению.

Давайте изменим наше приложение для аутентификации наших пользователей с помощью сервера авторизации, описанного в статьеthis:

security.oauth2.client.client-id=SampleClientId
security.oauth2.client.client-secret=secret
security.oauth2.client.access-token-uri=http://localhost:8081/auth/oauth/token
security.oauth2.client.user-authorization-uri=http://localhost:8081/auth/oauth/authorize
security.oauth2.resource.user-info-uri=http://localhost:8081/auth/user/me

Теперь, когда мы указываем на наш сервер авторизации, нам нужно создать оба экстрактора; в этом случае нашPrincipalExtractor собирается извлечьPrincipal изMap с помощью ключаname:

public class examplePrincipalExtractor
  implements PrincipalExtractor {

    @Override
    public Object extractPrincipal(Map map) {
        return map.get("name");
    }
}

Что касается полномочий, наш Сервер авторизации уже помещает их в свои данныеuser-info-uri.

Таким образом, мы собираемся извлекать и обогащать их:

public class exampleAuthoritiesExtractor
  implements AuthoritiesExtractor {

    @Override
    public List extractAuthorities
      (Map map) {
        return AuthorityUtils
          .commaSeparatedStringToAuthorityList(asAuthorities(map));
    }

    private String asAuthorities(Map map) {
        List authorities = new ArrayList<>();
        authorities.add("example_USER");
        List> authz =
          (List>) map.get("authorities");
        for (LinkedHashMap entry : authz) {
            authorities.add(entry.get("authority"));
        }
        return String.join(",", authorities);
    }
}

Затем мы добавим bean-компоненты в наш классSecurityConfig:

@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // ...

    @Bean
    public PrincipalExtractor examplePrincipalExtractor() {
        return new examplePrincipalExtractor();
    }

    @Bean
    public AuthoritiesExtractor exampleAuthoritiesExtractor() {
        return new exampleAuthoritiesExtractor();
    }
}

5. Заключение

В этой статье мы реализовали приложение, которое делегирует аутентификацию пользователя третьей стороне, а также настраиваемому серверу авторизации, и продемонстрировали, как настроить какPrincipal, так иAuthorities.

Как обычно, реализацию этого примера можно найтиover on Github.

При локальном запуске вы можете запустить и протестировать приложение наlocalhost:8082