我如何拥有登录(通过 spring 安全性)我的 Web 应用程序的所有用户的列表

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11271449/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 05:13:03  来源:igfitidea点击:

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

springweb-applicationsloginspring-security

提问by Matin Kh

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.

我在我的 web 应用程序中使用 spring security,现在我想要一个所有登录我的程序的用户的列表。

How can I have access to that list? Aren't they already kept somewhere within spring framework? Like SecurityContextHolderor SecurityContextRepository?

我如何才能访问该列表?它们不是已经保存在 spring 框架中的某个地方了吗?像SecurityContextHolderSecurityContextRepository

采纳答案by dimas

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

要访问所有登录用户的列表,您需要将 SessionRegistry 实例注入到您的 bean 中。

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

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

然后使用注入的 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());
    }
}

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.

但是在注入会话注册表之前,您需要在 spring-security.xml 中定义会话管理部分(查看Spring Security 参考文档中的会话管理部分),并且在并发控制部分中,您应该为会话注册表对象(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>

回答by Adam

In JavaConfig, it would look like this:

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

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 SecurityUseris my own class which implements UserDetails.

请注意,这SecurityUser是我自己的类,它实现了UserDetails.

回答by elysch

Please correct me if I'm wrong.

如果我错了,请纠正我。

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

我认为@Adam 的回答不完整。我注意到列表中已经过期的会话再次出现。

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.

希望能帮助到你。

回答by rolyanos

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:

我认为@Adam 和@elysch 的回答不完整。我注意到需要添加监听器:

 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!

然后您将获得当前的在线用户!

回答by k13i

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

您需要注入SessionRegistry(如前所述),然后您可以像这样在一个管道中进行注入:

public List<UserDetails> findAllLoggedInUsers() {
    return sessionRegistry.getAllPrincipals()
            .stream()
            .filter(principal -> principal instanceof UserDetails)
            .map(UserDetails.class::cast)
            .collect(Collectors.toList());
}

回答by cyberdemon

Similar to @rolyanos solution, mine for me always works:

类似于@rolyanos 解决方案,我的对我来说总是有效:

- for the controller

- 对于控制器

@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";
}

- for the front end

- 对于前端

<tr th:each="activeuser, iterStat: ${activeuser}">
    <th><b>Active users: </b></th> <td align="center" th:text="${activeuser}"></td>
    </tr>

- for spring confing

- 用于春季会议

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

回答by jvleminc

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."

“[21] SessionManagementFilter 不会检测到通过在身份验证后执行重定向的机制(例如表单登录)进行身份验证,因为在身份验证请求期间不会调用过滤器。会话管理功能必须在这些中单独处理案。”

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

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 sessionAuthenticationStrategyto my custom authenticationFilter:

此外,显然很多人在让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;
}