在@PreAuthorize 中使用请求 header 值

Using a request header value in @PreAuthorize

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

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

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

谢谢, 马特

也许你可以试试这个

@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
}

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

由于您打算为每个控制器方法检查一个特定的 header/cookie/request-attribute,您应该选择一个过滤器,因为这将是一个标准,并且您可以保证它对每个方法都执行而且从 OncePerRequestFilter

扩展也只有一次

话虽如此,您可以通过两种方式实现这一目标:

  1. 通过扩展 AbstractAuthenticationProcessingFilterOncePerRequestFilter

    为此,您可以参考 spring-security jwt 令牌验证流程,所有人都提倡:

    • 在您想要的控制器方法中添加方法安全性 @PreAuthorize("hasAuthority('USER_ROLE')")
    • 拦截 UsernamePasswordAuthenticationFilter 之前的请求,从请求中提取 Authentication header 或 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. dm-tr 所述,使用 @RequestHeader 在 rest 控制器中访问 HTTP Headers