1

I want to get the list of all authenticated users.
I took the basic spring-security example from the official Spring site.

As it was recommended in other relative questions (51992610), I injected the DefaultSimpUserRegistry into the code. Still, the list is empty.

@Configuration
public class UsersConfig {
    final private SimpUserRegistry userRegistry = new DefaultSimpUserRegistry();
    
    @Bean
    @Primary
    public SimpUserRegistry userRegistry() {
        return userRegistry;
    }
}
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
            http
                    .authorizeRequests()
                            .antMatchers("/", "/home").permitAll()
                            .anyRequest().authenticated()
                            .and()
                    .formLogin()
                            .loginPage("/login")
                            .permitAll()
                            .and()
                    .logout()
                            .permitAll();
    }

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
            UserDetails user =
                     User.withDefaultPasswordEncoder()
                            .username("u")
                            .password("11")
                            .roles("USER")
                            .build();

            return new InMemoryUserDetailsManager(user);
    }
}
@RestController
public class WebSocketController {

    @Autowired
    private final SimpUserRegistry simpUserRegistry;

    public WebSocketController(SimpUserRegistry simpUserRegistry) {
        this.simpUserRegistry = simpUserRegistry;
    }

    @GetMapping("/users")
    public String connectedEquipments() {
        
        return this.simpUserRegistry
                .getUsers()
                .stream()
                .map(SimpUser::getName)
                .collect(Collectors.toList()).toString();
    }
}

Build jar, launch locally, login, enter http://localhost:8080/users. Result:

[]

The full code may be taken from the Spring site. The topics on SimpUserRegistry are so rare, I can't find a full example with it. The similar posts are unanswered yet (48804780, 58925128).

Sorry, I am new to Spring, is SimpUserRegistry the correct way to list users with Spring? If so, how to use it properly? Thanks.

HoRn
  • 1,458
  • 5
  • 20
  • 25
  • Are you trying to get a list of all users that are currently connected with WebSockets, or a list of all existing users within `InMemoryUserDetailsManager`? – g00glen00b Dec 05 '19 at 13:12
  • @g00glen00b according to https://stackoverflow.com/a/54254980/1453413, DefaultSimpUserRegistry keeps track of connected websocket users – HoRn Dec 05 '19 at 13:19
  • 1
    You're right, but from your question it appears that you launch the application, and immediately expect a user to be visible. However, if you don't make a WebSocket connection after launching your application, then that list will obviously be empty. That's why I'm asking what exactly you want to see. – g00glen00b Dec 05 '19 at 13:21
  • Thanks for the tip g00glen00b, I'll figure out with this – HoRn Dec 05 '19 at 13:32

2 Answers2

1

Within your question, you're trying a few things:

  1. You're setting up InMemoryUserDetailsManager with a list of allowed users (in your case a user called u).
  2. You're using SimpUserRegistry to get a list of all connected users through Spring messaging (for example using WebSockets).

If you're just trying to get a list of all users, and you're not using WebSockets, then the second approach won't work.

If you're trying to get a list of all users that are stored within InMemoryUserDetailsManager, then the answer is that it's not possible to get that list. InMemoryUserDetailsManager uses an in-memory Map to store all users, and it doesn't expose that list.

If you really want such a list, you'll have to create a custom in-memory UserDetailsService, for example:

@Service
public class ListingInMemoryUserDetailsService implements UserDetailsService {
    private final Map<String, InMemoryUser> users;

    public ListingInMemoryUserDetailsService() {
        this.users = new HashMap<>();
    }

    public ListingInMemoryUserDetailsService(UserDetails... userDetails) {
        this.users = stream(userDetails)
            .collect(Collectors.toMap(UserDetails::getUsername, InMemoryUser::new));
    }

    public Collection<String> getUsernames() {
        return users
            .values()
            .stream()
            .map(InMemoryUser::getUsername)
            .collect(toList());
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return Optional
            .ofNullable(users.get(username))
            .orElseThrow(() -> new UsernameNotFoundException("User does not exist"));
    }
}

In this example, InMemoryUser is an implementation of the UserDetails interface. When you create a custom implementation like that, you'll have to configure it with Spring Security:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .userDetailsService(userDetailsService)
        .passwordEncoder(passwordEncoder);
}

Alternatively, if you're interested in retrieving a list of all created sessions, there's a better approach. First, you'll have to create a SessionRegistry bean:

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

Then, you'll have to configure Spring Security to set up sessions, and to use your SessionRegistry to do that:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/", "/home").permitAll()
            .anyRequest().authenticated()
            .and()
        .formLogin()
            .loginPage("/login").permitAll()
            .and()
        .logout().permitAll()
        // Add something like this:
        .sessionManagement()
            .maximumSessions(1)
            .sessionRegistry(sessionRegistry);
}

After that, you can autowire SessionRegistry, and use the getAllPrincipals() method:

@GetMapping("/users")
public Collection<String> findUsers() {
    return sessionRegistry
        .getAllPrincipals()
        .stream()
        .map(this::getUsername)
        .flatMap(Optional::stream)
        .collect(toList());
}

private Optional<String> getUsername(Object principal) {
    if (principal instanceof UserDetails) {
        return Optional.ofNullable(((UserDetails) principal).getUsername());
    } else {
        return Optional.empty();
    }
}

This will list all usernames of users that logged in within the application, and had a session. This also includes expired sessions, so you may want to filter on those as well.

g00glen00b
  • 41,995
  • 13
  • 95
  • 133
0

At the time of Class Loading, your simpUserRegistry will be null. At starting, you will not get list of users. Instead of calling simpUserRegistry in connectedEquipments() method, call userRegistry() method by Using USersConfig class. Something like below code.

@GetMapping("/users")
public String connectedEquipments() {

    return userRegistry()
            .getUsers()
            .stream()
            .map(SimpUser::getName)
            .collect(Collectors.toList()).toString();
}