【问题标题】:How to allow a User only access their own data in Spring Boot / Spring Security?如何允许用户在 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。

【问题讨论】:

    标签: spring rest spring-boot spring-security spring-security-oauth2


    【解决方案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])

    你可以自己写表达式。

    1. 创建bean
        @Component("userSecurity")
        public class UserSecurity {
             public boolean hasUserId(Authentication authentication, Long userId) {
                // do your check(s) here
            }
        }
    
    1. 用你的表达方式
        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)
        猜你喜欢
        • 2022-07-05
        • 1970-01-01
        • 2015-05-25
        • 2015-07-22
        • 1970-01-01
        • 2015-10-09
        • 2017-09-15
        • 2021-10-23
        • 2014-09-02
        相关资源
        最近更新 更多