【问题标题】:Spring Security LDAP Authentication and gather user details from local databaseSpring Security LDAP 身份验证并从本地数据库收集用户详细信息
【发布时间】:2019-03-29 02:51:31
【问题描述】:

总之,用户正在通过身份验证,但我似乎确实已经登录了用户帐户。

我目前正致力于在一个项目中实施 LDAP 身份验证。从我的应用程序确实接受正确的凭据的意义上说,事情的身份验证部分似乎正在工作。我遇到的问题是我似乎无法在我的 jsp 视图中访问“主体”。 (在切换到 LDAP 之前,我能够访问所有这些)。运行跟踪时,我的 CustomUserDetails 服务正在查询并提取正确的帐户信息。任何帮助表示赞赏

这将显示正确的用户名:

<sec:authorize access="isAuthenticated()">
   <h2><sec:authentication property="name"/></h2>
</sec:authorize>

这不行(它在 LDAP 之前确实有效)

<sec:authorize access="isAuthenticated()">
   <h2><sec:authentication property="principal.firstName"/></h2>
</sec:authorize>

相关代码 安全配置.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.ldap.authentication.UserDetailsServiceLdapAuthoritiesPopulator;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Bean
    public CustomSaltSource customSaltSource(){ return new CustomSaltSource();}

    @Bean
    public AuthenticationSuccessHandler myAuthenticationSuccessHandler(){
        return new AuthenticationSuccessHandler();
    }



    @Autowired
    void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.ldapAuthentication().contextSource()
                .url("ldap://bar.foo.com")
                .port(####)
                .and()
                .userDnPatterns("cn={0},cn=users,dc=ms,dc=ds,dc=foo,dc=com")
                .ldapAuthoritiesPopulator(new UserDetailsServiceLdapAuthoritiesPopulator(userDetailsService));
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
                .antMatchers("/skins/**", "/css/**", "/**/laggingComponents", "/assets/**").permitAll().and()
                .formLogin().loginPage("/login").permitAll().defaultSuccessUrl("/", true).successHandler(myAuthenticationSuccessHandler())
                .and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).deleteCookies("JSESSIONID").permitAll()
                .and().authorizeRequests().antMatchers("/api/**").anonymous()
                .and().authorizeRequests().anyRequest().authenticated().and().rememberMe().key("KEY").userDetailsService(userDetailsService);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
        handler.setPermissionEvaluator(new PermissionEvaluator());
        web.expressionHandler(handler);
        web.ignoring().antMatchers( "/skins/**", "/css/**", "/api/**", "/assets/**", "/health"); //"/**/test/**"
    }
}

CustomUserDetaulsService.java

import org.hibernate.Session;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Set;

@Service
public class CustomUserDetailsService implements UserDetailsService{

    @Override
    public CustomUserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
        Session session = DBFactory.factory.openSession();
        User user = (User) session.createQuery("from User where userName =:userName")
                .setParameter("userName", username).uniqueResult();
        if(user == null){
            throw new UsernameNotFoundException("User Not Found");
        }
        //Needed to initialize permissions
        Set<Role> roles = user.getRoles();
        int i = roles.size();
        for(Role role: roles){
            int j = role.getPermissions().size();
        }
        CustomUserDetails userDetails = new CustomUserDetails(user);

        session.close();
        return userDetails;
    }


}

【问题讨论】:

    标签: java spring spring-security ldap spring-security-ldap


    【解决方案1】:

    如果我没记错的话, 您切换到 Ldap 授权,设置 url 和 DN 模式,但仍然提供在数据库中搜索用户的 userDetailsS​​ervice。 您需要通过实现接口并创建自定义接口来设置 UserDetailsContextMapper。这会将数据从 ldap 目录上下文映射到您的自定义 UserDetails 并通过 mapUserFromContext 方法返回。

    这是一个示例CustomUserDetailsContextMapper

    public class CustomUserDetailsContextMapper implements UserDetailsContextMapper {
    
    
        private LdapUser ldapUser = null;
        private String commonName;
    
        @Override
        public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
            Attributes attributes = ctx.getAttributes();
            UserDetails ldapUserDetails = (UserDetails) super.mapUserFromContext(ctx,username,authorities);
            try {
                commonName = attributes.get("cn").get().toString();
            } catch (NamingException e) {
                e.printStackTrace();
            }
            ldapUser = new LdapUser(ldapUserDetails);
            ldapUser.setCommonName(commonName);
            return ldapUser;
        }
    
        @Override
        public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
    
        }
    }
    

    我的自定义LdapUser

    public class LdapUser implements UserDetails
    {
        private String commonName;
        private UserDetails ldapUserDetails;
    
        public LdapUser(LdapUserDetails ldapUserDetails) {
            this.ldapUserDetails = ldapUserDetails;
        }
    
        @Override
        public String getDn() {
            return ldapUserDetails.getDn();
        }
    
        @Override
        public void eraseCredentials() {
    
        }
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return ldapUserDetails.getAuthorities();
        }
    
        @Override
        public String getPassword() {
            return ldapUserDetails.getPassword();
        }
    
        @Override
        public String getUsername() {
            return ldapUserDetails.getUsername();
        }
    
        @Override
        public boolean isAccountNonExpired() {
            return ldapUserDetails.isAccountNonExpired();
        }
    
        @Override
        public boolean isAccountNonLocked() {
            return ldapUserDetails.isAccountNonLocked();
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return ldapUserDetails.isCredentialsNonExpired();
        }
    
        @Override
        public boolean isEnabled() {
            return ldapUserDetails.isEnabled();
        }
    }
    

    然后在 auth 配置中设置 CustomUserDetailsContextMapper。这就是您从 authentication.getPrincipal() 获取用户的方式。 我希望我正确理解您的问题并回答。

    【讨论】:

    • 您可能是指extends LdapUserDetailsMapper 而不是implements UserDetailsContextMapper 来调用super 并丰富LdapUser
    【解决方案2】:

    我必须测试 LDAP 身份验证 + 开发人员、管理员、用户的不同角色

    非常感谢@Yernar Arystanov

    代码不是那么干净,但可以工作......

    public class LdapUser implements UserDetails
    {
        private String commonName;
        private List<String>  groups;
        private UserDetails ldapUserDetails;
    
        public LdapUser(LdapUserDetails ldapUserDetails) {
            this.ldapUserDetails = ldapUserDetails;
        }
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return ldapUserDetails.getAuthorities();
        }
    
        @Override
        public String getPassword() {
            return ldapUserDetails.getPassword();
        }
    
        @Override
        public String getUsername() {
            return ldapUserDetails.getUsername();
        }
    
        @Override
        public boolean isAccountNonExpired() {
            return ldapUserDetails.isAccountNonExpired();
        }
    
        @Override
        public boolean isAccountNonLocked() {
            return ldapUserDetails.isAccountNonLocked();
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return ldapUserDetails.isCredentialsNonExpired();
        }
    
        @Override
        public boolean isEnabled() {
            return ldapUserDetails.isEnabled();
        }
    
        public String getCommonName() {
            return commonName;
        }
    
        public void setCommonName(String commonName) {
            this.commonName = commonName;
        }
    
        public List<String> getGroups() {
            return groups;
        }
    
        public void setGroups(List<String> groups) {
            this.groups = groups;
        }
    }
    

    @Ivan Baranuk 的建议是正确的(扩展了 LdapUserDetailsMapper):

    public class CustomUserDetailsContextMapper extends LdapUserDetailsMapper {
    
        private LdapUser ldapUser = null;
        private String commonName;
        private List<String> groups = new LinkedList<String>();
    
        @Override
        public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
            Attributes attributes = ctx.getAttributes();
            UserDetails ldapUserDetails;
            ldapUserDetails = (UserDetails) super.mapUserFromContext(ctx,username,authorities);
            try {
                commonName = attributes.get("cn").get().toString();
    
                Arrays.stream(ctx.getObjectAttributes("memberOf"))
                        .iterator()
                        .forEachRemaining( m -> {
                            groups.add(m.toString());
                        });
    
            } catch (NamingException | javax.naming.NamingException e) {
                e.printStackTrace();
            }
            ldapUser = new LdapUser((LdapUserDetails) ldapUserDetails);
            ldapUser.setCommonName(commonName);
            ldapUser.setGroups(groups);
            return ldapUser;
        }
    
        @Override
        public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
    
        }
    }
    

    最后在 SecurityConfig 中:

    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ...
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
    ...
    auth.ldapAuthentication()
                    .userSearchBase("enter your search base")
                    .userSearchFilter("(&(objectClass=user)(sAMAccountName={0}))")
                    .userDetailsContextMapper(ldapUserDetailsMapper())
                    .ldapAuthoritiesPopulator(ldapAuthoritiesPopulator())
                    .contextSource()
                    .url(yourProperties.getLdapUrl())
                    .managerDn(yourProperties.getManagerDn())
                    .managerPassword(yourProperties.getManagerPassword());
    }
    
    private LdapAuthoritiesPopulator ldapAuthoritiesPopulator() {
            return new LdapAuthoritiesPopulator() {
                @Override
                public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData,
                                                                                    String username) {
    
                    LinkedList<SimpleGrantedAuthority> res = new LinkedList();
                    Arrays.stream(userData.getObjectAttributes("memberOf"))
                            .iterator()
                            .forEachRemaining( m -> {
                                if(m.toString().equals("your_dev_group"))
                                    res.add(new SimpleGrantedAuthority("DEV_USER"));
                                if(m.toString().equals("your_admin_group"))
                                    res.add(new SimpleGrantedAuthority("ADMIN_USER"));
                            });
                    if(res.isEmpty())
                        return Arrays.asList(new SimpleGrantedAuthority("USER"));
                    else
                        return res;
                }
            };
        }
    @Bean
    @Override
    protected UserDetailsService userDetailsService() {
        return super.userDetailsService();
    }
    
    @Bean
        protected LdapUserDetailsMapper ldapUserDetailsMapper() {return new CustomUserDetailsContextMapper();}
    
    }
    

    最后——一些简单的get方法(仅限授权用户):

    @RequestMapping("/logger")
    public String testLogger(Authentication authentication) {
        if (authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_DEV_USER"))) {
            LdapUser userDetails = (LdapUser) authentication.getPrincipal();
            log.info("WELCOME :" + userDetails.getCommonName());
            userDetails.getGroups().iterator().forEachRemaining((g) - > log.info("group: " + g.toString()));
        }
        return ""
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-24
      • 2011-05-23
      • 2013-01-11
      • 2015-07-22
      • 2012-02-18
      • 2016-09-01
      • 2011-05-30
      相关资源
      最近更新 更多