spring security 3.1 destroys existing session with active user logged in - spring-security

I have an application with spring security 3.1 and Ldap integration. Below are the key points in the requirement and implementation so far:
The application will have multiple roles for single user but these
roles does not exist in ldap, so the application authenticates only
the username(or userid) from ldap.
The roles are stored separately in the database
Upon successful authentication from ldap, the userdetails and the roles are set into principal object custom userdetails object by implementing UserDetailsService
Problem:
User A logs in the application
User B logs in the application, User A session is getting destroyed(which should not have happened because User A has not logged out yet!)
User B logs out User A gets page not found, since its session is already destroyed when User B logged in.
The applicationContext-security.xml looks like this:
<beans:bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/login.jsp" />
<beans:property name="forceHttps" value="true" />
</beans:bean>
<beans:bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
<beans:property name="sessionRegistry" ref="sessionRegistry" />
<beans:property name="expiredUrl" value="/login.jsp?login_error=2" />
</beans:bean>
<beans:bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<beans:constructor-arg value="/login.jsp" />
<beans:constructor-arg>
<beans:list>
<beans:ref bean="logoutEventBroadcaster" />
<beans:bean id="securityContextLogoutHandler" class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
</beans:list>
</beans:constructor-arg>
<beans:property name="filterProcessesUrl" value="/j_spring_security_logout" />
</beans:bean>
<beans:bean id="myAuthFilter" class="com.*.security.CustomAuthenticationProcessingFilter">
<beans:property name="sessionAuthenticationStrategy" ref="sas" />
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="authenticationFailureHandler" ref="failureHandler" />
<beans:property name="authenticationSuccessHandler" ref="successHandler" />
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="adAuthenticationProvider" />
</authentication-manager>
<beans:bean id="adAuthenticationProvider" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
<beans:constructor-arg value="*.*.net" />
<beans:constructor-arg value="ldap://*.*.net:389/" />
<beans:property name="userDetailsContextMapper">
<beans:bean class="com.ezadvice.service.CustomUserDetailsContextMapper" />
</beans:property>
<beans:property name="useAuthenticationRequestCredentials" value="true" />
</beans:bean>
<beans:bean id="failureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/login.jsp?login_error=1" />
</beans:bean>
<beans:bean id="successHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/home.do" />
<beans:property name="alwaysUseDefaultTargetUrl" value="true"/>
</beans:bean>
<beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<beans:property name="maximumSessions" value="1" />
<beans:property name="exceptionIfMaximumExceeded" value="true" />
<beans:property name="migrateSessionAttributes" value="false" />
</beans:bean>
<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
The CustomAuthenticationProcessingFilter class looks like this:
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
String roleId = request.getParameter("roleId");
String username = request.getParameter("j_username");
TbEzaLoginHistory tbEzaLoginHistory = null;
// check if the user has authority for the role
TbEzaUser tbEzaUser = userManagementService.checkUserAndRole(roleId, username);
if (null != tbEzaUser) {
tbEzaLoginHistory = userManagementService.saveLoginHistory(tbEzaUser, roleId);
request.setAttribute("loginHistoryId", tbEzaLoginHistory.getLoginKey());
request.setAttribute("roleId", roleId);
request.setAttribute("j_username", username);
if (UserTracker.increment(username, roleId)) {
try{
Authentication attemptAuthentication = super.attemptAuthentication(request, response);
if (null != attemptAuthentication) {
CustomUser principal = (CustomUser) attemptAuthentication.getPrincipal();
if (null == principal && null != tbEzaLoginHistory)
userManagementService.deleteFromLoginHistory(tbEzaLoginHistory.getLoginKey());
return attemptAuthentication;
}
}
catch (CommunicationException e) {
userManagementService.deleteFromLoginHistory(tbEzaLoginHistory.getLoginKey());
UserTracker.decrement(username, roleId);
RequestDispatcher dispatcher = request.getRequestDispatcher("/login.jsp?login_error=5");
try {
dispatcher.forward(request, response);
} catch (ServletException se) {
se.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
LOGGER.debug("Connection Timeout error for UserName:"+username +"\n" + e);
e.printStackTrace();
}
}else {
if (null != tbEzaLoginHistory)
userManagementService.deleteFromLoginHistory(tbEzaLoginHistory.getLoginKey());
RequestDispatcher dispatcher = request.getRequestDispatcher("/login.jsp?login_error=4");
try {
dispatcher.forward(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
} else {
RequestDispatcher dispatcher = request.getRequestDispatcher("/login.jsp?login_error=3");
try {
dispatcher.forward(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(EXITLOGGER + " attemptAuthentication");
}
return null;
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException, ServletException {
super.successfulAuthentication(request, response, authResult);
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authResult;
WebAuthenticationDetails details = (WebAuthenticationDetails) token.getDetails();
String address = details.getRemoteAddress();
CustomUser user = (CustomUser) authResult.getPrincipal();
String userName = user.getUsername();
System.out.println("Successful login from remote address: " + address + " by username: "+ userName);
}
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException, ServletException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(ENTRYLOGGER + " unsuccessfulAuthentication");
}
try {
Long loginHistoryId = (Long) request.getAttribute("loginHistoryId");
String username = (String) request.getAttribute("j_username");
String roleId = (String) request.getAttribute("roleId");
userManagementService.deleteFromLoginHistory(loginHistoryId);
super.unsuccessfulAuthentication(request, response, failed);
UserTracker.decrement(username, roleId);
} catch (Exception e) {
e.printStackTrace();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(EXITLOGGER + " unsuccessfulAuthentication");
}
}
The UserTracker class looks like this:
public class UserTracker {
private static Set<String> loggedInUsersDetails = new HashSet<String>();
#SuppressWarnings("unchecked")
synchronized public static boolean increment(String userName, String roleId) {
if(loggedInUsersDetails.add(userName.toLowerCase()+'~'+roleId)){
return true;
}else
return false;
}
synchronized public static void decrement(String userName, String roleId) {
loggedInUsersDetails.remove(userName.toLowerCase()+'~'+roleId);
}
Can anyone help me to find out, why the User A's session is getting destroyed ?

In the docs (SavedRequests and the RequestCache Interface), they talk about ExceptionTranslationFilter job to cache the current request before invoking the AuthenticationEntryPoint. This allows the request to be restored - by the SavedRequestAwareAuthenticationSuccessHandler (which is the default).
But I've noted another evel filter: RequestCacheAwareFilter.
AFTER the redirection to the origional request, the RequestCacheAwareFilter is invoked by the chain, and he calls 'getMatchingRequest()', that gets the request, and then removes it from the cache! then, when the second authentication succeeds (from the 2nd user), there is no URL in the cache, so Spring does not know where to redirect me to. so I believe this is the root-cause of the problem.
I've found out that this issue was born due to this jira:
SEC-1241: SavedRequest not destroyed after successful authentication

You can move your authentication code into custom AuthenticationManager. AuthenticationManager will have two dependencies on LdapAuthenticationProvider and DaoAuthenticationProvider. During authentication processing it will be responsible for:
calling LDAP provider
calling DB provider
combining two authentication objects into one (credentials from LDAP and roles from DB).

Finally found the solution to the above problem. There were multiple causes:
While testing the above problem I was making a mistake, that I was trying to achieve concurrency control when users opens the application in a tabbed browser.
Spring internally stores the ip address of the machine to prevent multiple users to login from same machine. Thus had to make code changes so that user's having multiple roles are not allowed to login from the same machine.

Remove
<beans:property name="maximumSessions" value="1" />
at
<beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<beans:property name="maximumSessions" value="1" />
<beans:property name="exceptionIfMaximumExceeded" value="true" />
<beans:property name="migrateSessionAttributes" value="false" />
</beans:bean>

Related

Concurrency control is not working

We are migrating from Spring Security 2.0 to 3.1.
In Spring Security 2.0 the concurrency control was configured adding the following line in the configuration file:
<concurrent-session-control max-sessions="1" />
Now, with 3.1, we added the following:
<session-management invalid-session-url="/index.jsf">
<concurrency-control max-sessions="1" />
</session-management>
We make the login from a backing bean. The code is the following:
public String login(){
Authentication authenticationResponseToken = authenticate();
if (authenticationResponseToken != null && authenticationResponseToken.isAuthenticated()) {
return NavigationConstants.PORTAL_REDIRECT_USER;
}
else{
return NavigationConstants.PORTAL_LOGIN + sessionManagedBean.getUrlQuery();
}
}
private Authentication authenticate(){
debug("authenticate");
AuthenticationManager authenticationManager = (AuthenticationManager) UtilidadesFaces.getSpringBean("authManager");
//simple token holder
Authentication authenticationRequestToken = createAuthenticationToken(sessionManagedBean);
Authentication authenticationResponseToken = null;
//authentication action
try {
authenticationResponseToken = authenticationManager.authenticate(authenticationRequestToken);
SecurityContextHolder.getContext().setAuthentication(authenticationResponseToken);
List<GrantedAuthority> authorities = (List<GrantedAuthority>) authenticationResponseToken.getAuthorities();
if(authorities.size() > 0){
this.sessionManagedBean.setRole(authorities.get(0).getAuthority());
}
CustomUser customUser = (CustomUser) authenticationResponseToken.getPrincipal();
this.sessionManagedBean.setIdCl(customUser.getIdCl());
} catch (BadCredentialsException badCredentialsException) {
UtilidadesFaces.addMessage(null, FacesMessage.SEVERITY_ERROR, "login.error.badCredentialsException", this.sessionManagedBean.getActualLanguage());
} catch (AuthenticationServiceException badCredentialsException) {
UtilidadesFaces.addMessage(null, FacesMessage.SEVERITY_ERROR, "login.error.badCredentialsException", this.sessionManagedBean.getActualLanguage());
} catch (LockedException lockedException) {
UtilidadesFaces.addMessage(null, FacesMessage.SEVERITY_ERROR, "login.error.lockedException", this.sessionManagedBean.getActualLanguage());
} catch (DisabledException disabledException) {
UtilidadesFaces.addMessage(null, FacesMessage.SEVERITY_ERROR, "login.error.disabledException", this.sessionManagedBean.getActualLanguage());
}
return authenticationResponseToken;
}
private Authentication createAuthenticationToken(SessionManagedBean sessionManagedBean) {
String username = sessionManagedBean.getUsername() + sessionManagedBean.getIdGuest();
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(username, sessionManagedBean.getPassword());
return usernamePasswordAuthenticationToken;
}
We already have implemented the equals() and hashCode() methods in the class which implements UserDetails.
No concurrency control is working now, one user can login several times and work with all the sessions simoultaneously.
Any help would be appreciated.
I am using spring 3.2.1 , i just did the following
// spring will inject ConcurrentSessionControlAuthenticationStrategy by default
#Autowired
SessionAuthenticationStrategy sessionAuthenticationStrategy;
and inside your authentication method i just called
sessionAuthenticationStrategy.onAuthentication(authenticationResponseToken, httpReq, httpResp);
And in security.xml file
<session-management invalid-session-url="/login.jsp?time=1">
<concurrency-control error-if-maximum-exceeded="false" max-sessions="1" expired-url="/logout"/>
</session-management>
As all these are default values i think we dont need to set it.
This did the work for me.
Try this..according to me its work
<session-management session-fixation-protection="newSession">
<concurrency-control error-if-maximum-exceeded="true" max-sessions="1" expired-url="/loginexpired" />
</session-management>
Finally i solved the issue calling SessionAuthenticationStrategy#onAuthentication and adding some spring security filters.
My code:
<http auto-config="false" use-expressions="true">
<session-management session-authentication-strategy-ref="sas" invalid-session-url="/index.jsf" />
......
<custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
</http>
<beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<beans:property name="maximumSessions" value="1" />
</beans:bean>
<beans:bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
<beans:property name="sessionRegistry" ref="sessionRegistry" />
<beans:property name="expiredUrl" value="/index.jsf" />
</beans:bean>
<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
....
and adding sessionAuthenticationStrategy.onAuthentication()...
private Authentication authenticate(){
debug("authenticate");
AuthenticationManager authenticationManager = (AuthenticationManager) UtilidadesFaces.getSpringBean("authManager");
//simple token holder
Authentication authenticationRequestToken = createAuthenticationToken(sessionManagedBean);
Authentication authenticationResponseToken = null;
//authentication action
try {
authenticationResponseToken = authenticationManager.authenticate(authenticationRequestToken);
SecurityContextHolder.getContext().setAuthentication(authenticationResponseToken);
List<GrantedAuthority> authorities = (List<GrantedAuthority>) authenticationResponseToken.getAuthorities();
if(authorities.size() > 0){
this.sessionManagedBean.setRole(authorities.get(0).getAuthority());
}
CustomUser customUser = (CustomUser) authenticationResponseToken.getPrincipal();
this.sessionManagedBean.setIdCl(customUser.getIdCl());
HttpServletRequest httpReq = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
HttpServletResponse httpResp = (HttpServletResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse();
SessionAuthenticationStrategy sessionAuthenticationStrategy = (SessionAuthenticationStrategy) UtilidadesFaces.getSpringBean("sas");;
sessionAuthenticationStrategy.onAuthentication(authenticationResponseToken, httpReq, httpResp);
} catch (BadCredentialsException badCredentialsException) {
UtilidadesFaces.addMessage(null, FacesMessage.SEVERITY_ERROR, "login.error.badCredentialsException", this.sessionManagedBean.getActualLanguage());
} catch (AuthenticationServiceException badCredentialsException) {
UtilidadesFaces.addMessage(null, FacesMessage.SEVERITY_ERROR, "login.error.badCredentialsException", this.sessionManagedBean.getActualLanguage());
} catch (LockedException lockedException) {
UtilidadesFaces.addMessage(null, FacesMessage.SEVERITY_ERROR, "login.error.lockedException", this.sessionManagedBean.getActualLanguage());
} catch (DisabledException disabledException) {
UtilidadesFaces.addMessage(null, FacesMessage.SEVERITY_ERROR, "login.error.disabledException", this.sessionManagedBean.getActualLanguage());
}
return authenticationResponseToken;
}

Why the exception UsernameNotFoundException does not stop application?

I am using Spring Security to do authentication. Here is my code :
Controller
#RequestMapping(value="/custom_login", method = RequestMethod.GET)
public String customLogin(){
return VIEW_LOGIN_SUCCESS_NAME;
}
Authentication
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
com.startup.app.models.entities.User domainUser = userDao.getUserByLogin(username);
if (domainUser == null) {
LOGGER.error("No user found with username: " + username);
throw new UsernameNotFoundException("No user found with username: " + username);
}
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
return new User(
domainUser.getEmail(),
domainUser.getPassword(),
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
getAuthorities(domainUser.getRole())
);
}
Here is the spring configuration file:
<?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/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd">
<!-- configuration des urls -->
<security:http auto-config="true">
<!-- Define the default login page -->
<security:form-login login-page="/custom_login" username-parameter="email" password-parameter="password" default-target-url="/loginSuccess"/>
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider
ref="authenticationProvider" />
</security:authentication-manager>
<bean id="authenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService" />
</bean>
<bean id="webexpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler" />
<bean id="userDetailsService" class="com.startup.app.services.login.CustomUserDetailsServiceImpl" />
</beans>
In the above configuration, i know i did not restrict access to page. I just first want to test the login process is working well.
I don't understand why the application does not stop when the UsernameNotFoundException is thrown. I don't have message in eclipse about except the logger i've used. Then the application continue to execute and display the login success page.
Someone could explain me please ?
please change your login config:
<security:form-login
authentication-success-handler-ref="authenticationSuccessHandler"
login-page="/custom_login"
username-parameter="email"
password-parameter="password"
default-target-url="/loginSuccess"
always-use-default-target="true"
authentication-failure-url="/custom_login" />
For even more control over the destination, you can use the authentication-success-handler-ref attribute as an alternative to default-target-url. The referenced bean should be an instance of AuthenticationSuccessHandler.

Spring Framework role based method security not working in Apache CXF webservice

I have a simple Apache CXF webservice with the following beans.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns="http://www.springframework.org/schema/security"
xmlns:ssec="http://cxf.apache.org/spring-security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
http://cxf.apache.org/spring-security
http://cxf-spring-security.googlecode.com/svn/trunk/cxf-spring-security/src/main/resources/schemas/spring-security.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">
<beans:import resource="classpath:META-INF/cxf/cxf.xml" />
<http auto-config='true' >
<http-basic/>
<anonymous enabled="false"/>
</http>
<beans:bean id="methodSecurityInterceptor"
class="org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="accessDecisionManager" ref="accessDecisionManager"/>
<beans:property name="securityMetadataSource">
<beans:value>
org.mycompany.com.CxfSpringSecuredService.HelloWorldImpl.sayHi=ROLE_OPERATOR
org.mycompany.com.CxfSpringSecuredService.HelloWorldImpl.sayHiAdmin*=ROLE_ADMIN,ROLE_SUPERVISOR
org.mycompany.com.CxfSpringSecuredService.HelloWorldImpl.deleteAccounts*=ROLE_SUPERVISOR
</beans:value>
</beans:property>
</beans:bean>
<beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<beans:property name="decisionVoters">
<beans:list>
<beans:bean class="org.springframework.security.access.vote.RoleVoter" />
</beans:list>
</beans:property>
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="operator" password="operator" authorities="ROLE_OPERATOR" />
<user name="admin" password="admin" authorities="ROLE_ADMIN" />
<user name="sup" password="sup" authorities="ROLE_SUPERVISOR" />
</user-service>
</authentication-provider>
</authentication-manager>
<jaxws:endpoint
id="helloWorld"
implementor="org.mycompany.com.CxfSpringSecuredService.HelloWorldImpl"
address="/HelloWorld" />
</beans:beans>
My webservice implementation is the following three simple methods:
#WebService(endpointInterface = "org.mycompany.com.CxfSpringSecuredService.HelloWorld")
public class HelloWorldImpl implements HelloWorld {
public String sayHi(String text) {
SecurityContext context = SecurityContextHolder.getContext();
if (context != null){
Authentication authentication = context.getAuthentication();
if (authentication != null){
Collection<GrantedAuthority> roles = authentication.getAuthorities();
if (roles != null){
GrantedAuthority[] authorities = new GrantedAuthority[roles.size()];
roles.toArray(authorities);
for (int i = 0; i < authorities.length; i++)
text = text + " " + authorities[i];
}
}
}
return "Hello " + text;
}
public String sayHiAdmin(){
return "Hello admin";
}
public String deleteAccounts(String name){
return "Accounts deleted by " + name;
}
}
I have a C# client that calls the web service and passes authentication information within the SOAP header. I know that my client is passing authentication information as I get the following message in an exception:
The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Basic realm="Spring Security Application"'.
if I issue invalid credentials. I issue proper credentials I get the correct response for each web service call. So far, so good.
If I pass credential information for operator, and call method deleteAccounts, I expected the same authorized error as above, but webservice method is invoked correctly.
I have looked through the docs here Spring Framework but cannot determine what may be missing.
Any ideas?
TIA.
Edit: Corrected user config.

How to use salt from database field in Spring Security

I am using spring-security-3.1.0 with spring-framework-3.0.6.
For login security check i'm using salt.But having problem in using salt in salt
source.
if i use beans:property name="userPropertyToUse" value="username" then
everything is fine
but having problem in <beans:property name="userPropertyToUse" value="lalt">
even tough i have configured all the necessary configuration for "salt".
It sowing this message
Unable to find salt method on user Object. Does the class 'org.springframework.security.core.userdetails.User'
have a method or getter named 'salt' ?
My spring-security.xml looks like this
<beans:bean id="saltSource" class="org.springframework.security.authentication.dao.ReflectionSaltSource">
<beans:property name="userPropertyToUse" value="salt" />
</beans:bean>
<beans:bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder"/>
<beans:bean id="loggerListener" class="org.springframework.security.authentication.event.LoggerListener" />
<authentication-manager>
<authentication-provider user-service-ref="jdbcUserService">
<password-encoder ref="passwordEncoder">
<salt-source ref="saltSource"/>
</password-encoder>
</authentication-provider>
</authentication-manager>
<beans:bean id="jdbcUserService" class="controllers.CustomJdbcDaoImpl">
<beans:property name="dataSource" ref="dataSource"/>
<beans:property name="usersByUsernameQuery">
<beans:value>SELECT U.userid AS username,
U.userpassword as password,
'true' AS enabled,
U.userpasswordsalt AS salt
FROM users U WHERE U.userid=?
</beans:value>
</beans:property>
<beans:property name="authoritiesByUsernameQuery">
<beans:value>SELECT U.userid AS username,
U.userrole as authority
FROM users U
WHERE U.userid=?
</beans:value>
</beans:property>
</beans:bean>
My jdbcUserService.java for salt is
public class CustomJdbcDaoImpl extends JdbcDaoImpl {
#Override
protected List<UserDetails> loadUsersByUsername(String username) {
return getJdbcTemplate().query(getUsersByUsernameQuery(),new String[] {username},
new RowMapper<UserDetails>() {
public UserDetails mapRow(ResultSet rs, int rowNum)throws SQLException {
String username = rs.getString(1);
String password = rs.getString(2);
boolean enabled = rs.getBoolean(3);
String salt = rs.getString(4);
System.out.println("CustomJdbcDaoImpl Salt : "+salt);
return new SaltedUser(username, password,enabled, true, true, true,AuthorityUtils.NO_AUTHORITIES, salt);
}
});
}
}
And My SaltedUser.java is
public class SaltedUser extends User{
private String salt;
public SaltedUser(String username, String password,boolean enabled,
boolean accountNonExpired, boolean credentialsNonExpired,
boolean accountNonLocked, List<GrantedAuthority>authorities, String salt) {
super(username, password, enabled,accountNonExpired, credentialsNonExpired,accountNonLocked, authorities);
this.salt = salt;
System.out.println("SaltedUser Salt : "+salt);
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
}
Any one can help me....?
You need to override the createUserDetails method which creates the final UserDetails implementation returned by the class. Take a look at the source for JdbcDaoImpl.
Note that if you aren't building this for a legacy system which already has a password-hashing system already in place, then using something like BCrypt to encode your passwords would be a better and simpler option.

Spring 3.0 Security - Authorization with Authentication

I am new to Spring and my requirement is that I do not want to authenticate the user with username and password.
The user is authenticate is some other application and my app get the request with folloing details:
User name
Roles
I just want use Spring Security to secure the pages according to the roles in the request.
I've given a thought about writing UserDetailService, but that only add request-data, Spring still ask for authentication information.
Then I thought about writing something like the following:
public class UserLogin {
/*
#Resource(name = "userDetailsService")
private UserDetailsService userDetailsService;
*/
#Resource(name = "authenticationManager")
private AuthenticationManager authenticationManager;
public boolean login(UserEntity user) {
//UserDetails ud = userDetailsService.loadUserByUsername(username);
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (String role : user.getAuthorities()) {
authorities.add(new GrantedAuthorityImpl(role));
}
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), authorities);
try {
Authentication auth = authenticationManager.authenticate(token);
SecurityContext securityContext = new SecurityContextImpl();
// Places in ThredLocal for future retrieval
SecurityContextHolder.setContext(securityContext);
SecurityContextHolder.getContext().setAuthentication(auth);
} catch (AuthenticationException e) {
return false;
}
return true;
}
}
Am I going in the right direction. If so, how to configure the whole thing .. in spring-xml .
You're in what's called a Pre-Authentication scenario, where you configure Spring Security to only Authorize access, not Authenticate access. See http://static.springsource.org/spring-security/site/docs/3.0.x/reference/preauth.html. Here is a full configuration, where you need to implement AbstractPreAuthenticatedProcessingFilter to grep your authentication scheme's UserPrincipal, and the custom UserDetailsService you mention above.
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns:security="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<security:global-method-security secured-annotations="enabled" />
<beans:bean id="preAuthenticatedProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint" />
<security:http auto-config="false" entry-point-ref="preAuthenticatedProcessingFilterEntryPoint">
<security:custom-filter position="PRE_AUTH_FILTER" ref="myCustomPreAuthFilter" />
</security:http>
<beans:bean id="myCustomPreAuthFilter" class="com.mypackage.MyCustomPreAuthFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="preauthAuthProvider" />
</security:authentication-manager>
<beans:bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService">
<beans:bean id="userDetailsServiceWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<beans:property name="userDetailsService" ref="myCustomUserDetailsService"/>
</beans:bean>
</beans:property>
</beans:bean>

Resources