Spring Security:绕过登录表单

Posted

技术标签:

【中文标题】Spring Security:绕过登录表单【英文标题】:Spring Security : Bypass login form 【发布时间】:2011-04-19 08:05:20 【问题描述】:

我想在某些场景下绕过 Spring webflow (Spring 2.0.5) 应用程序的登录表单(因此为普通用户呈现登录表单,但当 URL 类似于 http://server.com/myspringapp/fakelogin?username=FakeUser&password=FakePassword 时,不应呈现用户登录表单,但只是根据请求参数在内部进行身份验证,然后带到安全页面)。

所以我不想要 Preauthenticastion,而是在特殊场合(当 URL 如上所述)时进行透明身份验证。我看到了诸如http://forum.springsource.org/showthread.php?t=59108 之类的线程,但没有提到解决方案。我尝试实现 AuthenticationProcessingFilter 但不太确定如何实现 requiresAuthentication() 方法。

以下是我当前的安全 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:security="http://www.springframework.org/schema/security"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">

    <bean id="customAuthenticationProvider" class="com.myco.jsf.spring.security.MyAuthenticationProvider">
        <security:custom-authentication-provider/>
        <constructor-arg>
            <ref bean="webSessionFactory"/>
        </constructor-arg>
        <constructor-arg>
            <ref bean="authenticationBridge"/>
        </constructor-arg>
    </bean>

    <bean id="myEntryPoint" class="com.myco.web.filter.CustomAuthenticationEntryPoint">
        <property name="loginFormUrl" value="/spring/login" />
    </bean>

    <bean id="myProcessingFilter" class="com.myco.web.filter.CustomAuthenticationProcessingFilter">
        <security:custom-filter position="AUTHENTICATION_PROCESSING_FILTER" />
        <property name="defaultTargetUrl" value="/spring/secure" /> 
        <property name="authenticationFailureUrl" value="/spring/login" />
        <property name="alwaysUseDefaultTargetUrl" value="false" />
        <property name="filterProcessesUrl" value="/spring/j_spring_security_check" />    
        <property name="authenticationManager" ref="authenticationManager" />            
        <!--             
        <property name="allowSessionCreation" value="true" />                
         -->
    </bean>    

    <security:authentication-manager alias="authenticationManager"/>

        <security:http auto-config="false" access-denied-page="/spring/notpermitted" entry-point-ref="myEntryPoint">       
        <security:anonymous/>
        <!--     
        <security:form-login login-page="/spring/login" login-processing-url="/spring/j_spring_security_check" default-target-url="/spring/secure" 
            always-use-default-target="false" authentication-failure-url="/spring/login" />
         -->
        <security:logout logout-url="/spring/j_spring_security_logout" logout-success-url="/spring/pages/logout" />
        </security:http>

</beans> 

以下是我的过滤器类:

public class CustomAuthenticationProcessingFilter extends
        AuthenticationProcessingFilter 

    @Override
    protected void successfulAuthentication(HttpServletRequest request,
            HttpServletResponse response, Authentication authResult)
            throws IOException, ServletException 
        super.successfulAuthentication(request, response, authResult);

        System.out.println("==successful login==");
    

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request,
            HttpServletResponse response, AuthenticationException failed)
            throws IOException, ServletException 
        super.unsuccessfulAuthentication(request, response, failed);

        System.out.println("==failed login==");
    

    @Override
    protected boolean requiresAuthentication(HttpServletRequest request,
            HttpServletResponse response) 
        boolean retVal = false;
        String username = request.getParameter("j_username");
        String password = request.getParameter("j_password");

        if (username != null && password != null) 
            Authentication authResult = null;
            try 
                authResult = attemptAuthentication(request);                
                if (authResult == null) 
                    retVal = false;
                

             catch (AuthenticationException failed) 
                try 
                    unsuccessfulAuthentication(request, response, failed);
                 catch (Exception e) 
                    retVal = false;
                
                retVal = false;
            

            try 
                successfulAuthentication(request, response, authResult);
             catch (Exception e) 
                retVal = false;
            

            return false;
         else 
            retVal = super.requiresAuthentication(request, response);
        
        return retVal;
    


我能够使用提供的请求参数进行身份验证,并且成功创建了身份验证对象。一旦过滤器继续前进,我就会得到异常:

15:29:08,734 INFO  [STDOUT] 53453 ERROR [http-127.0.0.1-8080-2]     org.ajax4jsf.webapp.BaseXMLFilter     - Exception in the filter chain
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:659)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:552)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:206)
    at org.ajax4jsf.webapp.BaseFilter.handleRequest(BaseFilter.java:290)
    at org.ajax4jsf.webapp.BaseFilter.processUploadsAndHandleRequest(BaseFilter.java:388)
    at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:515)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at com.myco.jsf.filter.CharsetFilter.doFilter(CharsetFilter.java:38)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:378)
    at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
    at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.SessionFixationProtectionFilter.doFilterHttp(SessionFixationProtectionFilter.java:67)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.ExceptionTranslationFilter.doFilterHttp(ExceptionTranslationFilter.java:101)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.providers.anonymous.AnonymousProcessingFilter.doFilterHttp(AnonymousProcessingFilter.java:105)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter.doFilterHttp(SecurityContextHolderAwareRequestFilter.java:91)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:278)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.logout.LogoutFilter.doFilterHttp(LogoutFilter.java:89)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:235)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:175)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
    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:230)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:173)
    at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:182)
    at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
    at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:157)
    at org.apache.catalina.authenticator.SingleSignOn.invoke(SingleSignOn.java:393)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:241)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
    at java.lang.Thread.run(Thread.java:619)
Caused by: java.lang.IllegalStateException
    at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:407)
    at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:108)
    at org.springframework.security.context.HttpSessionContextIntegrationFilter$OnRedirectUpdateSessionResponseWrapper.sendError(HttpSessionContextIntegrationFilter.java:498)
    at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:108)
    at org.ajax4jsf.webapp.FilterServletResponseWrapper.sendError(FilterServletResponseWrapper.java:655)
    at com.sun.facelets.FaceletViewHandler.handleFaceletNotFound(FaceletViewHandler.java:711)
    at com.sun.facelets.FaceletViewHandler.renderView(FaceletViewHandler.java:658)
    at org.ajax4jsf.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:100)
    at org.ajax4jsf.application.AjaxViewHandler.renderView(AjaxViewHandler.java:176)
    at org.springframework.faces.mvc.JsfView.renderMergedOutputModel(JsfView.java:83)
    at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:250)
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1060)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:798)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:716)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:647)
    ... 61 more

你能帮我解释一下为什么会出现这个错误吗?我是否使用了正确类型的自定义过滤器?感谢您的帮助。

【问题讨论】:

【参考方案1】:

我对 Spring Security 3 做了类似的事情,我认为旧版本也应该可以。我已经修改了我的代码,所以它适合你的情况。您可能需要弄清楚一些细节,但它应该为您提供基本的想法。

您可以使用过滤器来处理它:

public class MyAuthenticationFilter extends DelegatingFilterProxy

    public void doFilter ...
    
            String username = request.getParameter("username");
            String password = request.getParameter("password");

            // build authentication token for user
            final Authentication auth = new UsernamePasswordAuthenticationToken(...);
            auth.setAuthenticated(true);

            // set authentication in context
            SecurityContextHolder.getContext().setAuthentication(auth);
    

在您的 web.xml 中:

<filter>
    <filter-name>myAuthenticationFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
    <filter-name>myAuthenticationFilter</filter-name>
    <url-pattern>/fakelogin*</url-pattern>
</filter-mapping>

在你的 spring.xml 中:

<bean id="myAuthenticationFilter" class=... />

另一种选择是允许所有用户访问 fakeLogin

<intercept-url pattern="/fakelogin/**" access="permitAll" />

并将身份验证放入 Web Flow Action 中的安全上下文中。

【讨论】:

你也可以看看 Spring 的预认证过滤器。

以上是关于Spring Security:绕过登录表单的主要内容,如果未能解决你的问题,请参考以下文章

spring security为不同用户显示各自的登录成功页面

如何使用 Spring Security / Spring MVC 处理表单登录

未调用 Spring Security 自定义登录表单

Spring security *always* 重定向到登录表单

如何使初始登陆页面不是spring security的登录表单?

自定义登录表单无法使用 Spring Security