How can I have list of all users logged in (via spring security) my web application

后端 未结 7 2256
独厮守ぢ
独厮守ぢ 2020-11-27 12:27

I\'m using spring security in my web application, and now I want to have a list of all users who are logged in my program.

How can I have access to that list? Aren\'

相关标签:
7条回答
  • 2020-11-27 12:27

    For accessing the list of all logged in users you need to inject SessionRegistry instance to your bean.

    @Autowired
    @Qualifier("sessionRegistry")
    private SessionRegistry sessionRegistry;
    

    And then using injcted SessionRegistry you can access the list of all principals:

    List<Object> principals = sessionRegistry.getAllPrincipals();
    
    List<String> usersNamesList = new ArrayList<String>();
    
    for (Object principal: principals) {
        if (principal instanceof User) {
            usersNamesList.add(((User) principal).getUsername());
        }
    }
    

    But before injecting session registry you need to define session management part in your spring-security.xml (look at Session Management section in Spring Security reference documentation) and in concurrency-control section you should set alias for session registry object (session-registry-alias) by which you will inject it.

        <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>
    
    0 讨论(0)
  • 2020-11-27 12:27

    You need to inject SessionRegistry (as mentioned eariler) and then you can do it in one pipeline like this:

    public List<UserDetails> findAllLoggedInUsers() {
        return sessionRegistry.getAllPrincipals()
                .stream()
                .filter(principal -> principal instanceof UserDetails)
                .map(UserDetails.class::cast)
                .collect(Collectors.toList());
    }
    
    0 讨论(0)
  • 2020-11-27 12:28

    In JavaConfig, it would look like this:

    @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());
        }
    }
    

    With the calling code looking like this:

    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);
                }
            }
        }
    }
    

    Note that SecurityUser is my own class which implements UserDetails.

    0 讨论(0)
  • 2020-11-27 12:40

    Found this note to be quite important and relevant:

    "[21] Authentication by mechanisms which perform a redirect after authenticating (such as form-login) will not be detected by SessionManagementFilter, as the filter will not be invoked during the authenticating request. Session-management functionality has to be handled separately in these cases."

    https://docs.spring.io/spring-security/site/docs/3.1.x/reference/session-mgmt.html#d0e4399

    Also, apparently a lot of people have troubles getting sessionRegistry.getAllPrincipals() returning something different from an empty array. In my case, I fixed it by adding the sessionAuthenticationStrategy to my custom 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;
    }
    
    0 讨论(0)
  • 2020-11-27 12:41

    Please correct me if I'm wrong too.

    I think @Adam's and @elysch`s answer is incomplete. I noticed that there are needed to add listener:

     servletContext.addListener(HttpSessionEventPublisher.class);
    

    to

    public class AppInitializer implements WebApplicationInitializer {
    
    @Override
    public void onStartup(ServletContext servletContext) {
      ...
    servletContext.addListener(HttpSessionEventPublisher.class);
    }
    

    with security 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();
        }
    }
    

    And then you will get current online users!

    0 讨论(0)
  • 2020-11-27 12:42

    Please correct me if I'm wrong.

    I think @Adam's answer is incomplete. I noticed that sessions already expired in the list were appearing again.

    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);
                    }
                }
            }
        }
    }
    

    Hope it helps.

    0 讨论(0)
提交回复
热议问题