Wednesday, July 12, 2017

Spring-Security: MySQL JDBC Authentication fails

Leave a Comment

I am manipulating an open source project in this repo. The file bank.sql is the schema of the database in mysql. Here is the pom.xml:

 <dependencies>      <!-- https://mvnrepository.com/artifact/org.apache.tomcat/juli -->     <dependency>         <groupId>org.apache.tomcat</groupId>         <artifactId>juli</artifactId>         <version>6.0.26</version>     </dependency>      <dependency>         <groupId>javax.servlet</groupId>         <artifactId>jsp-api</artifactId>         <version>2.0</version>         <scope>provided</scope>     </dependency>        <dependency>         <groupId>javax.servlet</groupId>         <artifactId>servlet-api</artifactId>         <version>2.5</version>         <scope>provided</scope>     </dependency>    <dependency>    <groupId>junit</groupId>    <artifactId>junit</artifactId>    <version>4.11</version>    <scope>test</scope>   </dependency>    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-core</artifactId>      <version>3.2.3.RELEASE</version>   </dependency>    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-web</artifactId>      <version>3.2.3.RELEASE</version>   </dependency>    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-webmvc</artifactId>      <version>3.2.3.RELEASE</version>   </dependency>    <dependency>       <groupId>org.springframework.security</groupId>       <artifactId>spring-security-core</artifactId>       <version>3.2.3.RELEASE</version>   </dependency>    <dependency>       <groupId>org.springframework.security</groupId>       <artifactId>spring-security-config</artifactId>       <version>3.2.3.RELEASE</version>   </dependency>    <dependency>        <groupId>org.springframework.security</groupId>        <artifactId>spring-security-web</artifactId>        <version>3.2.3.RELEASE</version>   </dependency>    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-jdbc</artifactId>        <version>3.2.3.RELEASE</version>   </dependency>    <dependency>        <groupId>mysql</groupId>        <artifactId>mysql-connector-java</artifactId>        <version>5.1.6</version>   </dependency>    <dependency>        <groupId>jstl</groupId>        <artifactId>jstl</artifactId>        <version>1.2</version>   </dependency>    <dependency>        <groupId>opensymphony</groupId>        <artifactId>sitemesh</artifactId>        <version>2.4.2</version>   </dependency>  </dependencies> 

I have a login form as below:

    <form name="loginForm" class="form-login"         action="<c:url value="/j_spring_security_check" />" method="POST">         <h2>Please sign in</h2>          <c:if test="${not empty error}">             <div class="alert alert-danger">${error}</div>         </c:if>         <c:if test="${not empty msg}">             <div class="alert alert-info">${msg}</div>         </c:if>          <input type="text" class="form-control" placeholder="Username" name="username">         <input type="password" class="form-control" placeholder="Password" name="password" />         <button type="submit" class="btn btn-lg btn-primary btn-block" name="submit">Login</button>         <input type="hidden" name="${_csrf.parameterName}"             value="${_csrf.token}" />      </form> 

The file Spring-Security.xml is as below:

<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.2.xsd">      <http auto-config="true" use-expressions="true">         <intercept-url pattern="/admin**" access="hasRole('ROLE_ADMIN')" />         <intercept-url pattern="/user**" access="hasAnyRole('ROLE_USER', 'ROLE_ADMIN')" />         <intercept-url pattern="/change**" access="hasRole('ROLE_NEWUSER')" />          <access-denied-handler error-page="/403" />          <form-login              login-page="/login"              authentication-success-handler-ref="bankCustomAuthenticationSuccessHandler"             authentication-failure-url="/login?error"              username-parameter="username"             password-parameter="password" />         <logout logout-success-url="/login?logout"  />         <!-- enable csrf protection -->         <csrf/>     </http>      <beans:bean id="bankCustomAuthenticationSuccessHandler"         class="ee.mikkelsaar.bank.security.MyUrlAuthenticationSuccessHandler" />      <authentication-manager>         <authentication-provider>             <password-encoder hash="sha" />             <jdbc-user-service data-source-ref="dataSource" users-by-username-query="select username,password, enabled from users where username=?" authorities-by-username-query="select u.username, a.authority from users u, authorities a where u.username = a.username and u.username =?" />         </authentication-provider>     </authentication-manager>      <beans:import resource="spring-datasource.xml" />      <beans:bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.MessageDigestPasswordEncoder">       <beans:constructor-arg value="sha" />     </beans:bean>   </beans:beans> 

And there is a bean to obtain the datasource to provide it for Authentication-manager such as below:

<beans xmlns="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-2.5.xsd">      <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/bank" />         <property name="username" value="root" />         <property name="password" value="" />     </bean>  </beans> 

I am sure the MySQL Server is running on port 3306 well.

The correct credential is username:Tom and password:Tom but everytime I try to login with them it fails. I am wondering, what is wrong with my authentication process?

How can i fix it?

I guess, maybe the datasource bean is not created correctly, but i have no idea how to check it?

Update:

When i add the <http security="none" pattern="/login"/> to my Spring-Security.xml it complains with

HTTP Status 405 - Request method 'POST' not supported for (username, password) `(Tom, tom)`, which is not a valid credential. But for a valid credential like `(Tom,Tom)` is still navigates to the login page again.  

but it happens

3 Answers

Answers 1

First of all, in Spring Security prior to version 4, default parameter names are j_username and j_password (like in the post you mentioned) and not username/password.

In Spring Security 4, the default names are username and password, but the default URL to which UsernamePasswordAuthenticationFilter binds is /login and not /j_spring_security_check.

So in all Spring Security versions, your URL and parameter names combination does not match the defaults.

Here is an example of how username-password authentication against a database may be configured: http://www.mkyong.com/spring-security/spring-security-form-login-using-database/ (for Spring Security 3.x)

Another example (much more short and simple), for Spring Security 4: https://spring.io/guides/gs/securing-web/

How the parameters are passed

Basically, if you have a form-based authentication, it works like this:

  1. User tries to access some URL that requires authentication; user lacks that authentication
  2. Spring Security redirects the user to a login page
  3. User enters login and password on that page and submits it; in the default configuration in Spring Security versions before 4, username is submitted as j_username and password as j_password to /j_spring_security_check
  4. Spring Security-supplied UsernamePasswordAuthenticationFilter processes submissions to /j_spring_security_check URL. Once it gets a request (from login form), it extracts the parameters (username/password), packs them into UsernamePasswordAuthenticationToken and feeds it to AuthenticationManager for authentication.
  5. AuthenticationManager checks access (JDBC can be used to check against a database, for example)
  6. If the authentication is successful (user exists with the supplied name, password matches), the result Authentication is constructed (which contains information about roles), saved, and AuthenticationSuccessHandler is invoked; it gets that Authentication result
  7. After authentication succeeds, the user is redirected back to the URL he tried to access on step 1, and only here a business logic controller will be executed.

Answers 2

You can do one change, try for and for Authentication-manager Authentication-manager

    <security:authentication-manager alias="authManager">         <security:authentication-provider             ref="daoAuthProvider">         </security:authentication-provider>     </security:authentication-manager>    <beans:bean id="daoAuthProvider"         class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">         <beans:property name="userDetailsService">             <beans:ref bean="userDetailsService" />         </beans:property>         <beans:property name="passwordEncoder" ref="encoder"/>     </beans:bean>  <beans:bean id="userDetailsService"         class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">         <beans:property name="dataSource">             <beans:ref bean="coreDataSource" />         </beans:property>         <beans:property name="usersByUsernameQuery">             <beans:value>                 SELECT username,password as password FROM userdetails WHERE                 password != '' and username= ?              </beans:value>         </beans:property>         <beans:property name="authoritiesByUsernameQuery">             <beans:value>                 SELECT username,authority FROM authorities JOIN userdetails ON authorities.user_id = userdetails.user_id ..                 WHERE userdetails.username= ? and             </beans:value>         </beans:property>   <security:http pattern="/admin/admin.jsp" security="none" /> <security:http pattern="/**/*.js" security="none" /> ..           <security:custom-filter ref="formAuthFilter"             after="FORM_LOGIN_FILTER" />     <beans:bean id="formAuthFilter"         class="com.sca.security.SCAAuthenticationProcessingFilter">          <beans:property name="authenticationManager" ref="authManager" />         <beans:property name="allowSessionCreation" value="true" />         <beans:property name="authenticationFailureHandler"             ref="authFailureHandler" /> <!-- define authFailureHandler -->         <beans:property name="authenticationSuccessHandler"             ref="authSuccessHandler" /><!-- define authSuccessHandler -->         <beans:property name="filterProcessesUrl" value="/j_spring_security_check" /> <!-- define userDAO, globalFilter-->          </beans:property>       </beans:bean> 

This is what exactly you are asking for. Please do not forget to accept the answer if you are satisfied or ask more if needed.

Answers 3

Your spring security config is right. The key for resolving this problem is understanding spring-security csrf protection strategy.More detail you can see [org.springframework.security.web.csrf.CsrfFilter.java][1] source code.

Server side CSRF token repository is session based. It will generated when you first get request. Get request will not trigger CSRF validation, so it can pass.But if your client side token is wrong or empty server will prevent your any modify request(.eg POST,PUT,DELETE), response 403 state code.

Your error is caused by that your page is holding an old csrf token in the hidden input, and every login request is forwarded to an error page, so your client side csrf token can not be refreshed.

So simply, you can try refresh you login page and try login again.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment