token,session,cookie,jwt,oauth2傻傻分不清楚

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料: 

👉这是一个或许对你有用的开源项目

国产Star破10w的开源项目,前端包括管理后台、微信小程序,后端支持单体、微服务架构

RBAC权限、数据权限、SaaS多租户、商城、支付、工作流、大屏报表、ERP、CRMAI大模型、IoT物联网等功能:

  • 多模块:https://gitee.com/zhijiantianya/ruoyi-vue-pro

  • 微服务:https://gitee.com/zhijiantianya/yudao-cloud

  • 视频教程:https://doc.iocoder.cn

【国内首批】支持 JDK17/21+SpringBoot3、JDK8/11+Spring Boot2双版本 

来源:苏三说技术


前言

最近发现有些小伙伴,对Token、Session、Cookie、JWT、OAuth2这些概念非常容易搞混。

有些小伙伴在工作中可能会遇到过这样的困惑:

  • 做登录功能时,到底该用Session还是JWT?

  • OAuth2和Token是什么关系?

  • 为什么有的方案要把Token存在Cookie里?

今天这篇文章专门跟大家一起聊聊这个话题,希望对你会有所帮助。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

一、从餐厅就餐模型开始讲

为了让大家更好理解,我先用一个餐厅就餐的比喻来解释这些概念:

现在,让我们深入每个概念的技术细节。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud

  • 视频教程:https://doc.iocoder.cn/video/

二、Cookie:HTTP的世界身份证

2.1 什么是Cookie?

Cookie是存储在浏览器端的一小段文本数据 ,由服务器通过HTTP响应头的Set-Cookie字段发送给浏览器,浏览器随后会自动在每次请求中通过Cookie头将其带回给服务器。

工作原理 :

2.2 Cookie实战代码

// 服务器设置Cookie
@PostMapping("/login")
public ResponseEntity login(@RequestBody User user, HttpServletResponse response) {
    if (authService.authenticate(user)) {
        Cookie cookie = new Cookie("session_id", generateSessionId());
        cookie.setMaxAge(3600); // 1小时有效期
        cookie.setHttpOnly(true); // 防止XSS攻击
        cookie.setSecure(true); // 仅HTTPS传输
        cookie.setPath("/"); // 对整个站点有效
        response.addCookie(cookie);
        return ResponseEntity.ok().build();
    }
    return ResponseEntity.status(401).build();
}

// 读取Cookie
@GetMapping("/profile")
public ResponseEntity getProfile(@CookieValue("session_id") String sessionId) {
    User user = sessionService.getUserBySession(sessionId);
    return ResponseEntity.ok(user);
}

2.3 Cookie的重要属性

属性

作用

安全建议

HttpOnly

防止JavaScript访问

必须设置为true,防XSS

Secure

仅通过HTTPS传输

生产环境必须设置为true

SameSite

控制跨站请求时是否发送Cookie

建议设置为Strict或Lax

Max-Age

设置Cookie有效期

根据业务安全性要求设置

三、Session:服务端的用户档案

3.1 什么是Session?

Session是存储在服务器端的用户状态信息 。服务器为每个用户创建一个唯一的Session ID,并通过Cookie将这个ID传递给浏览器,浏览器后续请求时带上这个ID,服务器就能识别用户身份。

Session存储结构 :

// 典型的Session数据结构
public class UserSession {
    private String sessionId;
    private String userId;
    private String username;
    private Date loginTime;
    private Date lastAccessTime;
    private Map<String, Object> attributes; // 自定义属性
    
    // 省略getter/setter
}

3.2 Session实战代码

// 基于Spring Session的实现
@PostMapping("/login")
public String login(@RequestParam String username, 
                   @RequestParam String password,
                   HttpSession session) {
    User user = userService.authenticate(username, password);
    if (user != null) {
        // 将用户信息存入Session
        session.setAttribute("currentUser", user);
        session.setAttribute("loginTime", new Date());
        return"redirect:/dashboard";
    }
    return"login?error=true";
}

@GetMapping("/dashboard")
public String dashboard(HttpSession session) {
    // 从Session获取用户信息
    User user = (User) session.getAttribute("currentUser");
    if (user == null) {
        return"redirect:/login";
    }
    return"dashboard";
}

3.3 Session的存储方案

1. 内存存储(默认)

# application.yml
server:
  servlet:
    session:
      timeout: 1800 # 30分钟过期时间

2. Redis分布式存储

@Configuration
@EnableRedisHttpSession // 启用Redis Session存储
public class SessionConfig {
    @Bean
    public LettuceConnectionFactory connectionFactory() {
        return new LettuceConnectionFactory();
    }
}

3. Session集群同步问题

四、Token:去中心化的身份令牌

4.1 什么是Token?

Token是一种自包含的身份凭证 ,服务器不需要在服务端存储会话状态,所有必要信息都包含在Token本身中。

Token vs Session 核心区别 :

4.2 Token实战代码

// 生成Token
public String generateToken(User user) {
    long currentTime = System.currentTimeMillis();
    return JWT.create()
            .withIssuer("myapp") // 签发者
            .withSubject(user.getId()) // 用户ID
            .withClaim("username", user.getUsername())
            .withClaim("role", user.getRole())
            .withIssuedAt(new Date(currentTime)) // 签发时间
            .withExpiresAt(new Date(currentTime + 3600000)) // 过期时间
            .sign(Algorithm.HMAC256(secret)); // 签名密钥
}

// 验证Token
public boolean validateToken(String token) {
    try {
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret))
                .withIssuer("myapp")
                .build();
        DecodedJWT jwt = verifier.verify(token);
        returntrue;
    } catch (JWTVerificationException exception) {
        returnfalse;
    }
}

五、JWT:现代化的Token标准

5.1 什么是JWT?

JWT(JSON Web Token)是一种开放标准(RFC 7519) ,用于在各方之间安全地传输信息作为JSON对象。

这种信息可以被验证和信任,因为它是数字签名的。

JWT结构 :

header.payload.signature

解码示例 :

// Header
{
"alg": "HS256",
"typ": "JWT"
}

// Payload
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622
}

// Signature
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

5.2 JWT实战代码

// 创建JWT
public String createJWT(User user) {
    return Jwts.builder()
            .setHeaderParam("typ", "JWT")
            .setSubject(user.getId())
            .setIssuer("myapp")
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + 3600000))
            .claim("username", user.getUsername())
            .claim("role", user.getRole())
            .signWith(SignatureAlgorithm.HS256, secret.getBytes())
            .compact();
}

// 解析JWT
public Claims parseJWT(String jwt) {
    return Jwts.parser()
            .setSigningKey(secret.getBytes())
            .parseClaimsJws(jwt)
            .getBody();
}

// 在Spring Security中使用JWT
@Component
publicclass JwtFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                  HttpServletResponse response, 
                                  FilterChain chain) {
        String token = resolveToken(request);
        if (token != null && validateToken(token)) {
            Authentication auth = getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(auth);
        }
        chain.doFilter(request, response);
    }
}

5.3 JWT的最佳实践

1. 安全存储

// 前端安全存储方案
// 不推荐:localStorage(易受XSS攻击)
// 推荐:HttpOnly Cookie(防XSS)或内存存储

2. 令牌刷新机制

// 双Token机制:Access Token + Refresh Token
publicclass TokenPair {
    private String accessToken;  // 短期有效:1小时
    private String refreshToken; // 长期有效:7天
}

// 刷新令牌接口
@PostMapping("/refresh")
public ResponseEntity refresh(@RequestBody RefreshRequest request) {
    String refreshToken = request.getRefreshToken();
    if (validateRefreshToken(refreshToken)) {
        String userId = extractUserId(refreshToken);
        String newAccessToken = generateAccessToken(userId);
        return ResponseEntity.ok(new TokenPair(newAccessToken, refreshToken));
    }
    return ResponseEntity.status(401).build();
}

六、OAuth 2.0:授权框架之王

6.1 什么是OAuth 2.0?

OAuth 2.0是一个授权框架 ,允许第三方应用在获得用户授权后,代表用户访问受保护的资源。

OAuth 2.0角色 :

  • 资源所有者(Resource Owner) :用户

  • 客户端(Client) :第三方应用

  • 授权服务器(Authorization Server) :颁发访问令牌

  • 资源服务器(Resource Server) :托管受保护资源

6.2 OAuth 2.0授权码流程

6.3 OAuth 2.0实战代码

// Spring Security OAuth2配置
@Configuration
@EnableAuthorizationServer
publicclass AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    
    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("clientapp")
                .secret(passwordEncoder.encode("123456"))
                .authorizedGrantTypes("authorization_code", "refresh_token")
                .scopes("read", "write")
                .redirectUris("http://localhost:8080/callback");
    }
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager)
                .tokenStore(tokenStore())
                .accessTokenConverter(accessTokenConverter());
    }
}

// 资源服务器配置
@Configuration
@EnableResourceServer
publicclass ResourceServerConfig extends ResourceServerConfigurerAdapter {
    
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/api/public/**").permitAll()
                .antMatchers("/api/private/**").authenticated()
                .antMatchers("/api/admin/**").hasRole("ADMIN");
    }
}

七、五大概念对比

为了让大家更清晰地理解这五个概念的关系和区别,我准备了以下对比表格:

7.1 功能定位对比

概念

本质

存储位置

主要用途

特点

Cookie

HTTP状态管理机制

浏览器

维持会话状态

自动携带,有大小限制

Session

服务端会话信息

服务器

存储用户状态

服务端状态,需要存储管理

Token

访问凭证

客户端/服务端

身份认证

自包含,可验证

JWT

Token的一种实现标准

客户端/服务端

安全传输信息

标准化,自包含,可签名

OAuth2

授权框架

不直接存储

第三方授权

标准化授权流程

7.2 应用场景对比

场景

推荐方案

原因说明

传统Web应用

Session + Cookie

简单易用,生态成熟

前后端分离应用

JWT

无状态,适合API认证

第三方登录

OAuth 2.0

标准化授权,安全可靠

微服务架构

JWT

分布式认证,无需会话同步

移动端应用

Token

轻量级,适合移动网络

7.3 安全考虑对比

安全威胁

Cookie方案防护

Token/JWT方案防护

XSS攻击

HttpOnly Cookie

避免localStorage存储

CSRF攻击

SameSite Cookie

自定义Header+CSRF Token

令牌泄露

短期有效+HTTPS

短期有效+HTTPS+刷新机制

数据篡改

服务端验证

签名验证

总结

通过今天的深入探讨,我们可以得出以下结论:

  1. Cookie是载体 :HTTP协议的状态管理机制,是Session和Token的传输媒介之一。

  2. Session是状态 :服务端维护的会话状态,需要借助Cookie或URL重写来实现。

  3. Token是凭证 :认证授权的凭证,可以放在Cookie、Header或URL中。

  4. JWT是标准 :Token的一种标准化实现,自包含、可验证、可信任。

  5. OAuth2是框架 :授权框架,定义了完整的第三方授权流程。

最终建议 :

  • 简单Web应用:Session + Cookie

  • 前后端分离:JWT + HTTP Header

  • 第三方授权:OAuth 2.0 + JWT

没有最好的方案,只有最合适的方案。

理解每个技术的本质和适用场景,才能做出正确的架构决策。


欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
下载代码方式:https://pan.quark.cn/s/e6c2e312b658 在苹果公司的Mac操作系统环境中,当用户尝试安装非原厂驱动程序时,可能会遭遇系统无法正常启动的困境。这种情况常常源于名为.kext的内核扩展驱动程序存在兼容性问题或安装过程中出现失误。这份指南介绍了一种无需重新安装操作系统且能够保护所有用户数据的修复方法,这一方案对于先前许多面临类似挑战的用户而言,曾是极为棘手的情况。文档中提及的“用户模式启动”实际是指单用户模式,这种启动方式仅加载核心系统功能,而忽略图形用户界面及常规应用程序的加载。在单用户模式下,用户能够访问命令行界面,进而执行一系列修复指令。解决此问题的首要环节是验证存储设备是否存在故障,因为这是导致系统无法启动的常见诱因。借助终端指令`/sbin/fsck -f`,可以诊断并纠正文件系统层面的错误。倘若系统在启动过程中检测到文件系统异常,通常会自动执行`fsck`命令,然而,如果系统卡在进度条100%无法继续,手动运行该命令则显得尤为必要。指令`mount -uw /`的功能是将根目录切换为可读写状态,由于系统默认是以只读模式启动的。这一操作的目的是为了在不重新进入正常模式的前提下,对系统进行必要的调整。随后,文档提供了一个关键操作:对存在问题的驱动程序文件进行修改或更名。在Mac系统中,第三方驱动程序一般安装在`/Library/Extensions/`目录下。每个驱动程序都包含一个以.kext为后缀名的文件夹,例如在此案例中的AX88772.kext。通过命令行将故障的.kext文件更名(例如改为.kext.bak),可以临时禁用该驱动程序。这一操作需在命令行环境中完成,首先使用`cd /Library/Exte...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值