如何使用 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();
}
在你的问题中,你尝试了一些事情:
- 您正在设置
InMemoryUserDetailsManager
允许的用户列表(在您的例子中是一个名为 u
的用户)。
- 您正在使用
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"));
}
}
在此示例中,InMemoryUser
是 UserDetails
接口的实现。当您创建这样的自定义实现时,您必须使用 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();
}
}
这将列出在应用程序中登录并进行会话的所有用户的用户名。这也包括过期的会话,因此您可能也想过滤这些会话。
我想获取所有经过身份验证的用户的列表。
我从官方 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();
}
在你的问题中,你尝试了一些事情:
- 您正在设置
InMemoryUserDetailsManager
允许的用户列表(在您的例子中是一个名为u
的用户)。 - 您正在使用
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"));
}
}
在此示例中,InMemoryUser
是 UserDetails
接口的实现。当您创建这样的自定义实现时,您必须使用 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();
}
}
这将列出在应用程序中登录并进行会话的所有用户的用户名。这也包括过期的会话,因此您可能也想过滤这些会话。