如何让@PropertySource访问受Spring Security保护的远程配置?
你遇到的401错误,核心原因是**@PropertySource默认的资源加载逻辑不会自动解析URL中的user:xxx@格式认证信息并添加到请求头**,而curl会自动处理这种格式生成Basic认证头,所以curl能正常访问,而@PropertySource的请求没带认证信息,被Spring Security拦截返回401。
另外你提到的“默认无法访问子文件夹配置”是误解,Spring Cloud Config客户端原生支持拉取子目录下的配置,不需要手动用@PropertySource硬编码URL。
方案一:改用Spring Cloud Config客户端原生配置(推荐)
这是Spring官方推荐的方式,无需手动处理认证和URL,客户端会自动传递认证信息到Config服务。
在客户端的bootstrap.yml(Spring Boot 3+可直接用application.yml)中添加以下配置:
spring: cloud: config: # Config服务地址 uri: http://localhost:8888 # Config服务的认证账号密码 username: user password: xxx # 对应Config服务中配置仓库的应用名(即你之前的ConfigRepo) name: ConfigRepo # 环境profile profile: dev # Git分支/标签 label: main # 拉取子目录下的额外配置文件 additional-config-locations: "config:classpath:/db/database.properties"
客户端启动时会自动从Config服务拉取ConfigRepo-dev-main.properties以及db/database.properties的配置,且认证信息会被正确传递,不会出现401。
方案二:自定义PropertySourceFactory处理认证
如果你一定要用@PropertySource,可以自定义资源加载工厂,手动解析认证信息并添加到请求头:
- 编写自定义工厂类:
import org.springframework.core.io.EncodedResource; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.core.io.support.DefaultPropertySourceFactory; import org.springframework.core.io.support.PropertySourceFactory; import org.springframework.core.env.PropertySource; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Base64; public class AuthenticatedHttpPropertySourceFactory implements PropertySourceFactory { @Override public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException { if (resource.getResource() instanceof UrlResource urlResource) { URL url = urlResource.getURL(); String userInfo = url.getUserInfo(); if (userInfo != null && userInfo.contains(":")) { String[] credentials = userInfo.split(":", 2); String authHeader = "Basic " + Base64.getEncoder().encodeToString((credentials[0] + ":" + credentials[1]).getBytes(StandardCharsets.UTF_8)); // 创建带认证头的连接 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestProperty("Authorization", authHeader); // 包装资源,返回带认证的连接 Resource authenticatedResource = new UrlResource(url) { @Override public URLConnection getURLConnection() throws IOException { return connection; } }; return new DefaultPropertySourceFactory().createPropertySource(name, new EncodedResource(authenticatedResource)); } } return new DefaultPropertySourceFactory().createPropertySource(name, resource); } }
- 在
@PropertySource中指定该工厂:
@PropertySource( value = "http://user:xxx@localhost:8888/ConfigRepo/dev/main/db/database.properties", ignoreResourceNotFound = true, factory = AuthenticatedHttpPropertySourceFactory.class )
这样@PropertySource会通过自定义工厂添加Basic认证头,就能正常访问Config服务的受保护资源。
额外注意点
你的Config服务Spring Security配置中,antMatchers("/ConfigRepo").permitAll()只放行精确匹配/ConfigRepo的请求,而实际请求的是/ConfigRepo/dev/main/...这类子路径,这些请求会被要求认证,这是正确的配置——只要请求带正确的认证头就能正常访问。
内容的提问来源于stack exchange,提问作者user3593261

