Как я могу получить список всех зарегистрированных пользователей (через spring security) мое веб-приложение
Я использую Spring Security в своем веб-приложении, и теперь я хочу получить список всех пользователей, которые вошли в мою программу.
Как я могу получить доступ к этому списку? Разве они не находятся где-то в весенних рамках? Нравится SecurityContextHolder или SecurityContextRepository?
Ответы
Ответ 1
Для доступа к списку всех зарегистрированных пользователей вам необходимо ввести экземпляр SessionRegistry на ваш bean.
@Autowired
@Qualifier("sessionRegistry")
private SessionRegistry sessionRegistry;
И затем, используя проверенный SessionRegistry, вы можете получить доступ к списку всех участников:
List<Object> principals = sessionRegistry.getAllPrincipals();
List<String> usersNamesList = new ArrayList<String>();
for (Object principal: principals) {
if (principal instanceof User) {
usersNamesList.add(((User) principal).getUsername());
}
}
Но перед тем, как вводить сеансовый реестр, вам нужно определить часть управления сеансом в spring -security.xml(смотрите раздел "Управление сеансом" в Spring справочной документации по безопасности) и в разделе concurrency -control вы должны установить псевдоним для объекта реестра сеанса (session-registry-alias), которым вы его будете вводить.
<security:http access-denied-page="/error403.jsp" use-expressions="true" auto-config="false">
<security:session-management session-fixation-protection="migrateSession" session-authentication-error-url="/login.jsp?authFailed=true">
<security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" expired-url="/login.html" session-registry-alias="sessionRegistry"/>
</security:session-management>
...
</security:http>
Ответ 2
В JavaConfig это будет выглядеть так:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final HttpSecurity http) throws Exception {
// ...
http.sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry());
}
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
}
}
С вызывающим кодом, выглядящим следующим образом:
public class UserController {
@Autowired
private SessionRegistry sessionRegistry;
public void listLoggedInUsers() {
final List<Object> allPrincipals = sessionRegistry.getAllPrincipals();
for(final Object principal : allPrincipals) {
if(principal instanceof SecurityUser) {
final SecurityUser user = (SecurityUser) principal;
// Do something with user
System.out.println(user);
}
}
}
}
Обратите внимание, что SecurityUser
- это мой собственный класс, который реализует UserDetails
.
Ответ 3
Пожалуйста, поправьте меня, если я ошибаюсь.
Я думаю, что ответ @Адама неполный. Я заметил, что сеансы, которые уже истекли в списке, появляются снова.
public class UserController {
@Autowired
private SessionRegistry sessionRegistry;
public void listLoggedInUsers() {
final List<Object> allPrincipals = sessionRegistry.getAllPrincipals();
for (final Object principal : allPrincipals) {
if (principal instanceof SecurityUser) {
final SecurityUser user = (SecurityUser) principal;
List<SessionInformation> activeUserSessions =
sessionRegistry.getAllSessions(principal,
/* includeExpiredSessions */ false); // Should not return null;
if (!activeUserSessions.isEmpty()) {
// Do something with user
System.out.println(user);
}
}
}
}
}
Надеюсь, поможет.
Ответ 4
Пожалуйста, поправьте меня, если я ошибаюсь.
Я думаю, что ответ @Adam и @elysch неполный. Я заметил, что необходимо добавить слушателя:
servletContext.addListener(HttpSessionEventPublisher.class);
к
public class AppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) {
...
servletContext.addListener(HttpSessionEventPublisher.class);
}
с безопасностью conf:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final HttpSecurity http) throws Exception {
// ...
http.sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry());
}
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
}
И тогда вы получите онлайн-пользователей!
Ответ 5
Вам нужно внедрить SessionRegistry
(как упоминалось ранее), а затем вы можете сделать это в одном конвейере следующим образом:
public List<UserDetails> findAllLoggedInUsers() {
return sessionRegistry.getAllPrincipals()
.stream()
.filter(principal -> principal instanceof UserDetails)
.map(UserDetails.class::cast)
.collect(Collectors.toList());
}
Ответ 6
Нашел это примечание весьма важным и актуальным:
"[21] Аутентификация с помощью механизмов, которые выполняют перенаправление после аутентификации (такой как вход в систему), не будет обнаружена SessionManagementFilter, так как фильтр не будет вызываться во время запроса аутентификации. Функциональность управления сеансом должна обрабатываться отдельно в этих случаи ".
https://docs.spring.io/spring-security/site/docs/3.1.x/reference/session-mgmt.html#d0e4399
Также, очевидно, у многих людей возникают проблемы с получением sessionRegistry.getAllPrincipals(), возвращающего что-то отличное от пустого массива. В моем случае я исправил это, добавив sessionAuthenticationStrategy в мой пользовательский AuthenticationFilter:
@Bean
public CustomUsernamePasswordAuthenticationFilter authenticationFilter() throws Exception {
...
authenticationFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
}
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
//cf. https://stackoverflow.com/questions/32463022/sessionregistry-is-empty-when-i-use-concurrentsessioncontrolauthenticationstrate
public SessionAuthenticationStrategy sessionAuthenticationStrategy() {
List<SessionAuthenticationStrategy> stratList = new ArrayList<>();
SessionFixationProtectionStrategy concStrat = new SessionFixationProtectionStrategy();
stratList.add(concStrat);
RegisterSessionAuthenticationStrategy regStrat = new RegisterSessionAuthenticationStrategy(sessionRegistry());
stratList.add(regStrat);
CompositeSessionAuthenticationStrategy compStrat = new CompositeSessionAuthenticationStrategy(stratList);
return compStrat;
}
Ответ 7
Подобно решению @rolyanos, у меня всегда работает:
- для контроллера
@RequestMapping(value = "/admin")
public String admin(Map<String, Object> model) {
if(sessionRegistry.getAllPrincipals().size() != 0) {
logger.info("ACTIVE USER: " + sessionRegistry.getAllPrincipals().size());
model.put("activeuser", sessionRegistry.getAllPrincipals().size());
}
else
logger.warn("EMPTY" );
logger.debug(log_msg_a + " access ADMIN page. Access granted." + ANSI_RESET);
return "admin";
}
- для передней части
<tr th:each="activeuser, iterStat: ${activeuser}">
<th><b>Active users: </b></th> <td align="center" th:text="${activeuser}"></td>
</tr>
- на весенний конфинг
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.logout()
.logoutSuccessUrl("/home")
.logoutUrl("/logout")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID");
http.authorizeRequests()
.antMatchers("/", "/home")
.permitAll()
.antMatchers("/admin")
.hasRole("ADMIN")
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/home")
.defaultSuccessUrl("/main")
.permitAll()
.and()
.logout()
.permitAll();
http.sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry());
http.authorizeRequests().antMatchers("/webjars/**").permitAll();
http.exceptionHandling().accessDeniedPage("/403");
}