【问题标题】:Using a request header value in @PreAuthorize在 @PreAuthorize 中使用请求标头值
【发布时间】:2021-01-02 20:44:13
【问题描述】:

是否可以在@PreAuthorize 中使用请求标头值?

在我的应用中,所有请求都包含一个自定义标头,我需要将其与用户角色一起使用,以确定是否应允许它们访问控制器。

如果有人手动指定标头是可以的,因为这不会是安全问题,因为最终角色将控制它。但我需要使用它来减少在每个控制器方法中手动检查的次数。

谢谢, 马特

【问题讨论】:

  • @Controller ? @RestController?
  • 抱歉,是的,在 @RestController 类中
  • 如果你想为每个调用执行这样的请求,为什么不写一个过滤器??
  • 很多控制器方法都需要这个,但不是全部...... @JuanBC。谢谢大家的回答和回复!

标签: spring spring-boot spring-security spring-expression-language


【解决方案1】:

1 - 如果您只在少数地方使用它,这可能是最快的方法。

@GetMapping(value = "/private-api-method")
@PreAuthorize("#request.getHeader('header-name') == 'localhost:8080'")
public ResponseEntity<String> privateApiMethod(HttpServletRequest request) {
    return ResponseEntity.ok("OK!");
}

OR

@GetMapping(value = "/private-api-method")
@PreAuthorize("#header == 'localhost:8080'")
public ResponseEntity<String> privateApiMethod(@RequestHeader("header-name") String header) {
    return ResponseEntity.ok("OK!");
}

2 - 如果您将在许多地方使用它,这可能是最好的方法。 (在SecurityServise中,可以添加多种不同的检查方法。)

@GetMapping(value = "/private-api-method")
@PreAuthorize("@securityService.checkHeader(#request)")
public ResponseEntity<String> privateApiMethod(HttpServletRequest request) {
    return ResponseEntity.ok("OK!");
}

3 - 你可以为自己选择一种特殊的方法

A Custom Security Expression with Spring Security

【讨论】:

    【解决方案2】:

    由于您打算为每个控制器方法检查特定的标头/cookie/请求属性,因此您应该选择过滤器,因为这将是一个标准,您可以保证每个方法都执行它从OncePerRequestFilter扩展也只有一次

    话虽如此,有两种方法可以实现:

    1. 通过扩展AbstractAuthenticationProcessingFilterOncePerRequestFilter

      为此,您可以参考大家都提倡的 spring-security jwt 令牌验证流程:

      • 在所需的控制器方法中添加方法安全性为@PreAuthorize("hasAuthority('USER_ROLE')")
      • UsernamePasswordAuthenticationFilter 之前拦截请求,从请求中提取 Authentication 标头或 cookies 并验证声明的令牌值。
    public class CustomHeaderAuthFilter extends AbstractAuthenticationProcessingFilter{
    
    @Override
      public Authentication attemptAuthentication(
          HttpServletRequest request, HttpServletResponse response){
    
    // Get all the headers from request, throw exception if your header not found    
    Enumeration<String> reqHeaders =  request.getHeaderNames();
    
        Assert.notNull(reqHeaders, "No headers found. Abort operation!");
    
        Collections.list(reqHeaders)
            .stream()
            .filter(header_ -> header_.equals("TARGET_HEADER_NAME"))
    .findAny().ifPresent(header_ -> {
              // header found, would go for success-andler
        });
        
        // Here it means request has no target header
        SecurityContextHolder.clearContext();
        failureHandler.onAuthenticationFailure(request, response, new CustomException(""));
      }
    
    }
    

    通过这种方式,您需要使用WebSecurityConfigurerAdapter 注册您的过滤器,如果您从AbstractAuthenticationProcessingFilter 扩展,您还可以提供您的AuthenticationProvider

    1. 通过使用@RequestHeader 访问rest 控制器中的HTTP Headers,正如dm-tr 提到的那样。

    【讨论】:

      【解决方案3】:

      也许你可以试试这个

      @PreAuthorize("hasAuthority('ROLE_SOMETHING')")
      @RequestMapping("PATH")
      public void checkIt(@RequestHeader("header-name") String header) {
          if (null != header /* && header meets certain condition*/) {
              // stuff
          } else throw new ResponseStatusException(HttpStatus.FORBIDDEN); // PERMISSION NOT GRANTED, 403 ERROR
      }
      

      【讨论】:

      • 谢谢!将这种方法与此处的 SEL 检查结合起来,我很好奇这是否可行:@PreAuthorize(@myEvaluationService.checkSecurity(#header, hasAuthority(ROLE_SOMETHING)))——这不完全是因为语法肯定是错误的,但是看起来可能可以外部化到服务。我会深入研究并在帖子上回复它的进展情况。再次感谢您的帮助!
      • 把我的评论/* &amp;&amp; header..... */换成你的情况@myEvaluationService.checkSecurity(header)
      猜你喜欢
      • 2016-09-23
      • 2021-09-23
      • 2017-06-10
      • 2019-11-21
      • 2018-10-04
      • 2023-03-17
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      相关资源
      最近更新 更多