簡體   English   中英

使用 Spring 中的每個請求獲取 OAuth2/OIDC 訪問令牌

[英]Get the OAuth2/OIDC access token with every request in Spring

我正在嘗試為以前的單用戶系統啟用多租戶。 該應用程序過去在本地服務器上運行,並且內置了一個相對簡單的前端。

現在我想在雲環境下讓多個用戶同時使用。 我繼續使用 OIDC 和 PKCE 實施 Auth2,以將用戶重定向到外部 Auth Provider。 我現在想要的是,對於每個請求,用戶將他的訪問令牌與請求一起發送,以便我決定提供哪些數據作為答案。

我不知道如何獲取該數據,因為 spring 框架(默認情況下)似乎只隨請求發送 ID 令牌。 我懷疑我的軟件同時是客戶端和資源服務器這一事實與它有關。

這是我的第一個問題,所以如果我忘記了什么,我很樂意修改或擴展我的問題。

我到目前為止所做的嘗試:

我已經使用 Postman 來驗證這三個令牌(ID 令牌、刷新令牌和訪問令牌)是否正確頒發並且可以使用我的憑據檢索。

我嘗試從請求本身獲取訪問令牌。 但是,controller 中包含令牌的任何參數(如@AuthenticationPrincipal OidcUser oidcUser )僅顯示 ID 令牌,而不顯示訪問令牌。

通過OAuth2AuthorizedClientService獲取令牌也不起作用,同樣的問題,因為我只能獲取 ID 令牌,而不能獲取訪問令牌。


更新 #1,2022 年 12 月 13 日/11:40:我使用 PingIdentity 的 PingOne 作為身份驗證提供程序,以下依賴項與此問題相關或可能相關或有幫助:

  • spring-boot-starter-web
  • 彈簧啟動啟動器安全
  • spring-boot-starter-百里香葉
  • 彈簧啟動啟動器網絡服務
  • spring-boot-starter-oauth2-資源服務器

你能在你的項目中提供一些授權提供者信息和相關依賴嗎?

對我來說,當基於 spring 安全時,使用 Keycloak 作為 auth provider(它是一個開源身份和訪問管理解決方案)。 獲取令牌和 idtoken 很容易,因為官方提供了安全適配器。 以上是例子:

    @GetMapping("/info")
    public Response<Map<String, Object>> user(@AuthenticationPrincipal KeycloakAuthenticationToken principal, HttpServletRequest request) {
        principal.getAccount().getKeycloakSecurityContext().getIdToken();
        principal.getAccount().getKeycloakSecurityContext().getToken();
    }

其實就是通過自定義的AuthenticationProvider來設置授權信息,這樣我們就可以方便的獲取信息。 我認為模仿適配器實現等類似操作是個好主意。

資源服務器安全過濾器鏈

提醒一下,資源服務器是使用 OAuth2 保護的 REST API。 @RestController@Controller@ResponseBody的所有端點都應使用包含http.oauth2ResourceServer()SecurityFilterChain進行保護。 使用 Spring 配置資源服務器的最簡單方法是使用spring-boot-starter-oauth2-resource-server (或者我圍繞它編寫的非常薄的包裝器之一,可以從具有 0 java conf 的屬性配置它)

默認情況下,使用此類過濾器鏈,Spring 使用AbstractOAuth2TokenAuthenticationToken<?>的子類填充安全上下文。 您可以從中檢索訪問令牌。 至少有 2 個選項可以訪問此身份驗證實例:

  • SecurityContextHolder.getContext().getAuthentication()
  • 讓 Spring 自動神奇地注入AbstractOAuth2TokenAuthenticationToken<?> auth作為控制器方法參數

您還可以使用@RequestHeader(value = HttpHeaders.AUTHORIZATION) String authorizationHeader將原始授權標頭作為控制器方法參數注入。

我在本教程中介紹了配置資源服務器安全過濾器鏈的各種方法。

客戶端安全過濾器鏈

@Controller返回模板名稱並使用 OAuth2 保護的端點是客戶端。 配置 Spring 客戶端的最簡單方法是使用spring-boot-starter-oauth2-clienthttp.oauth2Login()

請注意,在此配置中,瀏覽器和 Spring 客戶端之間的請求不是 OAuth2(它最常使用會話 cookie 進行保護,而不是Authorization標頭中的Bearer訪問令牌)。 只有 Spring 客戶端(在服務器上)發送到資源服務器的請求才受到 OAuth2 的保護。

使用客戶端安全過濾器鏈,安全上下文填充有OAuth2AuthenticationToken ,它有意公開 ID 令牌而不是訪問令牌。 提醒一下,ID 令牌持有用戶身份數據,旨在供客戶端在訪問令牌受眾是資源服務器時使用,並且專為訪問控制而設計。 客戶端應將訪問令牌視為黑盒,並僅使用它來授權他們對資源服務器的請求(設置Bearer Authorization標頭)。

您可以從OAuth2AuthorizedClient獲取訪問令牌字符串: authorizedClient.getAccessToken().getTokenValue() ,它本身是從OAuth2AuthorizedClientService檢索的,您可以在控制器中自動連接: authorizedClientService.loadAuthorizedClient("your-client-id", auth.getName())auth是通過SecurityContextHolder或控制器方法參數注入從安全上下文中檢索到的OAuth2AuthenticationToken實例)

如果您需要授權從客戶端到資源服務器的WebClient請求,您可以做比檢索訪問令牌和位置授權標頭更簡單的事情:按照我對您問題的評論中已鏈接的教程UiController進行操作:

final var authorizedClient = authorizedClientService.loadAuthorizedClient("spring-addons-public", auth.getName());
final var response = webClient.get().uri("http://localhost:8080/api/greet")
                    .attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))
                    ...

WebClient 配置如下:

    @Bean
    WebClient webClient(ClientRegistrationRepository clientRegistrationRepository,
            OAuth2AuthorizedClientService authorizedClientService) {
        var oauth = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
                new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository,
                        authorizedClientService));
        oauth.setDefaultClientRegistrationId("spring-addons-public");
        return WebClient.builder().apply(oauth.oauth2Configuration()).build();
    }

感謝那些試圖幫助我的人,但最終我自己弄明白了。

我通過兩個屬性擴展了我的控制器: OAuth2AuthenticationToken authenticationHttpServletRequest request

控制器方法簽名

另外,我在OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository@Autowired

自動裝配的客戶端存儲庫

然后允許以下調用返回 accessToken 的值: (oAuth2AuthorizedClientRepository.loadAuthorizedClient(myClientRegistrationId, authentication, request)).client.getAccessToken().getTokenValue(); .

之后,它只是解析令牌並使用JWTParser.parse()檢索值並從JWT -result 提供的方法。

個人注意事項:不要在您的控制器中執行解析和檢索值部分,以將任何更復雜的邏輯排除在外。

我希望這對某人有幫助!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM