Spring Web客户端Windows NTLM身份验证

uqdfh47h  于 2022-12-10  发布在  Spring
关注(0)|答案(1)|浏览(181)

我找不到任何好的示例或文档来清楚地解释这一点。我可以成功地使用(旧的)RestTemplate进行身份验证:

HttpClientBuilder httpClient = HttpClients.custom();
BasicCredentialsProvider provider = new BasicCredentialsProvider();
Credentials cred = new NTCredentials("my-user", "my-password", null, "my-domain");

provider.setCredentials(AuthScope.ANY, cred);
httpClient.setDefaultCredentialsProvider(provider);

HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.httpClient = httpClient.build();

RestTemplate restTemplate = new RestTemplate(request);

restTemplate.getForEntity("https://my.url.com", String.class)

我一直无法找到将NTCredentials(或Credentials)传递到WebClient的方法,已尝试

WebClient client = WebClient.builder()
.filter(ExchangeFilterFunctions.basicAuthentication("user", "password"))
.build();

也可以

WebClient client = WebClient.builder().build();
client.get().headers(h -> h.setBasicAuth("user", "password"))...

但这两种方法都不适用于WebClient

问题:

1.如何使用Spring WebClient进行Windows/NTLM身份验证?
1.当使用当前用户上下文在Windows中运行时,是否有任何方法可以在不提供用户/密码的情况下获得NTLM或Windows身份验证?

qoefvg9y

qoefvg9y1#

我最近一直在寻找Spring WebClient的NTLM。首先我尝试使用Apache HttpClient,但请求时没有得到响应。我没有进一步调查原因...
由于我无论如何都想使用netty HttpClient,所以我使用JCIFS实现(与Apache HttpClient使用的相同)实现了我自己的ExchangeFilterFunction

public final class NtlmAuthorizedClientExchangeFilterFunction implements ExchangeFilterFunction {

    private final NtlmPasswordAuthentication ntlmPasswordAuthentication;
    private final boolean doSigning;

    public NtlmAuthorizedClientExchangeFilterFunction(String domain, String username, String password, boolean doSigning, int lmCompatibility) {
        this.ntlmPasswordAuthentication = new NtlmPasswordAuthentication(domain, username, password);
        this.doSigning = doSigning;
        System.setProperty("jcifs.smb.lmCompatibility", Integer.toString(lmCompatibility));
    }

    @Override
    public Mono<ClientResponse> filter(final ClientRequest request, final ExchangeFunction next) {
        NtlmContext ntlmContext = new NtlmContext(ntlmPasswordAuthentication, doSigning);
        try {
            return next.exchange(addNtlmHeader(request, ntlmContext.initSecContext(new byte[0], 0, 0)))
                .publishOn(Schedulers.single()) // this is necessary to make sure that the requests are processed sequential and thus http keep alive is working
                .flatMap(clientResponse -> {
                    List<String> ntlmAuthHeaders = getNtlmAuthHeaders(clientResponse);
                    if (ntlmAuthHeaders.isEmpty()) return Mono.error(...);
                    String ntlmHeader = ntlmAuthHeaders.get(0);
                    if (ntlmHeader.length() <= 5) return Mono.error(...);
                    try {
                        byte[] type2 = Base64.decode(ntlmHeader.substring(5));
                        return next.exchange(addNtlmHeader(request, ntlmContext.initSecContext(type2, 0, type2.length)));
                    } catch (IOException e) {
                        return Mono.error(...);
                    }
                });
        } catch (SmbException e) {
            return Mono.error(...);
        }
    }

    @NotNull
    private static List<String> getNtlmAuthHeaders(ClientResponse clientResponse) {
        List<String> wwwAuthHeaders = clientResponse.headers().header(HttpHeaders.WWW_AUTHENTICATE);
        List<String> ntlmAuthHeaders = wwwAuthHeaders.stream().filter(h -> h.startsWith("NTLM")).sorted(Comparator.comparingInt(String::length)).collect(Collectors.toList());
        return ntlmAuthHeaders;
    }

    private ClientRequest addNtlmHeader(ClientRequest clientRequest, byte[] ntlmPayload) {
        ClientRequest.Builder request = ClientRequest
            .from(clientRequest)
            .header(HttpHeaders.AUTHORIZATION, "NTLM ".concat(Base64.encode(ntlmPayload)));
        return request.build();
    }

我无法回答你的第二个问题。我的系统是Linux。

相关问题