如何使用 Spring 正确获取用户列表?

How to get the list of users properly with Spring?

我想获取所有经过身份验证的用户的列表。
我从官方 Spring 站点获取了基本 spring-安全性 example

按照其他相关问题(51992610)的建议,我将 DefaultSimpUserRegistry 注入到代码中。 尽管如此,列表还是空的。

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

构建 jar,在本地启动,登录,输入 http://localhost:8080/users。结果:

[]

完整代码可以从 Spring 站点获取。 SimpUserRegistry 上的主题很少见,我找不到完整的示例。类似帖子尚未回复 (48804780, 58925128)。

抱歉,我是 Spring 的新手,SimpUserRegistry 是使用 Spring 列出用户的正确方法吗?如果可以,如何正确使用?谢谢。

在Class加载时,您的 simpUserRegistry 将为空。开始时,您不会获得用户列表。不要在 connectedEquipments() 方法中调用 simpUserRegistry,而是使用 USersConfig class 调用 userRegistry() 方法。类似于下面的代码。

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

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

在你的问题中,你尝试了一些事情:

  1. 您正在设置 InMemoryUserDetailsManager 允许的用户列表(在您的例子中是一个名为 u 的用户)。
  2. 您正在使用 SimpUserRegistry 通过 Spring 消息传递(例如使用 WebSockets)获取所有已连接用户的列表。

如果您只是想获取所有用户的列表,并且您没有使用 WebSockets,那么第二种方法将行不通

如果您试图获取存储在 InMemoryUserDetailsManager 中的所有用户的列表,那么答案是无法获取该列表。 InMemoryUserDetailsManager 使用内存中的 Map 来存储所有用户,并且不会公开该列表。

如果您确实需要这样的列表,则必须在内存中创建自定义 UserDetailsService,例如:

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

在此示例中,InMemoryUserUserDetails 接口的实现。当您创建这样的自定义实现时,您必须使用 Spring Security:

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

或者,如果您有兴趣检索所有已创建会话的列表,还有更好的方法。首先,您必须创建一个 SessionRegistry bean:

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

然后,您必须配置 Spring 安全以设置会话,并使用您的 SessionRegistry 来执行此操作:

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

之后,你可以自动装配SessionRegistry,并使用getAllPrincipals()方法:

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

这将列出在应用程序中登录并进行会话的所有用户的用户名。这也包括过期的会话,因此您可能也想过滤这些会话。