[英]Spring OAuth2 Generate Access Token per request to the Token Endpoint
[英]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 安全時,使用 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()
AbstractOAuth2TokenAuthenticationToken<?> auth
作為控制器方法參數您還可以使用@RequestHeader(value = HttpHeaders.AUTHORIZATION) String authorizationHeader
將原始授權標頭作為控制器方法參數注入。
我在本教程中介紹了配置資源服務器安全過濾器鏈的各種方法。
@Controller
返回模板名稱並使用 OAuth2 保護的端點是客戶端。 配置 Spring 客戶端的最簡單方法是使用spring-boot-starter-oauth2-client
和http.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 authentication
和HttpServletRequest 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.