简体   繁体   English

Spring Data Rest查找通过安全性检查嵌套实体

[英]Spring data rest findBy securityCheck for nested entity

I am using Spring-data-rest for my REST interface and I have implemented custom security checks on my exposed endpoints. 我将Spring-data-rest用于REST接口,并且在暴露的端点上实现了自定义安全检查。 Everything works fine but now I came across some non-trivial scenario and I am wondering if spring data rest is capable of solving this. 一切正常,但是现在我遇到了一些不平常的情况,我想知道spring数据剩余是否能够解决这个问题。

I have the following model: 我有以下模型:

@Entity
public class Cycle {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(name = "unique_cycle_id")
  private long id;

  @Column(name = "user_id")
  private long userId;

  ...

  @OneToMany(cascade = CascadeType.ALL)
  @JoinTable(name = "cycles_records", joinColumns = @JoinColumn(name = "unique_cycle_id"),
      inverseJoinColumns = @JoinColumn(name = "unique_record_id"))
  private List<Record> records;
}

@Entity
public class Record {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(name = "unique_record_id")
  private long id;

  ...
}

When fetching a cycle, I check that the logged-in user has the same id as the userId in my Cycle entity. 在获取周期时,我检查登录用户的ID是否与我的Cycle实体中的userId相同。

I have implemented cystom security check like this: 我已经实现了像这样的膀胱囊肿安全检查:

@Slf4j
@Component
public class SecurityCheck {

  public boolean check(Record record, Authentication authentication) {
    log.debug("===Security check for record===");
    if (record == null || record.getCycle() == null) {
      return false;
    }

    return Long.toString(record.getCycle().getUserId()).equals(authentication.getName());
  }

  public boolean check(Cycle cycle, Authentication authentication) {
    if (cycle == null) {
      return false;
    }

    return Long.toString(cycle.getUserId()).equals(authentication.getName());
  }
}

But now, I am trying to implement a similar security check for records. 但是现在,我正在尝试对记录进行类似的安全检查。 So when fetching records for a given cycle, I need to check that the cycle's userId is matching the id in the authentication object. 因此,在获取给定周期的记录时,我需要检查周期的userId是否与身份验证对象中的id匹配。

I have the following method on my RecordRepository: 我的RecordRepository上有以下方法:

@Repository
public interface RecordRepository extends JpaRepository<Record, Long> {

      @PreAuthorize("hasRole('ROLE_ADMIN') OR @securityCheck.check(???, authentication)")
      Page<Record> findByCycle_Id(@Param("id") Long id, Pageable pageable);
}

Is it possible to access the userId inside the cycle with the id I am querying with this method inside this securityCheck? 是否可以在此securityCheck中使用此方法查询的id访问周期内的userId? If not, what would be the correct Spring way of achieving this functionality? 如果没有,Spring实现此功能的正确方法是什么?

Sorry it my question is not clear. 抱歉,我的问题不清楚。 Let me know if further explanation is necessary. 让我知道是否需要进一步说明。

EDIT: I found quick and dirty solution by accessing the returned page in a post-filter. 编辑:我通过访问后过滤器中返回的页面找到了快速又肮脏的解决方案。 The downside is that I can access records not belonging to my logged-in user when the returned array is empty (so I am still looking for some more elegant solution) 缺点是,当返回的数组为空时,我可以访问不属于登录用户的记录(因此,我仍在寻找更优雅的解决方案)

@PostAuthorize("hasRole('ROLE_ADMIN') OR @securityCheck.check(returnObject, authentication)")
Page<Record> findByCycle_Id(@Param("id") Long id, Pageable pageable);

public boolean check(Page<Record> page, Authentication authentication) {
    log.debug("===Security check for page===");
    if (!page.hasContent()) {
      return true;
    }

    long userId = page.getContent().get(0).getCycle().getUserId();

    return Long.toString(userId).equals(authentication.getName());
  }

If I've understood you correctly... 如果我正确理解你的话...

First, make sure that SpEL EvaluationContext extension is enabled. 首先,确保启用了SpEL EvaluationContext扩展

Then do something like this: 然后执行以下操作:

public interface RecordRepository extends JpaRepository<Record, Long> {

    @Query("select r from Record r join r.cycle c where c.id = ?1 and (c.userId = ?#{@RecordRepository.toLong(principal)} or 1 = ?#{hasRole('ROLE_ADMIN') ? 1 : 0})")
    Page<Record> findByCycleId(Long id, Pageable pageable);

    default Long toLong(String name) {
        return Long.valueOf(name);
    }
}

I suppose that principal contains the string representation of userId so here I convert it to long then compare them... 我想这个principal包含userId的字符串表示形式,因此在这里我将其转换为long形式然后进行比较...

Also you can check my example related to this issue... 您也可以检查我与此问题相关的示例 ...

UPDATE 更新

Instead of @RecordRepository.toLong(principal) in SpEL expression try to use T(java.lang.Long).valueOf(principal) and delete toLong method from your repo. 相反的@RecordRepository.toLong(principal)在规划环境地政司表达尝试用T(java.lang.Long).valueOf(principal) ,并删除toLong从您的回购方法。

If you just needed to get a specific record of "Cycle" while checking that the userId is the same as your logged user, i would just use something like: 如果您只需要在检查userId与您登录的用户相同的同时获取“周期”的特定记录,我将使用类似以下内容的方法:

Page<Record> findByCycle_IdAndUserId(Long cycleId, Long userId, Pageable pageable);

JPA handles a lot of stuff by itself without having you to manually create queries. JPA本身可以处理很多工作,而无需您手动创建查询。 See query method at https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods . 请参阅https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods中的查询方法。

Of course this means the security checks need to be handled on the Controller side with Spring Security instead of the Model side. 当然,这意味着安全检查需要在控制器端通过Spring Security而不是模型端进行处理。 Your DAOs should not have to care if the request is valid/authorized because those checks are done beforehand. 您的DAO不必担心请求是否有效/授权,因为这些检查是事先完成的。

Cheers 干杯

You can introduce two handlers into your application like below:- 您可以在应用程序中引入两个处理程序,如下所示:-

  //This is your Request handler

public class CxfHttpRequestHandler extends AbstractPhaseInterceptor<Message> {


    public CxfHttpRequestHandler() {
        super("marshal");
    }

    @Override
    public void handleMessage(final Message message) throws Fault {
        //handle your all request here
       }
}

//this is your Response handler
public class CxfHttpResponseHandler extends AbstractPhaseInterceptor<Message> {

    public CxfHttpResponseHandler() {
        super("pre-stream");
    }

    @Override
    public void handleMessage(final Message message) throws Fault {
        //handle your outgoing message here
    }
}


Add the following  in your spring-bean.xml file:-

<bean id="cxfHttpRequestHandler" class="com.yourpackage.CxfHttpRequestHandler" >
        <description>
            This Bean implements Interceptor for all request
        </description>
</bean>

<bean id="cxfHttpResponseHandler" class="com.yourpackage.CxfHttpResponseHandler">
        <description>
            This Bean implements Interceptor for all response
        </description>
    </bean>

<cxf:bus>
        <cxf:inInterceptors>
            <ref bean="cxfHttpRequestHandler" />
        </cxf:inInterceptors>

        <cxf:outInterceptors>
            <ref bean="cxfHttpResponseHandler" />
        </cxf:outInterceptors>

</cxf:bus>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM