0

我正在尝试做一个基本的弹簧安全 D/B 身份验证程序。我通过两种方式进行了尝试,即

方法 1:使用自定义表进行 Spring Security 身份验证。
方法2:使用Spring security特定的数据库表进行用户认证和授权。

文件位置:
1. index.jsp -> webapp/index.jsp
2. welcome.jsp -> webapp/pages/welcome.jsp
3. login.jsp -> webapp/pages/login.jsp

对于方法 1,Spring security 没有拦截请求,我没有在控制台中看到错误。我没有拦截请求,而是直接带到了 welcome.jsp。

PS - 因为我没有尝试授权,所以我没有在安全上下文 xml 中使用下面的“authorities-by-username-query”属性。我不确定是否也必须创建一个授权表。

下面是我的security-context.xml:

<?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:jee="http://www.springframework.org/schema/jee"
xmlns:security="http://www.springframework.org/schema/security"
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/security
      http://www.springframework.org/schema/security/spring-security-3.1.xsd
      http://www.springframework.org/schema/tx 
      http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

<security:http auto-config="true">
    <security:intercept-url pattern="/welcome.html" />
    <security:form-login login-page="/login.html"
        default-target-url="/welcome.html" authentication-failure-url="/loginfailed.html" />
    <security:logout logout-success-url="/logout.html" />
</security:http>

<security:authentication-manager>
    <security:authentication-provider>
        <security:jdbc-user-service data-source-ref="dataSource"
         users-by-username-query="select FIRST_NAME,LAST_NAME,PASSWORD from USER_AUTHENTICATION where FIRST_NAME=?" />
    </security:authentication-provider>
</security:authentication-manager>

网页.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" id="WebApp_ID" version="2.5">
 <display-name>SpringPOC</display-name>
 <servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
        /WEB-INF/applicationContextDirect.xml
        /WEB-INF/applicationContext-security.xml
    </param-value>
</context-param>
<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>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

基本控制器

//@RequestMapping(value="/login", method = RequestMethod.GET)
@RequestMapping("/login")
public ModelAndView login(Model model) {
    //System.out.println("Inside /login...");
    return new ModelAndView("login");
}
/*public String login(ModelMap model) {

    System.out.println("Inside /login...");
    return "login";

}*/

@RequestMapping(value="/loginfailed", method = RequestMethod.GET)
public String loginerror(ModelMap model) {

    model.addAttribute("error", "true");
    return "login";

}

@RequestMapping(value="/logout", method = RequestMethod.GET)
public String logout(ModelMap model) {

    return "login";

}

登录.jsp

    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
          <html>
         <head>
         <title>Login Page</title>
         <style>
         .errorblock {
    color: #ff0000;
    background-color: #ffEEEE;
    border: 3px solid #ff0000;
    padding: 8px;
    margin: 16px;
     }
     </style>
     </head>
     <body onload='document.f.j_username.focus();'>
    <h3>Login with Username and Password (Authentication with Database)</h3>

    <c:if test="${not empty error}">
        <div class="errorblock">
            Your login attempt was not successful, try again.<br /> Caused :
            ${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
        </div>
    </c:if>

    <form name='f' action="<c:url value='j_spring_security_check' />"
        method='POST'>

        <table>
            <tr>
                <td>User:</td>
                <td><input type='text' name='j_username' value=''>
                </td>
            </tr>
            <tr>
                <td>Password:</td>
                <td><input type='password' name='j_password' />
                </td>
            </tr>
            <tr>
                <td colspan='2'><input name="submit" type="submit"
                    value="submit" />
                </td>
            </tr>
            <tr>
                <td colspan='2'><input name="reset" type="reset" />
                </td>
            </tr>
        </table>

    </form>

索引.jsp

    <body>
    <div id="content">
   <h1>Home Page</h1>
   <p>
   Anyone can view this page.
   </p>
   <p><a href="welcome.html">Login page</a></p>
   </div>
   </body>

对于方法 2,我按照以下链接创建了名为“USERS”和“AUTHORITIES”的 spring 特定数据库表。这里的 SQL 查询没有在 xml 中使用,如下所示。

 http://www.raistudies.com/spring-security-tutorial/authentication-authorization-spring-security-mysql-database/

除了 security-context.xml 之外,一切都保持不变。

  <?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:jee="http://www.springframework.org/schema/jee"
xmlns:security="http://www.springframework.org/schema/security"
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/security
      http://www.springframework.org/schema/security/spring-security-3.1.xsd
      http://www.springframework.org/schema/tx 
      http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

<security:http realm="Project Realm" auto-config="true">
    <security:intercept-url pattern="/welcome.html" access="ROLE_USER"/>
    <security:form-login login-page="/login.html"
        default-target-url="/welcome.html" authentication-failure-url="/loginfailed.html" />
    <security:logout logout-success-url="/logout.html" />   
</security:http>

<security:authentication-manager>
    <security:authentication-provider>
    <security:password-encoder hash="md5"/>
    <security:jdbc-user-service data-source-ref="dataSource"/>
    </security:authentication-provider>
</security:authentication-manager>
    </beans>

当我尝试上述方式时,即使我输入了正确的用户名和密码,我也会收到“错误凭据”消息[但是是的,在这种情况下,spring security 正在拦截请求]。我正在使用 Oracle 数据库。

[更新]:我启用了 spring 调试日志来查找两种方法中错误的根本原因。我无法从日志中弄清楚或理解到底出了什么问题,所以我比较了尝试这两种方法后得到的日志。因为,对于方法 1,Spring 安全性没有拦截请求,而对于方法 2,我能够登录(Spring 安全性是至少拦截请求)但即使在输入正确的用户名和密码后我也收到了“错误的凭据”消息。

下面是方法 2 的代码片段[这里我得到登录页面,但身份验证失败]

            firing Filter: 'FilterSecurityInterceptor'
        DEBUG: org.springframework.security.web.util.AntPathRequestMatcher - Checking match of request : '/welcome.html'; against 

        '/welcome.html'
        DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: 

        /welcome.html; Attributes: [ROLE_USER]
        DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated: 

        org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2bc: Principal: anonymousUser; Credentials: 

        [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: 

        RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
        DEBUG: org.springframework.security.access.vote.AffirmativeBased - Voter: 

        org.springframework.security.access.vote.RoleVoter@19432e0, returned: -1
        DEBUG: org.springframework.security.access.vote.AffirmativeBased - Voter: 

        org.springframework.security.access.vote.AuthenticatedVoter@9830bc, returned: 0
        DEBUG: org.springframework.security.web.access.ExceptionTranslationFilter - Access is denied (user is anonymous); 

        redirecting to authentication entry point
        org.springframework.security.access.AccessDeniedException: Access is denied
            at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
            at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation

        (AbstractSecurityInterceptor.java:206)
            at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke

        (FilterSecurityInterceptor.java:115)
            at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter

        (FilterSecurityInterceptor.java:84)
            at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
            at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
            at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
            at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
            at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
            at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter

        (AnonymousAuthenticationFilter.java:113)
            at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
            at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter

        (SecurityContextHolderAwareRequestFilter.java:54)
            at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
            at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
            at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
            at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter

        (BasicAuthenticationFilter.java:150)
            at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
            at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter

        (AbstractAuthenticationProcessingFilter.java:183)
            at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
            at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
            at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
            at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter

        (SecurityContextPersistenceFilter.java:87)
            at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
            at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
            at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
            at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
            at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
            at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
            at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
            at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
            at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
            at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
            at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
            at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
            at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
            at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:857)
            at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
            at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
            at java.lang.Thread.run(Thread.java:662)
        DEBUG: org.springframework.security.web.savedrequest.HttpSessionRequestCache - DefaultSavedRequest added to Session: 

        DefaultSavedRequest[http://localhost:8080/itrade-web/welcome.html]
        DEBUG: org.springframework.security.web.access.ExceptionTranslationFilter - Calling Authentication entry point.
        DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to 'http://localhost:8080/itrade-

        web/login.html;jsessionid=3FD72892F4F4EF2E65B0C90ABE115354'
        DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext is empty or contents 

        are anonymous - context will not be stored in HttpSession.
        DEBUG: org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as 

        request processing completed
        DEBUG: org.springframework.security.web.FilterChainProxy - /login.html at position 1 of 10 in additional filter chain; 

        firing Filter: 'SecurityContextPersistenceFilter'
        DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - HttpSession returned null object for SPRING_SECURITY_CONTEXT
        firing Filter: 'UsernamePasswordAuthenticationFilter'
        ...
        DEBUG: org.springframework.security.web.FilterChainProxy - /login.html at position 7 of 10 in additional filter chain; 

        firing Filter: 'AnonymousAuthenticationFilter'
        DEBUG: org.springframework.security.web.authentication.AnonymousAuthenticationFilter - Populated SecurityContextHolder with 

        anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@6fa8940c: Principal: 

        anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: 

        org.springframework.security.web.authentication.WebAuthenticationDetails@fffde5d4: RemoteIpAddress: 0:0:0:0:0:0:0:1; 

        SessionId: 3FD72892F4F4EF2E65B0C90ABE115354; Granted Authorities: ROLE_ANONYMOUS'
                    ...
        DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Request is to process 

        authentication
        DEBUG: org.springframework.security.authentication.ProviderManager - Authentication attempt using 

        org.springframework.security.authentication.dao.DaoAuthenticationProvider
        DEBUG: org.springframework.security.provisioning.JdbcUserDetailsManager - Query returned no results for user 'admin'
        DEBUG: org.springframework.security.authentication.dao.DaoAuthenticationProvider - User 'admin' not found
        DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Authentication request 

        failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
        DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Updated SecurityContextHolder 

        to contain null Authentication
        DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Delegating to authentication 

        failure handler org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler@1882c1a
        DEBUG: org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler - Redirecting to 

        /loginfailed.html
        DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to '/itrade-web/loginfailed.html'
        DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext is empty or contents 

        are anonymous - context will not be stored in HttpSession.
        DEBUG: org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as 

        request processing completed

[更新] 对于方法 1,我在为“授权”创建自定义表后添加了“权限-用户名-查询”标签。现在我正在登录屏幕,所以我知道为了让 spring security 拦截我需要有'authorities-by-username-query'标签。但是在输入用户名和密码后,我收到以下错误消息:

 Caused : PreparedStatementCallback; uncategorized SQLException for SQL [select          FIRST_NAME,LAST_NAME,PASSWORD from USER_AUTHENTICATION where FIRST_NAME=?]; SQL state   [null]; error code [17059]; Fail to convert to internal representation; nested exception is   java.sql.SQLException: Fail to convert to internal representation 

我在调试模式下看到以下几行:

            DEBUG: org.springframework.security.authentication.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
        INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
        DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Authentication request failed: org.springframework.security.authentication.AuthenticationServiceException: PreparedStatementCallback; uncategorized SQLException for SQL [select FIRST_NAME,LAST_NAME,PASSWORD from USER_AUTHENTICATION where FIRST_NAME=?]; SQL state [null]; error code [17059]; Fail to convert to internal representation; nested exception is java.sql.SQLException: Fail to convert to internal representation
        DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Updated SecurityContextHolder to contain null Authentication
        DEBUG: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Delegating to authentication failure handler org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler@e7736c
        DEBUG: org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler - Redirecting to /loginfailed.html
        DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to '/itrade-web/loginfailed.html'

[更新]:现在对于这两种方法,尽管我输入了正确的用户名和密码,但我都遇到了同样的错误。此外,由于我可以从 D/B 获取数据,我确信我不会因为数据不存在而出错在 D/B。

 DEBUG: org.springframework.security.provisioning.JdbcUserDetailsManager - Query returned no results for user 'user'

我觉得这个错误背后应该有任何其他原因。

[编辑] 现在我在 D/B 中关注 'users_detail' 表:

USER_ID 整数

用户名 VARCHAR2(50 字节)

密码 VARCHAR2(50 字节)

启用整数

“users_detail”表中的数据:

USER_ID 用户名密码已启用

100 个用户 123456 1

我的查询在 security-context.xml 中:

  "select username,password, enabled from users_detail where username=?"

当我手动执行查询时,即选择用户名,密码,从users_detail 启用,其中用户名='user'。我得到了结果集。

我哪里错了?为什么 JdbcUserDetailsManager 类总是返回 'Query returned no results for user 'user' ' 即使在 D/B 中有相同的条目。

当我收到上述错误时,调试模式不显示正在执行 JdbcUserDetailsManager 类的哪个方法。我怎么知道?另外,spring 在保存密码字段的同时是否在内部做任何加密/解密技术?

4

1 回答 1

0

使用默认模式时,日志消息“未找到用户 'admin'”似乎很明显是身份验证失败的原因。为什么不手动执行命令并查看它是否返回用户数据?

此外,是否显示登录屏幕并不取决于您是否设置了“'authorities-by-username-query”。它仅取决于intercept-url您请求的 URL 是否适用于哪些值。唯一的例外是,如果您自定义了拒绝访问行为(对于权限不足的经过身份验证的用户)以显示登录页面(此处不是这种情况)。

您的 SQL 异常可能是由于您的自定义表具有错误的列类型。您最终需要得到与从标准模式获得的结果集兼容的东西。除非您有充分的理由不这样做,否则最好坚持使用默认设置。

更好的是,在您掌握使用 HSQLDB 等简单测试数据库的基础知识之前,请完全忘记 Oracle。

于 2013-03-29T14:45:50.213 回答