Я использую безопасность Spring с файлом cookie JSESSION, поэтому каждый пользователь получает этот файл cookie после входа в систему с учетными данными:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().httpBasic()
return http.build();
}
Я создал UserService для создания, извлечения, обновления и удаления пользователей и UserDetailsServiceImpl (который использует UserService) для реализации UserDetailsService:
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userService.findByUsername(username);
}
}
Таким образом, пользователи могут быть удалены. Возможно, команда удаления выполняется другим пользователем, а не им, поэтому у меня нет контекста сеанса.
Я хочу аннулировать все файлы cookie, связанные с пользователем, когда я удаляю его, чтобы он не мог снова войти в приложение, используя свои старые файлы cookie.
Там в любом случае?
Заранее спасибо.
Привет, ребята! Но требование кажется законным, @M.Deinum (пользователь-администратор хочет *немедленно*/как можно скорее удалить пользователей и аннулировать их сеансы), и решение (для одного сервера) показано здесь ... для "распределенных/lb "вы должны использовать это как-то
Неважно, используете ли вы один сервер или нет. Факт остается фактом: вы не можете получить прямой доступ к сеансу пользователя, должно быть что-то промежуточное, что контролируется Spring Security. Вот что такое SessionRegistry (который может быть в памяти, распределенным и т. д.).
Вы можете реализовать org.springframework.security.core.session.SessionRegistry или расширить реализацию по умолчанию org.springframework.security.core.session.SessionRegistryImpl, чтобы использовать getAllSessions для связанного принципала пользователя и аннулировать соответствующие в соответствии с вашими требованиями.
Чтобы ваш реестр сеансов получал уведомления о событиях жизненного цикла сеанса, вам необходимо зарегистрировать org.springframework.security.web.session.HttpSessionEventPublisher в контексте сервлета, как показано ниже:
@Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
ServletListenerRegistrationBean<HttpSessionEventPublisher> listenerRegBean = new ServletListenerRegistrationBean<>();
listenerRegBean.setListener(new HttpSessionEventPublisher());
return listenerRegBean;
}
@Bean
public SessionRegistry sessionRegistry(){
return new SessionRegistryImpl();
}
@Bean
InMemoryUserDetailsManager userDetailsService() {
UserDetails alice = User.withUsername("alice")
.password("{noop}alice")
.roles("ADMIN")
.build();
UserDetails bob = User.withUsername("bob")
.password("{noop}bob")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(alice, bob);
}
hello.html:
<form sec:authorize = "hasRole('ROLE_ADMIN')" th:action = "@{/invalidate}" method = "post">
<input type = "text" name = "user" />
<input type = "submit" value = "Delete User" />
</form>
@Controller
public class AdminController {
@Autowired
InMemoryUserDetailsManager userService;
@Autowired
SessionRegistry sessionRegistry; // !
@PostMapping("/invalidate")
public String invalidate(@RequestParam String user) {
// omitted (any) validation for brevity...
// (optional) load user to invalidate:
User bob = (User) userService.loadUserByUsername(user);
// all "principals":
sessionRegistry.getAllPrincipals()
.stream()
// filter correct user name:
.filter(p -> p instanceof UserDetails prncp && prncp.getUsername().equals(bob.getUsername()))
// "expire" all sessions for principal:
.forEach(p -> sessionRegistry.getAllSessions(p, false)
.forEach(SessionInformation::expireNow) // !
);
// (optional) custom user deletion/modification..
// userService.updateUser(...);
// (sample) custom response
return "redirect:/hello";
}
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(...)
...
.sessionManagement(sm -> sm // 1.
.maximumSessions(2) // <- example
.sessionRegistry(sessionRegistry()) // 2.
.expiredSessionStrategy(expireStrategy())) // 3.
.build();
}
@Bean // org.springframework.security.core.session.*:
SessionRegistry sessionRegistry() { // 2a.
return new SessionRegistryImpl();
}
@Bean //org.springframework.security.web.session.*:
SessionInformationExpiredStrategy expireStrategy() { // 3a.
// "/" refers to (custom, session invalidation) redirect url
return new SimpleRedirectSessionInformationExpiredStrategy("/");
}
Боб входит в систему с помощью:
.. для отображения /hello
Алиса входит в систему (с хромом) и отправляет bob с «недействительной формой».
В следующий раз, когда bob попытается запросить любой (незащищенный) ресурс (из любого браузера), (цепочка по умолчанию Spring Security) ConcurrentSessionFilter вскочит:
SessionInformationExpiredEvent(.. пока сервер не перезапустится/пользовательская служба не будет сброшена)
Мы можем обеспечить «липкие сеансы» или
..что касается более отказоустойчивого решения, реализуйте собственное SessionRegistry.
Нет, к счастью, поскольку это было бы довольно рискованно для безопасности. Если вы используете Spring Session и отслеживание сеансов в Spring Security, вы можете аннулировать этот сеанс, связанный с пользователем.