Using global-method-security, Access Denied errors

2019-07-01 14:30发布

问题:

I'm trying to use Spring Security Annotations for security instead of defining the rules in XML. It seems to work, but when I run into an access denied error I get a HTTP status code of 500 returned. I do not see any exceptions in my tomcat log files. And when the execution hits my AuthenticationEntryPoint, the response is committed.

If I revert to having the rules in XML and get an access denied error, I get a HTTP status code of 401 returned.

The method is annotated with @PreAuthorize

@GET
@Produces(MediaType.APPLICATION_JSON)
@PreAuthorize("hasRole('user')")
public String list() throws IOException 

Here is my XML (the previous XML rule is commented out)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd">

    <security:debug/>

    <security:global-method-security pre-post-annotations="enabled"/>

    <security:authentication-manager id="authenticationManager">
        <security:authentication-provider user-service-ref="userDao">
            <security:password-encoder ref="passwordEncoder"/>
        </security:authentication-provider>
    </security:authentication-manager>

    <security:http
            realm="Protected API"
            use-expressions="true"
            auto-config="false"
            create-session="stateless"
            entry-point-ref="unauthorizedEntryPoint"
            authentication-manager-ref="authenticationManager">
        <security:access-denied-handler ref="accessDeniedHandler"/>
        <security:custom-filter ref="tokenAuthenticationProcessingFilter" position="FORM_LOGIN_FILTER"/>
        <security:custom-filter ref="tokenFilter" position="REMEMBER_ME_FILTER"/>
        <!--<security:intercept-url method="GET" pattern="/rest/news/**" access="hasRole('user')"/>-->
    </security:http>

</beans>

回答1:

This issue is unrelated to Spring Security. The problem was with Jersey.

Jersey was intercepting the AccessDeniedException and rethrowing it as a ServletException.

What I had to do was write an ExceptionMapper. More information https://jersey.java.net/documentation/latest/representations.html#d0e4866

@Provider
/**
 * AccessDeniedMapper is instantiated by Jersey directly through the "jersey.config.server.provider.packages" setting
 */
public class AccessDeniedMapper implements ExceptionMapper<AccessDeniedException> {
    @Override
    public Response toResponse(AccessDeniedException e) {
        return Response.status(401)
                .build();
    }
}

On startup Jersey scans for @Provider's using the jersey.config.server.provider.packages property. From my web.xml

<!-- Map the REST Servlet to /rest/ -->
<servlet>
    <servlet-name>RestService</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <!--Every class inside of this package (com.unsubcentral.rest) will be available to Jersey-->
        <param-name>jersey.config.server.provider.packages</param-name>
        <param-value>com.rince.rest</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>RestService</servlet-name>
    <url-pattern>/rest/*</url-pattern>
</servlet-mapping>