Jersey ContainerRequestFilter:在抛出状态为禁止的 'abortWith' 后获取 404 (403)

Jersey ContainerRequestFilter: Getting 404 after throwing 'abortWith' with status forbidden (403)

Jersey 2.22 API 具有令牌身份验证 + 基于角色的授权(我保护 API 的方式基于此 post 中接受的答案:Best practice for REST token-based authentication with JAX-RS and Jersey。在尝试理解我的问题之前阅读它可能会更好):

这是我的 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>

当我调用我的 tasks 网络服务时,流程进入第一个过滤器 (AuthenticationFilter) 没有任何问题 ( @Priority(Priorities.AUTHENTICATION)), 验证我的令牌,从解码令牌中获取用户然后将其注册为 Principal 然后通过在第二个过滤器中,AuthorizationFilter (@Priority(Priorities.AUTHORIZATION)) 我从安全上下文中获取用户,获取他的角色然后检查他是否有权拨打电话。如果是,则正常退出过滤器如果不是,使用javax.ws.rs.container.ContainerRequestContext.abortWith方法发送状态为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());
    }

当用户具有正确的角色时,将调用该服务,我会得到包含正确信息的正确响应。 我的问题是,当我的变量 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) {
... }

您应该将此 Jersey init-param jersey.config.server.response.setStatusOverSendError 设置为 true。这是 Javadoc

中的说明

Whenever response status is 4xx or 5xx it is possible to choose between sendError or setStatus on container specific Response implementation. E.g. on servlet container Jersey can call HttpServletResponse.setStatus(...) or HttpServletResponse.sendError(...).

Calling sendError(...) method usually resets entity, response headers and provide error page for specified status code (e.g. servlet error-page configuration). However if you want to post-process response (e.g. by servlet filter) the only way to do it is calling setStatus(...) on container Response object.

If property value is true the method Response.setStatus(...) is used over default Response.sendError(...).

Type of the property value is boolean. The default value is 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;
    }
}