You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot传递用户名密码调用RestTemplate API及RestInterceptor 302错误排查

问题1:Spring Boot中使用RestTemplate传递用户名密码调用API

我整理了两种最常用的实现方式,你可以根据目标API的认证方式来选:

方式1:Basic 认证(适用于API采用HTTP Basic Auth的场景)

如果目标API是基于Basic认证的,你可以给RestTemplate配置一个认证拦截器,让它自动把用户名密码加到请求头里:

import org.springframework.http.HttpHeaders;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.security.crypto.codec.Base64;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;
import java.util.Collections;

public class RestTemplateConfig {

    public RestTemplate restTemplateWithBasicAuth(String username, String password) {
        RestTemplate restTemplate = new RestTemplate();
        
        // 构造Basic Auth的请求头
        String auth = username + ":" + password;
        byte[] encodedAuth = Base64.encode(auth.getBytes(StandardCharsets.UTF_8));
        String authHeader = "Basic " + new String(encodedAuth);

        ClientHttpRequestInterceptor interceptor = (request, body, execution) -> {
            request.getHeaders().set(HttpHeaders.AUTHORIZATION, authHeader);
            return execution.execute(request, body);
        };

        restTemplate.setInterceptors(Collections.singletonList(interceptor));
        return restTemplate;
    }
}

使用时直接注入这个配置好的RestTemplate,调用API时会自动带上认证头,不用每次手动处理。

方式2:表单参数传递用户名密码(适用于API接收表单登录的场景)

如果目标API是通过表单字段(比如usernamepassword)接收认证信息的,你可以构造表单参数作为请求体:

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

public class ApiCaller {
    public void callApiWithFormAuth(String apiUrl, String username, String password) {
        RestTemplate restTemplate = new RestTemplate();
        
        // 设置请求头为表单类型
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        // 构造表单参数
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("username", username);
        params.add("password", password);

        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
        
        // 发送POST请求(根据API的实际请求方法调整)
        String response = restTemplate.postForObject(apiUrl, request, String.class);
        // 后续处理响应逻辑...
    }
}

问题2:解决RestInterceptor调用API时出现302重定向的错误

你遇到的<302,[Location:"/TicketingSystem/Index.html", ...]>是典型的重定向响应,大概率是请求没通过目标服务的认证,被强制重定向到登录页面了。下面是具体的排查和解决步骤:

1. 先检查RestTemplate的自动重定向设置

RestTemplate默认是自动跟随重定向的,但如果重定向后的请求没携带原有的认证信息,就会再次被拦截重定向,甚至陷入循环。你可以先关闭自动重定向,手动处理:

import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

public class RestTemplateConfig {
    public RestTemplate restTemplateWithoutAutoRedirect() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        // 关闭自动跟随重定向
        factory.setFollowRedirects(false);
        
        RestTemplate restTemplate = new RestTemplate(factory);
        // 添加上你的RestInterceptor
        return restTemplate;
    }
}

关闭后,你可以在拦截器或者调用代码里主动处理302响应:读取Location头,带着认证信息重新请求这个地址。

2. 确保拦截器在重定向请求中也携带认证信息

如果目标服务的重定向需要保持会话或认证状态,你的拦截器必须保证重定向后的请求也带上认证凭证

  • 如果是Basic认证,前面问题1的拦截器已经能做到对所有请求添加Auth头;
  • 如果是基于Cookie的会话认证,需要把原请求的Cookie传递到重定向请求中,示例代码如下:
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpRequestWrapper;

import java.io.IOException;
import java.net.URI;

public class AuthInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        // 先执行原请求,获取响应
        ClientHttpResponse response = execution.execute(request, body);
        
        // 如果是302重定向,处理Cookie传递
        if (response.getStatusCode() == HttpStatus.FOUND) {
            String redirectUrl = response.getHeaders().getFirst(HttpHeaders.LOCATION);
            if (redirectUrl != null) {
                // 构造新的请求,带上原请求的Cookie和认证信息
                HttpRequest newRequest = new HttpRequestWrapper(request) {
                    @Override
                    public URI getURI() {
                        return URI.create(redirectUrl);
                    }
                };
                // 复制原请求的Cookie到新请求
                newRequest.getHeaders().putAll(request.getHeaders());
                // 关闭原响应,重新执行请求
                response.close();
                return execution.execute(newRequest, body);
            }
        }
        return response;
    }
}

3. 排查认证信息是否有效

302到登录页最常见的原因是原请求的认证信息无效或缺失

  • 检查你的RestInterceptor是否正确添加了认证头(比如Basic Auth的头格式是否正确,用户名密码是否正确);
  • 如果目标服务是基于Session的,确认第一次请求后是否获取了Session Cookie,并在后续请求(包括重定向)中携带;
  • 可以先用Postman手动测试API,传递用户名密码,看看是否会返回302,以此确认是代码问题还是认证信息的问题。

内容的提问来源于stack exchange,提问作者jay

火山引擎 最新活动