resteasy 3.x以前的版本是只有拦截器,3.x版本以后以前的拦截器被标记为过时了,新版适用过滤器代替
jboss官方说明地址(版本4.7.0.Final):https://docs.jboss.org/resteasy/docs/4.7.0.Final/userguide/html/Interceptors.html
官方新版说明不太详细,这里整理一下实测结果。
采用springboot + resteasy ,集成参考:https://github.com/paypal/resteasy-spring-boot
接口身份验证过滤器,适用 jwt 生成token
代码较糙,后续优化,请谅解哈
@Provider
@JaxrsCheckLogin
@Slf4j
public class JaxrsAuthorizationInterceptor implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
try {
// 从cookie中获取token
String token = "";
Map<String, Cookie> cookies = requestContext.getCookies();
if (CollectionUtils.isEmpty(cookies)) {
throw new BusinessException("cookies is null");
}
//获取token
for (Map.Entry<String, Cookie> entry : cookies.entrySet()) {
if ("token".equals(entry.getKey())) {
token = entry.getValue()==null?null:entry.getValue().getValue();
}
}
// 开始验证登录
if (StringUtil.isBlank(token)) {
throw new BusinessException(AuthResultCode.NOT_LOGIN);
}
try {
// jwt 验证 token 是否正确
JwtUtils.checkToken(token);
} catch (JWTVerificationException e) {
log.warn("jwt token check exception :", e);
throw new BusinessException(AuthResultCode.TOKEN_IS_INVALID);
}
List<String> list = JWT.decode(token).getAudience();
log.info("JWT decode token result : {}", JSONObject.toJSONString(list));
LoginInfoVO loginInfoVO = JSONObject.parseObject(list.get(0), LoginInfoVO.class);
// 需验证登录,token过期时间
if (LoginInfoVO.getTokenExp().before(LocalDateTime.now().toDate())) {
throw new BusinessException(AuthResultCode.TOKEN_EXPIRE);
}
requestContext.getHeaders().add("userInfo", JSON.toJSONString(loginInfoVO));
} catch (BusinessException e) {
log.error("Authorization businessException : ", e);
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity(e.getMessage()).build());
} catch (Throwable e) {
log.error("Authorization Throwable: ", e);
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity(e.getMessage()).build());
}
}
}
注意如果filter方法抛出异常,会重定向到/error页面。
@Provider注解是必须的。
ContainerRequestFilter 默认是匹配资源方法后触发的过滤器,如果想要匹配资源前触发增加@PreMatching。
相关源码如下
// execute pre match filters
for (ContainerRequestFilter filter : preMatchFilters) {
filter.filter(requestContext);
if (isAborted(requestContext)) {
sendAbortionToClient(requestContext);
return;
}
}
// match the HTTP request to a resource class and method
JaxrsMethod method = matchMethod(requestContext);
// Execute post match filters
for (ContainerRequestFilter filter : postMatchFilters) {
filter.filter(requestContext);
if (isAborted(requestContext)) {
sendAbortionToClient(requestContext);
return;
}
}
// execute resource class method
method.execute(request);
// execute response filters
for (ContainerResponseFilter filter : responseFilters) {
filter.filter(requestContext, responseContext);
}
@BindingPriority(3):此注解是标志过滤器优先级,方法参数值越大优先级越高。此注解适用在方法上。
与springboot继承后,官方的写法并不会生效,结合与springboot集成说明,增加注册过滤器。
@Component
@ApplicationPath("/rs")
public class JaxrsApplication extends Application{
@Override
public Set<Object> getSingletons() {
Set<Object> singletons = new HashSet<>();
singletons.add(new MyInterceptor());
return singletons;
}
}
@JaxrsCheckLogin 是自定义注解,作用是,绑定此过滤器到添加此注解的资源方法上,可以指定在类上或方法上。写法如下
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface JaxrsCheckLogin {
}
LoginInfoVO 要实现一个静态方法valueOf(String header)
参数是从header中获取的字符串,返回当前类型的对象,就可以在资源方法里自动获取这个对象了
接口方法写法如下:
@Component
@Path("/wado")
@JaxrsCheckLogin
public class WadoRs {
@GET
@Path("/studies")
public Response studies(@PathParam("studyUID") String studyUID,@HeaderParam("userInfo") LoginInfoVO userInfo) {
Response res = null;
return res;
}
}