【问题标题】:Spring Boot: Who to write a custom Prefilter, considering the user role?Spring Boot:考虑到用户角色,如何编写自定义 Pre Filter?
【发布时间】:2019-03-13 05:39:53
【问题描述】:

我需要某种@preFilter(或者,如果不可能,则比@postFilter)来过滤我的REST API 的结果。我不能使用preFilterannotation,因为我需要考虑用户角色。我有三个不同的角色:

  • user 普通用户,只能访问自己拥有的数据

  • teamleader这个角色应该访问他团队的所有数据

  • admin 可以访问所有数据。

因为我们的数据库结构非常复杂,所以在我决定用户是否可以访问请求的数据或请求的部分数据之前,有必要访问一些其他数据。

sn-p 仅适用于角色useradmin。对于teamleader来说会更复杂,然后会有一堆masterDataId必须和or连接。

这是一些伪代码,希望它不会混淆:

public class RoleFilter {

    DimensionAttributeValueRepository dimensionAttributeValueRepository;


    public void doFilter(Collection<AllDatas> data) {
        if (user.getRole() != "admin") {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();

            DimensionAttributeValue tmpValue = dimensionAttributeValueRepository.findByChrValue(auth.getUsername());

            MasterData masterData = tmpValue.getMasterData();

            data.filter(data.masterDataId == masterData.getMasterDataID());
        }
    }
}

更新:示例

假设我有两个用户,用户 A 是角色“用户”的普通用户。用户 B 是具有“admin”角色的管理员。

有一个数据库表,其中存储了userData。该表如下所示。

| ID | username | name | email |

他们都向/userData发送了一个简单的认证GET请求。

现在我的后端根据authentication 标头检测用户并添加角色。

根据角色,用户 A 应该只得到一个包含他的个人数据的答案,用户 B 应该得到所有可以通过/userData 访问的数据。

用户 A 的响应:

{
   "res":[
      {
         "id":1,
         "username":"userA",
         "name":"A",
         "email":"userA@mail.com"
      }
   ]
}

用户 B 的响应:

{
   "res":[
      {
         "id":1,
         "username":"userA",
         "name":"A",
         "email":"userA@mail.com"
      },
      {
         "id":2,
         "username":"userB",
         "name":"B",
         "email":"userB@mail.com"
      },
      {
         "id":3,
         "username":"userC",
         "name":"C",
         "email":"userC@mail.com"
      }
   ]
}

【问题讨论】:

    标签: rest security spring-boot filter user-roles


    【解决方案1】:

    对于您的用例,我建议使用自定义过滤器并将其集成到 spring-security 过滤器链中。 Here 是一个教程,一般性地解释它。您可以配置自定义过滤器,以便它根据数据库检查您的复杂角色,然后用新的身份验证对象覆盖当前用户身份验证对象。

    示例实现:

    public class CustomFilter extends GenericFilterBean {
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
        throws IOException, ServletException {
                // HERE GOES YOUR CODE
    
                // Depending on the extracted authentication details of the current user, you can now overwrite the users GrantedAuthorities
    
                Collection<SimpleGrantedAuthority> oldAuthorities = (Collection<SimpleGrantedAuthority>)SecurityContextHolder.getContext().getAuthentication().getAuthorities();
                SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_TEAMLEADER");
                List<SimpleGrantedAuthority> updatedAuthorities = new ArrayList<SimpleGrantedAuthority>();
                updatedAuthorities.add(authority);
                updatedAuthorities.addAll(oldAuthorities);
    
                SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(     
                    SecurityContextHolder.getContext().getAuthentication().getPrincipal(),      
                    SecurityContextHolder.getContext().getAuthentication().getCredentials(),
                    updatedAuthorities));
    
                chain.doFilter(request, response);
        }
    }
    

    之后,您可以使用以下语句检查您的角色:@PreAuthorize("hasRole('ROLE_TEAMLEADER')")

    然后您可以在spring-security-context 对象的帮助下访问用户角色:SecurityContextHolder.getContext().getAuthentication().getAuthorities()

    根据其结果,您现在可以根据存储在此对象中的角色自定义您的答案。例如,您可以像这样在 /userData 上实现 RestCall:

    @GetMapping("/userData")
    public List<Object> getUserData() {
      List<SimpleGrantedAuthority> roles = (List<SimpleGrantedAuthority>) SecurityContextHolder.getContext().getAuthentication().getAuthorities();
      SimpleGrantedAuthority authorityTeamLeader = new SimpleGrantedAuthority("ROLE_TEAMLEADER");
    
      List<Object> result = new ArrayList<>();
    
      if (roles.contains(authorityTeamLeader)) {
        result = getAllUsers();
      } else {
        result = getPersonalUser(roles);
      }
    
      return result;
    }
    

    【讨论】:

    • 您好,感谢您的回答。但是,如果我对您的理解正确,这不会解决我的问题。我知道用户角色,问题是,我喜欢根据角色返回一组不同的数据。
    • 更新了我的答案:)
    • 再次感谢,但仍无法找到解决方案。让我再澄清一下这个问题。我有那个用户角色,我也实现了类似的东西,比如你的代码确实检查了哪种用户发送了请求。我将用一个例子更新我的原始帖子。感谢您尝试帮助我!
    • 我已经添加了示例
    • 我添加了另一个自定义 Get-Controller 的示例实现
    猜你喜欢
    • 2022-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-05
    • 2022-01-10
    • 2011-07-21
    • 2016-10-03
    • 2016-03-22
    相关资源
    最近更新 更多