机甲战魔神话之裔绿色中文版
45.5G · 2025-09-21
这是最传统、最广为人知的方案。其核心原理是利用HTTP协议本身的特性。
HttpSession
对象,为其生成一个唯一的SessionID
。Set-Cookie: JSESSIONID=xxxxxx
命令,将SessionID
返回给浏览器。Cookie: JSESSIONID=xxxxxx
请求头将其带回服务器。SessionID
,从而找到对应的HttpSession
对象,获取其中存储的用户状态信息(如用户ID、用户名等)。Spring Boot应用默认就支持此模式,无需额外配置。
@RestController
@SessionAttributes("user") // 可选的,用于在控制器内跨请求临时存储模型属性
public class TraditionalSessionController {
@PostMapping("/login")
public String login(@RequestParam String username,
HttpServletRequest request) {
// 模拟用户验证
if ("admin".equals(username)) {
// 获取Session,如果不存在则创建(create参数默认为true)
HttpSession session = request.getSession();
// 向Session中存储用户信息
session.setAttribute("user", username);
session.setMaxInactiveInterval(1800); // 设置Session30分钟后过期
return "Login successful. Session ID: " + session.getId();
}
return "Login failed";
}
@GetMapping("/profile")
public String getProfile(HttpServletRequest request) {
// 从请求中获取当前Session,如果不存在则不创建(false)
HttpSession session = request.getSession(false);
if (session != null) {
String user = (String) session.getAttribute("user");
if (user != null) {
return "Hello, " + user + ". Your session ID is: " + session.getId();
}
}
return "Please login first";
}
@GetMapping("/logout")
public String logout(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate(); // 使当前Session失效
}
return "Logged out";
}
}
优点:简单易用,内置支持;Session数据存储在服务器端,相对安全。
缺点:
JWT是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间作为JSON对象安全地传输信息。
无状态:服务器不存储任何会话信息。所有必要的用户状态都编码在Token本身中。
Token结构:Header.Payload.Signature
流程:用户登录后,服务器生成一个JWT并返回给客户端。客户端在后续请求的Authorization
头中携带此Token(如 Bearer <token>
)。服务器验证签名并解析Payload即可获取用户信息,无需查询数据库或Session存储。
首先,添加依赖:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
然后,编写工具类和控制器:
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.security.Key;
import java.util.Date;
@Component
public class JwtUtil {
// 在实际生产中,这个密钥必须很长、很复杂,且从安全配置中读取,绝不能硬编码!
private static final String SECRET = "ThisIsASecretKeyThatIsVeryLongAndSecureEnoughForHS512";
private Key key;
@PostConstruct
public void init() {
this.key = Keys.hmacShaKeyFor(SECRET.getBytes());
}
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10小时有效期
.signWith(key, SignatureAlgorithm.HS512)
.compact();
}
public String extractUsername(String token) {
return Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
// 记录日志或处理异常
return false;
}
}
}
@RestController
public class JwtController {
@Autowired
private JwtUtil jwtUtil;
@PostMapping("/auth/login")
public ResponseEntity<String> login(@RequestParam String username) {
// ... 验证用户名和密码
if ("admin".equals(username)) {
String token = jwtUtil.generateToken(username);
return ResponseEntity.ok().body(token); // 将Token返回给客户端
}
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
@GetMapping("/api/protected")
public ResponseEntity<String> protectedResource(@RequestHeader("Authorization") String authHeader) {
// 检查Authorization头
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
if (jwtUtil.validateToken(token)) {
String username = jwtUtil.extractUsername(token);
return ResponseEntity.ok("Accessing protected resource for: " + username);
}
}
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Invalid or missing token");
}
}
提示:在实际项目中,应使用Spring Security配置JWT过滤器(Filter)来集中处理Token验证,而不是在每个Controller中手动编写。
优点:
缺点:
这个方案是对传统Session模式的升级,旨在解决其扩展性问题。它将会话数据外部化到一个共享的存储中(如Redis),使得应用集群中的所有实例都可以访问相同的会话数据。
SessionRepository
接口,抽象了HttpSession
的实现。RedisIndexedSessionRepository
),将会话数据存储到Redis等外部数据源中。JSESSIONID
)来跟踪会话,但这个ID对应的是Redis中的一个条目,而不是本地服务器内存中的一个对象。首先,添加依赖:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId> <!-- 或使用Jedis -->
</dependency>
在application.properties
中配置Redis:
spring.redis.host=localhost
spring.redis.port=6379
# 配置使用Redis作为Session存储
spring.session.store-type=redis
server.servlet.session.timeout=1800s # Session过期时间
编写控制器(代码与传统Session模式几乎完全一样!):
@RestController
public class SpringSessionController {
@PostMapping("/redis-login")
public String login(@RequestParam String username, HttpServletRequest request) {
HttpSession session = request.getSession();
session.setAttribute("user", username);
return "Login successful (Redis). Session ID: " + session.getId();
}
@GetMapping("/redis-profile")
public String getProfile(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
String user = (String) session.getAttribute("user");
return "Hello (from Redis), " + user + ". Your session ID is: " + session.getId();
}
return "Please login first";
}
}
神奇之处在于,你几乎不需要修改业务代码,只需引入依赖和配置,应用就自动获得了分布式会话能力。
优点:
缺点:
HttpSession
,Spring Security的SecurityContext
(存储认证对象Authentication)也需要共享。Spring Session会自动处理这一点,确保在一个实例上登录后,其他实例也能通过SessionID获取到正确的认证信息。Spring Authorization Server
或整合Keycloak
等方案是更专业的选择。JWT通常是OAuth2中Access Token的一种实现形式。特性/方案 | 传统Cookie-Session | JWT | Spring Session + Redis |
---|---|---|---|
状态管理 | 有状态(服务器端) | 无状态(客户端Token) | 有状态(外部化存储) |
扩展性 | 差(需粘性会话或复制) | 极佳(天生分布式) | 极佳(共享存储) |
性能 | 高(内存读取) | 中(需验证签名) | 中高(网络IO,Redis极快) |
安全性 | 较好(服务器端存储) | 需注意Token盗用与废止问题 | 较好(服务器端逻辑,存储可控) |
适用场景 | 简单单体应用 | 分布式API、移动端、SSO | Spring分布式Web应用集群 |
代码侵入性 | 无(内置) | 中高(需自行处理Token生成验证) | 低(配置即用) |
最终决策指南:
希望这篇深度解析能帮助你在技术选型时不再迷茫。没有最好的方案,只有最适合你当前场景的方案。架构之道,在于权衡。
Happy Coding!
Win11 史诗级更新:Vista 时代 19 年后微软首次支持视频动态壁纸功能
奥特曼超时空英雄兑换码大全 奥特曼超时空英雄(2025永久)兑换码合集一览