Jersey ContainerRequestFilter:抛出“ abortWith”且状态被禁止后得到404(403)

jon*_*jon 2 java rest restful-authentication httpresponse jersey-2.0

带有令牌身份验证+基于角色的授权的Jersey 2.22 API(我保护API的方式基于本文的公认答案:使用JAX-RS和Jersey进行基于REST令牌身份验证的最佳实践。最好在阅读之前阅读它试图理解我的问题):

这是我的web.xml

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>

<!-- LISTENERS -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>JerseySpringServlet</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.packages</param-name>
        <param-value>ca.toto.api.filters</param-value>
    </init-param>
    <init-param>
        <param-name>jersey.config.server.provider.packages</param-name>
        <param-value>ca.toto.api.restapi</param-value>
    </init-param>

    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>ca.toto.api.filters.AuthenticationFilter;ca.toto.api.filters.AuthorizationFilter;com.toto.api.restapi.TaskRestService</param-value>
    </init-param>

    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>JerseySpringServlet</servlet-name>
    <url-pattern>/filters/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>JerseySpringServlet</servlet-name>
    <url-pattern>/restapi/*</url-pattern>
</servlet-mapping>
Run Code Online (Sandbox Code Playgroud)

当我调用任务 Web服务时,流程毫无问题地进入第一个过滤器(AuthenticationFilter)(@Priority(Priorities.AUTHENTICATION)),验证我的令牌,从解码的令牌中获取用户,然后将其注册为委托人然后传入第二个过滤器AuthorizationFilter@Priority(Priorities.AUTHORIZATION)),在这里我从安全上下文中获取用户,获取其角色,然后检查他是否有权进行调用。如果是,请正常退出过滤器(如果否),请使用javax.ws.rs.container.ContainerRequestContextabortWith方法发送状态为403的响应:

@Secured
@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {

    ...
try {
        boolean isAllowed = false;
        // Check if the user is allowed to execute the method
        // The method annotations override the class annotations
        if (methodRoles.isEmpty()) {
            logger.info("Checking permissions on CLASS level");
            isAllowed = checkPermissions(classRoles);
        } else {
            logger.info("Checking permissions on METHOD level");
            isAllowed = checkPermissions(methodRoles);
        }

        // Throw an Exception if the user has not permission to execute the method
        if(isAllowed == false) {
            logger.warn("USER IS NOT ALLOWED TO COMPLETE THE REQUEST. ABORT.");
             requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build());
        }

    } catch (Exception e) {
        requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build());
    }
Run Code Online (Sandbox Code Playgroud)

当用户具有正确的角色时,将调用该服务,并且我会收到带有正确信息的正确响应。我的问题是,当我的变量isAllowed等于false时,我得到的是404而不是403,我不知道为什么...

这是我的TaskRestService服务定义:

@Path("/tasks")
@Secured({RoleEnum.admin})
public class TaskRestService {
    ...
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Transactional(readOnly = true)
    public List<Task> getTasks(@QueryParam("code") String code) {
... }
Run Code Online (Sandbox Code Playgroud)

Pau*_*tha 7

您应该将此Jersey init-param设置jersey.config.server.response.setStatusOverSendErrortrue。这是Javadoc中的内容

每当响应状态为4XX或者5XX,可以选择sendErrorsetStatus集装箱的具体Response实施。例如,在servlet容器Jersey上可以调用HttpServletResponse.setStatus(...)HttpServletResponse.sendError(...)

调用sendError(...)方法通常会重置实体,响应标头,并为指定的状态码提供错误页面(例如servlet错误页面配置)。但是,如果要对响应进行后处理(例如,通过servlet过滤器),则唯一的方法是调用setStatus(...)容器Response对象。

如果属性值为true,Response.setStatus(...)则使用默认方法Response.sendError(...)

属性值的类型为boolean。默认值为false

因此,发生的错误是导致容器尝试将您发送到错误页面的错误,并且在未配置错误页面的情况下,您会得到404。因此,将属性设置为时true,会导致使用setStatus而不是sendError


注意:如果您不使用web.xml,则ResourceConfig可以使用property(property, value)方法。对于Application子类,您可以覆盖Map<String, Object> getProperties()

public class MyApp extends ResourceConfig {
    public MyApp() {
        property(ServletProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, true);
    }
}

public class MyApp extends Application {
    @Override
    public Map<String, Object> getProperties() {
        Map<String, Object> props = new HashMap<>();
        props.put("jersey.config.server.response.setStatusOverSendError", true);
        return props;
    }
}
Run Code Online (Sandbox Code Playgroud)