Announcement Announcement Module
Collapse
No announcement yet.
ShaPasswordEncoder - Authentication problem Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • ShaPasswordEncoder - Authentication problem

    Hi,

    I am taking a hands-on approach to Spring Security by trying to implement it on a website that I am creating. I have successfully managed to:

    - login with the default login form
    - login with my own login form (and logout too!)
    - login using credentials stored in database (plaintext)

    ...but I have got stuck with trying to login using encryption. Here are the relevant code snippets:

    spring-security.xml
    Code:
    <?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: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.3.xsd">
     
    	<http auto-config="true">
    		<intercept-url pattern="/login.htm" access="ROLE_ANONYMOUS" />
    		<intercept-url pattern="/logout.htm" access="ROLE_ANONYMOUS" />
    		<intercept-url pattern="/resources/**" filters="none" />
    		<intercept-url pattern="/**" access="ROLE_USER" />
    		<form-login login-page="/login.htm" default-target-url="/home.htm" authentication-failure-url="/login.htm?error=true" />
    		<logout logout-success-url="/logout.htm" />
    	</http>
     
    	<authentication-manager>
    	  <authentication-provider>
    	  	<!--<password-encoder hash="sha"/>-->
    	  	<password-encoder base64="true" ref="passwordEncoder"/>
    	  	
    	  	<jdbc-user-service data-source-ref="dataSource"
    	  		users-by-username-query="select USER_NAME, PASSWORD, ENABLED from apphub.users where USER_NAME=?"
    	  		authorities-by-username-query="select u.USER_NAME, ur.AUTHORITY from apphub.users u, apphub.user_roles ur where u.USER_ID = ur.USER_ID and u.USER_NAME=?" 
    		/>	    
    	  </authentication-provider>
    	</authentication-manager>
    	
    </beans:beans>


    <app_name>-servlet.xml
    Code:
        <!-- Start: Datasources -->
        <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/apphub" />
    		<property name="username" value="root" />
    		<property name="password" value="password" />
       	</bean>
       	<!-- End: Datasources -->
       	
       	
       	
       	<!-- Start: Password Encoder -->
       	<bean class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" id="passwordEncoder"/>
       	
       	<bean class="com.classifieds.security.DatabaseBootstrapPasswordEncoder" init-method="secureDatabase" depends-on="dataSource">
      		<property name="dataSource" ref="dataSource" />
      		<property name="passwordEncoder" ref="passwordEncoder" />
      	</bean>
       	<!-- End: Password Encoder -->
                
    </beans>

    DatabaseBootstrapPasswordEncoder.java
    Code:
    package com.classifieds.security;
    
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.jdbc.core.RowCallbackHandler;
    import org.springframework.jdbc.core.support.JdbcDaoSupport;
    import org.springframework.security.authentication.encoding.PasswordEncoder;
    
    
    /**
     * A class, which will encode the bootstrap passwords loaded via SQL. 
     * 
     * NB: the application assumes a number of accounts will have been set up in the database
     * using a SQL script. These are considered 'bootstrap' accounts and the passwords for 
     * these accounts will appear in the database in plain-text. The purpose of this class is
     * to encode the plain-text passwords of the bootstrap accounts. The class executes an init 
     * method, which is invoked after the embedded-database bean is instantiated.
     * 
     * @author Dan.Mortimer
     */
    public class DatabaseBootstrapPasswordEncoder extends JdbcDaoSupport {
    	private PasswordEncoder passwordEncoder;
    	
    	private final Log logger = LogFactory.getLog(getClass());
    
    	/**
    	 * @param passwordEncoder the passwordEncoder to set
    	 */
    	public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
    		this.passwordEncoder = passwordEncoder;
    	}
    	
    	/**
    	 * The init method, which is invoked after the embedded-database bean is instantiated.
    	 * Spring JdbcTemplate functionality is used to loop through all the users in the database 
    	 * and encode the password using the injected PasswordEncoder reference. Each password is 
    	 * updated individually.
    	 */
    	public void secureDatabase() {
    	    getJdbcTemplate().query("select USER_NAME, PASSWORD from apphub.users",
    	                             new RowCallbackHandler() {
    	      public void processRow(ResultSet rs) throws SQLException {
    	    	  String username = rs.getString(1);
    	          String password = rs.getString(2);
    	          
    	          String encodedPassword = passwordEncoder.encodePassword(password, null);
    	          
    	          getJdbcTemplate().update("update apphub.users set PASSWORD = ? where USER_NAME = ?", encodedPassword, username);
    	          
    	          logger.info("Updating password for username:" + username + " to:" + encodedPassword);
    	      }
    	    });
    	}
    }
    I have turned up the logging for Spring and I can see the following error:

    2012-07-23 14:05:40,125 DaoAuthenticationProvider [DEBUG] Authentication failed: password does not match stored value

    The problem is that the password I get directly out of the database DOES match with what gets created in the java class. So I cannot understand what is going wrong.

    I'd be very grateful for some advice, please!

  • #2
    <password-encoder base64="true" ref="passwordEncoder"/>

    I think you have now one encoder with hex and one with base64 encoding.

    Comment


    • #3
      Hi spgmx,

      Thanks for getting in touch!

      Is there any way you could shed a little more light on this one for me please...? I'll keep digging around in the meantime!

      Thanks!

      Comment


      • #4
        Hi,

        I have struggled with this one some more, but I am still getting the same problem. Here's the change...

        <app_name>-servlet.xml
        Code:
        <!-- Start: Datasources -->
            <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/apphub" />
        		<property name="username" value="root" />
        		<property name="password" value="password" />
           	</bean>
           	<!-- End: Datasources -->
           	
           	
           	
           	<!-- Start: Password Encoder -->
           	<bean class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" id="passwordEncoder">
           		<property name="encodeHashAsBase64">
          			<value>false</value>
          		</property>
           	</bean>
           	
           	<bean class="com.classifieds.security.DatabaseBootstrapPasswordEncoder" init-method="secureDatabase" depends-on="dataSource">
          		<property name="dataSource" ref="dataSource" />
          		<property name="passwordEncoder" ref="passwordEncoder" />
          	</bean>
           	<!-- End: Password Encoder -->
        As you can see, I have tried to specify the value for the encodeHashAsBase64 property. This has not allowed me to login successfully.

        With Spring Logging turned up, I still get:

        2012-07-24 11:51:47,059 DaoAuthenticationProvider [DEBUG] Authentication failed: password does not match stored value

        I'd be very grateful for a bit more assistance with this one, please...

        Cheers!

        Comment


        • #5
          Ähm...

          <password-encoder base64="true" ref="passwordEncoder"/>

          vs.

          <property name="encodeHashAsBase64">
          <value>false</value>
          </property>

          Comment


          • #6
            Hi spgmx,

            Thanks for posting another reply...

            Apologies - I didn't post my other config file, did I?

            I can assure you that I have actually:
            - set both the fields you have highlighted to false and I cannot login
            - set both the fields you have highlighted to true and I cannot login

            In each case, I get the same error in my 'turned-up' Spring logging:
            2012-07-24 11:51:47,059 DaoAuthenticationProvider [DEBUG] Authentication failed: password does not match stored value

            Please let me know if I need to post anything else...

            Any further suggestions would be greatly appreciated.

            Comment


            • #7
              I would debug the PasswordEncoder now...
              If you are using maven: select the Encoder, hit F3, wait until the source is downloaded, set your breakpoint and debug...

              Comment


              • #8
                spgmx,

                I'm not using Maven, but I have done some debugging (of sorts) using the class I wrote, DatabaseBootstrapPasswordEncoder.java. The upddated source is here:

                DatabaseBootstrapPasswordEncoder.java
                Code:
                package com.classifieds.security;
                
                import java.sql.ResultSet;
                import java.sql.SQLException;
                
                import org.apache.commons.logging.Log;
                import org.apache.commons.logging.LogFactory;
                import org.springframework.jdbc.core.RowCallbackHandler;
                import org.springframework.jdbc.core.support.JdbcDaoSupport;
                import org.springframework.security.authentication.encoding.PasswordEncoder;
                
                
                /**
                 * A class, which will encode the bootstrap passwords loaded via SQL. 
                 * 
                 * NB: the application assumes a number of accounts will have been set up in the database
                 * using a SQL script. These are considered 'bootstrap' accounts and the passwords for 
                 * these accounts will appear in the database in plain-text. The purpose of this class is
                 * to encode the plain-text passwords of the bootstrap accounts. The class executes an init 
                 * method, which is invoked after the embedded-database bean is instantiated.
                 * 
                 * @author Dan.Mortimer
                 */
                public class DatabaseBootstrapPasswordEncoder extends JdbcDaoSupport {
                	private PasswordEncoder passwordEncoder;
                	
                	private final Log logger = LogFactory.getLog(getClass());
                
                	/**
                	 * @param passwordEncoder the passwordEncoder to set
                	 */
                	public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
                		this.passwordEncoder = passwordEncoder;
                	}
                	
                	/**
                	 * The init method, which is invoked after the embedded-database bean is instantiated.
                	 * Spring JdbcTemplate functionality is used to loop through all the users in the database 
                	 * and encode the password using the injected PasswordEncoder reference. Each password is 
                	 * updated individually.
                	 */
                	public void secureDatabase() {
                	    getJdbcTemplate().query("select USER_NAME, PASSWORD from apphub.users",
                	                             new RowCallbackHandler() {
                	      public void processRow(ResultSet rs) throws SQLException {
                	    	  String username = rs.getString(1);
                	          String password = rs.getString(2);
                	          logger.info("Username:" + username + " Password:" + password);
                	          
                	          String encodedPassword = passwordEncoder.encodePassword(password, null);
                	          logger.info("Username:" + username + " Password:" + password + " encodedPassword" + encodedPassword);
                	          
                	          getJdbcTemplate().update("update apphub.users set PASSWORD = ? where USER_NAME = ?", encodedPassword, username);
                	          
                	          logger.info("Updating password for username:" + username + " to:" + encodedPassword);
                	          
                	          if (passwordEncoder.isPasswordValid(encodedPassword, password, null)) {
                	        	  logger.info("Password is valid - encoded:" + encodedPassword + " raw:" + password);
                	          } else {
                	        	  logger.info("Password NOT valid - encoded:" + encodedPassword + " raw:" + password);
                	          }
                	      }
                	    });
                	}
                }


                For a given user account (User:testuser Passwordassword), output in the log is as follows:
                2012-07-24 14:05:13,279 DatabaseBootstrapPasswordEncoder [INFO] Username:testuser Password:5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
                (...surely this should be 'Passwordassword' - looks like some encoding has taken place already...???)

                2012-07-24 14:05:13,279 DatabaseBootstrapPasswordEncoder [INFO] Username:testuser Password:5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8 encodedPassword353e8061f2befecb6818ba0c034c632fb0b cae1b
                (...here, '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8' is getting encoded, not 'password')

                2012-07-24 14:05:13,295 DatabaseBootstrapPasswordEncoder [INFO] Updating password for username:testuser to:353e8061f2befecb6818ba0c034c632fb0bcae1b
                2012-07-24 14:05:13,295 DatabaseBootstrapPasswordEncoder [INFO] Password is valid - encoded:353e8061f2befecb6818ba0c034c632fb0bcae1b raw:5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8


                So, in my website frontend, I can log in with the credentials:
                User:testuser
                Password: 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8



                But I cannot login with (this is the behaviour, I want):
                User:testuser
                Password: password


                Why is it that I am not getting the plain-text value ('password') out of the database in the class?
                (I am getting what looks like an already encoded value)

                Thank you!

                Comment


                • #9
                  You must debug the Encoder!

                  And: You will never see the password in cleartext again because hashing is a oneway encryption.

                  Comment


                  • #10
                    Originally posted by spgmx View Post
                    You must debug the Encoder!
                    The ShaPasswordEncoder... not your own DatabaseBootstrapPasswordEncoder

                    Comment


                    • #11
                      OK...I'll see if I can debug the ShaPasswordEncoder.

                      However, if you look at the logging output of my own DatabaseBootstrapPasswordEncoder, specifically:

                      2012-07-24 14:05:13,279 DatabaseBootstrapPasswordEncoder [INFO] Username:testuser Password:5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8

                      ...you can see that I am not getting 'password' back as the result of a direct query on the database before the ShaPasswordEncoder ever gets called.

                      This seems wierd, doesn't it...?

                      Comment

                      Working...
                      X