Java 实现这一功能主要有两种场景:

- 客户端验证:Java 应用程序作为客户端,去访问需要 Windows 身份验证的资源(如 HTTP 资源、网络共享文件夹)。
- 服务器端验证:Java 应用程序作为服务器(如 Tomcat),接收来自 Windows 客户端的请求,并验证其提供的 Windows 凭据。
下面我们分别对这两种场景进行详细说明。
客户端验证(Java 应用访问 Windows 资源)
这是最常见的需求,你的 Java 程序需要访问一个配置了 Windows 身份验证的 IIS 网站,或者访问一个网络共享盘。
访问配置了 Windows 身份验证的 HTTP/HTTPS 资源
当使用 HttpURLConnection 或第三方库(如 Apache HttpClient, OkHttp)访问一个 IIS 网站时,如果该站点配置了“集成 Windows 身份验证”,标准的 HTTP 请求会收到一个 401 Unauthorized 响应,其中包含一个 WWW-Authenticate 头,提示使用 Negotiate 或 NTLM 协议。
关键点:

Negotiate协议:这是首选,它会自动在 Kerberos 和 NTLM 之间选择,如果客户端和服务器在同一个 Active Directory (AD) 域内,并且配置正确,它会使用更安全、性能更好的 Kerberos。NTLM协议:一种较旧的挑战-响应协议,不依赖于域控制器,但在跨平台和非域环境下兼容性更好。
实现方式:
由于 Java 标准库不直接支持 NTLM/Kerberos,通常需要借助第三方库。
示例:使用 Apache HttpClient (推荐)
HttpClient 对 NTLM 和 SPNEGO (Kerberos 的上层协议) 有很好的支持。

添加 Maven 依赖:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version> <!-- 使用较新稳定版本 -->
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient-cache</artifactId>
<version>4.5.14</version>
</dependency>
编写 Java 代码:
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.NTCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
public class WindowsAuthHttpClient {
public static void main(String[] args) {
// 目标 URL,配置了 Windows 身份验证的 IIS 网站
String url = "http://your-windows-server/protected-site";
// --- 关键配置 ---
// 1. 当前登录 Windows 的用户名
String username = System.getProperty("user.name");
// 2. 当前登录 Windows 的域名
// 如果是本机账户,域名通常是计算机名,如果是域账户,是域名。
// 你也可以手动指定,如 "YOURDOMAIN"
String domain = System.getenv("USERDOMAIN"); // 从环境变量获取
// 3. 密码 (通常为 null,让 JVM 使用当前登录用户的凭据)
String password = null;
// 创建凭证提供者
BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
// NTCredentials 用于 NTLM 认证
credsProvider.setCredentials(
new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT),
new NTCredentials(username, password, "", domain));
// 创建 HttpClient,并设置凭证提供者
try (CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.build()) {
HttpGet httpGet = new HttpGet(url);
System.out.println("Executing request " + httpGet.getRequestLine() + " to " + url);
HttpResponse response = httpClient.execute(httpGet);
System.out.println("Response status: " + response.getStatusLine());
if (response.getEntity() != null) {
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("Response body (first 200 chars): " + responseBody.substring(0, Math.min(200, responseBody.length())));
}
EntityUtils.consume(response.getEntity());
} catch (Exception e) {
e.printStackTrace();
}
}
}
代码解释:
System.getProperty("user.name")和System.getenv("USERDOMAIN")是获取当前 Windows 用户信息的标准方法。NTCredentials是HttpClient中用于 NTLM 认证的凭证类,密码设为null是关键,这会指示 JVM 使用当前登录用户的 安全上下文(Security Context),即用户登录时输入的密码,无需在代码中硬编码。HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build()创建了一个会自动使用这些凭证的 HttpClient。
访问网络共享文件夹 (SMB)
Java 访问网络共享文件夹通常使用 JCIFS 或 SmbJ 库,SmbJ 是目前更现代和活跃的选择。
添加 Maven 依赖 (SmbJ):
<dependency>
<groupId>com.hierynomus</groupId>
<artifactId>smbj</artifactId>
<version>0.11.5</version> <!-- 使用较新稳定版本 -->
</dependency>
编写 Java 代码:
import com.hierynomus.smbj.SMBClient;
import com.hierynomus.smbj.auth.AuthenticationContext;
import com.hierynomus.smbj.connection.Connection;
import com.hierynomus.smbj.session.Session;
import com.hierynomus.smbj.share.DiskShare;
import com.hierynomus.smbj.SMBException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
public class WindowsAuthSMB {
public static void main(String[] args) {
String server = "your-file-server"; // 文件服务器 IP 或主机名
String shareName = "SharedFolder"; // 共享文件夹名
String remotePath = "test.txt"; // 要访问的文件
String domain = System.getenv("USERDOMAIN");
String username = System.getProperty("user.name");
SMBClient client = new SMBClient();
try (Connection connection = client.connect(server)) {
if (connection == null) {
throw new RuntimeException("Failed to connect to " + server);
}
// 使用当前登录用户的凭据进行认证
AuthenticationContext ac = new AuthenticationContext(username, null, domain);
try (Session session = connection.authenticate(ac)) {
System.out.println("Authentication successful.");
try (DiskShare share = (DiskShare) session.connectShare(shareName)) {
System.out.println("Connected to share: " + shareName);
// 现在可以进行文件操作,如 list, exists, openFile 等
boolean fileExists = share.fileExists(remotePath);
System.out.println("File " + remotePath + " exists: " + fileExists);
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException | SMBException e) {
e.printStackTrace();
} finally {
client.close();
}
}
}
代码解释:
- 和 HttpClient 一样,
AuthenticationContext的密码也设为null,让 SmbJ 使用当前用户的登录凭据。 - 这需要 Java 进程以用户的身份运行,而不是作为服务运行(除非服务配置了“允许服务与桌面交互”并登录了特定用户,但这不推荐)。
服务器端验证(Java 应用接收 Windows 认证)
当你的 Java Web 应用(如运行在 Tomcat 上的应用)需要接收来自 Windows 客户端的请求,并验证这些客户端的 Windows 身份时,你需要配置 Web 服务器(如 Tomcat)来处理这个流程。
核心流程:
- 客户端(如 Windows 上的 IE/Edge)访问你的 Java Web 应用。
- Tomcat(或其前端的反向代理如 IIS/Apache)收到请求,并要求客户端进行 Windows 身份验证。
- 客户端将其 Windows 凭据(通过 NTLM/Kerberos 协议)发送给服务器。
- 服务器(Tomcat)将这些凭据转发给你的 Java 应用。
- Java 应用需要解析这些凭据,并向 Windows 域控制器进行验证。
实现方式:
这通常需要一个 Servlet 过滤器来拦截请求,并处理身份验证,一个强大的库是 Spring Security,它极大地简化了这个过程。
示例:使用 Spring Security 实现
项目配置 (Spring Boot + Spring Security):
创建一个 Spring Boot 项目,并添加 spring-boot-starter-security 依赖。
配置 Spring Security:
你需要配置一个 SecurityFilterChain 来启用 Windows 身份验证,Spring Security 内置了对 RequestHeaderAuthenticationFilter 的支持,该过滤器可以从特定的 HTTP 头中提取用户信息。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.header.writers.ClearSiteDataHeaderWriter;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 1. 关闭 CSRF,因为通常这些是内部服务到服务的调用
http.csrf().disable();
// 2. 配置 URL 权限
http.authorizeRequests()
.antMatchers("/public/**").permitAll() // 公开访问的路径
.anyRequest().authenticated(); // 其他所有路径都需要认证
// 3. 添加 Windows 身份验证过滤器
// 这个过滤器会从请求头中提取用户名
http.addFilterBefore(new WindowsAuthenticationFilter(), BasicAuthenticationFilter.class);
}
// 如果你使用 Spring Security 5.6+,推荐使用这种方式
/*
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector);
http
.csrf().disable()
.authorizeRequests()
.requestMatchers(mvcMatcherBuilder.antMatchers("/public/**")).permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(new WindowsAuthenticationFilter(), BasicAuthenticationFilter.class);
return http.build();
}
*/
}
自定义 Windows 认证过滤器:
这是核心逻辑,你需要从请求头中获取用户信息,并创建一个 Authentication 对象。
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.web.util.WebUtils;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class WindowsAuthenticationFilter extends BasicAuthenticationFilter {
public WindowsAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 从请求头中获取用户信息
// 头的名称取决于服务器配置,通常是 'LOGON_USER' 或 'AUTH_USER'
String username = request.getHeader("LOGON_USER");
if (username == null || username.isEmpty()) {
// 如果没有头信息,说明没有进行 Windows 认证,继续过滤器链
chain.doFilter(request, response);
return;
}
// 去除域名前缀,如 "DOMAIN\\username" -> "username"
String shortUsername = username.contains("\\") ? username.split("\\\\")[1] : username;
// 创建一个简单的认证令牌
// 在实际应用中,你可能需要更复杂的验证逻辑,比如联系域控制器
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER")); // 给予一个默认角色
Authentication auth = new UsernamePasswordAuthenticationToken(shortUsername, null, authorities);
// 设置认证详情
AuthenticationDetailsSource<HttpServletRequest, ?> ads = this.getAuthenticationDetailsSource();
auth.setDetails(ads.buildDetails(request));
// 将认证信息存入 SecurityContext
SecurityContextHolder.getContext().setAuthentication(auth);
chain.doFilter(request, response);
}
}
在 Controller 中获取当前用户: 在你的 Controller 中,你可以轻松地获取到已认证的用户信息。
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HomeController {
@GetMapping("/user-info")
public String getUserInfo() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
return "Hello, " + authentication.getName() + "!";
}
return "Not authenticated.";
}
@GetMapping("/admin")
public String getAdminPage() {
// 只有拥有 ROLE_ADMIN 角色的用户才能访问
return "Admin Page";
}
}
服务器配置 (Tomcat + IIS/Apache):
为了让 IIS 能将 LOGON_USER 头传递给 Tomcat,你需要配置 IIS 的“身份验证”和“URL 重写”模块,Tomcat 本身不直接处理 Windows 身份验证,通常需要一个前端 Web 服务器(如 IIS)来处理它,然后将已验证的用户信息通过代理头传递给 Tomcat。
重要注意事项和常见问题
-
运行环境:
- 客户端验证:Java 进程必须以目标 Windows 账户的用户身份运行,如果作为服务运行,需要配置服务以特定用户身份登录,并勾选“允许服务与桌面交互”(不推荐,有安全风险)。
- 服务器端验证:前端服务器(IIS)必须正确配置,并信任客户端。
-
Kerberos vs. NTLM:
- Kerberos:性能更好,安全性更高,是域环境下的首选,但配置复杂,需要 SPN(服务主体名称)的正确设置。
- NTLM:配置简单,但性能较差,且存在已知的漏洞(如 Pass-the-Hash),仅在没有 Kerberos 选项时使用。
-
调试:
- 客户端:开启 HttpClient 的日志,可以看到完整的 NTLM/Kerberos 握手过程。
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; // ... PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(cm) .setCredentialsProvider(credsProvider) .build(); // 然后使用这个 httpClient - 服务器端:检查
LOGON_USER头是否存在,使用浏览器开发者工具查看网络请求的响应头。
- 客户端:开启 HttpClient 的日志,可以看到完整的 NTLM/Kerberos 握手过程。
-
跨域/跨信任:如果客户端和服务器不在同一个域或没有信任关系,Kerberos 认证会失败,此时可能需要回退到 NTLM 或手动配置信任关系。
-
Java 版本:较新的 Java 版本对 Kerberos 的支持更好,在
java.security文件中可以配置 Kerberos 的登录模块 (com.sun.security.module.JndiLoginModule)。
希望这份详细的指南能帮助你理解和实现 Java 中的 Windows 验证!
