I need to switch my authentication/authorization code from Spring Security to Java ee. Actually I have this class:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
@PropertySource(value = { "classpath:application.properties" })
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("myAuthPopulator")
LdapAuthoritiesPopulator myAuthPopulator;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl("ldaps://vdap.fg.loal:636/");
//contextSource.setBase("DC=fg,DC=local");
contextSource.setReferral("follow");
contextSource.setUserDn("CN=A0XXX32,CN=Administration,CN=fdam,DC=fg,DC=local");
contextSource.setPassword("password!");
contextSource.afterPropertiesSet();
LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthenticationProviderConfigurer = auth.ldapAuthentication();
ldapAuthenticationProviderConfigurer
.contextSource(contextSource)
.userSearchBase("CN=ProxyUsers,CN=fdam,DC=fg,DC=local")
.userSearchFilter("CN={0}")
.ldapAuthoritiesPopulator(myAuthPopulator);
}
@Configuration
@Order(1)
public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.antMatcher("/client/**")
.authorizeRequests()
//Exclude send file from authentication because it doesn't work with spring authentication
.antMatchers(HttpMethod.POST, "/client/file").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
}
}
@Configuration
@Order(2)
public static class FormWebSecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
RoleServices roleServices;
@Override
public void configure(WebSecurity web) throws Exception {
web
//Spring Security ignores request to static resources such as CSS or JS files.
.ignoring()
.antMatchers("/static/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
List<Role> roles=roleServices.getRoles();
//Retrieve array of roles(only string field without id)
String[] rolesArray = new String[roles.size()];
int i=0;
for (Role role:roles){
rolesArray[i++] = role.getRole();
}
http
.authorizeRequests() //Authorize Request Configuration
.anyRequest().hasAnyRole(rolesArray)//.authenticated()
.and() //Login Form configuration for all others
.formLogin()
.loginPage("/login")
//important because otherwise it goes in a loop because login page require authentication and authentication require login page
.permitAll()
.and()
//redirect on page 403 if user doens't exist in DART database
.exceptionHandling().accessDeniedPage("/403")
.and()
.logout()
.logoutSuccessUrl("/login?logout")
.permitAll();
}
}
}
I'm using remote Ldap to authenticate user and database to load roles, but Spring has several problem retrieving user in CN=ProxUsers so I'd like to use simple java code instead configureGlobal
method.
I already have java code to check user in Ldap:
@Override
public void isAuthenticated(String username, String password) throws LdapException{
if (databaseMatlabClientServices.getByUsersEnabled(username)== null)
throw new LdapException("User doesn't exist into DART database. Please contact the administrator!");
String dn="";;
//First query to retriev DN
Hashtable<String, Object> ldapEnv = new Hashtable<String, Object>();
ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
ldapEnv.put(Context.PROVIDER_URL, env.getRequiredProperty(PROPERTY_NAME_LDAP_URL));
//Without authentication ldapEnv.put(Context.SECURITY_AUTHENTICATION, "none");
//With authentication to access to LDAP server
ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
ldapEnv.put(Context.SECURITY_PRINCIPAL, env.getRequiredProperty(PROPERTY_NAME_LDAP_NAME));
ldapEnv.put(Context.SECURITY_CREDENTIALS, env.getRequiredProperty(PROPERTY_NAME_LDAP_PASSWORD));
String[] returnAttribute = {"dn"};
DirContext ctx = null;
NamingEnumeration<SearchResult> results = null;
try {
ctx = new InitialDirContext(ldapEnv);
SearchControls controls = new SearchControls();
controls.setReturningAttributes(returnAttribute);
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// without authentication on local server String filter = "uid=" + username ;
String filter = "CN=" + username ;
results = ctx.search(env.getRequiredProperty(PROPERTY_NAME_LDAP_USERSEARCHBASE), filter, controls);
if (results.hasMore())
dn = results.nextElement().getNameInNamespace();
else
throw new LdapException("Wrong username. Please retry!");
} catch (NamingException e) {
throw new LdapException(e);
} finally {
try{
if (results != null)
results.close();
if (ctx != null)
ctx.close();
}catch(Exception e){
throw new LdapException(e);
}
}
//Second query to try to access with obtained Dn and given password
Hashtable<String, Object> authEnv = new Hashtable<String, Object>();
authEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
authEnv.put(Context.PROVIDER_URL, env.getRequiredProperty(PROPERTY_NAME_LDAP_URL));
authEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
authEnv.put(Context.SECURITY_PRINCIPAL, dn);
authEnv.put(Context.SECURITY_CREDENTIALS, password);
DirContext ctx2 = null;
try {
ctx2 = new InitialDirContext(authEnv);
} catch (AuthenticationException authEx) {
throw new LdapException("Authentication error. Password was wrong");
} catch(Exception e){
throw new LdapException(e);
}finally {
try{
if (ctx2 != null)
ctx2.close();
}catch(Exception e){
throw new LdapException(e);
}
}
}
In Spring I get roles with this class
@Service("myAuthPopulator")
public class MyAuthoritiesPopulator implements LdapAuthoritiesPopulator {
@Autowired
private UserServices userServices;
static final Logger LOG = LoggerFactory.getLogger(MyAuthoritiesPopulator.class);
@Transactional(readOnly=true)
@Override
public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
try{
com.domain.User user = userServices.getByUsersEnabled(username);
if (user==null){
LOG.error("Threw exception in MyAuthoritiesPopulator::getGrantedAuthorities : User doesn't exist into DART database" );
}
else{
//Use this if a user can have different roles
// for(Role role : user.getRole()) {
// authorities.add(new SimpleGrantedAuthority(role.getRole()));
// }
authorities.add(new SimpleGrantedAuthority(user.getRole().getRole()));
return authorities;
}
}catch(Exception e){
LOG.error("Threw exception in MyAuthoritiesPopulator::getGrantedAuthorities : " + ErrorExceptionBuilder.buildErrorResponse(e)); }
return authorities;
}
}
This is my login page:
<body class="hold-transition login-page">
<div class="login-box">
<div class="login-logo">
<b>DART</b> Sign in
</div>
<!-- /.login-logo -->
<div class="login-box-body">
<p class="login-box-msg">Sign in to start your session</p>
<form th:action="@{/login}" method="post">
<div th:if="${param.error}">
<script>
notifyMessage("Invalid username and password!", 'error');
</script>
</div>
<!-- <div th:if="${param.logout}">
<script>
notifyMessage("You have been logged out!", 'error');
</script>
</div> -->
<div class="form-group has-feedback">
<input id="username" name="username" type="text"
class="form-control" placeholder="Username"> <span
class="glyphicon glyphicon-user form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<input id="password" name="password" type="password"
class="form-control" placeholder="Password"> <span
class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<div class="row">
<div class="col-xs-8">
<div class="checkbox icheck">
<label> <input type="checkbox"> Remember Me
</label>
</div>
</div>
<!-- /.col -->
<div class="col-xs-4">
<button id="signinButton" type="submit"
class="btn btn-primary btn-block btn-flat">Sign In</button>
</div>
<!-- /.col -->
</div>
</form>
<div >
<img th:src="@{/static/assets/dist/img/fca login.jpg}" class="img-responsive" alt="FCA">
</div>
</div>
<!-- /.login-box-body -->
</div>
<!-- /.login-box -->
</body>
I've read a lot of documents but I don't understand how I can use java ee to my purpose, could you help me?
Update I have created a class
@Configuration
public class LdapConfiguration {
@Bean
public LdapContextSource contextSource () {
LdapContextSource contextSource= new LdapContextSource();
contextSource.setUrl("ldaps://vdap.fg.loal:636/");
contextSource.setBase("DC=fg,DC=local");
contextSource.setReferral("follow");
contextSource.setUserDn("CN=A0XXX32,CN=Administration,CN=fdam,DC=fg,DC=local");
contextSource.setPassword("password!");
contextSource.afterPropertiesSet();
return contextSource;
}
@Bean(name = "ldapTemplate")
public LdapTemplate ldapTemplate() {
return new LdapTemplate(contextSource());
}
}
and a test web service:
@RequestMapping(value = { "/test/{commonName}/{password}"}, method = RequestMethod.GET)
public @ResponseBody Response appBufferSearch(ModelMap model, @PathVariable String commonName, @PathVariable String password){
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("CN", commonName));
boolean isAuthenticated = ldapTemplate.authenticate("CN=ProxyUsers,CN=fdam", filter.encode(), password);
return new Response(true, true, isAuthenticated, null);
}
and it find my user into LDAP server, so the problem is in my configureGlobal
, do you know why?