本文目录导读:

我来为您详细介绍Java中HSTS(HTTP Strict Transport Security)的实现案例。
什么是HSTS
HSTS是一种安全机制,强制浏览器只能通过HTTPS访问网站,防止中间人攻击和SSL剥离攻击。
实现方式
在Servlet Filter中实现
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HSTSFilter implements Filter {
// HSTS配置参数
private static final long MAX_AGE = 31536000L; // 1年
private static final boolean INCLUDE_SUBDOMAINS = true;
private static final boolean PRELOAD = false;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化配置
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 只在HTTPS请求时添加HSTS头
if (httpRequest.isSecure()) {
StringBuilder hstsValue = new StringBuilder();
hstsValue.append("max-age=").append(MAX_AGE);
if (INCLUDE_SUBDOMAINS) {
hstsValue.append("; includeSubDomains");
}
if (PRELOAD) {
hstsValue.append("; preload");
}
httpResponse.setHeader("Strict-Transport-Security", hstsValue.toString());
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
// 清理资源
}
}
Spring Boot配置方式
使用配置类
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.header.writers.StaticHeadersWriter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requiresChannel()
.anyRequest().requiresSecure() // 强制HTTPS
.and()
.headers()
.httpStrictTransportSecurity()
.includeSubDomains(true)
.maxAgeInSeconds(31536000)
.preload(false);
}
}
使用Filter注册
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HSTSConfiguration {
@Bean
public FilterRegistrationBean<HSTSFilter> hstsFilter() {
FilterRegistrationBean<HSTSFilter> registrationBean =
new FilterRegistrationBean<>();
registrationBean.setFilter(new HSTSFilter());
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(1);
return registrationBean;
}
}
完整的HSTS过滤器实现
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.logging.Logger;
@WebFilter("/*")
public class CompleteHSTSFilter implements Filter {
private static final Logger LOGGER =
Logger.getLogger(CompleteHSTSFilter.class.getName());
private HSTSConfig config;
public static class HSTSConfig {
private long maxAge = 31536000; // 1年
private boolean includeSubDomains = true;
private boolean preload = false;
private String[] excludedPaths = new String[]{};
// Getters and setters
public long getMaxAge() { return maxAge; }
public void setMaxAge(long maxAge) { this.maxAge = maxAge; }
public boolean isIncludeSubDomains() { return includeSubDomains; }
public void setIncludeSubDomains(boolean includeSubDomains) {
this.includeSubDomains = includeSubDomains;
}
public boolean isPreload() { return preload; }
public void setPreload(boolean preload) { this.preload = preload; }
public String[] getExcludedPaths() { return excludedPaths; }
public void setExcludedPaths(String[] excludedPaths) {
this.excludedPaths = excludedPaths;
}
}
@Override
public void init(FilterConfig filterConfig) {
this.config = new HSTSConfig();
// 从web.xml或配置读取参数
String maxAge = filterConfig.getInitParameter("maxAge");
if (maxAge != null) {
config.setMaxAge(Long.parseLong(maxAge));
}
String includeSubDomains = filterConfig.getInitParameter("includeSubDomains");
if (includeSubDomains != null) {
config.setIncludeSubDomains(Boolean.parseBoolean(includeSubDomains));
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String requestURI = httpRequest.getRequestURI();
// 检查是否在排除路径中
if (!isExcludedPath(requestURI)) {
// 只在HTTPS请求时添加HSTS头
if (httpRequest.isSecure()) {
String hstsValue = buildHSTSHeader();
httpResponse.setHeader("Strict-Transport-Security", hstsValue);
LOGGER.fine("Added HSTS header to: " + requestURI);
} else {
// 可选:重定向HTTP到HTTPS
String httpsURL = "https://" + httpRequest.getServerName()
+ httpRequest.getRequestURI();
httpResponse.sendRedirect(httpsURL);
return;
}
}
chain.doFilter(request, response);
}
private String buildHSTSHeader() {
StringBuilder hstsValue = new StringBuilder();
hstsValue.append("max-age=").append(config.getMaxAge());
if (config.isIncludeSubDomains()) {
hstsValue.append("; includeSubDomains");
}
if (config.isPreload()) {
hstsValue.append("; preload");
}
return hstsValue.toString();
}
private boolean isExcludedPath(String path) {
for (String excludedPath : config.getExcludedPaths()) {
if (path.startsWith(excludedPath)) {
return true;
}
}
return false;
}
@Override
public void destroy() {
// 清理资源
}
}
web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- HSTS Filter -->
<filter>
<filter-name>hstsFilter</filter-name>
<filter-class>com.example.HSTSFilter</filter-class>
<init-param>
<param-name>maxAge</param-name>
<param-value>31536000</param-value>
</init-param>
<init-param>
<param-name>includeSubDomains</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>preload</param-name>
<param-value>false</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>hstsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
测试用例
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class HSTSFilterTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testHSTSHeaderPresent() throws Exception {
mockMvc.perform(get("/api/test").secure(true))
.andExpect(status().isOk())
.andExpect(header().string("Strict-Transport-Security",
"max-age=31536000; includeSubDomains"));
}
@Test
public void testHSTSHeaderNotOnHTTP() throws Exception {
mockMvc.perform(get("/api/test").secure(false))
.andExpect(status().is3xxRedirection())
.andExpect(header().string("Location").startsWith("https://"));
}
}
最佳实践
配置建议
// 生产环境配置
@Configuration
public class ProductionHSTSConfig {
@Bean
public FilterRegistrationBean<HSTSFilter> hstsFilter() {
FilterRegistrationBean<HSTSFilter> registrationBean =
new FilterRegistrationBean<>();
CompleteHSTSFilter.HSTSConfig config = new CompleteHSTSFilter.HSTSConfig();
config.setMaxAge(63072000); // 2年
config.setIncludeSubDomains(true);
config.setPreload(true);
// 排除不需要HSTS的路径
config.setExcludedPaths(new String[]{"/health", "/metrics"});
registrationBean.setFilter(new CompleteHSTSFilter());
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(1);
return registrationBean;
}
}
验证HSTS配置
# 使用curl测试HSTS头 curl -I -k https://yourdomain.com # 期望输出 # Strict-Transport-Security: max-age=31536000; includeSubDomains
注意事项
- 先确保HTTPS正常工作:启用HSTS前,确保所有资源都通过HTTPS提供服务
- 合理设置max-age:开发环境短时间,生产环境至少1年
- 预加载(preload):使用前需提交到浏览器预加载列表
- 排除健康检查路径:避免影响监控系统
通过以上实现,您的Java应用就可以正确配置HSTS,提高Web安全性。