Spring-Security:MySQL JDBC 身份验证失败

Posted

技术标签:

【中文标题】Spring-Security:MySQL JDBC 身份验证失败【英文标题】:Spring-Security: MySQL JDBC Authentication fails 【发布时间】:2017-12-06 13:13:30 【问题描述】:

我正在this repo 中操作一个开源项目。文件bank.sqlmysql中数据库的schema。这里是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>

我有一个如下的登录表单:

    <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>

文件Spring-Security.xml如下:

<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>

并且有一个bean来获取数据源来为Authentication-manager提供它,如下所示:

<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>

我确信 MySQL 服务器在 3306 端口上运行良好。

正确的凭据是username:Tompassword:Tom,但每次我尝试使用它们登录时都会失败。我想知道,我的身份验证过程有什么问题?

我该如何解决?

我猜,也许数据源 bean 没有正确创建,但我不知道如何检查它?

更新:

当我将&lt;http security="none" pattern="/login"/&gt; 添加到我的Spring-Security.xml 时,它会抱怨

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. 

但它发生了

【问题讨论】:

首先,“失败”是什么意思?您是否收到一些错误(如果是,请发布堆栈跟踪)?或者您只是再次被重定向到相同的登录页面?第二:我没有在您的配置中看到您的登录页面的安全禁用。尝试将以下 &lt;http security="none" pattern="/login"/&gt; 添加到您的 Spring-Security.xml,就在您现有的 &lt;http&gt; 元素上方。 另外,尝试为org.springframework.security 包启用调试日志记录 @RomanPuchkovskiy 失败,我的意思是它再次导航到登录页面。我已经用你的回答更新了我的问题 您可以克隆存储库并调查问题吗?这是一个小应用程序。 在 IDE 中尝试数据库连接怎么样?只是为了确保它正常工作.. 【参考方案1】:

首先,在 Spring Security 4 之前的版本中,默认参数名称是 j_usernamej_password(就像你提到的帖子中一样)而不是 username/password

在 Spring Security 4 中,默认名称是 usernamepassword,但 UsernamePasswordAuthenticationFilter 绑定的默认 URL 是 /login 而不是 /j_spring_security_check

所以在所有 Spring Security 版本中,您的 URL 和参数名称组合都与默认值不匹配。

以下是如何配置针对数据库的用户名-密码身份验证的示例:http://www.mkyong.com/spring-security/spring-security-form-login-using-database/(适用于 Spring Security 3.x)

另一个例子(更短更简单),用于 Spring Security 4:https://spring.io/guides/gs/securing-web/

参数如何传递

基本上,如果您有基于表单的身份验证,它的工作方式如下:

    用户试图访问一些需要身份验证的 URL;用户缺少该身份验证 Spring Security 将用户重定向到登录页面 用户在该页面输入登录名和密码并提交;在Spring Security 4之前的版本默认配置中,用户名j_username提交密码j_password/j_spring_security_check/j_spring_security_check Spring Security 提供的UsernamePasswordAuthenticationFilter 处理提交到/j_spring_security_check URL。一旦收到请求(来自登录表单),它就会提取参数(用户名/密码),将它们打包到 UsernamePasswordAuthenticationToken 并将其提供给 AuthenticationManager 以进行身份​​验证。 AuthenticationManager 检查访问(例如,JDBC 可用于检查数据库) 如果身份验证成功(用户存在,提供的名称,密码匹配),则构造结果Authentication(其中包含有关角色的信息),保存并调用AuthenticationSuccessHandler;它得到Authentication 结果 身份验证成功后,用户将被重定向回他在步骤 1 中尝试访问的 URL,并且仅在此处执行业务逻辑控制器

【讨论】:

【参考方案2】:

你可以做一个改变,尝试 和身份验证管理器 身份验证管理器

    <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>

这正是您所要求的。如果您满意,请不要忘记接受答案,如果需要,请询问更多信息。

【讨论】:

【参考方案3】:

您的 spring 安全配置是正确的。 解决这个问题的关键是理解spring-security csrf的保护策略。更详细的可以看[org.springframework.security.web.csrf.CsrfFilter.java][1]源码。

服务器端 CSRF 令牌存储库是基于会话的。它会在您第一次收到请求时生成。获取请求不会触发 CSRF 验证,所以可以通过。但是如果您的客户端令牌错误或空服务器将阻止您的任何修改请求(例如 POST,PUT,DELETE),响应 403 状态码。

您的错误是由于您的页面在隐藏输入中持有旧的 csrf 令牌,并且每个登录请求都被转发到错误页面,因此您的客户端 csrf 令牌无法刷新。

很简单,您可以尝试刷新登录页面并再次尝试登录。

【讨论】:

以上是关于Spring-Security:MySQL JDBC 身份验证失败的主要内容,如果未能解决你的问题,请参考以下文章

Loading class `com.mysql.jdbc.Driver‘. This is deprecated. The new driver class is `com.mysql.cj.jdb

Loading class `com.mysql.jdbc.Driver‘. This is deprecated. The new driver class is `com.mysql.cj.jdb

jdb的应用

jdb的应用

使用 jdb 调试 Java servlet。如何将 jdb 与 Tomcat 连接

java jdb命令详解