java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > java cookie、session、token

深度解析Java视角下Cookie、Session、Token实战教程

作者:柒.梧.

本文从Java技术栈出发,详细介绍了Cookie、Session和Token三种主流的状态管理机制,Cookie是客户端的状态载体,Session是服务器端的状态管理,Token是无状态的身份凭证,通过源码级解析和实战案例,感兴趣的朋友跟随小编一起看看吧

在Java Web开发中,用户身份认证与状态保持是核心需求之一。Cookie、Session、Token作为三种主流的状态管理机制,贯穿于从传统JSP/Servlet项目到现代Spring Boot/Cloud微服务架构的各类应用中。很多开发者对三者的区别、适用场景及底层实现一知半解,本文将从Java技术栈出发,结合源码级解析与实战案例,全面拆解Cookie、Session、Token的核心逻辑,帮助大家构建清晰的知识体系。

一、前置知识:HTTP协议的无状态性

要理解Cookie、Session、Token的设计初衷,首先要明确HTTP协议的核心特性——无状态性。HTTP协议本身不保存客户端与服务器之间的交互状态,即服务器无法通过HTTP协议自动识别两次请求是否来自同一客户端。例如:用户第一次访问服务器登录成功后,第二次请求时服务器无法直接知晓该用户已登录,这就导致无法实现购物车、个人中心等需要状态保持的功能。

为了解决HTTP无状态性带来的问题,Cookie、Session、Token应运而生。三者的核心目标都是实现“客户端身份识别”与“状态保持”,但实现思路、存储位置、安全特性存在本质差异。

二、Cookie:客户端的状态载体

2.1 什么是Cookie?

Cookie是服务器发送给客户端浏览器的一小段文本数据(通常不超过4KB),浏览器会将其保存到本地(内存或磁盘)。之后,客户端每次向同一服务器发送请求时,都会自动携带该Cookie数据,从而让服务器识别出客户端身份。

从Java Web角度看,Cookie是Servlet规范中的标准组件,由javax.servlet.http.Cookie类封装,服务器通过response对象向客户端写入Cookie,通过request对象读取客户端携带的Cookie。

2.2 Cookie的核心原理与Java实现

2.2.1 核心原理

2.2.2 Java实战:Cookie的创建与使用

以下是基于Servlet的Cookie核心操作示例,涵盖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;
import java.io.PrintWriter;
@WebServlet("/cookieDemo")
public class CookieDemoServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        // 1. 读取客户端携带的Cookie
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            out.write("客户端携带的Cookie:
");
            for (Cookie cookie : cookies) {
                // 获取Cookie的名称和值
                String name = cookie.getName();
                String value = cookie.getValue();
                out.write("名称:" + name + ",值:" + value + "
");
            }
        } else {
            out.write("客户端首次访问,未携带Cookie
");
        }
        // 2. 创建Cookie并写入客户端
        Cookie userCookie = new Cookie("username", "zhangsan");
        // 设置Cookie的过期时间:单位为秒,正数表示持久化到磁盘,负数表示仅存于内存(会话结束后销毁),0表示立即删除
        userCookie.setMaxAge(3600);
        // 设置Cookie的有效路径:仅当请求路径匹配该路径时,才携带此Cookie
        userCookie.setPath("/");
        // 设置Cookie的有效域名:指定哪些域名可以访问该Cookie,防止跨域盗用
        userCookie.setDomain("localhost");
        // 设置Cookie为HttpOnly:禁止JavaScript读取,防止XSS攻击
        userCookie.setHttpOnly(true);
        // 设置Cookie为Secure:仅在HTTPS协议下才携带此Cookie
        // userCookie.setSecure(true);
        // 将Cookie写入响应
        response.addCookie(userCookie);
        // 3. 销毁Cookie(通过设置maxAge为0实现)
        Cookie deleteCookie = new Cookie("oldCookie", "");
        deleteCookie.setMaxAge(0);
        deleteCookie.setPath("/");
        response.addCookie(deleteCookie);
        out.close();
    }
}
    

2.3 Cookie的核心属性详解

属性名

作用

Java方法

注意事项

Name

Cookie的名称,唯一标识一个Cookie

setName(String name)

名称一旦确定,无法修改,只能通过同名Cookie覆盖

Value

Cookie存储的文本数据

setValue(String value)

不能包含空格、逗号、分号等特殊字符,需URL编码

Max-Age

过期时间(秒)

setMaxAge(int maxAge)

默认-1(内存存储),0表示立即删除,正数表示持久化

Path

有效路径,仅匹配路径的请求才携带Cookie

setPath(String path)

默认是当前Servlet的路径,设置为“/”表示整个应用有效

Domain

有效域名,限制Cookie的访问范围

setDomain(String domain)

例如“example.com”表示子域名(www.example.com)也可访问

HttpOnly

禁止JavaScript读取Cookie

setHttpOnly(boolean httpOnly)

有效防止XSS攻击,Java EE 6及以上支持

Secure

仅在HTTPS协议下携带Cookie

setSecure(boolean secure)

提升安全性,生产环境建议开启

2.4 Cookie的优缺点

优点

缺点

三、Session:服务器端的状态管理

3.1 什么是Session?

Session(会话)是服务器为每个客户端(浏览器)创建的内存级状态对象,用于存储客户端的会话信息(如登录状态、购物车数据等)。服务器通过Session ID唯一标识每个Session,而Session ID通常通过Cookie发送给客户端,客户端后续请求时携带Session ID,服务器通过该ID找到对应的Session对象,从而实现状态保持。

在Java Web中,Session由Servlet容器(如Tomcat、Jetty)管理,通过javax.servlet.http.HttpSession接口封装,开发者无需手动管理Session的创建、销毁及Session ID的传递。

3.2 Session的核心原理与Java实现

3.2.1 核心原理

注意:Session的底层依赖Cookie传递Session ID,但也支持URL重写(将Session ID拼接在URL后)作为Cookie禁用时的替代方案。

3.2.2 Java实战:Session的创建与使用

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
@WebServlet("/sessionDemo")
public class SessionDemoServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        // 1. 获取Session(若不存在则创建新Session)
        HttpSession session = request.getSession();
        // 获取Session ID
        String sessionId = session.getId();
        out.write("Session ID:" + sessionId + "
");
        // 获取Session创建时间
        Date createTime = new Date(session.getCreationTime());
        out.write("Session创建时间:" + createTime + "
");
        // 获取最后访问时间
        Date lastAccessTime = new Date(session.getLastAccessedTime());
        out.write("最后访问时间:" + lastAccessTime + "
");
        // 2. 向Session中存储数据(支持任意Java对象)
        session.setAttribute("user", new User("zhangsan", 20));
        session.setAttribute("isLogin", true);
        // 3. 从Session中读取数据
        User user = (User) session.getAttribute("user");
        boolean isLogin = (boolean) session.getAttribute("isLogin");
        out.write("当前登录用户:" + user.getName() + ",年龄:" + user.getAge() + "
");
        out.write("登录状态:" + (isLogin ? "已登录" : "未登录") + "
");
        // 4. 设置Session的过期时间(单位:秒)
        // 方式1:通过API设置(优先级高于web.xml配置)
        session.setMaxInactiveInterval(1800); // 30分钟无操作过期
        // 方式2:在web.xml中配置(全局生效)
        /*
        <session-config>
            <session-timeout>30</session-timeout>  <!-- 单位:分钟 -->
        </session-config>
        */
        // 5. 手动销毁Session(用于退出登录)
        // session.invalidate();
        // 6. URL重写(Cookie禁用时使用)
        String url1 = response.encodeURL("/test");
        String url2 = response.encodeRedirectURL("/test");
        out.write("重写后的URL:" + url1 + "
");
        out.close();
    }
    // 自定义User类(需实现Serializable接口,支持Session钝化/活化)
    static class User implements java.io.Serializable {
        private String name;
        private int age;
        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
        // getter/setter
        public String getName() { return name; }
        public int getAge() { return age; }
    }
}
    

3.3 Session的核心机制:钝化与活化

由于Session默认存储在服务器内存中,当服务器重启或Session数量过多时,会导致内存溢出或Session丢失。因此,Servlet容器提供了Session钝化(Passivation)活化(Activation)机制:

注意:要实现Session钝化/活化,Session中存储的对象必须实现java.io.Serializable接口,否则会抛出序列化异常。

3.4 Session的优缺点

优点

缺点

四、Token:无状态的身份凭证

4.1 什么是Token?

Token(令牌)是服务器为客户端生成的加密字符串凭证,包含客户端身份信息、过期时间等核心数据。客户端登录成功后,服务器生成Token并返回给客户端,客户端将Token存储在本地(Cookie、LocalStorage、SessionStorage等),后续请求时通过请求头(如Authorization)携带Token,服务器通过解密Token验证客户端身份,无需在服务器端存储会话状态。

Token是解决分布式架构下Session共享问题的核心方案,主流的Token标准有JWT(JSON Web Token)、OAuth2.0等。在Java开发中,常用JJWT(Java JWT)库实现JWT的生成与验证。

4.2 Token的核心原理(以JWT为例)

JWT由三部分组成,用“.”分隔:Header.Payload.Signature,整体结构如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InpoYW5nc2FuIiwiaWF0IjoxNzEzMzM4NzY0LCJleHAiOjE3MTMzNDIzNjR9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

4.2.1 各部分解析

4.2.2 JWT的核心流程

4.3 Java实战:基于JJWT实现JWT

4.3.1 引入依赖(Maven)

<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>

4.3.2 实现JWT工具类

/**
 * JWT工具类
 */
public class JwtUtil {
    //有效期为
    public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 *1000  一个小时
    //设置秘钥明文
    public static final String JWT_KEY = "qcby";
    /**
     * 创建token
     * @param id  //用户id
     * @param subject  // 用户名
     * @param ttlMillis // 有效期
     * @return
     */
    public static String createJWT(String id, String subject, Long ttlMillis) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        if(ttlMillis==null){
            ttlMillis=JwtUtil.JWT_TTL;
        }
        //签名时间
        long expMillis = nowMillis + ttlMillis;
        Date expDate = new Date(expMillis);
        SecretKey secretKey = generalKey();
        JwtBuilder builder = Jwts.builder()
                .setId(id)              //唯一的ID
                .setSubject(subject)   // 主题  可以是JSON数据
                .setIssuer("wd")     // 签发者
                .setIssuedAt(now)      // 签发时间
                .signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥
                .setExpiration(expDate);// 设置过期时间
        return builder.compact();
    }
    /**
     * 生成加密后的秘钥 secretKey
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }
    /**
     * 解析
     *
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String jwt) throws Exception {
        SecretKey secretKey = generalKey();
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(jwt)
                .getBody();
    }
    public static void main(String[] args) {
        String token = JwtUtil.createJWT(UUID.randomUUID().toString(),"qd",null );
        System.out.println(token);
    }
}

4.4 Token的优缺点

优点

缺点

五、Cookie、Session、Token核心对比与适用场景

5.1 核心维度对比

对比维度

Cookie

Session

Token(JWT)

存储位置

客户端(浏览器)

服务器端(内存/磁盘)

客户端(任意存储方式)

数据大小

单个≤4KB,总数有限

无限制(受服务器内存)

无严格限制(建议不超过1KB,避免请求头过大)

数据类型

仅文本

任意Java对象

文本(JSON格式)

状态管理

客户端状态

服务器端状态

无状态

安全性

较低(易被篡改、XSS攻击)

较高(数据在服务器端)

中高(签名验证,可防篡改)

分布式支持

天然支持(客户端携带)

需额外实现共享(Redis/集群复制)

天然支持(无状态)

实现复杂度

低(Servlet原生支持)

低(容器自动管理)

高(需手动处理生成、验证、刷新)

5.2 适用场景推荐

六、总结与最佳实践

Cookie、Session、Token并非对立关系,而是互补关系,核心目标都是解决HTTP无状态性带来的状态保持问题。在实际开发中,需根据项目架构、安全性要求、客户端类型等因素选择合适的方案:

通过本文的讲解,相信大家对Cookie、Session、Token的底层逻辑、Java实现及适用场景有了全面的理解。在实际开发中,需灵活结合三者的优势,根据项目需求选择最优方案,同时注重安全性设计,避免常见的安全漏洞。

到此这篇关于Java视角下Cookie、Session、Token深度解析与实战的文章就介绍到这了,更多相关java cookie、session、token内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文