我有一个用Sping Boot RestControllers编写的API,用keycloak保护。我正在一个单独的Sping Boot 应用程序中编写一个Thymeleaf客户端来使用这个API。客户端的浏览器成功登录到keycloak,并且在控制器上,我能够通过@AuthenticationPrincipal访问OAuth2 AuthenticatedPrincipal。
如何配置RestTemplate以使用这个已经建立的信任关系,而不是为每个RestTemplate建立一个新的信任关系?
下面的代码是使用在浏览器中经过身份验证的用户成功地通过API进行身份验证的代码。
控制器
@Controller
@RequestMapping("/product")
public class ProductController {
private final RestTemplate restTemplate;
private final ProductService productService;
private final ClientRegistrationRepository clientRegistrationRepository;
// only needed to validate registration during debug
private final InMemoryClientRegistrationRepository clientRegistrationRepository;
public ProductController(RestTemplate restTemplate, ProductService productService,
ClientRegistrationRepository clientRegistrationRepository,
InMemoryClientRegistrationRepository clientRegistrationRepository) {
this.restTemplate = restTemplate;
this.productService = productService;
this.clientRegistrationRepository = clientRegistrationRepository;
this.clientRegistrationRepository = clientRegistrationRepository;
}
@GetMapping("")
@PreAuthorize("isAuthenticated()")
public String index(@AuthenticationPrincipal OAuth2AuthenticatedPrincipal principal,
Authentication auth,
HttpServletRequest servletRequest,
Model model) {
OAuth2AuthorizedClient accessToken = clientRepository.loadAuthorizedClient("keycloak-confidential-user",
auth, servletRequest);
log.debug("accessToken is null [{}]", accessToken == null);
model.addAttribute("products",
productService.getProductWithDetailsForUser(UUID.fromString(principal.getName()), accessToken));
return "product/list";
}
字符串
服务方式
public List<ProductListInfo> getProductWithDetailsForUser(UUID userId, String token) {
List<ProductListInfo> products = productRepository.findByUser_UniqueUserOrderByNameAsc(userId,
Pageable.ofSize(10));
if(token != null) {
header.setBearerAuth(token);
for (ProductListInfo product : products) {
ProductPublicDto publicData = restTemplate.exchange(
"https://localhost:8043/product/%s/public".formatted(product.getProductId()),
HttpMethod.GET, new HttpEntity<>(header), ProductPublicDto.class).getBody();
productMapper.partialUpdateDetails(publicData product);
}
}
return products;
}
型
pom.xml片段
...
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
...
型
1条答案
按热度按时间krcsximq1#
如果您的应用程序使用Thymeleaf控制器配置为OAuth2客户端,则其他使用REST API的应用程序应配置为OAuth2资源服务器,并且可能是无状态的(无会话)。
对资源服务器的请求使用承载访问令牌而不是会话进行授权。在客户端中,配置REST客户端(
RestTemplate
并不是很流行,您可能会看一下WebClient
或@FeignClient
)来设置一个Authorization
头,其中包含一个Bearer
字符串,其中包含从OAuth2AuthorizedClient
获得的访问令牌。(您可以在OAuth2客户端控制器中自动连接OAuth2AuthorizedClientRepository
,并查询它以检索所需的OAuth2AuthorizedClient
)。我有一个完整的工作示例
WebClient
(不是RestTemplate
,对不起)there。在本教程中,客户端和资源服务器部分合并在单个应用程序中,但这两个部分具有不同的SecurityFilterChain
bean,并与REST客户端通信。内部请求使用Bearer访问令牌进行授权,这可能是您所需要的。