使用Spring WebFlux WebClient从ADFS获取access_token失败
WebClient调用ADFS认证端点无响应问题排查与解决
我们的代码调用ADFS UI认证端点时,传入grant_type=authorization_code、redirect_uri=<redirect_uri>、client_id=<client_id>、code=<code>这4个参数,用JerseyClient能正常获取access_token,但换成Spring WebClient后完全拿不到响应,即便设置了followredirect(true)也无效,还遇到无法添加敏感请求头的问题,可按以下步骤排查解决:
1. 确保重定向配置正确传递请求头
WebClient默认的重定向策略会丢弃Cookie、Authorization这类敏感头,而ADFS的认证流程可能需要在重定向时保留这些头。可以通过Reactor Netty的HttpClient自定义重定向逻辑:
HttpClient httpClient = HttpClient.create() .followRedirect(true) // 重定向时复制原请求的所有头 .redirect((req, res) -> Redirect.to(req.headers().copy())); WebClient webClient = WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .build();
2. 严格按照application/x-www-form-urlencoded格式提交参数
ADFS的token端点要求参数以表单形式提交,WebClient需要显式设置内容类型并正确构造表单参数,不能直接拼接在URL中:
// 构造表单参数 MultiValueMap<String, String> formParams = new LinkedMultiValueMap<>(); formParams.add("grant_type", "authorization_code"); formParams.add("redirect_uri", "<你的redirect_uri>"); formParams.add("client_id", "<你的client_id>"); formParams.add("code", "<你的授权code>"); // 发送请求 Mono<Map> tokenResponse = webClient.post() .uri("https://你的ADFS域名/adfs/oauth2/token") .contentType(MediaType.APPLICATION_FORM_URLENCODED) .body(BodyInserters.fromFormData(formParams)) .retrieve() .bodyToMono(Map.class); // 订阅获取结果(根据实际业务调整处理逻辑) tokenResponse.subscribe( resp -> System.out.println("获取到的token:" + resp), err -> err.printStackTrace() );
3. 自定义重定向策略处理复杂跳转
如果ADFS存在多次重定向或特殊的跳转逻辑,可以进一步自定义重定向策略,确保所有必要头都被携带:
HttpClient httpClient = HttpClient.create() .followRedirect(true) .redirect((request, response) -> { // 构造重定向请求,复制原请求的所有头信息 HttpRequest redirectedReq = request.mutate() .uri(response.responseHeaders().location()) .headers(headers -> headers.addAll(request.headers())) .build(); return Redirect.to(redirectedReq); });
4. 开启请求日志定位问题
添加日志过滤器,打印WebClient的请求、响应细节,排查是请求未发送、重定向失败还是ADFS返回了未被处理的错误:
WebClient webClient = WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) // 打印请求详情 .filter(ExchangeFilterFunction.ofRequestProcessor(req -> { System.out.println("请求方法:" + req.method() + ",请求地址:" + req.url()); req.headers().forEach((k, v) -> System.out.println(k + ": " + v)); return Mono.just(req); })) // 打印响应详情 .filter(ExchangeFilterFunction.ofResponseProcessor(resp -> { System.out.println("响应状态码:" + resp.statusCode()); resp.headers().asHttpHeaders().forEach((k, v) -> System.out.println(k + ": " + v)); return Mono.just(resp); })) .build();
5. 对比JerseyClient与WebClient的请求差异
用抓包工具(如Fiddler、Wireshark)分别捕获两种客户端的请求,对比请求头、参数编码、重定向流程的差异,找出WebClient缺失的配置(比如Jersey自动添加的特定头信息)。
内容的提问来源于stack exchange,提问作者user2113885
相关产品推荐
相关产品推荐

