Spring security UserDetailsService 的配置方式


在 Spring Security 中,UserDetailsService 是用于加载用户信息的接口。一般情况下,需要自定义实现 UserDetailsService 接口来加载用户信息。常见的 UserDetailsService 配置方法如下:

  1. 基于内存的 UserDetailsService 配置

在这种方式下,可以使用 InMemoryUserDetailsManager 类来存储用户信息,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
User.UserBuilder users = User.withDefaultPasswordEncoder();
auth.inMemoryAuthentication()
.withUser(users.username("user").password("password").roles("USER"))
.withUser(users.username("admin").password("password").roles("USER", "ADMIN"));
}

// ... other configurations
}

在这个示例中,创建了两个用户:user 和 admin,他们的密码均为 “password”,其中 admin 拥有 USER 和 ADMIN 两个角色。

  1. 基于数据库的 UserDetailsService 配置

在这种方式下,需要实现自定义的 UserDetailsService 类,以便从数据库中加载用户信息。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Service
public class CustomUserDetailsService implements UserDetailsService {

@Autowired
private UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found");
}
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
getAuthorities(user));
}

private Collection<? extends GrantedAuthority> getAuthorities(User user) {
List<GrantedAuthority> authorities = new ArrayList<>();
for (Role role : user.getRoles()) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
}

在这个示例中,自定义了 CustomUserDetailsService 类,并在 loadUserByUsername 方法中从数据库中获取用户信息。注意,将 Role 对象转换成 GrantedAuthority 对象,以便在 Spring Security 中进行角色授权。

然后,在 SecurityConfig 中,可以将自定义的 CustomUserDetailsService 注入到 Spring Security 中,以便 Spring Security 使用该服务来加载用户信息,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private CustomUserDetailsService userDetailsService;

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

// ... other configurations
}
  1. LDAP 的 UserDetailsService 配置步骤:

    在 Spring Security 中,可以使用 LDAP(Lightweight Directory Access Protocol)来实现用户身份验证。LDAP 是一个开放的标准协议,用于访问分布式目录服务,常用于企业内部的用户管理。在 Spring Security 中,可以使用 LdapUserDetailsManager 类来从 LDAP 目录中获取用户信息。

    1. 添加 LDAP 的依赖
    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-ldap</artifactId>
    <version>${spring-security.version}</version>
    </dependency>
    1. 配置 LDAP 连接信息

    在 Spring Boot 的 application.properties 文件中配置 LDAP 的连接信息,例如:

    1
    2
    3
    4
    spring.ldap.urls=ldap://localhost:8389/
    spring.ldap.base=dc=springframework,dc=org
    spring.ldap.username=cn=admin,dc=springframework,dc=org
    spring.ldap.password=secret
    1. 配置 LDAP 数据源

    在 Spring Boot 中,可以通过配置 LdapContextSource 对象来配置 LDAP 数据源。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    @Configuration
    public class LdapConfig {

    @Value("${spring.ldap.urls}")
    private String ldapUrls;

    @Value("${spring.ldap.base}")
    private String ldapBase;

    @Value("${spring.ldap.username}")
    private String ldapUsername;

    @Value("${spring.ldap.password}")
    private String ldapPassword;

    @Bean
    public LdapContextSource contextSource() {
    LdapContextSource contextSource = new LdapContextSource();
    contextSource.setUrl(ldapUrls);
    contextSource.setBase(ldapBase);
    contextSource.setUserDn(ldapUsername);
    contextSource.setPassword(ldapPassword);
    return contextSource;
    }
    }
    1. 配置 LDAP 的 UserDetailsService

    在 Spring Security 中,可以通过 LdapUserDetailsManager 类来配置基于 LDAP 的 UserDetailsService。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private LdapUserDetailsManager userDetailsManager;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.ldapAuthentication()
    .userSearchFilter("(uid={0})")
    .userDetailsContextMapper(userDetailsContextMapper())
    .contextSource(contextSource());
    }

    @Bean
    public UserDetailsContextMapper userDetailsContextMapper() {
    return new LdapUserDetailsMapper(userDetailsManager);
    }
    }

    在这个示例中,使用 auth.ldapAuthentication() 方法来配置 LDAP 的认证方式,使用 userSearchFilter() 方法来指定搜索用户的过滤器,使用 userDetailsContextMapper() 方法来指定自定义的用户详情映射器。

    1. 实现 UserDetailsContextMapper 接口

    需要实现 UserDetailsContextMapper 接口来将 LDAP 中的用户信息映射成 Spring Security 中的 UserDetails 对象。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @Service
    public class CustomUserDetailsContextMapper implements UserDetailsContextMapper {

    @Override
    public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
    List<GrantedAuthority> auths = new ArrayList<>();
    for (GrantedAuthority authority : authorities) {
    auths.add(new SimpleGrantedAuthority(authority.getAuthority()));
    }

    User.UserBuilder builder = User.withUsername(username);
    builder.password("<password>");
    builder.authorities(auths);
    return builder.build();
    }

    @Override
    public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
    throw new UnsupportedOperationException("Not implemented");
    }
    }

    在这个示例中,实现了 mapUserFromContext() 方法,将 DirContextOperations 对象中的用户信息映射为 UserDetails 对象,并返回。DirContextOperations 对象包含了从 LDAP 目录中获取到的用户信息,例如用户名、密码、邮箱、电话等等。可以使用 ctx.getStringAttribute("<attributeName>") 方法来获取这些信息。

    在这个示例中,假设密码是固定的,因此直接将密码设置为一个字符串。在实际应用中,应该从 LDAP 目录中获取用户的加密密码,并将其设置为 UserDetails 对象的密码属性。

    同时,也实现了 mapUserToContext() 方法,但是这个方法没有实现任何逻辑,而是直接抛出了 UnsupportedOperationException 异常。在实际应用中,可以使用这个方法将 UserDetails 对象中的信息映射回 LDAP 目录中。