Java中的授权与身份验证:Spring Security和JWT
组织对资源的安全访问是开发Java Web应用程序的关键任务之一。在本文中,我们将讨论两个基本概念:身份验证(验证用户身份)和授权(验证访问权限)。我们将通过使用流行框架Spring Security和JSON Web Tokens (JWT)技术的实际示例进行讲解。
1. 基本概念
在进入代码之前,明确区分这些术语很重要:
- 身份验证 (Authentication) — 验证用户身份的过程。通常通过登录名和密码、生物识别或令牌完成。
- 授权 (Authorization) — 确定允许用户做什么的过程。例如,访问特定的API端点或应用程序功能。
在现代Java应用程序中,实现这些机制最常用的是Spring Security + JWT组合。JWT允许将用户信息存储在一个紧凑的令牌中,便于在客户端和服务器之间传输。
2. 配置Spring Security与JWT
在pom.xml (Maven)中添加必要的依赖项:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId></dependency><dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.12.3</version></dependency><dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.12.3</version> <scope>runtime</scope></dependency><dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.12.3</version> <scope>runtime</scope></dependency>3. 创建用于处理JWT的服务
创建一个JwtService类,负责生成和验证令牌:
import io.jsonwebtoken.Claims;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import io.jsonwebtoken.security.Keys;import org.springframework.stereotype.Service;
import javax.crypto.SecretKey;import java.util.Date;import java.util.HashMap;import java.util.Map;
@Servicepublic class JwtService {
private final SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256); private final long expirationMs = 86400000; // 24小时
public String generateToken(String username) { Map<String, Object> claims = new HashMap<>(); return Jwts.builder() .setClaims(claims) .setSubject(username) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + expirationMs)) .signWith(secretKey) .compact(); }
public String extractUsername(String token) { return extractAllClaims(token).getSubject(); }
private Claims extractAllClaims(String token) { return Jwts.parserBuilder() .setSigningKey(secretKey) .build() .parseClaimsJws(token) .getBody(); }
public boolean isTokenValid(String token, String username) { final String extractedUsername = extractUsername(token); return (extractedUsername.equals(username) && !isTokenExpired(token)); }
private boolean isTokenExpired(String token) { return extractAllClaims(token).getExpiration().befo