In summary, user is being authenticated, but I do appear to actually have logged into the users account.
I’m currently working on implementing LDAP authentication on a project. It appears that the authentication portion of things are working in the sense that my application does accept the correct credentials. The issue I’m having is that I cant seem to access ‘principal’ in my jsp views. (I was able to access all of this before making the switch to LDAP). When running a trace my CustomUserDetails service is querying and pulling the correct account information. Any assistance is appreciated
This will display the proper username:
<sec:authorize access="isAuthenticated()"> <h2><sec:authentication property="name"/></h2> </sec:authorize>
This does not (it did work before LDAP)
<sec:authorize access="isAuthenticated()"> <h2><sec:authentication property="principal.firstName"/></h2> </sec:authorize>
Relevant Code SecurityConfig.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; } }
Advertisement
Answer
If I’m not wrong, You switched to Ldap Authorization, set url and DN patterns but still provide userDetailsService which search user in database. You need to set UserDetailsContextMapper by implementing the interface and creating your custom one. This will map data from ldap directory context to your custom UserDetails and return it through mapUserFromContext method.
Here is an example 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) { } }
My custom 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(); } }
Then set CustomUserDetailsContextMapper in auth configuration. This is how you will be able to get your user from authentication.getPrincipal(). I hope I correctly understand your problem and answered.