1. Обзор
В этом руководстве мы обсудим, как реализовать SSO — единый вход — с использованием Spring Security OAuth и Spring Boot.
Мы будем использовать три отдельных приложения:
- Сервер авторизации — центральный механизм аутентификации.
- Два клиентских приложения: приложения, использующие SSO
Проще говоря, когда пользователь пытается получить доступ к защищенной странице в клиентском приложении, он сначала будет перенаправлен для аутентификации через сервер аутентификации.
И мы собираемся использовать тип предоставления кода авторизации
из OAuth2 для делегирования аутентификации.
Примечание . В этой статье используется устаревший проект Spring OAuth . Версию этой статьи, использующую новый стек Spring Security 5, можно найти в нашей статье Simple Single Sign-On with 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 для получения токенов доступа.userAuthorizationUri
— это URI авторизации, на который будут перенаправлены пользователи .userInfoUri
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 .