Java中的安全认证:如何不搞砸安全考试

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么你的密码不仅仅是一个字符串

\n\n

想象一下:你写了一个很棒的在线图书馆Java应用程序。用户注册、保存书签、撰写评论。突然——数据库泄露。所有密码都是明文。灾难?绝对是!2024年,数据泄露每39秒发生一次,其中81%与弱认证有关。如果你用Java编写代码,并且希望你的项目不要出现在网络安全新闻中——这篇文章就是为你准备的。

\n\n

我们将从头到尾剖析Java应用程序中的安全认证:从密码哈希到JWT和CSRF防护。没有枯燥的理论,只有可运行的代码和真实实践。开始吧!

\n\n

1. 密码哈希:第一道防线

\n\n

安全的第一条规则:永远不要以明文形式存储密码。即使你的数据库是圣杯,密码加密也是最低要求。在Java中,使用bcrypt、scrypt或Argon2等算法。为什么不用MD5或SHA?因为它们太快了——攻击者几秒钟就能破解密码。

\n\n

实践:bcrypt实战

\n

在pom.xml(Maven)中添加依赖:

\n
<dependency>\n    <groupId>org.springframework.security</groupId>\n    <artifactId>spring-security-crypto</artifactId>\n    <version>6.2.0</version>\n</dependency>\n
\n\n

现在在注册时对密码进行哈希处理:

\n
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\n\npublic class PasswordService {\n    private final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();\n\n    public String hashPassword(String rawPassword) {\n        return encoder.encode(rawPassword); // 自动生成盐值\n    }\n\n    public boolean checkPassword(String rawPassword, String storedHash) {\n        return encoder.matches(rawPassword, storedHash);\n    }\n}\n
\n\n

重要提示: bcrypt会自动添加随机盐值,因此即使相同的密码也会看起来不同。默认成本因子为10,但对于关键系统,可以提高到12-14。

\n\n

2. JWT(JSON Web Tokens):轻量且安全的会话

\n\n

注册后,用户需要某种方式来验证身份。Cookie?可以,但JWT是REST API的现代标准。令牌包含用户信息,并使用密钥签名。服务器上无需会话——所有信息都在令牌中。

\n\n

在Java中创建JWT

\n

添加库io.jsonwebtoken:jjwt:

\n
<dependency>\n    <groupId>io.jsonwebtoken</groupId>\n    <artifactId>jjwt-api</artifactId>\n    <version>0.12.3</version>\n</dependency>\n
\n\n

生成令牌:

\n
import io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.security.Keys;\nimport javax.crypto.SecretKey;\nimport java.util.Date;\n\npublic class JwtUtil {\n    private final SecretKey key = Keys.hmacShaKeyFor("长度不少于256位的密钥".getBytes());\n\n    public String generateToken(String username) {\n        return Jwts.builder()\n                .subject(username)\n                .issuedAt(new Date())\n                .expiration(new Date(System.currentTimeMillis() + 3600000)) // 1小时\n                .signWith(key)\n                .compact();\n    }\n\n    public String extractUsername(String token) {\n        return Jwts.parser()\n                .verifyWith(key)\n                .build()\n                .parseSignedClaims(token)\n                .getPayload()\n                .getSubject();\n    }\n}\n
\n\n

建议:不要将密钥存储在代码中!使用环境变量或Vault。并且永远不要在JWT中放置敏感数据(密码、卡号)——令牌可以在没有签名的情况下解码。

\n\n

3. CSRF防护:

博客

书籍推荐