Безопасность Spring: удалить файлы cookie от пользователя, когда я удаляю пользователя

Я использую безопасность 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.

Там в любом случае?

Заранее спасибо.

Нет, к счастью, поскольку это было бы довольно рискованно для безопасности. Если вы используете Spring Session и отслеживание сеансов в Spring Security, вы можете аннулировать этот сеанс, связанный с пользователем.

M. Deinum 06.06.2023 11:16

Привет, ребята! Но требование кажется законным, @M.Deinum (пользователь-администратор хочет *немедленно*/как можно скорее удалить пользователей и аннулировать их сеансы), и решение (для одного сервера) показано здесь ... для "распределенных/lb "вы должны использовать это как-то

xerx593 06.06.2023 11:21

Неважно, используете ли вы один сервер или нет. Факт остается фактом: вы не можете получить прямой доступ к сеансу пользователя, должно быть что-то промежуточное, что контролируется Spring Security. Вот что такое SessionRegistry (который может быть в памяти, распределенным и т. д.).

M. Deinum 06.06.2023 11:26
1
3
53
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете реализовать 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();
    }
Ответ принят как подходящий

у меня так работает

  1. На основе gs-securing-web
  2. Дайте нам несколько тестовых пользователей/изменение:
     @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);
     }
    
  3. Добавление (элементарного административного) элемента управления в 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>
    
  4. (Зачаточный) контроллер «аннулировать/удалить пользователя»:
    @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";
       }
    }
    
  5. Чтобы все это подключить, нам (только) нужно:
     @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("/");
     }
    

Время тестирования

  1. Боб входит в систему с помощью:

    • Хром (анонимно)
    • и браузер Опера

    .. для отображения /hello

  2. Алиса входит в систему (с хромом) и отправляет bob с «недействительной формой».

  3. В следующий раз, когда bob попытается запросить любой (незащищенный) ресурс (из любого браузера), (цепочка по умолчанию Spring Security) ConcurrentSessionFilter вскочит:

    • определяет истечение сеанса (установлено нами)
    • выполняет выход из системы (обработчик)
    • и "выстреливает" SessionInformationExpiredEvent
  4. (.. пока сервер не перезапустится/пользовательская служба не будет сброшена)

Для среды с балансировкой нагрузки/распределенной (серверной)

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

  • Для распределения/балансировки нагрузки (сеансы должны быть фиксированными или) SessionRegistry должен знать обо всех узлах/экземплярах/участниках/сеансах.
  • для (большей) устойчивости нам нужно предварительно заполнить наш SessionRegistry действительными, активными/постоянными сеансами. (Я обнаружил, что они пусты после «devtools-reload», в то время как базовые (контейнерные) сеансы все еще сохранялись.)

Другие вопросы по теме