ПРИМЕЧАНИЕ. Это проверено на Spring MVC и 4.3.9.RELEASE, я еще не использовал Spring Boot.
Я нашел решение, позвольте мне поделиться, как оно сработало со мной.
1) Я настроил HttpSecurity с SessionManagement следующим образом:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/login**").permitAll() // 1
.antMatchers(...)
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.deleteCookies("JSESSIONID")
.permitAll()
.and()
.sessionManagement() // 2
.maximumSessions(1) // 3
.maxSessionsPreventsLogin(false) // 4
.expiredUrl("/login?expired") // 5
.sessionRegistry(getSessionRegistry()) // 6
;
}
С помощью документа Spring Doc > HttpSecurity > sessionManagement()
Пример конфигурации
Следующая конфигурация демонстрирует, как обеспечить, чтобы только один экземпляр пользователя аутентифицировался одновременно. Если пользователь аутентифицируется с именем пользователя «user» без выхода из системы и предпринимается попытка аутентификации с «user», первый сеанс будет принудительно завершен и отправлен на URL-адрес «/login?expired».
@Configuration
@EnableWebSecurity
public class SessionManagementSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().hasRole("USER").and().formLogin()
.permitAll().and().sessionManagement().maximumSessions(1)
.expiredUrl("/login?expired");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
} }
При использовании SessionManagementConfigurer.maximumSessions(int) не забудьте настроить HttpSessionEventPublisher для приложения, чтобы обеспечить очистку просроченных сеансов. В файле web.xml это можно настроить с помощью следующего:
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
Кроме того, AbstractSecurityWebApplicationInitializer.enableHttpSessionEventPublisher() может возвращать значение true.
Мы могли бы знать, зачем нам нужны sessionManagement()
, maximumSessions(1)
и, конечно же, expiredUrl("/login?expired")
.
- Так зачем нужен
antMatchers("/login**").permitAll()
? Чтобы у вас было разрешение на перенаправление на /login?expired
, иначе вы будете перенаправлены на /login
, потому что anyRequest().authenticated()
, с текущей конфигурацией HttpSecurity
, permitAll()
применяется к /login
и /login?logout
.
2) Если вам действительно нужен доступ к текущему вошедшему в систему пользователю или expireNow()
конкретному сеансу для конкретного пользователя, такого как я, вам может понадобиться getSessionRegistry()
, но без него maximumSessions(1)
работает нормально.
Итак, снова с помощью документа:
При использовании SessionManagementConfigurer.maximumSessions(int) не забудьте настроить HttpSessionEventPublisher для приложения, чтобы обеспечить очистку просроченных сеансов. В файле web.xml это можно настроить с помощью следующего:
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
Кроме того, AbstractSecurityWebApplicationInitializer.enableHttpSessionEventPublisher() может возвращать значение true.
Поэтому я должен изменить свое переопределение enableHttpSessionEventPublisher()
в моем классе SecurityWebInitializer.java
:
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {
@Override
protected boolean enableHttpSessionEventPublisher() {
return true;
}
}
3) Теперь еще одна последняя вещь, которую я обнаружил это была моя проблема: поскольку я новичок в Spring framework, я научился делать пользовательские UserDetails, но с немного не очень хорошей реализацией, но я мог бы сделать лучше позже , я создал сущность, которая действует как Entity
, так и UserDetails
:
@Entity
@Component("user")
public class User implements UserDetails, Serializable {
private static final long serialVersionUID = 1L;
// ...
@Override
public boolean equals(Object obj) {
if (obj instanceof User) {
return username.equals( ((User) obj).getUsername() );
}
return false;
}
@Override
public int hashCode() {
return username != null ? username.hashCode() : 0;
}
}
Несколько лет назад я нашел на форуме здесь, что вы должны реализовать оба метода hashCode()
equals()
, и если вы посмотрите на исходный код реализации по умолчанию для UserDetails User.java
вы найдете что в нем реализованы оба метода, я сделал это, и это сработало как шарм.
Вот и все, надеюсь, что это полезно.
Вы также можете прочитать эту ссылку: Spring - Истечение срока действия всех сеансов пользователя
person
Ahmed Shendy
schedule
21.08.2017