Spring Security 如何配置RestTemplate使用浏览器的session进行API调用?

bsxbgnwa  于 2023-08-05  发布在  Spring
关注(0)|答案(1)|浏览(162)

我有一个用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>
...

krcsximq

krcsximq1#

如果您的应用程序使用Thymeleaf控制器配置为OAuth2客户端,则其他使用REST API的应用程序应配置为OAuth2资源服务器,并且可能是无状态的(无会话)。
对资源服务器的请求使用承载访问令牌而不是会话进行授权。在客户端中,配置REST客户端(RestTemplate并不是很流行,您可能会看一下WebClient@FeignClient)来设置一个Authorization头,其中包含一个Bearer字符串,其中包含从OAuth2AuthorizedClient获得的访问令牌。(您可以在OAuth2客户端控制器中自动连接OAuth2AuthorizedClientRepository,并查询它以检索所需的OAuth2AuthorizedClient)。
我有一个完整的工作示例WebClient(不是RestTemplate,对不起)there。在本教程中,客户端和资源服务器部分合并在单个应用程序中,但这两个部分具有不同的SecurityFilterChain bean,并与REST客户端通信。内部请求使用Bearer访问令牌进行授权,这可能是您所需要的。

相关问题