Servlet 操作 Cookie & Session
在 Web 开发中,Session 和 Cookie 是用于管理用户会话状态的重要机制,使用 Servlet 操作 Cookie 和 Session 非常简单
操作 Cookie
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/cookieOperations")
public class CookieOperationsServlet extends HttpServlet {
private void createCookie(HttpServletRequest req, HttpServletResponse resp) {
// 创建一个名为 user 的 Cookie,值为 John
Cookie cookie = new Cookie("user", "John");
// 设置 Cookie 的最大存活时间为 3600 秒(1 小时)
cookie.setMaxAge(3600);
// 将 Cookie 添加到响应中
resp.addCookie(cookie);
}
private void readCookie(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 获取请求中的所有 Cookie
Cookie[] cookies = req.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("user".equals(cookie.getName())) {
// ...
return;
}
}
}
out.println("<p>未找到指定的 Cookie!</p>");
}
private void modifyCookie(HttpServletRequest req, HttpServletResponse resp) {
Cookie[] cookies = req.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("user".equals(cookie.getName())) {
// 修改 Cookie 的值为 Tom
cookie.setValue("Tom");
// 设置 Cookie 的最大存活时间为 3600 秒(1 小时)
cookie.setMaxAge(3600);
// 将修改后的 Cookie 添加到响应中
resp.addCookie(cookie);
break;
}
}
}
}
private void deleteCookie(HttpServletRequest req, HttpServletResponse resp) {
Cookie[] cookies = req.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("user".equals(cookie.getName())) {
// 将 Cookie 的最大存活时间设置为 0,即删除该 Cookie
cookie.setMaxAge(0);
// 将修改后的 Cookie 添加到响应中
resp.addCookie(cookie);
break;
}
}
}
}
}
操作 Session
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/sessionCookieOperations")
public class SessionCookieOperationsServlet extends HttpServlet {
private void createSession(HttpServletRequest req, HttpServletResponse resp) {
// 获取当前会话,如果不存在则创建一个新的会话
HttpSession session = req.getSession(true);
// 向会话中存储数据
session.setAttribute("username", "John");
}
private void readSession(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 获取当前会话,如果不存在则返回 null
HttpSession session = req.getSession(false);
if (session != null) {
String username = (String) session.getAttribute("username");
// ...
}
}
private void modifySession(HttpServletRequest req, HttpServletResponse resp) {
HttpSession session = req.getSession(false);
if (session != null) {
// 修改会话中的数据
session.setAttribute("username", "Tom");
}
}
private void deleteSession(HttpServletRequest req, HttpServletResponse resp) {
HttpSession session = req.getSession(false);
if (session != null) {
// 使会话失效
session.invalidate();
}
}
}
Spring Session
Spring Session 是 Spring 提供的一个会话管理库,通过将会话存储从应用内存转移到外部存储(数据库、Redis 等),实现了会话的集中管理、集群化和持久化
- 会话共享:在分布式系统中,多个应用实例可以共享会话数据,解决传统基于内存的会话存储在负载均衡环境中的局限性。
- 会话持久化:将会话数据持久化到外部存储,确保会话在服务器重启或故障后依然可用。
看一下如何在 SpringBoot 中使用 Spring Session,并把 Session 信息存储到 Redis
添加依赖
在 pom.xml 中添加以下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
配置 Redis
在 application.properties 中配置 Redis 连接信息:
spring.redis.host=localhost
spring.redis.port=6379
启用 Spring Session
在 Spring Boot 主应用类上添加 @EnableRedisHttpSession 注解:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
@SpringBootApplication
@EnableRedisHttpSession
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
开始使用
在控制器中使用 HttpSession 来管理会话:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
@RestController
public class SessionController {
@GetMapping("/setSession")
public String setSession(HttpSession session) {
session.setAttribute("username", "John");
return "Session set successfully";
}
@GetMapping("/getSession")
public String getSession(HttpSession session) {
String username = (String) session.getAttribute("username");
return "Username in session: " + username;
}
}
JWT
JWT(JSON Web Token)是一种用于在网络应用中安全传输信息的开放标准,通常用于身份验证和授权。服务器在用户登录后生成 JWT 并返回给客户端,客户端在后续请求中携带该令牌,服务器通过验证 JWT 来确认用户身份。
JWT 通过自包含的令牌机制实现无状态认证,服务端无需存储会话信息,客户端只需在请求头携带令牌即可完成身份验证,显著降低服务器存储压力,尤其适合分布式系统,在单点登录 SSO、OAuth 三方授权等场景有应用。
工作流程
- 生成令牌:用户登录成功后,服务器生成 JWT 并返回给客户端。
- 携带令牌:客户端在后续请求通过 HTTP Header
Authorization: Bearer <token>携带令牌。 - 验证令牌:服务器接收到请求后,验证 JWT 的签名和有效性,并提取用户信息。
简单应用
添加依赖
<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>
生成 JWT
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class JwtUtil {
private static final String SECRET_KEY = "your-secret-key-keep-it-safe";
private static final long EXPIRATION_TIME = 86400000; // 24小时(毫秒)
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
}
验证 JWT
public static boolean validateToken(String token) {
try {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
return !claims.getExpiration().before(new Date());
} catch (Exception e) {
return false;
}
}
OAuth2.0
OAuth 2.0 是一种开放授权协议,允许第三方应用在用户授权后访问其存储在资源服务器上的数据,而无需共享用户凭据(如密码)。它通过令牌(Token,可能就是 JWT 格式的)机制实现安全的权限委托,是互联网服务间数据共享的通用标准
关键角色
| 角色 | 职责描述 | 典型示例 |
|---|---|---|
| 资源所有者 | 数据的实际拥有者,决定是否授权第三方访问资源 | 用户 |
| 授权服务器 | 验证用户身份并颁发访问令牌(Access Token) | 微信 OAuth 服务 |
| 客户端 | 请求访问资源的第三方应用(需预先在授权服务器注册) | 电商网站 |
| 资源服务器 | 存储受保护资源并根据令牌权限响应请求 | 订单列表 |
- 很多时候 Client 和资源服务器都是我们自己的 Web 应用
- 授权服务器返回的令牌可以是 JWT 格式
简单应用
许多网站提供使用 Google、Facebook、微信等第三方账号登录的功能,就是基于 OAuth 2.0 实现的,Spring 对 OAuth 2.0 提供了全面的支持,在应用中集成 OAuth 2.0 变得非常简便
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
配置 Client
在 application.yml 中配置 OAuth 2.0 客户端信息,使用 GitHub 作为授权服务器
spring:
security:
oauth2:
client:
registration:
github:
client-id: YOUR_GITHUB_CLIENT_ID
client-secret: YOUR_GITHUB_CLIENT_SECRET
scope: read:user
provider:
github:
authorization-uri: https://github.com/login/oauth/authorize
token-uri: https://github.com/login/oauth/access_token
user-info-uri: https://api.github.com/user
user-name-attribute: id
配置 Spring Security
创建一个安全配置类,配置 OAuth2 登录流程
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;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll() // 允许匿名访问的路径
.anyRequest().authenticated() // 其他路径需要认证
.and()
.oauth2Login() // 启用 OAuth2 登录
.loginPage("/login"); // 自定义登录页面(可选)
}
}
创建控制器
创建一个简单的控制器,用于演示 OAuth2 登录
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping("/")
public String home() {
return "home"; // 返回主页视图
}
@GetMapping("/login")
public String login() {
return "login"; // 返回登录页视图
}
@GetMapping("/user")
public String user(@AuthenticationPrincipal OAuth2User principal, Model model) {
model.addAttribute("name", principal.getAttribute("name"));
model.addAttribute("email", principal.getAttribute("email"));
return "user"; // 返回用户信息视图
}
}