Java Spring QueryDsl分页筛选器(按ACL权限)
让我们假设下面的Spring基于JPA的存储库支持QueryDslJava Spring QueryDsl分页筛选器(按ACL权限),java,spring-boot,spring-security,spring-data-jpa,Java,Spring Boot,Spring Security,Spring Data Jpa,让我们假设下面的Spring基于JPA的存储库支持QueryDsl @Repository public interface TeamRepository extends JpaRepository<Team, Long>, QuerydslPredicateExecutor<Team> { } 这位官员建议使用支持分页的@query编写自定义查询 我如何编写这样一个支持QueryDsl谓词、分页和基于权限的过滤的复杂查询 20年3月24日进场 在另一个例子中,我遇到
@Repository
public interface TeamRepository extends JpaRepository<Team, Long>, QuerydslPredicateExecutor<Team> {
}
这位官员建议使用支持分页的@query
编写自定义查询
我如何编写这样一个支持QueryDsl谓词、分页和基于权限的过滤的复杂查询
20年3月24日进场
在另一个例子中,我遇到了以下基于QueryDsl的方法:不是本机查询或自定义查询,而是映射为@Immutable
JPA实体,从而生成Q类并使用它们手动筛选权限
@Entity
@Immutable
@Table(name = "acl_object_identity")
public class AclObjectIdentity implements Serializable {
...
}
如何使用自定义存储库来实现这一点,扩展QueryDslRepositorySupport
,以便查询中检查权限的部分自动附加并隐藏在自定义存储库实现中?基于此,我开发了一种可能性,这种可能性与其说是解决方案,不如说是肮脏的解决方法
方法是向现有谓词(例如由生成的谓词)添加额外的权限筛选器。为此,ACL表必须首先映射为@Immutable
JPA实体,以便QueryDsl可以生成相应的Q类
应附加ACL权限筛选器的此类谓词用以下注释进行标记
public Page<PostDTO> findAll(@QueryDslAclPermission(root = Post.class, permission = "READ") Predicate predicate, Pageable pageable) {
...
}
publicpagefindall(@QueryDslAclPermission(root=Post.class,permission=“READ”)谓词,可分页{
...
}
此注释主要保存有关构建筛选器查询所需的域类型的元信息
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.TYPE})
public @interface QueryDslAclPermission {
Class<?> root();
String permission();
String identifier() default "id";
}
@Retention(RetentionPolicy.RUNTIME)
@目标({ElementType.PARAMETER,ElementType.TYPE})
public@interface QueryDslAclPermission{
类root();
字符串权限();
字符串标识符()默认为“id”;
}
实际的过滤器查询是使用以下类和Spring的
@方面
@组成部分
公共类QueryDslAclPermissionAspect{
私人许可证工厂许可证工厂;
@自动连线
公共查询SlaclPermissionAspect(PermissionFactory PermissionFactory){
this.permissionFactory=permissionFactory;
}
@大约(value=“execution(**(..,@QueryDslAclPermission(*)))
公共对象addPermissionFilter(ProceedingJoinPoint joinPoint)抛出可丢弃的{
MethodSignature=(MethodSignature)joinPoint.getSignature();
Method=signature.getMethod();
Parameter[]parameters=method.getParameters();
Object[]arguments=joinPoint.getArgs();
对于(int index=0;index
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.TYPE})
public @interface QueryDslAclPermission {
Class<?> root();
String permission();
String identifier() default "id";
}
@Aspect
@Component
public class QueryDslAclPermissionAspect {
private PermissionFactory permissionFactory;
@Autowired
public QueryDslAclPermissionAspect(PermissionFactory permissionFactory) {
this.permissionFactory = permissionFactory;
}
@Around(value = "execution(* *(.., @QueryDslAclPermission (*), ..))")
public Object addPermissionFilter(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Parameter[] parameters = method.getParameters();
Object[] arguments = joinPoint.getArgs();
for(int index = 0; index < parameters.length; ++index) {
if(parameters[index].getType().equals(Predicate.class) &&
parameters[index].isAnnotationPresent(QueryDslAclPermission.class)) {
Predicate predicate = (Predicate) arguments[index];
QueryDslAclPermission aclPermission = parameters[index].getAnnotation(QueryDslAclPermission.class);
arguments[index] = addPermissionFilter(predicate, aclPermission);
}
}
return joinPoint.proceed(arguments);
}
private Predicate addPermissionFilter(Predicate predicate, QueryDslAclPermission aclPermission) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(null == authentication || !authentication.isAuthenticated()) {
throw new IllegalStateException("Permission filtering not possible for unauthenticated principal");
}
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
PrincipalSid principalSid = new PrincipalSid(userDetails.getUsername());
NumberPath<Long> idPath = new PathBuilderFactory().create(aclPermission.root())
.getNumber(aclPermission.identifier(), Long.class);
return idPath.in(selectPermitted(aclPermission.root(), principalSid,
permissionFactory.buildFromName(aclPermission.permission()))).and(predicate);
}
private JPQLQuery<Long> selectPermitted(Class<?> targetType, PrincipalSid sid, Permission permission) {
return selectAclEntry(targetType, sid, permission)
.select(QAclEntry.aclEntry.aclObjectIdentity.objectIdIdentity);
}
private JPQLQuery<AclEntry> selectAclEntry(Class<?> targetType, PrincipalSid sid, Permission permission) {
return new JPAQuery<AclEntry>().from(QAclEntry.aclEntry)
.where(QAclEntry.aclEntry.aclObjectIdentity.id.in(selectAclObjectIdentity(targetType)
.select(QAclObjectIdentity.aclObjectIdentity.id))
.and(QAclEntry.aclEntry.aclSid.id.eq(selectAclSid(sid).select(QAclSid.aclSid.id)))
.and(QAclEntry.aclEntry.mask.eq(permission.getMask())));
}
private JPQLQuery<AclObjectIdentity> selectAclObjectIdentity(Class<?> targetType) {
return new JPAQuery<AclObjectIdentity>().from(QAclObjectIdentity.aclObjectIdentity)
.where(QAclObjectIdentity.aclObjectIdentity.objectIdClass.id.eq(selectAclClass(targetType)
.select(QAclClass.aclClass.id)));
}
private JPQLQuery<AclSid> selectAclSid(PrincipalSid sid) {
return new JPAQuery<AclSid>().from(QAclSid.aclSid)
.where(QAclSid.aclSid.sid.eq(sid.getPrincipal()));
}
private JPQLQuery<AclClass> selectAclClass(Class<?> targetType) {
return new JPAQuery<AclClass>().from(QAclClass.aclClass)
.where(QAclClass.aclClass.className.eq(targetType.getSimpleName()));
}
}