2013-02-27 54 views
9

我试图使用注释和spring-security将方法级别安全性添加到我的开源项目中。我现在面临的问题是findAll方法,特别是寻呼的方法(例如返回一个页面)。Spring数据JPA和spring-security:数据库级别的过滤器(尤其是分页)

使用@PostFilter在列表上工作(但我个人认为它不是一个好主意在应用程序而不是数据库中过滤),但在分页查询时完全失败。

这是有问题的,因为我有一个包含List<Compound>的实体。化合物有不同的实现,用户可能只有权读取其中一个化合物。复合使用TABLE_PER_CLASS继承。知识库实施QueryDslPredicateExecutor

我的想法是为每个限制基于当前用户的返回结果的查询添加一个谓词。然而,我很迷茫,a)用户和角色的数据模型应该如何看待,以及b)如何创建谓词(一旦模型定义,这可能很容易)。或者querydsl已经提供基于类型的过滤(包含在被查询的类中的元素)?

+0

对于问题甲见[用户架构(http://static.springsource.org/spring-security/site/docs/3.2.x/reference/springsecurity-single.html#d0e8380])部分 – 2013-02-27 11:33:46

+0

我的意思所以我可以调整查询以考虑当前用户的角色,例如。从给定的实体到角色和从角色到用户必须有一种关系。 – 2013-02-27 13:56:03

+0

然后查看ACL http://static.springsource.org/spring-security/site/docs/3.2.x/reference/springsecurity-single.html#domain-acls和适当的数据库模式http:// static。 springsource.org/spring-security/site/docs/3.2.x/reference/springsecurity-single.html#dbschema-acl – 2013-02-27 14:07:48

回答

2

暂时想出了以下解决方案。由于我的项目相当简单,因此这可能无法用于更复杂的项目。

  1. 用户可以读取全部或没有某一类

的任何因此查询方法可以用含有hasRole@PreAuthorize进行注释的实体。

这个例外是我项目中的Container实体。它可以包含Compound的任何子类,并且用户可能没有权限查看它们全部。他们必须是过滤器。

为此,我创建了UserRole实体。 CompoundRole有一对一关系,该角色是Compound的“read_role”。 UserRole有一个ManyToMany关系。

@Entity 
public abstract class Compound {  
    //... 
    @OneToOne  
    private Role readRole; 
    //... 
} 

我所有的仓库实现QueryDSLPredicateExecutor这里变得很有一手。我们不是在存储库中创建自定义的findBy方法,而是仅在服务层创建它们,并使用repositry.findAll(predicate)repository.findOne(predicate)。谓词包含实际的用户输入+“安全过滤器”。

@PreAuthorize("hasRole('read_Container'") 
public T getById(Long id) {   
    Predicate predicate = QCompoundContainer.compoundContainer.id.eq(id); 
    predicate = addSecurityFilter(predicate); 
    T container = getRepository().findOne(predicate);   
    return container; 
} 

private Predicate addSecurityFilter(Predicate predicate){   
    String userName = SecurityContextHolder.getContext().getAuthentication().getName();    
    predicate = QCompoundContainer.compoundContainer.compound.readRole 
     .users.any().username.eq(userName).and(predicate);   
    return predicate; 
} 

注意:QCompoundContainer是由QueryDSL生成的“元模型”类。

最后,你可能需要将QueryDSL路径初始化从ContainerUser

@Entity 
public abstract class CompoundContainer<T extends Compound> 
    //... 
    @QueryInit("readRole.users") // INITIALIZE QUERY PATH 
    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, 
      targetEntity=Compound.class) 
    private T compound; 
    //... 
} 

忽略这最后一步可能导致NullPointerException

进一步提示:CompoundService自动设置保存角色:

if (compound.getReadRole() == null) { 
    Role role = roleRepository.findByRoleName("read_" + getCompoundClassSimpleName()); 
    if (role == null) { 
     role = new Role("read_" + getCompoundClassSimpleName()); 
     role = roleRepository.save(role); 
    } 
    compound.setReadRole(role); 
} 
compound = getRepository().save(compound) 

这工作。缺点是有点显而易见。同一个Role与同一个Compound类实现的每个实例相关联。

6

目前没有这样的支持,但我们在路线图上有。您可能想要按照DATACMNS-293获得一般进展。