Spring Security - 基于所有权的访问

Posted

技术标签:

【中文标题】Spring Security - 基于所有权的访问【英文标题】:Spring Security - Ownership based access 【发布时间】:2019-11-14 05:01:16 【问题描述】:

假设我有一个像下面这样的最小 RESTful 控制器,顺便说一下使用 Java 8 和 Spring Boot 2,...

@RestController
class MyController 

    @Autowired
    private PostService service;    

    @GetMapping
    public Post get() 

        return service.getLatest();
    

我已经使用 Spring Security 模块成功地保护了这条路线。现在我想只允许资源所有者访问该资源。对于资源所有者,我的意思是创建者或简单地说:

Post myPost = new Post();
...
myPost.getCreator().equals(currentUser); // Should be true when access is granted

我发现了很多关于基于角色的访问,但几乎没有检查所有权...当然我可以在控制器内放置一个 if 语句并抛出异常,但我打算使用类似 Spring 的 Expression - 基于访问控制

还有其他想法吗?有人对资源的所有权检查有一个好主意或例子吗?

【问题讨论】:

认为(但我不确定),“基于所有者的访问”被称为“自主访问控制” i> 因为所有者始终具有访问权限,并且所有者决定还有谁可以访问该资源。将此与“强制访问控制” 进行对比,系统策略确定谁有权访问资源。在强制访问控制系统中,资源的所有者/创建者可能无权访问该资源。 【参考方案1】:

在基于角色的方法中,无论响应/资源如何,都预先定义了角色。但是在您的情况下,我们需要先获取资源以检查用户是否是所有者。我们无法事先决定。

假设,如果用户有 ROLE_ADMIN,他将是所有资源的管理员。 在您的情况下,所有权完全取决于资源。我建议您使用手动if 检查。如果Post 对象很重,您可以维护一个单独的表,其中只有两列resource_id 和owner。然后如果所有者匹配,您可以稍后获取资源,否则您可以向用户发送 403 禁止响应。

【讨论】:

是的,正如我已经提到的,我试过这个,但是对于许多路线和不同的资源,你必须编写很多样板代码......【参考方案2】:

对于简单的获取操作,您只需返回链接到当前登录用户的帖子

@GetMapping
public Post getPost(Authentication authentication) 
    return service.getPostByUser(authentication.getName());

要更新现有帖子,您可以在 PreAuthorize 中检查创建者是否是登录用户。 authentication.getName() 在我的示例中返回一封电子邮件

@PutMapping
@PreAuthorize("#post.getCreator() == authentication.getName()")
public void update(@RequestBody Post post, Authentication authentication) 
    service.updatePost(post);

@Component 方式的基本示例

@Autowired
private CreatorCheck creatorCheck;

@PutMapping
@PreAuthorize("@creatorChecker.check(#post,authentication)")
public void update(@RequestBody Post post, Authentication authentication) 
    service.updatePost(post);

并且组件..可以扩展以检索原始帖子并检查该创建者..

@Component
public class CreatorCheck 

    public boolean check(Post post, Authentication authentication) 
       return post.getCreator().equals(authentication.getName());
    

如需更全面的教程,请查看0x1C1B 找到的此tutorial 链接

【讨论】:

但这意味着客户端在请求中设置了帖子的创建者,对吗?如何防止它操纵创作者?假设使用它自己的 id 来授予访问权限......或者对于 DELETE 请求,没有 DTO/payload 作为参数给出...... 您可以编写另一个@Component public CreatorChecker 类来处理检查。 @PreAuthorize("@creatorChecker.check(#post,authentication)") 在这个组件中,您从数据库中检索帖子并检查创建者 service.getPostId().getCreator() == authentication.getName() 就像这样跨度> 这个组件在哪里使用?作为过滤器链的一部分?这是一种常见的方法吗?我没有发现任何类似的东西......但似乎是一个有趣的解决方案 它将以与一些默认内容(例如 hasRole('FOO') )相同的方式进行评估,因为它只是需要评估的不同表达式。 Spring Docs Link 这是我最喜欢的解决方案...我找到了一个基本的example,关于使用外部组件进行授权。您能否更新您的答案以便我接受?【参考方案3】:

Here 是一个帮助我解决这个问题的小指南。 如果您使用 CRUD 存储库:

@Override
@PreAuthorize("#entity.id == authentication.principal.id")
<S extends User> S save(S entity);

对于资源所有者和管理员:

@PreAuthorize("#entity.id == authentication.principal.id || hasAuthority('ADMIN')")

所以,只需在您的子 CRUD 存储库中覆盖所需的方法。它适用于@RepositoryRestResource

【讨论】:

以上是关于Spring Security - 基于所有权的访问的主要内容,如果未能解决你的问题,请参考以下文章

使用spring security进行身份验证后如何根据角色进行重定向[重复]

Swagger + Spring security - 基于角色隐藏方法

Spring Security基于Oauth2的SSO单点登录怎样做?一个注解搞定

使用 @PreAuthorize 的 GraphQL 和 Spring Security?

Tomcat重启后基于spring security java的配置

Spring Security 实现最好没有弹簧启动