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