jax-rs 1.1 带有安全过滤器和依赖注入,如何实现?
jax-rs 1.1 with security filter and dependency injection, how to achieve this?
例如,我有以下 JAX-RS 1.1 方法接收 JWT token,检查它然后处理或拒绝如下请求:
@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
object, return 401错误如果无效.
实际上我可以通过在静态方法中提取它来优化它,该方法将执行此检查并 return User
object 成功或抛出 Exception
如果有什么出错。我还可以创建 webfilter,它将检查 header 并在它到达 jax-rs 方法之前验证令牌,如果它无效,将以 401 异常中止它。
但我想一起实现。 Webfilter
这将验证 JWT 令牌,如果它有效 将其转换 为 User
object 并 注入它 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?
来实现
我不知道有什么方法可以完全满足您的要求,但是用一些 thread local data 来实现一些东西并不难。基本上,过滤器将存储带有用户信息的 ThreadLocal 数据,服务方法将能够获取它。这有点 hack,但它可以让你做你想做的事。
不过要小心 - 线程本地数据与线程一起存在 - 尽量不要在其中存储一个巨大的对象,否则如果你有大量客户端,你可能 运行 进入内存问题。
编辑
稍微想了想,为什么不直接用session呢? 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");
}
注意这里的 "cheat" - 我们正在注入 HttpServletRequest 以便我们可以获取会话,并通过它获取用户。但是由于过滤器的原因,如果 JWT 验证失败,则不会调用此方法。
此代码不利用 JAX-RS and/or Servlet 规范之外的任何内容,应该与应用程序服务器无关。
您可以为此目的使用过滤器 and/or 拦截器。这是 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 方法接收 JWT token,检查它然后处理或拒绝如下请求:
@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
object, return 401错误如果无效.
实际上我可以通过在静态方法中提取它来优化它,该方法将执行此检查并 return User
object 成功或抛出 Exception
如果有什么出错。我还可以创建 webfilter,它将检查 header 并在它到达 jax-rs 方法之前验证令牌,如果它无效,将以 401 异常中止它。
但我想一起实现。 Webfilter
这将验证 JWT 令牌,如果它有效 将其转换 为 User
object 并 注入它 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?
来实现我不知道有什么方法可以完全满足您的要求,但是用一些 thread local data 来实现一些东西并不难。基本上,过滤器将存储带有用户信息的 ThreadLocal 数据,服务方法将能够获取它。这有点 hack,但它可以让你做你想做的事。
不过要小心 - 线程本地数据与线程一起存在 - 尽量不要在其中存储一个巨大的对象,否则如果你有大量客户端,你可能 运行 进入内存问题。
编辑
稍微想了想,为什么不直接用session呢? 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");
}
注意这里的 "cheat" - 我们正在注入 HttpServletRequest 以便我们可以获取会话,并通过它获取用户。但是由于过滤器的原因,如果 JWT 验证失败,则不会调用此方法。
此代码不利用 JAX-RS and/or Servlet 规范之外的任何内容,应该与应用程序服务器无关。
您可以为此目的使用过滤器 and/or 拦截器。这是 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());
}
}
}