【问题标题】:Spring security not intercepting requestSpring Security 不拦截请求
【发布时间】:2013-03-17 19:29:54
【问题描述】:

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

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

文件位置:
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。

P.S - 因为我没有尝试授权,所以我没有在安全上下文 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>

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

}

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>

index.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 特定的数据库表。这里xml中没有使用SQL查询如下所示。

 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的代码sn-p[这里我得到登录页面,但验证失败]

            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 启用,其中 username='user'。我得到了结果集。

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

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

【问题讨论】:

  • 你的问题太长了......但这是我的猜测:对于方法 1,intercept-url 你没有 access="authenticated"。没有它,任何请求都不会被过滤。
  • @HoàngLong - 如果我补充说我在启动服务器时遇到异常。原因:java.lang.IllegalArgumentException:不支持的配置属性:[authenticated]...
  • 也许你可以查看stackoverflow.com/questions/2527198/…。在这个问题中,他们提到了保护 URL 的另外两种方法:使用 isAuthenticated() 和 IS_AUTHENTICATED_FULLY。也许其中一种对你有用。
  • delcare springSecurityFilterChain 先于其他。
  • @KyelJmD 你想让我把 放在 web.xml 中的 标记之上吗?

标签: spring spring-mvc spring-security


【解决方案1】:

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

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

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

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

【讨论】:

  • 感谢您的回复。对于 SQL 异常,我搜索并知道这可能是因为列类型,所以后来将列类型更改为常规类型,因为在所有示例代码中,即使用用户名,密码,启用的列有效。是否必须使用“用户 ID”、“用户名”、密码、启用的列创建表?我不能用任何其他列/列类型创建表。不知道为什么 spring 不将 oracle 类型转换为相应的 jdbc 类型以避免此错误。对于“未找到用户 'admin'”,我非常确定 D/B 拥有这些数据,因为我可以毫无问题地获取这些数据。
  • 在 JdbcUserDetailsManager 类中,我可以看到检查权限的 'validateAuthorities' 方法。所以我认为可能是春天要求我们也有访问权限,而且因为它在包含“'authorities-by-username-query”标签后才起作用。我不确定,但在这里调用 'validateAuthorities' 方法时。如果我错了,请纠正我。
  • 默认架构在the manual 中定义。您可以根据需要添加其他人,但您需要相应地自定义查询。如果您搜索,您将找到有关编写自定义 UserDetailsS​​ervice 和添加额外属性的信息。如果您的查询返回的类型与结果集预期的类型不同,那么它将失败。我猜这很可能与 Oracle 和布尔值有关。
  • 登录页面是否显示与 JDBC 完全没有联系,或者确实与UserDetailsService 没有联系。 loadUserByUsername 需要用户名才能调用,这只能发生在登录页面提交之后。
  • 顺便说一句,您所有的拦截 URL 模式都以 *.html 结尾,但您引用的 pages/welcome.jsp 与它们中的任何一个都不匹配。您可能应该澄清您的问题,确切地提出您提出的请求以及您从中获得的日志输出。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-12
  • 1970-01-01
  • 2019-02-04
  • 1970-01-01
  • 1970-01-01
  • 2012-08-15
相关资源
最近更新 更多