带有安全过滤器和依赖注入的 jax-rs 1.1,如何实现这一点?

Posted

技术标签:

【中文标题】带有安全过滤器和依赖注入的 jax-rs 1.1,如何实现这一点?【英文标题】:jax-rs 1.1 with security filter and dependency injection, how to achieve this? 【发布时间】:2016-09-28 18:14:57 【问题描述】:

例如我有以下接收JWT token的JAX-RS 1.1方法,检查它然后处理或拒绝如下请求:

@GET
public Response getBook(@HeaderParam("Authorization") String authorizationToken, @QueryParam("id") String bookId) 

    //if(authorizationToken is a valid JWT token)
    //      get User object which represented by this token
    //      get required book from database
    //      return 200 code with a book within response
    //else if(authorizationToken is invalid by whatever reason it's)
    //      return 401 code within a response
    //else if(database error)
    //      return 500 code within a response


正如您在每个 jax-rs 方法中看到的,我需要使用相同的代码行:检查令牌将其转换为 User 对象如果无效则返回 401 错误

实际上,我可以通过在静态方法中提取它来优化它,该方法将执行此检查并在成功时返回 User 对象,如果出现问题则抛出 Exception。我还可以创建webfilter,它将在它到达 jax-rs 方法之前检查标头并验证令牌,如果它无效,它将以 401 异常中止它。

但我想一起实现。 Webfilter 将验证 JWT 令牌,如果它有效将其转换User 对象并在 jax-rs 方法中注入,如下所示:

@GET
public Response getBook(@RequestByUser User user, @QueryParam("id") String bookId) 

    //if(get required book from database was successfull)
    //      return 200 code with a book within response
    //else(database error)
    //      return 500 code within a response

因此,如果我达到了这一点,我可以确定用户是有效的,我不需要关心它。 JAX-RS 1.1 是否可以实现这一点?

【问题讨论】:

【参考方案1】:

我不知道有一种方法可以在这里完全按照您的要求做,但是用一些thread local data 来实现一些东西不会太难。基本上,过滤器会将 ThreadLocal 数据与用户信息一起存储,并且服务方法将能够获取它。这有点小技巧,但它可以让你做你想做的事。

但要小心 - 线程本地数据与线程一起存在 - 尽量不要在其中存储巨大的对象,否则如果您有大量客户端,您可能会遇到内存问题。

编辑

考虑了一下,为什么不直接使用会话呢? ThreadLocal 假设过滤器和服务在同一个线程上运行。这可能是真的,但不能保证。

所以在你的过滤器中:

JWTFilter.java

@WebFilter(urlPatterns="/*"
public class JWTFilter implements Filter 
        public void doFilter(ServletRequest request,
                             ServletResponse response,
                             FilterChain chain) throws IOException, ServletException 
        HttpServletRequest req = (HttpServletRequest) request;

        String jwt = req.getHeader("Authorization");
        User user = getUserFromJWT(jwt);  // you'll have to code this
        if(user != null) 
            req.getSession().setAttribute("user", user);
            chain.doFilter(request, response);
        
        else 
            HttpServletResponse resp = (HttpServletResponse)response;
            resp.sendError(HttpServletResponse.SC_FORBIDDEN);
        
    

YourService.java

@GET
public Response getBook(@QueryParam("id") String bookId,
                        @Context HttpServletRequest request) 
    User user = (User)request.getSession().getAttribute("user");    

请注意这里的“作弊”——我们正在注入 HttpServletRequest 以便我们可以获取会话以及用户。但是由于过滤器的原因,如果 JWT 验证失败,则不会调用此方法。

此代码不利用 JAX-RS 和/或 Servlet 规范之外的任何内容,并且应该与应用服务器无关。

【讨论】:

嗨,WebFilter 来自哪个包。它是 JAX-RS 1.1 的一部分吗?JAX-RS 1.1 已经在类路径中,但是 WebFilter 导入无法解析。你能取悦他吗;p ?【参考方案2】:

您可以为此目的使用过滤器和/或拦截器。这是Jersey 示例。

public class AuthorizationRequestFilter implements ContainerRequestFilter 

    @Override
    public void filter(ContainerRequestContext requestContext)
                    throws IOException 

        final SecurityContext securityContext =
                    requestContext.getSecurityContext();
        if (securityContext == null ||
                    !securityContext.isUserInRole("privileged")) 

                requestContext.abortWith(Response
                    .status(Response.Status.UNAUTHORIZED)
                    .entity("User cannot access the resource.")
                    .build());
        
    

【讨论】:

我提到我需要 jax-rs 1.1 的解决方案,ContainerRequestFilter 仅从 2.0 开始提供 jax-rs-spec.java.net/nonav/2.0-SNAPSHOT/apidocs/javax/ws/rs/…

以上是关于带有安全过滤器和依赖注入的 jax-rs 1.1,如何实现这一点?的主要内容,如果未能解决你的问题,请参考以下文章

JAX-RS + JBoss 7.1.1 + RESTEasy:使用 CDI 的 NullPointException

JAX-RS 安全性和身份验证

通过 JAX-RS 客户端支持 HTTP/1.1 和 HTTP/2

Jersey JAX-RS + Spring 安全应用示例

JAX-RS 将标头添加到分块响应

使用拦截器和注入在 JAX-RS 中进行身份验证/授权