Spring 3安全性:未调用AccessDeniedHandler

时间:2011-08-10 15:13:37

标签: java spring-mvc spring-security spring-3

我有一个spring 3应用程序,其配置如下。当任何用户尝试访问某个页面并且他/她未登录时,我会收到一个带有丑陋堆栈跟踪的 Access is Denied 异常。如何处理此异常并且不允许它转储堆栈跟踪。我实现了自己的访问被拒绝处理程序但不会被调用。

根据所请求资源的类型,我想显示自定义错误消息或页面。这是我的弹簧配置。

如何让Spring调用我的访问被拒绝处理程序。这是我的弹簧配置

 <security:http auto-config='true'>
    <security:intercept-url pattern="/static/**" filters="none"/>
    <security:intercept-url pattern="/login" filters="none"/>

      <security:intercept-url pattern="/**" access="ROLE_USER" />

      <security:form-login login-page="/index"
            default-target-url="/home" always-use-default-target="true"
            authentication-success-handler-ref="AuthenticationSuccessHandler"        
            login-processing-url="/j_spring_security_check" 
            authentication-failure-url="/index?error=true"/>

       <security:remember-me key="myLongSecretCookieKey" token-validity-seconds="1296000" 
            data-source-ref="jdbcDataSource" user-service-ref="AppUserDetailsService" />

       <security:access-denied-handler ref="myAccessDeniedHandler" />   

    </security:http>

    <bean id="myAccessDeniedHandler"
         class="web.exceptions.handlers.AccessDeniedExceptionHandler">
      <property name="errorPage" value="/public/403.htm" />
    </bean>

下面给出了处理此异常的自定义类

public class AccessDeniedExceptionHandler implements AccessDeniedHandler
{

    private String errorPage;

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
            AccessDeniedException arg2) throws IOException, ServletException {
        response.sendRedirect(errorPage);
    }

       public void setErrorPage(String errorPage) {
       if ((errorPage != null) && !errorPage.startsWith("/")) {
            throw new IllegalArgumentException("errorPage must begin with '/'");
        }
        this.errorPage = errorPage;
    }

}

当我运行此应用程序时,这是我得到的错误。我只粘贴了堆栈跟踪和Spring Debug日志的一部分。

20:39:46,173 DEBUG AffirmativeBased:53 - Voter: org.springframework.security.access.vote.RoleVoter@5b7da0d1, returned: -1
20:39:46,173 DEBUG AffirmativeBased:53 - Voter: org.springframework.security.access.vote.AuthenticatedVoter@14c92844, returned: 0
20:39:46,178 DEBUG ExceptionTranslationFilter:154 - 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:71)
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:204)

如何解决此问题?首先,我想阻止春天抛出那个例外。如果它仍然抛出它,我想处理它而不是举起任何旗帜。

更新:我也附上了我的web.xml的一部分。

<!-- Hibernate filter configuration -->

<filter>
        <filter-name>HibernateFilter</filter-name>
        <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HibernateFilter</filter-name> 
        <url-pattern>/*</url-pattern>       
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>

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

    <!--Dispatcher Servlet -->

   <servlet>
     <servlet-name>rowz</servlet-name>
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     <load-on-startup>1</load-on-startup>
   </servlet>

7 个答案:

答案 0 :(得分:12)

在您的配置中要求用户在您的网站上输入任何网址时始终进行身份验证:

<security:intercept-url pattern="/**" access="ROLE_USER" />

我认为当进入登录页时,您应该允许用户未经身份验证

<security:intercept-url pattern="/your-login-page-url" access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/your-login-process-url" access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/your-login-failure-url" access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/**" access="ROLE_USER" />

如果您使用以下网址:/login/start/login/error/login/failure您可以拥有:

<security:intercept-url pattern="/login/**" access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/**" access="ROLE_USER" />

<强>更新

具有此配置应使框架将所有未经身份验证的(匿名)用户重定向到登录页面,并且所有用户都通过AccessDeniedHandler进行身份验证。 AccessDeniedException是框架的核心部分之一,忽略它并不是一个好主意。如果您只提供Spring Security配置的部分内容,则很难提供更多帮助。

请务必阅读ExceptionTranslationFilter的JavaDoc,详细说明框架抛出的异常,默认情况下处理的原因和方式。

如果可能,请尝试删除您添加的任意数量的自定义部分,例如AuthenticationSuccessHandlerRememberMeAuthenticationFilterAccessDeniedHandler并查看问题是否有问题?尝试获得最小的连接并逐步添加新功能以查看错误的来源。

您在问题中未提及的一件重要事情是此错误消息的结果是什么?你得到HTTP 500吗?还是HTTP 403?或者您是否被重定向到登录页面?

如果您在问题中提到,用户未经身份验证并且他/她被重定向到登录页面,那么这就是它的工作方式。看起来您只是因为DEBUG级别设置为Spring Security类而得到ExceptionTranslationFilter:172记录的错误消息。如果是这样,那么它的工作方式也是如此,如果您不想记录错误,那么只需提高Spring Secyruty类的日志记录级别。

更新2:

filters="none"的模式必须与login-page中设置的login-processing-urlauthentication-failure-ur<security:form-login />属性相匹配,才能跳过显示登录页面的所有SpringSecurity检查页面并处理登录。

<security:http auto-config='true'>
  <security:intercept-url pattern="/static/**" filters="none"/>
  <security:intercept-url pattern="/index" filters="none"/>
  <security:intercept-url pattern="/j_spring_security_check" filters="none"/>
  <security:intercept-url pattern="/**" access="ROLE_USER" />

  <security:form-login login-page="/index"
        default-target-url="/home" always-use-default-target="true"
        authentication-success-handler-ref="AuthenticationSuccessHandler"        
        login-processing-url="/j_spring_security_check" 
        authentication-failure-url="/index?error=true"/>

   <security:remember-me key="myLongSecretCookieKey" token-validity-seconds="1296000" 
        data-source-ref="jdbcDataSource" user-service-ref="AppUserDetailsService" />

   <security:access-denied-handler ref="myAccessDeniedHandler" />   

</security:http>

答案 1 :(得分:6)

AccessDeniedHandler在用户登录时调用,且没有资源权限(source here)。如果您想在用户未登录时处理登录页面的请求,只需在 security-context 中进行配置:

<http ... entry-point-ref="customAuthenticationEntryPoint">

定义customAuthenticationEntryPoint:

<beans:bean id="customAuthenticationEntryPoint" class="pl.wsiadamy.webapp.controller.util.CustomAuthenticationEntryPoint">
</beans:bean>

提示不要试图与ExceptionTranslationFilter 对抗。 我试图覆盖org.springframework.security.web.access.ExceptionTranslationFilter,没有效果:

<beans:bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
  <beans:property name="authenticationEntryPoint"  ref="customAuthenticationEntryPoint"/>
  <beans:property name="accessDeniedHandler" ref="accessDeniedHandler"/>
</beans:bean>
<beans:bean id="accessDeniedHandler"
 class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
  <beans:property name="errorPage" value="/accessDenied.htm"/>
</beans:bean>

ref="customAuthenticationEntryPoint"刚刚没有被调用。

答案 2 :(得分:3)

我已经按照以下方式添加了Spring Access被拒绝的页面: 弹簧框架工作:3.1 Spring Security:3.1,Java 1.5 +

输入* -security.xml:

<security:access-denied-handler error-page="/<My Any error page controller name>" />

示例:

<security:access-denied-handler error-page="/accessDeniedPage.htm" />

错误页面始终以&#34; /&#34;

开头

输入控制器:

@Controller
public class RedirectAccessDenied {

    @RequestMapping(value = "/accessDeniedPage.htm", method = RequestMethod.GET)
    public String redirectAccessDenied(Model model) throws IOException, ServletException {
        System.out.println("############### Redirect Access Denied Handler!");
        return "403";
    }
}

这里403是我的JSP名称。

答案 3 :(得分:2)

Spring Security使用AuthenticationEntryPoint对象来决定用户需要身份验证时要执行的操作。您可以创建自己的AuthenticationEntryPoint bean(see javadoc),然后在http元素中设置entryPoint属性:

<http entry-point-ref="entryPointBean" .... />

但是,默认情况下,form-login元素会创建一个LoginUrlAuthenticationEntryPoint,它会将所有未经身份验证的用户重定向到登录页面,因此您不必自己执行此操作。实际上,您发布的日志声称它将用户转发到身份验证入口点:“拒绝访问(用户是匿名的);重定向到身份验证入口点”。

我想知道问题是你关闭了登录网址的过滤器链。而不是将过滤器设置为none,这意味着完全绕过Spring安全性,尝试保持过滤器的开启,但允许不受限制的访问,如下所示:

<security:intercept-url pattern="/login" access="permitAll" />

如果仍然没有帮助,请发布剩余的日志,以便我们可以看到请求转移到入口点后会发生什么。

答案 4 :(得分:1)

以编程方式解决方案:

@Order(1)
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //
    // ...
    //

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandlerImpl() {
            @Override
            public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
                super.handle(request, response, accessDeniedException);
                accessDeniedException.printStackTrace();
            }
        });

        //
        // ...
        //

    }

}

答案 5 :(得分:0)

你能检查你的web.xml是否支持转发请求?

errorPage是一个FORWARD请求,主要在web.xml中,我们只支持REDIRECTS。只是想到你的代码对我来说没问题。

已编辑

不同的观点,这只取决于工作代码。 看看Authenticated Voter class

禁用注释

<global-method-security pre-post-annotations="disabled"
    secured-annotations="disabled" access-decision-manager-ref="accessDecisionManager">
</global-method-security>

绕过过滤器

<http auto-config="true" use-expressions="true"
    access-decision-manager-ref="accessDecisionManager"
    access-denied-page="/accessDenied">
    <intercept-url pattern="/appsecurity/login.jsp" filters="none" />
    <intercept-url pattern="/changePassword" filters="none" />
    <intercept-url pattern="/pageNotFound" filters="none" />
    <intercept-url pattern="/accessDenied" filters="none" />
    <intercept-url pattern="/forgotPassword" filters="none" />
    <intercept-url pattern="/**" filters="none" />


    <form-login login-processing-url="/j_spring_security_check"
        default-target-url="/home" login-page="/loginDetails"
        authentication-failure-handler-ref="authenticationExceptionHandler"
        authentication-failure-url="/?login_error=t" />
    <logout logout-url="/j_spring_security_logout"
        invalidate-session="true" logout-success-url="/" />
    <remember-me />
    <!-- Uncomment to limit the number of sessions a user can have -->
    <session-management invalid-session-url="/">
        <concurrency-control max-sessions="1"
            error-if-maximum-exceeded="true" />
    </session-management>
</http>

自定义决策选民

<bean id="customVoter" class="xyz.appsecurity.helper.CustomDecisionVoter" />

访问决策管理器

<!-- Define AccessDesisionManager as UnanimousBased -->
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
    <property name="decisionVoters">
        <list>
            <ref bean="customVoter" />
            <!-- <bean class="org.springframework.security.access.vote.RoleVoter" 
                /> -->
            <bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
        </list>
    </property>
</bean>

验证异常处理程序

<bean id="authenticationExceptionHandler"
    class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
    <property name="exceptionMappings">
        <props>
            <!-- /error.jsp -->
            <prop
                key="org.springframework.security.authentication.BadCredentialsException">/?login_error=t</prop>
            <!-- /getnewpassword.jsp -->
            <prop
                key="org.springframework.security.authentication.CredentialsExpiredException">/changePassword</prop>
            <!-- /lockedoutpage.jsp -->
            <prop key="org.springframework.security.authentication.LockedException">/?login_error=t</prop>
            <!-- /unauthorizeduser.jsp -->
            <prop
                key="org.springframework.security.authentication.DisabledException">/?login_error=t</prop>
        </props>
    </property>
</bean>

答案 6 :(得分:0)

看起来spring试图重定向尚未登录到登录页面的用户,即&#34; / index&#34;,但这本身就是受保护的URL。

另一种可能性是,它试图显示/public/403.html,但它再次受到安全配置的保护。

您可以添加以下条目并尝试吗?

<security:intercept-url pattern="/login" filters="none" />
<security:intercept-url pattern="/public/**" filters="none" />