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.
Within your question, you're trying a few things:
InMemoryUserDetailsManager
with a list of allowed users (in your case a user called u
).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.