如何允许用户在 Spring Boot / Spring Security 中仅访问自己的数据?

Posted

技术标签:

【中文标题】如何允许用户在 Spring Boot / Spring Security 中仅访问自己的数据?【英文标题】:How to allow a User only access their own data in Spring Boot / Spring Security? 【发布时间】:2019-01-13 17:08:20 【问题描述】:

我有一些这样的 api:

/users/user_id
/users/user_id/orders
/users/user_id/orders/order_id

我必须如何保护它们?每个用户只能看到她/他的数据,但管理员可以看到所有数据。

我必须如何在 Spring Security 中实现 Id == 1 的用户无法看到 Id == 2 的用户数据,反之亦然,期望管理员角色的用户可以看到所有内容?

我是否在会话中的每个方法用户 ID 与传递给 api 的 user_id 参数相等之前检查?有没有更好的办法?

p.s:我使用 Spring Security 的 JWT。

【问题讨论】:

【参考方案1】:

在任何@Controller@RestController 注释的bean 中,您都可以直接使用Principal 作为方法参数。

    @RequestMapping("/users/user_id")
    public String getUserInfo(@PathVariable("user_id") Long userId, Principal principal)
        // test if userId is current principal or principal is an ADMIN
        ....
    

如果您不想在 Controllers 中进行安全检查,您可以使用 Spring EL 表达式。 您可能已经使用了一些内置表达式,例如 hasRole([role])

你可以自己写表达式。

    创建bean
    @Component("userSecurity")
    public class UserSecurity 
         public boolean hasUserId(Authentication authentication, Long userId) 
            // do your check(s) here
        
    
    用你的表达方式
    http
     .authorizeRequests()
     .antMatchers("/user/userId/**")
          .access("@userSecurity.hasUserId(authentication,#userId)")
        ...

好消息是您还可以组合以下表达式:

    hasRole('admin') or @userSecurity.hasUserId(authentication,#userId)

【讨论】:

我可以用 AOP 做吗? 如果您在使用自定义表达式时遇到问题,请在“public class UserSecurity”行上方添加“@Component("userSecurity") 但是“在任何 @Controller、@RestController 注释的 bean 中,您都可以直接使用 Principal 作为方法参数。”不管用。校长只是null @ACV 是的.... 主体应该可以访问参考:principal-in-restcontroller @ACV 这里是一个很好的概述spring.io guide: spring-security-architecture,你应该看看Web Security 部分【参考方案2】:

您也可以在服务接口上使用@PreAuthorize。如果您有自定义 userdetails 对象,那么您可以轻松完成。 在我的一个项目中,我是这样做的:

@PreAuthorize(value = "hasAuthority('ADMIN')"
        + "or authentication.principal.equals(#post.member) ")
void deletePost(Post post);

顺便说一句,这是在服务接口中。您必须确保添加正确的注释才能使预授权生效。

【讨论】:

【参考方案3】:

您应该首先选择您的安全策略, 您需要的名称为“Row Filtering”,这是3A(Authentication,Authorization,Audit)概念的授权概念之一。

如果您想实施综合解决方案,请查看:

https://docs.spring.io/spring-security/site/docs/3.0.x/reference/domain-acls.html

Spring ACL 完全涵盖了诸如“行过滤”、“白黑名单”、“角色基础授权”、“ACL 继承”、“角色投票者”等概念。

否则,您应该为每个要保护的业务案例保存所有者,并在您的服务层中过滤它们。

【讨论】:

【参考方案4】:

我们可以创建一个像@IsUser 这样的注解并将其与控制器方法一起使用。

例子:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize(value = "hasRole('ROLE_USER')" + "and authentication.principal.equals(#userId) ")
public @interface IsUser  

【讨论】:

应该是authentication.principal.username.equals(#userId)

以上是关于如何允许用户在 Spring Boot / Spring Security 中仅访问自己的数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring-Boot-REST 调用中获取用户信息?

Spring Boot 2.0详述

允许用户使用 jwt 在 Spring Boot 中选择资源

Spring Boot使用JavaMailSender发送邮件

spring boot + redis 实现session共享分析

如何从 Spring Boot 应用程序属性加载 Spring config xml $ 值