如何在 Spring 5 控制器中以声明方式使用 JWT 授权?

How to declaratively use authorization with JWT in Spring 5 controller?

有一个具有一个端点的简单 Web 服务 "GET /hello"
最好在控制器中声明性地描述需要 JWT 以便从中提取有关发出请求的授权用户的一些数据。

探索 Github 上的一些开源项目,我发现 @AuthenticationPrincipal 注释以某种方式参与了该过程。然而,none 我设法找到的教程提到了这种声明性方法——它们主要展示了如何创建 JWT,而不是如何处理 JWT。
如果您指出我遗漏的值得注意的例子,我将不胜感激。

显然这个问题是微不足道的,并且与 Spring 安全的基本功能有关,但我无法将这个难题放在一起。

请帮我找到一种正确(自然)的方式将 JWT 传递到控制器并从中获取数据
您能否分享一个带有依赖项的工作示例和一个展示如何在控制器中使用 JWT 的小测试?

SpringBoot 2.4.0

import org.springframework.   ???   .Jwt;
import org.springframework.security.core.annotation.AuthenticationPrincipal;


@RestController 
public class MyController {
 
    @GetMapping("hello")
    public Object getRequests(@AuthenticationPrincipal Jwt jwt) {
      
      String name = getPropertyFromJwt(jwt, "name");
      String id = getPropertyFromJwt(jwt, "id");
      
      return Map.of("name", name, "id", id);
    }
}

要成功验证,请求必须由 AuthenticationProvider 验证。一旦成功验证,它将 return 一个包含主体对象的 Authentication 对象。

@AuthenticationPrincipal 只是帮助访问这个原则对象。但是,spring-security 没有提供开箱即用的 JWT 认证的 AuthenticationProvider,这意味着您必须自己实现。在您自定义的 AuthenticationProvider 中,您验证 JWT 并解码您感兴趣的 属性 并创建一个包含这些属性的自定义主体对象,以便您可以通过 @AuthenticationPrincipal.[= 访问它们17=]

快速 google 发现这个 example 可能对您有所帮助。

深入研究后,我发现问题包括 三:

  1. 如何将JWT字符串解析为对象?
  2. 如何将对象传递给控制器​​方法?
  3. 在 sping boot 中 standard/default 方法是什么?

以下是简短的回答:

  1. 使用您喜欢的任何库将编码的 JWT 转换为对象。
    其中之一是 com.auth0:java-jwt.

  2. 注解为@AuthenticationPrincipal的参数可以是任意类型X 它来自 currentSecurityContext.getAuthorization().getPrincipal() 其中 currentSecurityContextorg.springframework.security.core.context.SecurityContext.
    类型 在 WebFlux 配置中,它来自 ServerSecurityContextRepository.load(...) 的调用 对于每个请求(见下面的实现)。

  3. 我没能找到#3 的答案,但我意识到 这个问题有几个开源的临时实现(好的和坏的)。 毕竟我最终实现了另一个只是为了理解陷阱。

请找到一个示例最小工作项目here

重点是:

  • 一个。使用 ServerSecurityContextRepository

    实现的配置
    @EnableWebFluxSecurity
    public class SecurityConfig {
    
      @Bean
      SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        ServerSecurityContextRepository repo = createContextRepository();
    
        return http
                   .authorizeExchange(e -> e.anyExchange().authenticated())
                   .securityContextRepository(repo)
                   .build();
       }
    }
    
  • 乙。创建 Authentication 接口和函数(或函数链)ServerWebExchange -> Authentication 的实现,该函数应应用于 ServerSecurityContextRepository.load 方法并插入 SecurityContext(即 SecurityContextImpl ) 每个服务器请求。

  • C。 Principal 可以是您选择的任何对象。 Return 它来自 Authentication.getPrincipal() 在 (B) 中创建的实现方法。