Java 安全开发实战:从代码防护到架构安全
Java 安全开发实战涵盖核心原则与威胁模型,重点解决 SQL 注入、XSS、CSRF 及文件上传漏洞。通过最小权限、纵深防御等原则构建防护体系。认证授权方面采用密码加密存储、JWT 安全配置及 RBAC 模型。微服务架构下需保障网关安全、服务间通信加密及配置中心安全。结合 SonarQube、OWASP ZAP 等工具进行静态扫描与动态渗透测试,实现生产环境服务器、中间件及数据库的安全加固与应急响应。

Java 安全开发实战涵盖核心原则与威胁模型,重点解决 SQL 注入、XSS、CSRF 及文件上传漏洞。通过最小权限、纵深防御等原则构建防护体系。认证授权方面采用密码加密存储、JWT 安全配置及 RBAC 模型。微服务架构下需保障网关安全、服务间通信加密及配置中心安全。结合 SonarQube、OWASP ZAP 等工具进行静态扫描与动态渗透测试,实现生产环境服务器、中间件及数据库的安全加固与应急响应。

💡 安全开发的核心是'最小权限、纵深防御、数据脱敏、可审计',从设计、编码、测试到部署全流程规避风险:
Java 应用面临的安全威胁主要来自'输入验证失效、认证授权缺陷、敏感数据泄露、配置不当'四大类,常见威胁如下:
| 威胁类型 | 典型场景 | 危害程度 |
|---|---|---|
| SQL 注入(SQL Injection) | 用户输入拼接 SQL 语句(如WHERE) | 数据库数据泄露、篡改、删除 |
| 跨站脚本(XSS) | 恶意脚本注入网页(如评论区输入<script>) | 窃取 Cookie、钓鱼攻击、会话劫持 |
| 跨站请求伪造(CSRF) | 伪造用户身份发起请求(如伪造转账操作) | 越权操作、财产损失 |
| 文件上传漏洞 | 上传恶意文件(如.jsp 脚本、病毒) | 服务器被入侵、挖矿、数据泄露 |
| 权限越界(Broken Access Control) | 低权限用户访问高权限资源(如普通用户查看他人订单) | 敏感信息泄露、越权操作 |
| 敏感数据泄露 | 密码明文存储、接口明文传输敏感数据 | 用户隐私泄露、账号被盗 |
| 依赖包漏洞(Supply Chain Attack) | 使用存在漏洞的第三方依赖(如 Log4j2 漏洞) | 远程代码执行、服务器被控制 |
| 配置不当 | 数据库弱密码、Redis 未授权访问 | 服务器被入侵、数据泄露 |
安全不是'事后补丁',而是贯穿软件全生命周期的工程实践,Java 应用安全开发生命周期(SDL)分为 5 个阶段:
💡 SQL 注入的本质是'用户输入被当作 SQL 语句执行',防护核心是'避免直接拼接 SQL,使用参数化查询或 ORM 框架'。
输入验证与过滤:对用户输入的 SQL 关键字(如OR、UNION、DROP)进行过滤,作为辅助防护。
示例(SQL 关键字过滤):
public String filterSqlKeyword(String input) {
if (input == null) return "";
String[] keywords = {"OR", "UNION", "SELECT", "INSERT", "DELETE", "DROP"};
for (String keyword : keywords) {
input = input.replaceAll("(?i)" + keyword, ""); // 不区分大小写过滤
}
return input;
}
使用 ORM 框架(MyBatis/Hibernate):ORM 框架默认支持参数化查询,需避免使用动态 SQL 拼接。
MyBatis 安全用法:
<!-- 安全:使用 #{}占位符(参数化查询) -->
<select id="findUser" parameterType="Long" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
危险用法(${}直接拼接,存在注入风险):
<!-- 危险:${}会直接替换字符串,如 id 传入 "1 OR 1=1" -->
<select id="findUser" parameterType="String" resultType="User">
SELECT * FROM user WHERE id = ${id}
</select>
⚠️ 注意:MyBatis 中若需动态表名、排序字段,必须手动过滤白名单,如:
// 动态排序字段,仅允许 id、create_time 排序
public String getSortSql(String sortField) {
List<String> allowedFields = Arrays.asList("id", "create_time");
if (!allowedFields.contains(sortField)) {
sortField = "id"; // 默认排序字段
}
return "ORDER BY " + sortField;
}
使用参数化查询(PreparedStatement):将用户输入作为参数传入 SQL,而非拼接字符串,避免 SQL 注入。
正例(JDBC 参数化查询):
// 安全:使用 PreparedStatement,用户输入作为参数
String sql = "SELECT * FROM user WHERE id = ? AND username = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setLong(1, userId); // 用户输入的 id(Long 类型,自动过滤非法字符)
pstmt.setString(2, username); // 用户输入的用户名
ResultSet rs = pstmt.executeQuery();
反例(字符串拼接,存在注入风险):
// 危险:直接拼接用户输入,攻击者可输入 "1' OR '1'='1" 绕过验证
String sql = "SELECT * FROM user WHERE id = '" + userId + "' AND username = '" + username + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);
Statement、StringBuilder 拼接 SQL 的场景。💡 XSS 的本质是'恶意脚本被浏览器执行',分为存储型 XSS(脚本存入数据库,如评论区)和反射型 XSS(脚本通过 URL 参数注入,如搜索框),防护核心是'输入过滤、输出编码、CSP 策略'。
使用 HttpOnly Cookie:设置 Cookie 的 HttpOnly 属性,防止 JavaScript 读取 Cookie,避免 XSS 窃取会话。
Spring Boot 配置:
server:
servlet:
session:
cookie:
http-only: true # 开启 HttpOnly
secure: true # 仅 HTTPS 传输(生产环境启用)
设置 CSP(内容安全策略):通过 HTTP 响应头限制网页可加载的资源(如仅允许加载本域名脚本),阻止恶意脚本执行。
Spring Boot 配置 CSP:
@Component
public class CspFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
// CSP 策略:仅允许本域名、CDN 域名的脚本和样式
httpResponse.setHeader("Content-Security-Policy", "default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self'");
chain.doFilter(request, response);
}
}
输出编码:将后端返回的数据进行 HTML 编码,确保浏览器当作文本解析,而非脚本执行。
Spring Boot 自动编码:Spring MVC 默认对 model.addAttribute() 传递的变量进行 HTML 编码,无需手动处理。
手动编码示例(Velocity/Freemarker 模板):
<!-- Velocity 模板:使用#escape 进行 HTML 编码 -->
#escape($!stringUtils.escapeHtml($content)) $content #end
<!-- Freemarker 模板:使用?html 进行 HTML 编码 -->
${content?html}
输入过滤:过滤用户输入中的 <script>、onclick、javascript: 等危险标签和属性。
示例(Java XSS 过滤工具类):
import org.jsoup.Jsoup;
import org.jsoup.safety.Whitelist;
public class XssFilterUtil {
// 使用 Jsoup 过滤 HTML 标签,仅允许白名单标签(如<p>、<img>)
public static String filterHtml(String html) {
if (html == null) return "";
// 白名单:允许普通文本标签,禁止脚本相关标签
Whitelist whitelist = Whitelist.basic()
.addTags("p", "img", "h1", "h2")
.addAttributes("img", "src", "alt")
.removeAttributes("*", "onclick", "onload", "onerror");
return Jsoup.clean(html, whitelist);
}
// 过滤普通文本(非 HTML 场景,如用户名、标题)
public static String filterText(String text) {
if (text == null) return "";
// 转义特殊字符:< → <,> → >," → "
text = text.replace("<", "<")
.replace(">", ">")
.replace("\"", """)
.replace("'", "'")
.replace("javascript:", "");
return text;
}
}
⚠️ 依赖:需引入 Jsoup 依赖(安全的 HTML 解析库):
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.17.2</version>
</dependency>
| 场景 | 防护方案 |
|---|---|
| 评论区、富文本编辑器 | 使用 Jsoup 过滤 HTML 标签,仅允许白名单标签 |
| 用户名、标题输入框 | 文本转义(<→<等),禁止 HTML 标签 |
| URL 参数传递 | 对参数进行 URL 编码,后端验证参数格式 |
| 前端渲染数据 | 模板引擎自动编码,避免使用 v-html 等危险指令 |
💡 CSRF 的本质是'攻击者利用用户已登录的会话,伪造用户身份发起请求',防护核心是'验证请求来源的合法性'。
使用 SameSite Cookie:设置 Cookie 的 SameSite 属性,限制 Cookie 仅在同站请求中携带,阻止跨站请求伪造。
配置示例:
server:
servlet:
session:
cookie:
same-site: Lax # 仅允许同站请求和 GET 跨站请求携带 Cookie
# same-site: Strict # 仅允许同站请求携带 Cookie(安全性更高)
验证 Referer/Origin 请求头:检查请求的 Referer(来源页面)或 Origin(来源域名)是否为信任域名,拒绝非法来源请求。
自定义 CSRF 过滤器:
@Component
public class RefererCsrfFilter implements Filter {
// 信任的域名列表
private static final List<String> TRUSTED_ORIGINS = Arrays.asList("https://www.example.com");
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 获取 Origin 请求头(优先验证 Origin,Referer 可被篡改)
String origin = httpRequest.getHeader("Origin");
if (origin != null && !TRUSTED_ORIGINS.contains(origin)) {
httpResponse.setStatus(HttpStatus.FORBIDDEN.value());
httpResponse.getWriter().write("CSRF attack detected");
return;
}
chain.doFilter(request, response);
}
}
使用 CSRF Token:服务器生成随机 Token(如 UUID),存储在 Session 或 Cookie 中,前端请求时携带 Token,服务器验证 Token 有效性。
Spring Security 自动实现 CSRF 防护(默认开启):
① 后端配置(Spring Boot):
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
return http.build();
}
}
② 前端携带 Token(Vue 示例):
// 从 Cookie 获取 CSRF Token(Spring Security 默认存储在 XSRF-TOKEN Cookie)
import Cookies from 'js-cookie';
const csrfToken = Cookies.get('XSRF-TOKEN');
// 发送请求时携带 Token(请求头 X-XSRF-TOKEN)
axios.post('/api/order/submit', orderData, {
headers: {'X-XSRF-TOKEN': csrfToken}
});
💡 文件上传漏洞的核心是'攻击者上传恶意文件(如.jsp、.php 脚本)并执行',防护核心是'严格校验文件类型、重命名文件、限制存储路径'。
.jpg、.png、.pdf)。Content-Type 头,如image/jpeg)。FF D8 FF)。public class FileUploadUtil {
// 允许的文件后缀白名单
private static final Set<String> ALLOWED_EXTENSIONS = new HashSet<>(Arrays.asList("jpg", "png", "pdf", "doc"));
// 允许的文件魔数映射(后缀→魔数前缀)
private static final Map<String, byte[]> ALLOWED_MAGIC_NUMBERS = new HashMap<>();
static {
ALLOWED_MAGIC_NUMBERS.put("jpg", new byte[]{(byte) 0xFF, (byte) 0xD8, (byte) 0xFF});
ALLOWED_MAGIC_NUMBERS.put("png", new byte[]{(byte) 0x89, (byte) 0x50, (byte) 0x4E, (byte) 0x47});
ALLOWED_MAGIC_NUMBERS.put("pdf", new byte[]{(byte) 0x25, (byte) 0x50, (byte) 0x44, (byte) 0x46});
}
// 校验文件合法性
public static boolean validateFile(MultipartFile file) throws IOException {
if (file.isEmpty()) return false;
// 1. 校验文件后缀
String originalFilename = file.getOriginalFilename();
String extension = FilenameUtils.getExtension(originalFilename).toLowerCase();
if (!ALLOWED_EXTENSIONS.contains(extension)) {
return false;
}
// 2. 校验文件魔数
byte[] magicNumber = new byte[4];
file.getInputStream().read(magicNumber);
byte[] expectedMagic = ALLOWED_MAGIC_NUMBERS.get(extension);
if (expectedMagic == null) return false;
for (int i = 0; i < expectedMagic.length; i++) {
if (magicNumber[i] != expectedMagic[i]) {
return false;
}
}
return true;
}
}
/data/upload),避免直接执行脚本文件。/api/file/{fileName}),而非直接暴露文件路径。chmod 644)。限制文件大小:防止超大文件上传导致磁盘空间耗尽或 DOS 攻击。
Spring Boot 配置:
spring:
servlet:
multipart:
max-file-size: 10MB # 单个文件最大 10MB
max-request-size: 50MB # 单次请求最大 50MB
文件重命名:上传文件后重命名为随机文件名(如 UUID),避免攻击者猜测文件路径和名称,同时防止文件名包含恶意字符。
示例:
public String uploadFile(MultipartFile file) throws IOException {
// 校验文件合法性(略)
String extension = FilenameUtils.getExtension(file.getOriginalFilename()).toLowerCase();
// 重命名文件:UUID + 后缀
String fileName = UUID.randomUUID().toString() + "." + extension;
// 存储文件(指定安全路径)
String storagePath = "/data/upload/files/";
File destFile = new File(storagePath + fileName);
file.transferTo(destFile);
// 返回访问路径(不暴露真实存储路径)
return "/api/file/" + fileName;
}
⚠️ 依赖:需引入 commons-io 获取文件后缀:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.15.1</version>
</dependency>
💡 敏感数据包括用户密码、身份证号、手机号、银行卡号等,防护核心是'加密存储、加密传输、脱敏展示'。
绝对禁止明文存储密码,必须使用不可逆加密算法(如 BCrypt、Argon2),避免 MD5、SHA-1 等弱哈希算法(易被彩虹表破解)。
Spring Security 集成 BCrypt 加密:
密码加密与验证:
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@Component
public class PasswordUtil {
private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
// 密码加密(存储到数据库)
public String encryptPassword(String rawPassword) {
return passwordEncoder.encode(rawPassword);
}
// 密码验证(登录时比对)
public boolean verifyPassword(String rawPassword, String encryptedPassword) {
return passwordEncoder.matches(rawPassword, encryptedPassword);
}
}
⚠️ 特点:BCrypt 自动生成随机盐值,每次加密结果不同,但验证时可通过盐值匹配,安全性高。
引入依赖(Spring Boot 已内置):
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
所有敏感数据传输必须使用 HTTPS,避免 HTTP 明文传输被窃听。
Spring Boot 配置 HTTPS:
强制 HTTP 跳转 HTTPS:
@Configuration
public class HttpsConfig {
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(redirectConnector());
return tomcat;
}
private Connector redirectConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(80);
connector.setSecure(false);
connector.setRedirectPort(443); // HTTP 跳转 HTTPS
return connector;
}
}
配置 application.yml:
server:
port: 443
ssl:
key-store: classpath:example.jks # 证书路径
key-store-password: 123456 # 证书密码
key-alias: example
key-store-type: JKS
生成 SSL 证书(使用 keytool):
keytool -genkeypair -alias example -keyalg RSA -keysize 2048 -keystore example.jks -validity 3650
接口返回和页面展示敏感数据时,需进行脱敏处理,仅展示部分信息:
public class SensitiveDataUtil {
// 手机号脱敏(保留前 3 位和后 4 位)
public static String maskPhone(String phone) {
if (phone == null || phone.length() != 11) return phone;
return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
}
// 身份证号脱敏(保留前 6 位和后 4 位)
public static String maskIdCard(String idCard) {
if (idCard == null || idCard.length() != 18) return idCard;
return idCard.replaceAll("(\\d{6})\\d{8}(\\d{4})", "$1********$2");
}
// 银行卡号脱敏(保留前 6 位和后 4 位)
public static String maskBankCard(String bankCard) {
if (bankCard == null || bankCard.length() < 10) return bankCard;
return bankCard.replaceAll("(\\d{6})\\d+(\\d{4})", "$1****$2");
}
}
认证的核心是'验证用户身份合法性',常见认证方式有'用户名密码认证、短信验证码认证、OAuth2.0 第三方认证',需避免'弱密码、会话劫持、认证绕过'等风险。
限制登录尝试次数:防止暴力破解,如 5 分钟内连续输错 5 次密码,锁定账号 15 分钟。
示例(基于 Redis 的登录次数限制):
@Component
public class LoginLimitUtil {
@Autowired
private StringRedisTemplate redisTemplate;
private static final String LOGIN_LIMIT_KEY = "login:limit:%s";
private static final int MAX_ATTEMPTS = 5; // 最大尝试次数
private static final int LOCK_MINUTES = 15; // 锁定时间(分钟)
public boolean checkLoginLimit(String username) {
String key = String.format(LOGIN_LIMIT_KEY, username);
String attempts = redisTemplate.opsForValue().get(key);
if (attempts == null) {
redisTemplate.opsForValue().set(key, "1", 5, TimeUnit.MINUTES);
return true;
}
int count = Integer.parseInt(attempts);
if (count >= MAX_ATTEMPTS) {
return false; // 已锁定
}
redisTemplate.opsForValue().increment(key);
return true;
}
// 登录成功后清除限制
public void clearLoginLimit(String username) {
String key = String.format(LOGIN_LIMIT_KEY, username);
redisTemplate.delete(key);
}
}
强制密码复杂度:要求密码长度≥8 位,包含大小写字母、数字、特殊字符,禁止弱密码(如123456、password)。
示例(密码复杂度校验):
public boolean validatePasswordStrength(String password) {
// 正则表达式:8-20 位,包含大小写字母、数字、特殊字符
String regex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@#$%^&*()])[A-Za-z\\d@#$%^&*()]{8,20}$";
return password.matches(regex);
}
JWT(JSON Web Token)是微服务架构中常用的无状态认证方式,需注意'令牌泄露、过期时间、签名安全'等问题:
Authorization: Bearer {token})传输,禁止通过 URL 参数或 Cookie 传输(易泄露)。使用强签名算法:采用 HS256(HMAC-SHA256)或 RS256(RSA-SHA256)算法,避免使用 HS512 以下的弱算法,禁止使用 None 算法(无签名)。
示例(JWT 工具类,使用 HS256):
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.util.Date;
@Component
public class JwtUtil {
// 密钥(生产环境需存储在配置中心,长度≥256 位)
@Value("${jwt.secret:abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRST}")
private String secret;
// 过期时间(1 小时)
@Value("${jwt.expire:3600000}")
private long expire;
// 生成密钥
private SecretKey getSecretKey() {
return Keys.hmacShaKeyFor(secret.getBytes());
}
// 生成 JWT 令牌
public String generateToken(Long userId, String username) {
return Jwts.builder()
.setSubject(userId.toString())
.claim("username", username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expire))
.signWith(getSecretKey(), io.jsonwebtoken.SignatureAlgorithm.HS256)
.compact();
}
// 验证令牌有效性
public boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(getSecretKey()).build().parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
}
OAuth2.0 用于第三方登录(如微信、QQ 登录),需注意'授权码泄露、回调地址篡改、Scope 权限控制':
snsapi_userinfo),避免过度授权。授权的核心是'验证用户是否有权执行操作或访问资源',Java 应用中常用 RBAC(基于角色的访问控制)模型,需避免'权限越界、垂直越权、水平越权'。
RBAC 模型包含'用户 - 角色 - 权限'三层关系(用户关联角色,角色关联权限),Spring Security 可快速实现:
方法级权限控制(注解方式):
@RestController
@RequestMapping("/api/order")
public class OrderController {
// 普通用户可访问(需 ORDER_VIEW 权限)
@GetMapping("/view/{id}")
@PreAuthorize("hasAuthority('order:view')")
public Order viewOrder(@PathVariable Long id) {
// 业务逻辑
}
// 仅管理员可访问(需 ORDER_DELETE 权限)
@DeleteMapping("/delete/{id}")
@PreAuthorize("hasAuthority('order:delete')")
public void deleteOrder(@PathVariable Long id) {
// 业务逻辑
}
// 水平越权防护:仅允许查看自己的订单
@GetMapping("/my/{id}")
@PreAuthorize("hasAuthority('order:view') and @orderSecurityService.isOwner(authentication, #id)")
public Order viewMyOrder(@PathVariable Long id) {
// 业务逻辑
}
}
// 订单安全服务:验证订单归属
@Service
public class OrderSecurityService {
@Autowired
private OrderRepository orderRepository;
public boolean isOwner(Authentication authentication, Long orderId) {
String username = authentication.getName();
Order order = orderRepository.findById(orderId).orElseThrow();
return order.getUsername().equals(username);
}
}
配置 Spring Security:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity // 开启方法级权限控制
public class SecurityConfig {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll() // 公开接口
.requestMatchers("/api/admin/**").hasRole("ADMIN") // 管理员接口
.requestMatchers("/api/order/view").hasAuthority(Permission.ORDER_VIEW.getValue())
.requestMatchers("/api/order/create").hasAuthority(Permission.ORDER_CREATE.getValue())
.anyRequest().authenticated())
.formLogin(form -> form.permitAll())
.logout(logout -> logout.permitAll());
return http.build();
}
// 密码加密器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
定义权限与角色:
// 权限枚举(如查看订单、创建订单、删除订单)
public enum Permission {
ORDER_VIEW("order:view"),
ORDER_CREATE("order:create"),
ORDER_DELETE("order:delete");
private final String value;
Permission(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
// 角色枚举(如普通用户、管理员)
public enum Role {
USER("ROLE_USER", Arrays.asList(Permission.ORDER_VIEW, Permission.ORDER_CREATE)),
ADMIN("ROLE_ADMIN", Arrays.asList(Permission.ORDER_VIEW, Permission.ORDER_CREATE, Permission.ORDER_DELETE));
private final String value;
private final List<Permission> permissions;
Role(String value, List<Permission> permissions) {
this.value = value;
this.permissions = permissions;
}
public String getValue() {
return value;
}
public List<Permission> getPermissions() {
return permissions;
}
}
引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
/api/admin/**仅允许管理员访问)。@PreAuthorize("hasRole('ADMIN')") 注解。网关作为微服务的统一入口,需承担'身份认证、权限校验、限流熔断、安全过滤'等核心安全职责,避免恶意请求进入后端服务。
安全过滤:网关层过滤 XSS、SQL 注入等恶意请求。
示例(网关 XSS 过滤过滤器):
@Component
public class GatewayXssFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 过滤请求参数中的 XSS 脚本
ServerHttpRequest filteredRequest = new XssFilteredRequest(request);
return chain.filter(exchange.mutate().request(filteredRequest).build());
}
@Override
public int getOrder() {
return -90; // 认证后执行
}
// 自定义请求包装类,过滤参数
private static class XssFilteredRequest extends ServerHttpRequestDecorator {
public XssFilteredRequest(ServerHttpRequest delegate) {
super(delegate);
}
@Override
public MultiValueMap<String, String> getQueryParams() {
MultiValueMap<String, String> params = super.getQueryParams();
return filterParams(params);
}
private MultiValueMap<String, String> filterParams(MultiValueMap<String, String> params) {
MultiValueMap<String, String> filtered = new LinkedMultiValueMap<>();
params.forEach((key, values) -> {
List<String> filteredValues = values.stream()
.map(XssFilterUtil::filterText)
.collect(Collectors.toList());
filtered.put(key, filteredValues);
});
return filtered;
}
}
}
限流与黑名单:网关层限制单 IP、单用户的请求频率,拦截恶意 IP。
示例(基于 Sentinel 的网关限流):
spring:
cloud:
sentinel:
scg:
fallback:
response-body: '{"code":429,"msg":"请求过于频繁"}'
transport:
dashboard: localhost:8083
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/order/**
filters:
- name: SentinelScgFilter
args:
resourceName: order-service
ruleType: PATH
controlBehavior: DEFAULT
count: 100 # 每秒最多 100 个请求
权限校验:网关层校验用户权限是否允许访问目标服务/接口。
示例(基于请求路径的权限校验):
// 在 GatewayAuthFilter 中添加权限校验逻辑
private boolean hasPermission(Claims claims, String path) {
List<String> permissions = claims.get("permissions", List.class);
// 路径与权限映射(如/api/order/create → order:create)
Map<String, String> pathPermissionMap = new HashMap<>();
pathPermissionMap.put("/api/order/create", "order:create");
pathPermissionMap.put("/api/order/delete", "order:delete");
String requiredPermission = pathPermissionMap.get(path);
if (requiredPermission == null) return true; // 无权限要求
return permissions.contains(requiredPermission);
}
统一认证:网关层统一校验 JWT 令牌,无需每个服务单独认证。
示例(网关全局认证过滤器):
@Component
public class GatewayAuthFilter implements GlobalFilter, Ordered {
@Autowired
private JwtUtil jwtUtil;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 白名单接口直接放行
String path = request.getPath().value();
if (path.startsWith("/api/public/") || path.startsWith("/actuator/health")) {
return chain.filter(exchange);
}
// 获取 JWT 令牌
String authHeader = request.getHeaders().getFirst("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
String token = authHeader.substring(7);
if (!jwtUtil.validateToken(token)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 解析用户信息,传递到下游服务(请求头)
Claims claims = jwtUtil.parseClaims(token);
String userId = claims.getSubject();
String username = claims.get("username", String.class);
ServerHttpRequest newRequest = request.mutate()
.header("X-User-Id", userId)
.header("X-User-Name", username)
.build();
return chain.filter(exchange.mutate().request(newRequest).build());
}
@Override
public int getOrder() {
return -100; // 优先执行认证
}
}
微服务间通信需避免'数据泄露、身份伪造、中间人攻击',核心防护手段是'HTTPS 通信、服务身份认证、接口权限控制'。
服务调用使用 HTTPS 协议(如 Feign 调用时指定https://)。
Feign 配置 HTTPS:
feign:
client:
config:
default:
url: https://user-service # HTTPS 协议
httpclient:
enabled: false
okhttp:
enabled: true
使用 Sentinel 或 Nacos 实现服务身份认证,避免非法服务接入:
服务间调用令牌校验:
服务 A 调用服务 B 时,在请求头携带服务令牌,服务 B 校验令牌有效性:
// 服务 A Feign 拦截器(添加服务令牌)
@Component
public class FeignAuthInterceptor implements RequestInterceptor {
@Value("${service.token:service-auth-token-123456}")
private String serviceToken;
@Override
public void apply(RequestTemplate template) {
template.header("Service-Token", serviceToken);
}
}
// 服务 B 过滤器(校验服务令牌)
@Component
public class ServiceAuthFilter implements Filter {
@Value("${service.token:service-auth-token-123456}")
private String serviceToken;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String token = httpRequest.getHeader("Service-Token");
if (!serviceToken.equals(token)) {
httpResponse.setStatus(HttpStatus.FORBIDDEN.value());
return;
}
chain.doFilter(request, response);
}
}
Nacos 服务认证:
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
username: nacos
password: nacos
config:
server-addr: localhost:8848
username: nacos
password: nacos
配置中心存储大量敏感配置(如数据库密码、API 密钥),需防护'未授权访问、配置泄露、配置篡改'。
敏感配置加密:使用 Nacos 配置加密功能,加密存储数据库密码等敏感信息。
① 配置加密密钥(Nacos 启动参数):
java -jar nacos-server.jar -Dnacos.encrypt.key=abcdefghijklmnopqrstuvwxyz123456
② 加密敏感配置(如数据库密码):
使用 Nacos 提供的加密工具加密密码,配置时前缀cipher::
spring:
datasource:
password: cipher:EncryptedPassword123 # 加密后的密码
开启身份认证:Nacos 启用用户名密码登录,避免匿名访问。
修改 Nacos 配置文件 conf/application.properties:
nacos.core.auth.enabled=true
nacos.core.auth.server.identity.key=serverIdentity
nacos.core.auth.server.identity.value=security
nacos.core.auth.plugin.nacos.token.secret.key=SecretKey012345678901234567890123456789012
Docker+K8s 环境下的微服务,需额外关注'容器安全、镜像安全、集群安全':
privileged: false),避免容器获取宿主机权限。readOnly: true),防止容器内文件篡改。SonarQube 是开源的代码质量与安全扫描工具,可检测 Java 代码中的安全漏洞、代码异味、重复代码等问题。
Maven 项目集成 SonarQube:
① 在 pom.xml 中添加插件:
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.9.1.2184</version>
</plugin>
② 执行扫描命令:
mvn sonar:sonar -Dsonar.host.url=http://localhost:9000 -Dsonar.login=admin -Dsonar.password=admin
部署 SonarQube(Docker 方式):
docker run -d --name sonarqube -p 9000:9000 -e SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true sonarqube:9.9-community
SonarQube 重点关注的 Java 安全规则:
Statement 拼接 SQL。OWASP ZAP(Zed Attack Proxy)是开源的 Web 应用渗透测试工具,可自动扫描接口中的 SQL 注入、XSS、CSRF 等漏洞。
http://localhost:8080/api)。依赖包漏洞是 Java 应用的重大安全隐患(如 Log4j2、Fastjson 漏洞),需定期扫描并修复。
target/dependency-check-report 目录下查看 HTML 报告,修复高危依赖(升级版本或替换安全依赖)。执行扫描命令:
mvn dependency-check:check
在 pom.xml 中添加插件:
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>8.4.0</version>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<format>HTML</format>
<outputDirectory>${project.build.directory}/dependency-check-report</outputDirectory>
</configuration>
</plugin>
安全日志是追溯安全事件的关键,需确保'日志完整、不可篡改、便于分析'。
记录所有敏感操作日志,包含'操作人、时间、IP、操作内容、结果':
@Service
public class OrderService {
private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
public Order createOrder(OrderDTO orderDTO, String username, String ip) {
// 记录操作日志
logger.info("用户 [{}](IP:{})创建订单,订单信息:{}", username, ip, orderDTO);
try {
Order order = saveOrder(orderDTO);
logger.info("用户 [{}] 创建订单成功,订单 ID:{}", username, order.getId());
return order;
} catch (Exception e) {
logger.error("用户 [{}] 创建订单失败,原因:{}", username, e.getMessage(), e);
throw e;
}
}
}
-XX:+AllowUserSignalHandlers)。tomcat-manager),或设置强密码。conf/server.xml,添加 server="Unknown")。secure_file_priv = ""),防止 SQL 注入读取本地文件。当发生安全事件(如服务器被入侵、数据泄露)时,需遵循'止损→排查→修复→追溯→优化'的应急响应流程:
✅ 本章全面覆盖了 Java 应用安全开发的核心知识,从代码级漏洞防护到架构级安全设计,从认证授权到微服务安全,结合工具实战和配置示例,构建了全维度的 Java 安全防护体系。
通过本章学习,读者应掌握:
安全开发是一个持续迭代的过程,没有绝对安全的系统,只有不断提升攻击成本。实际项目中,需结合业务场景和风险等级,制定合理的安全策略,平衡安全性与可用性。建议读者将安全开发融入日常开发流程,定期进行安全培训和漏洞扫描,构建'攻防兼备'的 Java 应用系统。
至此,《Java 开发从入门到精通》全书已完整呈现,从 Java 基础、Web 开发、框架应用、微服务架构、容器化部署到安全开发,形成了覆盖 Java 开发全生命周期的完整知识体系。希望读者能够将所学知识灵活应用于实际项目,开发出高性能、高可用、高安全的 Java 应用。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online