You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于Spring Security LDAP自定义登录流程,实现创建用户会话前从数据库获取用户允许的区域代码列表

如何基于Spring Security LDAP自定义登录流程,实现创建用户会话前从数据库获取用户允许的区域代码列表

嗨,我来帮你搞定这个需求!其实Spring Security的LDAP登录流程预留了很灵活的扩展点,咱们要做的就是在用户通过LDAP认证后、会话正式创建前,插入自定义逻辑去数据库拉取区域权限列表,再把这些信息绑定到用户认证对象里。下面一步步来:

1. 扩展UserDetails,存储区域代码

默认的User类没有咱们需要的区域代码字段,所以先自定义一个CustomUserDetails来扩展它:

import org.springframework.security.core.userdetails.User;
import java.util.List;

public class CustomUserDetails extends User {
    private final List<String> regionCodes;

    public CustomUserDetails(String username, String password, boolean enabled,
                            boolean accountNonExpired, boolean credentialsNonExpired,
                            boolean accountNonLocked, List<GrantedAuthority> authorities,
                            List<String> regionCodes) {
        super(username, password, enabled, accountNonExpired, credentialsNonExpired,
                accountNonLocked, authorities);
        this.regionCodes = regionCodes;
    }

    public List<String> getRegionCodes() {
        return regionCodes;
    }
}

2. 自定义UserDetailsContextMapper,注入数据库查询逻辑

这个接口是Spring Security用来把LDAP返回的用户信息转换成UserDetails的关键入口,咱们在这里注入数据库服务,根据LDAP里的用户职位去查区域代码:

import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
import org.springframework.stereotype.Component;
import java.util.List;

@Component
public class CustomLdapUserDetailsMapper implements UserDetailsContextMapper {

    // 注入你的数据库服务,用来根据职位查区域代码
    private final RegionCodeService regionCodeService;

    public CustomLdapUserDetailsMapper(RegionCodeService regionCodeService) {
        this.regionCodeService = regionCodeService;
    }

    @Override
    public UserDetails mapUserFromContext(DirContextOperations ctx, String username, List<GrantedAuthority> authorities) {
        // 从LDAP上下文获取用户的position属性(这里要和你LDAP里的属性名对应)
        String position = ctx.getStringAttribute("position");
        
        // 调用数据库服务,根据职位获取允许的区域代码列表
        List<String> allowedRegionCodes = regionCodeService.getAllowedCodesByPosition(position);
        
        // 构建自定义的UserDetails对象,把区域代码传进去
        return new CustomUserDetails(
                username,
                // LDAP认证一般不需要明文密码,这里可以传空或者用LDAP返回的加密密码
                "",
                true,
                true,
                true,
                true,
                authorities,
                allowedRegionCodes
        );
    }

    @Override
    public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
        // 如果不需要把用户信息写回LDAP,这个方法留空就行
    }
}

3. 配置Spring Security,启用自定义的Mapper

接下来在SecurityFilterChain里配置LDAP认证时,指定咱们的自定义UserDetailsContextMapper:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final CustomLdapUserDetailsMapper customLdapUserDetailsMapper;
    // 注入LDAP上下文配置(根据你实际的LDAP服务器信息配置)
    private final BaseLdapPathContextSource contextSource;

    public SecurityConfig(CustomLdapUserDetailsMapper customLdapUserDetailsMapper,
                          BaseLdapPathContextSource contextSource) {
        this.customLdapUserDetailsMapper = customLdapUserDetailsMapper;
        this.contextSource = contextSource;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        .anyRequest().authenticated()
                )
                .formLogin(form -> form.permitAll())
                .ldapAuthentication(ldap -> ldap
                        .userSearchFilter("(uid={0})") // 根据你LDAP的用户过滤规则调整
                        .userSearchBase("ou=users") // 用户所在的LDAP节点
                        .contextSource(contextSource)
                        // 关键:指定咱们的自定义UserDetailsMapper
                        .userDetailsContextMapper(customLdapUserDetailsMapper)
                );
        return http.build();
    }
}

4. 后续使用区域代码

当用户登录成功后,你可以在任何需要的地方(比如控制器、服务类)获取用户的区域代码列表:

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DocumentController {

    private final DocumentService documentService;

    public DocumentController(DocumentService documentService) {
        this.documentService = documentService;
    }

    @GetMapping("/documents")
    public List<Document> getUserDocuments() {
        // 获取当前认证的用户对象
        CustomUserDetails currentUser = (CustomUserDetails) SecurityContextHolder.getContext()
                .getAuthentication()
                .getPrincipal();
        
        // 获取允许的区域代码
        List<String> allowedRegions = currentUser.getRegionCodes();
        
        // 根据区域代码查询文档
        return documentService.getDocumentsByRegionCodes(allowedRegions);
    }
}

一些注意事项

  • 确保LDAP里的position属性名和你代码里的getStringAttribute("position")一致,别写错了!
  • 数据库查询的异常要处理好,比如如果用户的职位在数据库里没有对应记录,你可以返回空列表或者抛出认证异常,根据你的业务需求来。
  • 如果你的LDAP认证不需要密码校验(比如用证书或者其他方式),可以调整CustomUserDetails的密码参数,不过一般LDAP登录会自动处理密码验证。

备注:内容来源于stack exchange,提问作者Mahdi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.20 11:34:36