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

Перенаправление на разные страницы после входа в Spring Security

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

1. Обзор

Общим требованием к веб-приложению является перенаправление пользователей разных типов на разные страницы после входа в систему . Примером этого может быть перенаправление обычных пользователей на страницу /homepage.html и пользователей-администраторов, например, на страницу /console.html .

В этой статье будет показано, как быстро и безопасно реализовать этот механизм с помощью Spring Security. Статья также основана на руководстве по Spring MVC , в котором рассматривается настройка основных компонентов MVC, необходимых для проекта.

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

Spring Security предоставляет компонент, который непосредственно отвечает за принятие решения о том, что делать после успешной аутентификации — AuthenticationSuccessHandler .

2.1. Базовая конфигурация

Давайте сначала настроим базовые классы @Configuration и @Service :

@Configuration
@EnableWebSecurity
public class SecSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
// ... endpoints
.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/homepage.html", true)
// ... other configuration
}
}

Часть этой конфигурации, на которой следует сосредоточиться, — это метод defaultSuccessUrl() . После успешного входа любой пользователь будет перенаправлен на homepage.html .

Кроме того, нам нужно настроить пользователей и их роли. Для целей этой статьи мы реализуем простую службу UserDetailService с двумя пользователями, каждый из которых имеет одну роль. Подробнее на эту тему читайте в нашей статье Spring Security — Roles and Privileges .

@Service
public class MyUserDetailsService implements UserDetailsService {

private Map<String, User> roles = new HashMap<>();

@PostConstruct
public void init() {
roles.put("admin2", new User("admin", "{noop}admin1", getAuthority("ROLE_ADMIN")));
roles.put("user2", new User("user", "{noop}user1", getAuthority("ROLE_USER")));
}

@Override
public UserDetails loadUserByUsername(String username) {
return roles.get(username);
}

private List<GrantedAuthority> getAuthority(String role) {
return Collections.singletonList(new SimpleGrantedAuthority(role));
}
}

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

2.2. Добавление пользовательского обработчика успеха

Теперь у нас есть два пользователя с двумя разными ролями: пользователь и администратор . После успешного входа оба будут перенаправлены на hompeage.html . Давайте посмотрим, как у нас может быть другое перенаправление в зависимости от роли пользователя.

Во-первых, нам нужно определить собственный обработчик успеха как bean-компонент:

@Bean
public AuthenticationSuccessHandler myAuthenticationSuccessHandler(){
return new MySimpleUrlAuthenticationSuccessHandler();
}

А затем замените вызов defaultSuccessUrl на метод successHandler , который принимает наш пользовательский обработчик успеха в качестве параметра:

@Override
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
// endpoints
.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/login")
.successHandler(myAuthenticationSuccessHandler())
// other configuration
}

2.3. XML-конфигурация

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

<http use-expressions="true" >
<!-- other configuration -->
<form-login login-page='/login.html'
authentication-failure-url="/login.html?error=true"
authentication-success-handler-ref="myAuthenticationSuccessHandler"/>
<logout/>
</http>

<beans:bean id="myAuthenticationSuccessHandler"
class="com.foreach.security.MySimpleUrlAuthenticationSuccessHandler" />

<authentication-manager>
<authentication-provider>
<user-service>
<user name="user1" password="{noop}user1Pass" authorities="ROLE_USER" />
<user name="admin1" password="{noop}admin1Pass" authorities="ROLE_ADMIN" />
</user-service>
</authentication-provider>
</authentication-manager>

3. Пользовательский обработчик успешной аутентификации

Помимо интерфейса AuthenticationSuccessHandler , Spring также предоставляет разумное значение по умолчанию для этого компонента стратегии — AbstractAuthenticationTargetUrlRequestHandler и простую реализацию — SimpleUrlAuthenticationSuccessHandler . Обычно эти реализации определяют URL-адрес после входа в систему и выполняют перенаправление на этот URL-адрес.

Несмотря на некоторую гибкость, механизм определения этого целевого URL-адреса не позволяет выполнять определение программно, поэтому мы собираемся реализовать интерфейс и предоставить пользовательскую реализацию обработчика успеха. Эта реализация будет определять URL-адрес для перенаправления пользователя после входа в систему в зависимости от роли пользователя.

Прежде всего, нам нужно переопределить метод onAuthenticationSuccess :

public class MySimpleUrlAuthenticationSuccessHandler
implements AuthenticationSuccessHandler {

protected Log logger = LogFactory.getLog(this.getClass());

private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException {

handle(request, response, authentication);
clearAuthenticationAttributes(request);
}

Наш настроенный метод вызывает два вспомогательных метода:

protected void handle(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication
) throws IOException {

String targetUrl = determineTargetUrl(authentication);

if (response.isCommitted()) {
logger.debug(
"Response has already been committed. Unable to redirect to "
+ targetUrl);
return;
}

redirectStrategy.sendRedirect(request, response, targetUrl);
}

Где следующий метод выполняет фактическую работу и сопоставляет пользователя с целевым URL-адресом:

protected String determineTargetUrl(final Authentication authentication) {

Map<String, String> roleTargetUrlMap = new HashMap<>();
roleTargetUrlMap.put("ROLE_USER", "/homepage.html");
roleTargetUrlMap.put("ROLE_ADMIN", "/console.html");

final Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (final GrantedAuthority grantedAuthority : authorities) {
String authorityName = grantedAuthority.getAuthority();
if(roleTargetUrlMap.containsKey(authorityName)) {
return roleTargetUrlMap.get(authorityName);
}
}

throw new IllegalStateException();
}

Обратите внимание, что этот метод вернет сопоставленный URL-адрес для первой роли, которую имеет пользователь. Таким образом, если у пользователя есть несколько ролей, сопоставленный URL-адрес будет тем, который соответствует первой роли, указанной в коллекции полномочий .

protected void clearAuthenticationAttributes(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) {
return;
}
session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
}

DefinitTargetUrl , являющийся ядром стратегии, просто смотрит на тип пользователя (определяемый уполномоченным органом) и выбирает целевой URL-адрес на основе этой роли .

Таким образом, пользователь-администратор , определенный полномочиями ROLE_ADMIN , будет перенаправлен на страницу консоли после входа в систему, а обычный пользователь , определенный ROLE_USER , будет перенаправлен на домашнюю страницу.

4. Вывод

Как всегда, код, представленный в этой статье, доступен на GitHub . Это проект на основе Maven, поэтому его легко импортировать и запускать как есть.