代码之家  ›  专栏  ›  技术社区  ›  Joker

如何仅为用户自己的端点启用请求

  •  1
  • Joker  · 技术社区  · 7 年前

    /用户/{userId}/something

    我使用oauth2实现了身份验证。 我的Web安全配置如下所示:

    protected void configure(HttpSecurity http) throws Exception {
        http
        .authorizeRequests()
        .anyRequest().authenticated()
        .and()
        .formLogin()
        .loginPage("/login").permitAll();
    }
    

    我怎样才能只允许用户访问他们自己的端点(例如Id为100的用户只能访问他们自己的端点) /users/100/something )如果没有Being,则可以看到另一个端点(如 /users/200/something )?

    这可能吗?

    1 回复  |  直到 7 年前
        1
  •  2
  •   Glains    7 年前

    有很多方法可以解决这个问题,但是我选择了三种方法来解决这个问题。

    自定义安全表达式

    我建议使用自定义的基于安全性的注释方法。这将涉及实现自定义安全表达式、相关的表达式处理程序和方法安全配置。如果这对您来说工作量太大,那么下一种方法就简单一点。

    public class UserIdentityMethodSecurityExpressionRoot 
        extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
    
        public UserIdentityMethodSecurityExpressionRoot(Authentication authentication) {
            super(authentication);
        }
    
        public boolean userIdentity(Long userId) {
            User user = ((UserPrincipal) this.getPrincipal()).getUser();
            return user.getId() == userId;
        }
    }
    

    然后,可以使用新创建的安全表达式对Rest端点或服务方法进行注释:

    @PreAuthorize("userIdentity(#userId)")
    @GetMapping
    @ResponseBody
    public Resource fineOne(@PathVariable Long userId) {
        return resourceService.findOne(id);
    }
    

    请注意 userId 必须提供 在某处 @PathVariable 或 @RequestParam 用户ID 403 否则

    here 并且在这个问题中已经根据您的目的进行了修改。

    斯佩尔

    您还可以使用SpEL,它稍微简单一点:

    @PreAuthorize("#userId == principal.getId()")
    @GetMapping
    @ResponseBody
    public Resource fineOne(@PathVariable Long userId) {
        return resourceService.findOne(id);
    }
    

    其他考虑

    您还可以自己完成所有工作并获得更快的结果,而无需使用定义自定义表达式 SecurityContextHolder

    public static void checkUserIdentity(Long userId) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        // user did not provide a token
        if(auth == null) {
            throw new AccessDeniedException(); 
        }      
        UserDetails details = (UserDetails) auth.getPrincipal();
        if(userId != details.getId()) {
            throw new AccessDeniedException(); 
        }
    } 
    

    并且像这样使用它:

    @GetMapping
    @ResponseBody
    public Resource fineOne(@PathVariable Long userId) {
        SecurityUtils.checkUserIdentity(userId)
        return resourceService.findOne(id);
    }
    

    为什么这样做有效?这个 SecurityContextHolder