简体   繁体   English

Spring 3安全性:未调用AccessDeniedHandler

[英]Spring 3 Security: AccessDeniedHandler is not being invoked

I have a spring 3 application with the configurations given below. 我有一个spring 3应用程序,其配置如下。 When any user tries to access a page and he/she isn't logged in, I get an Access is Denied exception with an ugly stack trace. 当任何用户尝试访问某个页面并且他/她未登录时,我会收到一个带有丑陋堆栈跟踪的Access is Denied异常。 How do I handle this exception and not let it dump out a stack trace. 如何处理此异常并且不允许它转储堆栈跟踪。 I implemented my own access-denied-handler but that doesn't get invoked. 我实现了自己的访问被拒绝处理程序但不会被调用。

Based on the type of the requested resource, I would like to show custom error messages or pages. 根据所请求资源的类型,我想显示自定义错误消息或页面。 Here is my spring configuration. 这是我的弹簧配置。

How do I get Spring to invoke my access-denied-handler . 如何让Spring调用我的访问被拒绝处理程序。 Here is my spring configuration 这是我的弹簧配置

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

The custom class for handling this exception is given below 下面给出了处理此异常的自定义类

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

}

When I run this application, this is the error that I get. 当我运行此应用程序时,这是我得到的错误。 I am only pasting a part of the stacktrace and the Spring Debug logs. 我只粘贴了堆栈跟踪和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)

How do I fix this problem? 我该如何解决这个问题? Firstly, I want to stop spring from Throwing that exception. 首先,我想阻止春天抛出那个例外。 If it still throws it, I want to handle it and not raise any flags. 如果它仍然抛出它,我想处理它而不是举起任何旗帜。

Update: I have attached a part of my web.xml as well. 更新:我也附加了我的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>

In your configuration You require the user to be always authenticated when entering any URL on Your site: 在您的配置中您需要在您的站点上输入任何 URL时始终对用户进行身份验证:

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

I think You should allow the user to be unauthenticated when entering the login page : 我认为您应该允许用户在进入登录页面未经 身份 验证

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

If You use URL's like: /login/start , /login/error and /login/failure You can have: 如果您使用以下URL: /login/start/login/error/login/failure您可以:

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

Update: 更新:

Having this configuration should make the framework to redirect all unauthenticated (anonymous) users to login page, and all authenticated to AccessDeniedHandler . 具有此配置应该使框架将所有未经身份验证的(匿名)用户重定向到登录页面,并且所有用户都通过AccessDeniedHandler进行身份验证。 The AccessDeniedException is one of the core parts of the framework and ignoring it is not a good idea. AccessDeniedException是框架的核心部分之一,忽略它并不是一个好主意。 It's hard to help more if You only provide parts of Your Spring Security configuration. 如果您只提供Spring Security配置的部分内容,则很难提供更多帮助。

Be sure to read the JavaDoc for ExceptionTranslationFilter for detailed explanation of what exceptions are thrown by the framework, why and how are the handled by default. 请务必阅读JavaDoc for ExceptionTranslationFilter ,详细说明框架抛出的异常,默认情况下处理的原因和方式。

If possible, try removing as many custom parts You added, like AuthenticationSuccessHandler , RememberMeAuthenticationFilter and AccessDeniedHandler and see if the problem pesist? 如果可能的话,尝试删除你添加的尽可能多的自定义部分,如AuthenticationSuccessHandlerRememberMeAuthenticationFilterAccessDeniedHandler ,看看问题是否有问题? Try to get the minimal congiuration and add new features step by step to see where the error comes from. 尝试获取最小的集合并逐步添加新功能以查看错误的来源。

One important thing that You don't mention in Your question is what is the result of this error message? 您在问题中未提及的一个重要事项是此错误消息的结果是什么? Do You get HTTP 500 ? 你有HTTP 500吗? Or HTTP 403 ? 还是HTTP 403 Or do You get redirected to login page? 或者你被重定向到登录页面?

If, as You mentioned in the question, the user is unauthenticated and he/she gets redirected to login page, than that's how it's intended to work. 如果您在问题中提到,用户未经身份验证并且他/她被重定向到登录页面,那么这就是它的工作方式。 It looks like You get the error message logged by ExceptionTranslationFilter:172 only because You have DEBUG level set to Spring Security classes. 看起来只是因为您将DEBUG级别设置为Spring Security类而得到ExceptionTranslationFilter:172记录的错误消息。 If so, than that's also how it's intended to work, and if You don't want the error logged, than simply rise the logging level for Spring Secyruty classes. 如果是这样,那也是它的工作方式,如果你不想记录错误,那么只需提高Spring Secyruty类的日志记录级别。

Update 2: 更新2:

The patterns with filters="none" must match the login-page , login-processing-url and authentication-failure-ur attributes set in <security:form-login /> to skip all SpringSecurity checks on pages that display the login page and process the logging in. filters="none"的模式必须与<security:form-login />设置的login-pagelogin-processing-urlauthentication-failure-ur属性相匹配,以跳过显示登录页面的页面上的所有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>

AccessDeniedHandler is invoked when user is logged in and there is no permissions to resource ( source here ). 当用户登录并且没有资源权限( 此处source )时,将调用AccessDeniedHandler If you want to handle request for login page when user is not logged in , just configure in security-context : 如果要在用户未登录时处理登录页面请求,只需在security-context中配置:

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

And define customAuthenticationEntryPoint: 并定义customAuthenticationEntryPoint:

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

TIP , don't try to fight with ExceptionTranslationFilter . 提示不要试图与ExceptionTranslationFilter对抗 I have tried to override org.springframework.security.web.access.ExceptionTranslationFilter , without effects: 我试图覆盖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>

The ref="customAuthenticationEntryPoint" just didn't invoked. 刚刚没有调用ref="customAuthenticationEntryPoint"

I have added Spring Access denied page in follwing way: Spring Frame Work: 3.1 Spring Security: 3.1, Java 1.5+ 我已经按照以下方式添加了Spring Access被拒绝的页面:Spring Frame Work:3.1 Spring Security:3.1,Java 1.5+

Entry in *-security.xml: 输入* -security.xml:

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

Example: 例:

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

Error page will always start with "/" 错误页面始终以“/”开头

Entry for controller: 控制器进入:

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

Here 403 is my JSP name. 这里403是我的JSP名称。

Spring Security uses an AuthenticationEntryPoint object to decide what to do when a user requires authentication. Spring Security使用AuthenticationEntryPoint对象来决定用户需要身份验证时要执行的操作。 You can create your own AuthenticationEntryPoint bean ( see javadoc ), and then set the entryPoint attribute in the http element: 您可以创建自己的AuthenticationEntryPoint bean( 请参阅javadoc ),然后在http元素中设置entryPoint属性:

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

However, by default, the form-login element creates a LoginUrlAuthenticationEntryPoint which redirects all of your unauthenticated users to the login page, so you shouldn't have to do this yourself. 但是,默认情况下,form-login元素会创建一个LoginUrlAuthenticationEntryPoint,它会将所有未经身份验证的用户重定向到登录页面,因此您不必自己执行此操作。 In fact, the log you posted claims it is forwarding the user to the authentication entry point: "Access is denied (user is anonymous); redirecting to authentication entry point". 实际上,您发布的日志声称它将用户转发到身份验证入口点:“拒绝访问(用户是匿名的);重定向到身份验证入口点”。

I wonder if the problem is that you turned off the filter chain for the login url. 我想知道问题是你关闭了登录网址的过滤器链。 Instead of setting filters to none, which means spring security is bypassed entirely, try keeping the filters on but allowing unrestricted access like this: 而不是将过滤器设置为none,这意味着完全绕过了Spring安全性,请尝试保持过滤器,但允许不受限制的访问,如下所示:

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

If that still doesn't help, please post the rest of the log so we can see what happens after the request is transferred to the entry point. 如果仍然没有帮助,请发布剩余的日志,以便我们可以看到请求转移到入口点后会发生什么。

Programmatically solution: 以编程方式解决:

@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();
            }
        });

        //
        // ...
        //

    }

}

Can you check your web.xml is supporting forward request? 你能检查你的web.xml是否支持转发请求?

errorPage is a FORWARD request and mostly in web.xml we support REDIRECTS only. errorPage是一个FORWARD请求,主要在web.xml中,我们只支持REDIRECTS。 Just a thought else your code looks ok to me. 只是想到你的代码对我来说没问题。

Edited 编辑

A different point of view and This is been taken from working code only. 一个不同的观点,这只取决于工作代码。 Have a look at Authenticated Voter class 看看Authenticated Voter类

Disable the annotations 禁用注释

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

bypassing filters 绕过过滤器

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

custom Decision Voter 自定义决策选民

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

Access Decision Manager 访问决策管理器

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

Authentiation Exception Handler 验证异常处理程序

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

It looks like spring tries to redirect users who have not logged in to the login page, which is "/index", but that itself is a protected url. 看起来spring试图重定向尚未登录到登录页面的用户,即“/ index”,但这本身就是受保护的URL。

The other possibility is, it tries to display /public/403.html, but that is again protected by security configuration. 另一种可能性是,它尝试显示/public/403.html,但这又受到安全配置的保护。

Can you add the following entries and try? 您可以添加以下条目并尝试吗?

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM