【问题标题】:How to declaratively use authorization with JWT in Spring 5 controller?如何在 Spring 5 控制器中声明性地使用 JWT 授权?
【发布时间】:2021-04-28 07:30:18
【问题描述】:

有一个简单的 Web 服务,只有一个端点 "GET /hello"。
最好在控制器中以声明方式描述 JWT 是预期的,以便从中提取有关发出请求的授权用户的一些数据。

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

显然这个问题是微不足道的,并且与 Spring Security 的基本功能有关,但我不能把这个谜团放在一起。

请帮我找到一种将 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);
    }
}

【问题讨论】:

  • 您需要的一切都在这里:baeldung.com/get-user-in-spring-security 如果您想按原样读取 JWT(在 base64 中)添加 HttpServletRequest 请求作为方法参数:(公共对象 getRequests(,HttpServletRequest 请求) ) 并调用 request.getHeader("authorization");那么你将直接拥有 JWT
  • 另一种直接访问标头的选项(使用@RequestHeader 注解):stackoverflow.com/a/54911059/3942132
  • @RoieBeck 好的,我们将标题作为字符串。接下来怎么解析呢?
  • jwt.io给你一个java所有框架的列表:),github.com/auth0/java-jwtmaven包就是一个例子,在github页面搜索“Decode a Token”,真的很简单。每个库都有自己解码 JWT 的方法,您也可以自己对 jwt 主体进行 Base64.decode(JWT 的第二部分也称为有效负载):),作为一般规则,我认为您需要先更好地了解 JWT使用它...

标签: java spring-boot spring-security jwt spring-security-oauth2


【解决方案1】:

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

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

只要快速谷歌一下,发现这个example 可能对你有帮助。

【讨论】:

  • 谢谢陈健。我相信这个例子可以引导我正确的方式。一般来说,每个应用程序都应该从头开始实现自己的 AuthenticationProvider,您不会觉得奇怪,尽管 auth 的领域非常标准化。我希望在 SpringBoot 启动器中至少看到这个“JWT 关注点”的基本实现。在我看来,对于 WebFlux 来说,同样的东西会有额外的麻烦。
【解决方案2】:

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

  1. 如何将 JWT 字符串解析成对象?
  2. 如何将对象传递给控制器​​方法?
  3. 在 sping boot 中执行此操作的标准/默认方式是什么?

以下是简短的答案:

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

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

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

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

重点是:

  • A.使用ServerSecurityContextRepository的实现进行配置

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

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

【讨论】:

    猜你喜欢
    • 2020-04-10
    • 1970-01-01
    • 1970-01-01
    • 2018-07-19
    • 1970-01-01
    • 1970-01-01
    • 2019-08-12
    • 2014-02-20
    相关资源
    最近更新 更多