Jersey框架中的鉴权措施详解
0x00 前言
Jersey是一个开源的RESTful Web服务框架,实现了JAX-RS规范,提供了创建RESTful Web服务的API。与SpringMvc类似,Jersey也提供了过滤器和拦截器功能,可用于实现鉴权等业务需求。
0x01 常见鉴权措施
1.1 过滤器
Jersey过滤器是javax.ws.rs.container.ContainerRequestFilter和javax.ws.rs.container.ContainerResponseFilter接口的实现,分别用于处理HTTP请求和响应。
基本使用
ContainerRequestFilter接口包含一个方法:
public void filter(ContainerRequestContext requestContext) throws IOException;
示例鉴权过滤器:
public class AuthenticationFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) {
if (!isAuthenticated(requestContext)) {
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
}
}
private boolean isAuthenticated(ContainerRequestContext requestContext) {
String authHeader = requestContext.getHeaderString("Authorization");
// 验证逻辑
return true;
}
}
注册过滤器:
@Component
public class AppConfig extends ResourceConfig {
AppConfig() {
register(AuthenticationFilter.class);
}
}
@PreMatching注解
在Filter上使用@PreMatching可以在请求匹配之前执行过滤器,修改请求资源或影响匹配方法:
@PreMatching
public class AuthenticationFilter implements ContainerRequestFilter {
// 实现
}
1.1.1 UriInfo处理
通过ContainerRequestContext获取UriInfo处理路径信息:
UriInfo uriInfo = requestContext.getUriInfo();
常用方法:
getAbsolutePath(): 获取请求的绝对路径getPath(): 获取请求的路径部分getRequestUri(): 返回表示完整请求URI的URI对象getPathSegments(): 返回路径各段的字符串值List
注意:默认情况下Jersey不会对请求path进行解码操作,路径穿越符../也不会处理,但会处理;矩阵参数。/admin/manage和/admin/manage/访问的是同一个资源。
1.1.2 ResourceInfo处理
ResourceInfo可获取处理请求的资源类和方法:
@Context
private ResourceInfo resourceInfo;
// 获取资源类
Class<?> resourceClass = resourceInfo.getResourceClass();
// 获取资源方法
Method resourceMethod = resourceInfo.getResourceMethod();
注意:使用@PreMatching时无法通过ResourceInfo获取资源信息。
1.1.3 @NameBinding使用
名称绑定机制允许创建自定义注解与过滤器关联:
- 定义注解:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@NameBinding
public @interface RequireAdminRole {}
- 创建过滤器:
@Provider
@RequireAdminRole
public class AdminAuthFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) {
if (!isAdmin(requestContext)) {
requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build());
}
}
}
- 在资源方法上使用注解:
@Path("/admin")
@RequireAdminRole
public class AdminResource {
// 资源方法
}
1.1.4 动态绑定
实现DynamicFeature接口动态配置过滤器匹配规则:
public class AdminDynamicFeature implements DynamicFeature {
@Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
if (resourceInfo.getResourceClass().getName().contains("Admin")) {
context.register(AdminAuthFilter.class);
}
}
}
1.2 自定义注解
方法拦截器实现
- 定义注解:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@NameBinding
public @interface Login {}
- 创建拦截器:
@Login
public class AuthenticationInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
// 鉴权逻辑
return Response.ok().entity("未登陆,无权限访问").build();
}
}
- 实现
InterceptionService:
public class AuthInterceptor implements InterceptionService {
private static Map<Annotation, MethodInterceptor> map = new HashMap<>();
static {
Annotation[] annotations = AuthenticationInterceptor.class.getAnnotations();
for (Annotation annotation : annotations) {
map.put(annotation, new AuthenticationInterceptor());
}
}
@Override
public List<MethodInterceptor> getMethodInterceptors(Method method) {
List<MethodInterceptor> list = new ArrayList<>();
for (Annotation annotation : method.getAnnotations()) {
if (map.get(annotation) != null) {
list.add(map.get(annotation));
}
}
return list;
}
// 其他方法实现...
}
JAX-RS安全注解
Jersey提供标准安全注解:
@PermitAll@DenyAll@RolesAllowed
示例@RolesAllowed使用:
@RolesAllowed("ADMIN")
@Path("/secure")
public class SecureResource {
// 资源方法
}
在过滤器中检查:
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
Method method = resourceInfo.getResourceMethod();
if (method.isAnnotationPresent(RolesAllowed.class)) {
RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
Set<String> rolesSet = new HashSet<>(Arrays.asList(rolesAnnotation.value()));
if (!isUserAllowed(currentUser, rolesSet)) {
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED)
.entity("无权限访问").build());
}
}
}
1.3 鉴权框架
Jersey基于Servlet规范构建,可与Shiro、SpringSecurity等主流鉴权框架集成。但需注意:
- Jersey默认不对请求path解码
- 不处理路径穿越符
../ - 处理
;矩阵参数 /path和/path/访问同一资源
风险提示:低版本Shiro与Jersey路径解析差异可能导致权限绕过(CVE-2023-34478)。
0x02 其他
Jersey拦截器:
ReaderInterceptor: 操作请求Body的Entity数据流WriterInterceptor: 操作响应Body的Entity数据流
可用于数据压缩/解压缩、格式转换等。
审计要点
- 检查
UriInfo路径匹配是否存在规范化问题导致的绕过 - 验证动态绑定的匹配逻辑是否合理全面
- 确认过滤器与拦截器的执行顺序是否可能导致权限问题
- 检查与第三方安全框架集成时的路径解析差异
- 验证
@NameBinding注解是否覆盖所有需要保护的方法