Reputation: 9827
I'm implementing spring security 3.0.5 and in my form based login im extending the spring UserDetailsService. Currently my login form is only validating user name and not password. Where does spring security validate the password being posted to /j_spring_security_check?
security config:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
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/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config />
<context:component-scan base-package="dc" />
<global-method-security />
<http access-denied-page="/auth/denied.html">
<intercept-url filters="none" pattern="/javax.faces.resource/**" />
<intercept-url filters="none" pattern="/services/rest-api/1.0/**" />
<intercept-url filters="none" pattern="/preregistered/*"/>
<intercept-url
pattern="/**/*.xhtml"
access="ROLE_NONE_GETS_ACCESS" />
<intercept-url
pattern="/auth/**"
access="ROLE_ANONYMOUS,ROLE_USER" />
<intercept-url
pattern="/auth/*"
access="ROLE_ANONYMOUS" />
<intercept-url
pattern="/registered/*"
access="ROLE_USER" />
<intercept-url
pattern="/*"
access="ROLE_ANONYMOUS" />
<form-login
login-processing-url="/j_spring_security_check.html"
login-page="/auth/login.html"
default-target-url="/home.html"
authentication-failure-url="/login.html" />
<logout invalidate-session="true"
logout-url="logout.html"
success-handler-ref="SuccessHandler"/>
<anonymous username="guest" granted-authority="ROLE_ANONYMOUS"/>
<remember-me user-service-ref="userManager" key="dfdfdfdff"/>
<custom-filter after="FORM_LOGIN_FILTER" ref="xmlAuthenticationFilter"/>
</http>
<!-- Configure the authentication provider -->
<authentication-manager alias="am">
<authentication-provider user-service-ref="userManager">
<password-encoder ref="passwordEncoder" />
</authentication-provider>
<authentication-provider ref="xmlAuthenticationProvider" />
</authentication-manager>
</beans:beans>
beans:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.dc"/>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean id="springContextHolder" class="SpringContextHolder" factory-method="getInstance" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="MYSQL" />
<property name="showSql" value="true" />
</bean>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/webapp" />
<property name="username" value="userid" />
<property name="password" value="password" />
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="false"/>
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="localhost"/>
<property name="port" value="25"/>
</bean>
<bean id="utilities" class="UtilitiesImpl"/>
<bean id="xmlAuthenticationFilter" class="com.dc.api.service.impl.XMLAuthenticationFilter">
<property name="authenticationManager" ref="am" />
<property name="utilities" ref="utilities"/>
</bean>
<bean id="xmlAuthenticationProvider" class="com.dc.api.service.impl.XMLAuthenticationProvider">
<property name="userManager" ref="userManager"/>
</bean>
<bean id="DCLogoutSuccessHandler" class="LogoutSuccessHandler"/>
</beans>
UserDetails Implementation:
import javax.inject.Inject;
import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.dc.api.dao.AuthorityDAO;
import com.dc.api.dao.UserDAO;
import com.dc.api.exception.ApiDataException;
import com.dc.api.exception.EmailNotFoundException;
import com.dc.api.helper.MailContentHelper;
import com.dc.api.model.Users;
import com.dc.api.model.vo.APIResponse;
import com.dc.api.service.UserManager;
import com.dc.api.service.Utilities;
@Service("userManager")
public class UserManagerImpl extends UserDetailsService {
@Inject
UserDAO userDAO;
@Inject
AuthorityDAO authorityDAO;
@Inject
PasswordEncoder passwordEncoder;
@Inject
Utilities utilities;
private void encodePassword(Users user) {
if (user.getPassword() == null && user.getRawPassword() != null) {
user.setPassword(passwordEncoder.encodePassword(user.getRawPassword(), null));
user.setRawPassword(null);
}
}
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
Users user = null;
try {
user = userDAO.findByUsername(username);
if (user != null) {
}
} catch (DataAccessException ex) {
throw new UsernameNotFoundException("Invalid login", ex);
}
if (user == null) {
throw new UsernameNotFoundException("User not found.");
}
return user;
}
public Users getUser(String username) {
try {
return userDAO.findByUsername(username);
} catch (DataAccessException ex) {
// ignore
log.warn("Duplicate username: " + username);
}
return null;
}
public boolean isUsernameTaken(String username) {
try {
if (userDAO.findByUsername(username) == null) {
return false;
} else {
return true;
}
} catch (DataAccessException ex) {
// ignore
log.warn("Duplicate username: " + username);
}
return true;
}
public boolean isLoginValid(String username, String password) throws ApiDataException {
Users user = null;
try {
user = userDAO.findByUsername(username);
} catch (DataAccessException ex) {
throw new ApiDataException("Data Access Exception while verifying login");
}
if (user == null) {
return false;
}
if (passwordEncoder.isPasswordValid(user.getPassword(), password, null)) {
return true;
}
return false;
}
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public void saveUser(Users user) {
encodePassword(user);
userDAO.save(user);
}
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public void updateUser(Users user) {
encodePassword(user);
userDAO.update(user);
}
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public void resetPassword(String username, MailContentHelper mailContentHelper) {
String newPassword = utilities.generateSecret(8);
this.changePassword(username, newPassword, mailContentHelper);
}
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public void changePassword(String username, String password, MailContentHelper mailContentHelper) {
Users user = userDAO.findByUsername(username);
user.setPassword(null);
user.setRawPassword(password);
encodePassword(user);
userDAO.update(user);
String firstName = user.getFirstName();
firstName = (firstName == null) ? user.getUsername() : firstName;
//SimpleMailMessage message = mailContentHelper.retrieveContent(new Object[]{firstName, password, user.getEmail()});
//utilities.sendMail(message);
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/facelet/dc.taglib.xml</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
WEB-INF/dc-context-api.xml
WEB-INF/dc-context-security.xml
</param-value>
</context-param>
<context-param>
<param-name>resteasy.resource.method-interceptors</param-name>
<param-value>org.jboss.resteasy.core.ResourceMethodSecurityInterceptor</param-value>
</context-param>
<context-param>
<param-name>resteasy.resources</param-name>
<param-value>
com.dc.web.actions.GlobalWebService</param-value>
</context-param>
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/services/rest-api</param-value>
</context-param>
<context-param>
<param-name>resteasy.media.type.mappings</param-name>
<param-value>json : application/json, xml : application/xml</param-value>
</context-param>
<context-param>
<param-name>resteasy.resources</param-name>
<param-value>
com.WebService
</param-value>
</context-param>
<context-param>
<param-name>primefaces.THEME</param-name>
<param-value>none</param-value>
</context-param>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
</context-param>
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>1000</param-value>
</context-param>
<context-param>
<param-name>primefaces.PRIVATE_CAPTCHA_KEY</param-name>
<param-value>6LeL-MISAAAAAG6k07ch22oy-mxXBUi1MXKmrWiD</param-value>
</context-param>
<context-param>
<param-name>primefaces.PUBLIC_CAPTCHA_KEY</param-name>
<param-value>6LeL-MISAAAAAPTK5lYI9tK0SWWY2BqC2Hun7sH3</param-value>
</context-param>
<filter>
<filter-name>PrimeFaces FileUpload Filter</filter-name>
<filter-class>org.primefaces.webapp.filter.FileUploadFilter </filter-class>
<init-param>
<param-name>thresholdSize</param-name> <param-value>51200</param-value>
</init-param>
<init-param>
<param-name>uploadDirectory</param-name>
<param-value>url/upload</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>PrimeFaces FileUpload Filter</filter-name>
<servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>*.html</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>*.xhtml</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/dc_security_check</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<listener>
<listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<listener>
<listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<servlet>
<servlet-name>Resteasy</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
</servlet>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Resteasy</servlet-name>
<url-pattern>api/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
</web-app>
Upvotes: 1
Views: 9437
Reputation: 2661
It compares the password you submit to the password returned by the UserDetails
object returned by your UserDetailsService
. Please post your config and your UserDetailsService
if you need more help.
EDIT: Thanks for the info. It does exactly what you're guessing. The ProviderManager
(which is used by default) has the following in its JavaDoc:
If a subsequent provider successfully authenticates the request, the earlier authentication exception is disregarded and the successful authentication will be used.
So your problem is the latter provider "overruling" the decision of the first one.
Upvotes: 3