Wie kann ich eine Liste aller Benutzer haben, die (über Spring Security) in meiner Webanwendung angemeldet sind?

70

Ich verwende Spring Security in meiner Webanwendung und möchte jetzt eine Liste aller Benutzer haben, die in meinem Programm angemeldet sind.

Wie kann ich auf diese Liste zugreifen? Sind sie nicht schon irgendwo im Frühlingsrahmen? Wie SecurityContextHolder oder SecurityContextRepository ?

Matin Kh
quelle
Wenn Sie eine benutzerdefinierte Authentifizierung verwenden, funktioniert diese nicht sofort und muss einige Konfigurationen vornehmen. Siehe diesen stackoverflow.com/a/65542389/9004116
Es ist K

Antworten:

62

Um auf die Liste aller angemeldeten Benutzer zuzugreifen, müssen Sie die SessionRegistry-Instanz in Ihre Bean einfügen.

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

Und dann können Sie mit der verletzten SessionRegistry auf die Liste aller Principals zugreifen:

List<Object> principals = sessionRegistry.getAllPrincipals();

List<String> usersNamesList = new ArrayList<String>();

for (Object principal: principals) {
    if (principal instanceof User) {
        usersNamesList.add(((User) principal).getUsername());
    }
}

Bevor Sie jedoch die Sitzungsregistrierung einfügen, müssen Sie den Sitzungsverwaltungsteil in Ihrer spring-security.xml definieren (siehe Abschnitt Sitzungsverwaltung in der Spring Security-Referenzdokumentation ) und im Abschnitt zur Parallelitätskontrolle den Alias ​​für das Sitzungsregistrierungsobjekt ( Sitzungsregistrierung) festlegen. Alias ), mit dem Sie es injizieren.

    <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>
dimas
quelle
Ich verstehe nicht, wie sagt man, dass das Hauptobjekt eine Instanz der User Bean ist. Können Sie erklären? Gibt es eine Konfiguration, um die Sitzungsregistrierung mit der User Bean zu verbinden? Wie werden User Beans in Sessionregistry-Principals gespeichert?
Newinjava
45

In JavaConfig würde es so aussehen:

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

Der Aufrufcode sieht folgendermaßen aus:

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

Beachten Sie, dass dies SecurityUsermeine eigene Klasse ist, die implementiert UserDetails.

Adam
quelle
15
Diese Antwort scheint richtig zu sein, aber für mich ist es immer wieder eine leere Sammlung, eine Idee?
Azerafati
3
Für den Datensatz ist die ServletListenerRegistrationBean-Klasse Teil von Spring Boot, daher müssen Sie mit Spring Boot eine Abhängigkeit hinzufügen. In meiner Organisation sind nur Spring MVC und Spring Security zur Verwendung zugelassen, daher kann ich Ihre Lösung nicht verwenden (das sieht übrigens fantastisch aus). Muss einen anderen Weg finden, um dies zu erreichen. Vielen Dank!
Charles Morin
@mailman Ja, ich speichere Sitzung, und von dieser Sitzung erhalte ich eine Liste der angemeldeten Benutzer
Ravi H
@ Mailman lassen Sie mich wissen, wenn Sie Hilfe benötigen, und alternativ können Sie alle angemeldeten Benutzer mit httpListeners
Ravi H
3
Die Antwort von @azerafati funktioniert nicht, wenn Sie die manuelle Authentifizierung verwenden. Denken Sie also daran, dass Sie den Benutzer auch in die sessionRegistry einfügen müssen: sessionRegistry.registerNewSession (request.getSession (). getId (), auth.getPrincipal ());
MeetJoeBlack
9

Bitte korrigieren Sie mich, wenn ich falsch liege.

Ich denke, @ Adams Antwort ist unvollständig. Ich habe festgestellt, dass bereits abgelaufene Sitzungen in der Liste erneut angezeigt werden.

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

Ich hoffe es hilft.

elysch
quelle
Ich verstehe nicht, wie sagen Sie, dass das Hauptobjekt eine Instanz der SecurityUser-Bean ist? Können Sie erklären? Gibt es eine Konfiguration zum Verbinden der Sitzungsregistrierung mit der SecurityUser-Bean? Wie wird die SecurityUser-Bean in Sessionregistry-Principals gespeichert?
Newinjava
Ich kann momentan nicht auf meinen Quellcode zugreifen, aber ich kann sagen, dass er funktioniert. Ich hoffe, bald etwas Zeit zu finden, um mich damit zu befassen, aber ich kann es momentan nicht versprechen. Entschuldigung
elysch
7

Bitte korrigieren Sie mich, wenn ich mich auch irre.

Ich denke, die Antwort von @ Adam und @ elysch ist unvollständig. Mir ist aufgefallen, dass ein Listener hinzugefügt werden muss:

 servletContext.addListener(HttpSessionEventPublisher.class);

zu

public class AppInitializer implements WebApplicationInitializer {

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

mit Sicherheitskonf:

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

Und dann erhalten Sie aktuelle Online-Nutzer!

Rolyanos
quelle
Ich verwende Spring Boot und nachdem ich die HttpSessionEventPublisher-Bean deklariert habe, wurde sie automatisch aufgenommen und verwendet.
Fap
3

Sie müssen injizieren SessionRegistry(wie bereits erwähnt) und können dies dann in einer Pipeline wie folgt tun:

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

Fand diesen Hinweis sehr wichtig und relevant:

"[21] Die Authentifizierung durch Mechanismen, die nach der Authentifizierung eine Umleitung durchführen (z. B. Formularanmeldung), wird von SessionManagementFilter nicht erkannt, da der Filter während der Authentifizierungsanforderung nicht aufgerufen wird. Die Sitzungsverwaltungsfunktionen müssen in diesen separat behandelt werden Fälle."

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

Außerdem haben anscheinend viele Leute Probleme, sessionRegistry.getAllPrincipals () dazu zu bringen , etwas anderes als ein leeres Array zurückzugeben. In meinem Fall habe ich das Problem behoben, indem ich die sessionAuthenticationStrategy zu meinem benutzerdefinierten Authentifizierungsfilter hinzugefügt habe :

@Bean
public CustomUsernamePasswordAuthenticationFilter authenticationFilter() throws Exception {
...

  authenticationFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
}

@Bean
public SessionRegistry sessionRegistry() {
    return new SessionRegistryImpl();
}

//cf. /programming/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;
}
jvleminc
quelle
1

Ähnlich wie bei der @rolyanos-Lösung funktioniert meine für mich immer:

- für die Steuerung

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

- Für das Frontend

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

- für Frühlingskonfektion

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