Перейти к основному содержимому

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

· 5 мин. чтения

1. Обзор

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

Кроме того, мы продемонстрируем, как извлечь как принципала , так и авторитеты , используя интерфейсы Spring PrincipalExtractor и AuthoritiesExtractor .

Для ознакомления с Spring Security OAuth2 обратитесь к этим статьям.

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

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

<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>

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. Авторизация пользователя — следует за аутентификацией, когда пользователь разрешает нашему приложению выполнять определенные операции от его имени; тут на помощь приходят масштабы
  3. Получить пользовательские данные — использовать полученный токен OAuth для получения пользовательских данных.

Как только мы получаем данные пользователя, Spring может автоматически создать пользователя Principal и Authorities .

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

Для этого Spring предоставляет нам два интерфейса, которые мы можем использовать для переопределения его поведения по умолчанию :

  • PrincipalExtractor — интерфейс, который мы можем использовать, чтобы предоставить нашу пользовательскую логику для извлечения принципала .
  • AuthoritiesExtractor — похож на PrincipalExtractor , но вместо этого используется для настройки извлечения авторитетов .

По умолчанию Spring предоставляет два компонента — FixedPrincipalExtractor и FixedAuthoritiesExtractor которые реализуют эти интерфейсы и имеют предопределенную стратегию их создания для нас.

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

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

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

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

public class GithubPrincipalExtractor 
implements PrincipalExtractor {

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

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

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

@Override
public List<GrantedAuthority> extractAuthorities
(Map<String, Object> map) {

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

Затем нам также нужно создать bean-компоненты, используя эти классы:

@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, остаются прежними: PrincipalExtractor и AuthoritiesExtractor .

Нам просто нужно знать о данных, возвращаемых конечной точкой user-info-uri , и использовать их по своему усмотрению.

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

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 будет извлекать принципала из карты , используя ключ имени :

public class ForEachPrincipalExtractor 
implements PrincipalExtractor {

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

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

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

public class ForEachAuthoritiesExtractor 
implements AuthoritiesExtractor {

@Override
public List<GrantedAuthority> extractAuthorities
(Map<String, Object> map) {
return AuthorityUtils
.commaSeparatedStringToAuthorityList(asAuthorities(map));
}

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

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

@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {

// ...

@Bean
public PrincipalExtractor foreachPrincipalExtractor() {
return new ForEachPrincipalExtractor();
}

@Bean
public AuthoritiesExtractor foreachAuthoritiesExtractor() {
return new ForEachAuthoritiesExtractor();
}
}

5. Вывод

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

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

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