I am using Spring security 4.0. My login module is configured in Application server so I have to do authentication using JAAS but my user details are stored in database, so once authenticated user object will be created by querying database. Could you please let me know how to achieve this i.e. LDAP authentication and load user details from database. Also how cache the user object using eh-cache, so that the user object can be accessed in the service / dao layer.
This can be achieved using CustomAuthentication Provider. Below are the codes.
import java.util.Arrays;
import java.util.List;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.authentication.jaas.JaasGrantedAuthority;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.sun.security.auth.UserPrincipal;
public class CustomAutenticationProvider extends DaoAuthenticationProvider implements AuthenticationProvider {
private AuthenticationProvider delegate;
public CustomAutenticationProvider(AuthenticationProvider delegate) {
this.delegate = delegate;
}
@Override
public Authentication authenticate(Authentication authentication) {
Authentication a = delegate.authenticate(authentication);
if(a.isAuthenticated()){
a = super.authenticate(a);
}else{
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
return a;
}
private List<GrantedAuthority> loadRolesFromDatabaseHere(String name) {
GrantedAuthority grantedAuthority =new JaasGrantedAuthority(name, new UserPrincipal(name));
return Arrays.asList(grantedAuthority);
}
@Override
public boolean supports(Class<?> authentication) {
return delegate.supports(authentication);
}
/* (non-Javadoc)
* @see org.springframework.security.authentication.dao.DaoAuthenticationProvider#additionalAuthenticationChecks(org.springframework.security.core.userdetails.UserDetails, org.springframework.security.authentication.UsernamePasswordAuthenticationToken)
*/
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
if(!authentication.isAuthenticated())
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
}
UserDetails required for DAOAuthentication
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import com.testjaas.model.User;
import com.testjaas.model.UserRepositoryUserDetails;
@Component
public class AuthUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("loadUserByUsername called !!");
com.testjaas.model.User user = new User();
user.setName(username);
user.setUserRole("ROLE_ADMINISTRATOR");
if(null == user) {
throw new UsernameNotFoundException("User " + username + " not found.");
}
return new UserRepositoryUserDetails(user);
}
}
RoleGrantor - This will be a dummy class required for Spring JAAS authentication
import java.security.Principal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.springframework.security.authentication.jaas.AuthorityGranter;
public class RoleGranterFromMap implements AuthorityGranter {
private static Map<String, String> USER_ROLES = new HashMap<String, String>();
static {
USER_ROLES.put("test", "ROLE_ADMINISTRATOR");
//USER_ROLES.put("test", "TRUE");
}
public Set<String> grant(Principal principal) {
return Collections.singleton("DUMMY");
}
}
SampleLogin - This should be replaced with your login module
import java.io.Serializable;
import java.security.Principal;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
public class SampleLoginModule implements LoginModule {
private Subject subject;
private String password;
private String username;
private static Map<String, String> USER_PASSWORDS = new HashMap<String, String>();
static {
USER_PASSWORDS.put("test", "test");
}
public boolean abort() throws LoginException {
return true;
}
public boolean commit() throws LoginException {
return true;
}
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
this.subject = subject;
try {
NameCallback nameCallback = new NameCallback("prompt");
PasswordCallback passwordCallback = new PasswordCallback("prompt",false);
callbackHandler.handle(new Callback[] { nameCallback,passwordCallback });
this.password = new String(passwordCallback.getPassword());
this.username = nameCallback.getName();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public boolean login() throws LoginException {
if (USER_PASSWORDS.get(username) == null
|| !USER_PASSWORDS.get(username).equals(password)) {
throw new LoginException("username is not equal to password");
}
subject.getPrincipals().add(new CustomPrincipal(username));
return true;
}
public boolean logout() throws LoginException {
return true;
}
private static class CustomPrincipal implements Principal, Serializable {
private final String username;
public CustomPrincipal(String username) {
this.username = username;
}
public String getName() {
return username;
}
}
}
Spring XML configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<security:http auto-config="true">
<security:intercept-url pattern="/*" access="isAuthenticated()"/>
</security:http>
<!-- <security:authentication-manager>
<security:authentication-provider ref="jaasAuthProvider" />
</security:authentication-manager> -->
<bean id="userDetailsService" class="com.testjaas.service.AuthUserDetailsService"></bean>
<bean id="testService" class="com.testjaas.service.TestService"/>
<bean id="applicationContextProvider" class="com.testjaas.util.ApplicationContextProvider"></bean>
<security:authentication-manager>
<security:authentication-provider ref="customauthProvider"/>
</security:authentication-manager>
<bean id="customauthProvider" class="com.testjaas.security.CustomAutenticationProvider">
<constructor-arg name="delegate" ref="jaasAuthProvider" />
<property name="userDetailsService" ref="userDetailsService" />
</bean>
<bean id="jaasAuthProvider" class="org.springframework.security.authentication.jaas.JaasAuthenticationProvider">
<property name="loginConfig" value="classpath:pss_jaas.config" />
<property name="authorityGranters">
<list>
<bean class="com.testjaas.security.RoleGranterFromMap" />
</list>
</property>
<property name="loginContextName" value="JASSAuth" />
<property name="callbackHandlers">
<list>
<bean class="org.springframework.security.authentication.jaas.JaasNameCallbackHandler" />
<bean class="org.springframework.security.authentication.jaas.JaasPasswordCallbackHandler" />
</list>
</property>
</bean>
</beans>
Sample jaas config
JASSAuth {
com.testjaas.security.SampleLoginModule required;
};