Spring数据休息findBy securityCheck嵌套实体

时间:2017-12-14 11:17:29

标签: java spring-boot spring-data-rest

我在我的REST接口上使用Spring-data-rest,并且已经在我的暴露端点上实现了自定义安全检查。一切正常,但现在我遇到了一些非常重要的场景,我想知道Spring数据是否能够解决这个问题。

我有以下型号:

@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;

  ...
}

在获取周期时,我会检查登录用户是否与我的周期实体中的userId具有相同的ID。

我已经实现了这样的cystom安全检查:

@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());
  }
}

但是现在,我正在尝试对记录实施类似的安全检查。因此,在获取给定周期的记录时,我需要检查周期的userId是否与身份验证对象中的id匹配。

我的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);
}

是否可以使用我在此securityCheck中使用此方法查询的ID来访问循环内的userId?如果没有,那么实现此功能的Spring方法是什么?

很抱歉,我的问题不明确。如果有必要进一步解释,请告诉我。

编辑: 我通过在后置过滤器中访问返回的页面找到了快速而又脏的解决方案。缺点是当返回的数组为空时我可以访问不属于我登录用户的记录(所以我仍然在寻找更优雅的解决方案)

@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());
  }

3 个答案:

答案 0 :(得分:1)

如果我理解正确的话......

首先,确保已启用SpEL EvaluationContext extension

然后做这样的事情:

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);
    }
}

我认为principal包含userId的字符串表示,所以在这里我将其转换为long然后比较它们......

您也可以查看与此问题相关的example ...

<强>更新

而不是SpEL表达式中的@RecordRepository.toLong(principal)尝试使用T(java.lang.Long).valueOf(principal)并从您的回购中删除toLong方法。

答案 1 :(得分:0)

如果您只需要在检查userId与您的登录用户相同时获得“Cycle”的特定记录,我只会使用以下内容:

RewriteEngine on
RewriteBase /
RewriteRule ^(.+)\.php$ /$1 [R,L]
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.*?)/?$ /$1.php [NC,END]

JPA自己处理很多东西而无需手动创建查询。请参阅https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods处的查询方法。

当然这意味着需要使用 Spring Security 而不是Model端在Controller端处理安全检查。您的DAO不应该关心请求是否有效/授权,因为这些检查是事先完成的。

干杯

答案 2 :(得分:0)

您可以在应用程序中引入两个处理程序,如下所示: -

//
//  ViewController.swift
//  LoadLocalHtml
//
//  Created by Farhad on 9/20/16.
//  Copyright © 2016 WebInDream. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var webView: UIWebView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

    loadHtmlFile()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func loadHtmlFile() {
        let url = Bundle.main.url(forResource: "index", withExtension:"html")
        let request = NSURLRequest(url: url!)
        webView.loadRequest(request as URLRequest)
        webView.scrollView.bounces = false;
        webView.isOpaque = false;
    }

}