«1. Обзор

В этом руководстве мы обсудим, как реализовать SSO — единый вход — с использованием Spring Security OAuth и Spring Boot.

Мы будем использовать три отдельных приложения:

    Сервер авторизации — центральный механизм аутентификации. Два клиентских приложения: приложения, использующие SSO.

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

И мы собираемся использовать тип предоставления кода авторизации из OAuth2 для делегирования аутентификации.

Примечание. В этой статье используется устаревший проект Spring OAuth. Версию этой статьи, использующую новый стек Spring Security 5, можно найти в нашей статье Простой единый вход с помощью Spring Security OAuth2.

2. Клиентское приложение

Начнем с нашего клиентского приложения; мы, конечно же, будем использовать Spring Boot, чтобы минимизировать конфигурацию:

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

Во-первых, нам понадобятся следующие зависимости в нашем pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.0.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>

2.2. Конфигурация безопасности

Следующая, самая важная часть, конфигурация безопасности нашего клиентского приложения:

@Configuration
@EnableOAuth2Sso
public class UiSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/**")
          .authorizeRequests()
          .antMatchers("/", "/login**")
          .permitAll()
          .anyRequest()
          .authenticated();
    }
}

Основной частью этой конфигурации, конечно же, является аннотация @EnableOAuth2Sso, которую мы используем для включения единого входа. .

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

Наконец, мы также определили bean-компонент RequestContextListener для обработки областей запросов.

И application.yml:

server:
    port: 8082
    servlet:
        context-path: /ui
    session:
      cookie:
        name: UISESSION
security:
  basic:
    enabled: false
  oauth2:
    client:
      clientId: SampleClientId
      clientSecret: secret
      accessTokenUri: http://localhost:8081/auth/oauth/token
      userAuthorizationUri: http://localhost:8081/auth/oauth/authorize
    resource:
      userInfoUri: http://localhost:8081/auth/user/me
spring:
  thymeleaf:
    cache: false

Несколько быстрых замечаний:

    мы отключили базовую аутентификацию по умолчанию. accessTokenUri — это URI для получения токенов доступа. URI конечной точки пользователя для получения текущих сведений о пользователе

Также обратите внимание, что в нашем примере здесь мы развернули наш сервер авторизации, но, конечно, мы также можем использовать других сторонних поставщиков, таких как Facebook или GitHub.

2.3. Внешний интерфейс

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

Наше клиентское приложение имеет очень простой интерфейс; вот index.html:

<h1>Spring Security SSO</h1>
<a href="securedPage">Login</a>

И securePage.html:

<h1>Secured Page</h1>
Welcome, <span th:text="${#authentication.name}">Name</span>

Страница securePage.html требовала аутентификации пользователей. Если пользователь, не прошедший проверку подлинности, попытается получить доступ к securePage.html, он сначала будет перенаправлен на страницу входа.

3. Сервер авторизации

Теперь давайте обсудим наш сервер авторизации.

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

Во-первых, нам нужно определить зависимости в нашем файле pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>2.3.3.RELEASE</version>
</dependency>

3.2. Конфигурация OAuth

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

Давайте начнем с настройки нашего сервера ресурсов, который служит нашим основным загрузочным приложением:

@SpringBootApplication
@EnableResourceServer
public class AuthorizationServerApplication extends SpringBootServletInitializer {
    public static void main(String[] args) {
        SpringApplication.run(AuthorizationServerApplication.class, args);
    }
}

Затем мы настроим наш сервер авторизации:

@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
    
    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Override
    public void configure(
      AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.tokenKeyAccess("permitAll()")
          .checkTokenAccess("isAuthenticated()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
          .withClient("SampleClientId")
          .secret(passwordEncoder.encode("secret"))
          .authorizedGrantTypes("authorization_code")
          .scopes("user_info")
          .autoApprove(true) 
          .redirectUris(
            "http://localhost:8082/ui/login","http://localhost:8083/ui2/login"); 
    }
}

Обратите внимание, как мы только включение простого клиента с использованием типа гранта author_code.

Кроме того, обратите внимание, что autoApprove имеет значение true, чтобы нас не перенаправляли и не повышали, чтобы вручную утвердить какие-либо области.

3.3. Конфигурация безопасности

Во-первых, мы отключим обычную аутентификацию по умолчанию с помощью нашего application.properties:

server.port=8081
server.servlet.context-path=/auth

Теперь давайте перейдем к настройке и определим простой механизм входа в систему:

@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requestMatchers()
          .antMatchers("/login", "/oauth/authorize")
          .and()
          .authorizeRequests()
          .anyRequest().authenticated()
          .and()
          .formLogin().permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("john")
            .password(passwordEncoder().encode("123"))
            .roles("USER");
    }
    
    @Bean 
    public BCryptPasswordEncoder passwordEncoder(){ 
        return new BCryptPasswordEncoder(); 
    }
}

Примечание. что мы использовали простую аутентификацию в памяти, но мы можем просто заменить ее пользовательским сервисом userDetailsService.

3.4. Конечная точка пользователя

Наконец, мы создадим нашу конечную точку пользователя, которую мы использовали ранее в нашей конфигурации:

@RestController
public class UserController {
    @GetMapping("/user/me")
    public Principal user(Principal principal) {
        return principal;
    }
}

Естественно, это вернет пользовательские данные с представлением JSON.

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

«В этом кратком руководстве мы сосредоточились на реализации единого входа с использованием Spring Security Oauth2 и Spring Boot.

Как всегда, полный исходный код можно найти на GitHub.