Token,Session,Cookie,Jwt,Oauth2傻傻分不清楚
前言
最近發(fā)現(xiàn)有些小伙伴,對Token、Session、Cookie、JWT、OAuth2這些概念非常容易搞混。
有些小伙伴在工作中可能會遇到過這樣的困惑:
- 做登錄功能時,到底該用Session還是JWT?
 - OAuth2和Token是什么關(guān)系?
 - 為什么有的方案要把Token存在Cookie里?
 
今天這篇文章專門跟大家一起聊聊這個話題,希望對你會有所幫助。
一、從餐廳就餐模型開始講
為了讓大家更好理解,我先用一個餐廳就餐的比喻來解釋這些概念:
圖片
現(xiàn)在,讓我們深入每個概念的技術(shù)細(xì)節(jié)。
二、Cookie:HTTP的世界身份證
2.1 什么是Cookie?
Cookie是存儲在瀏覽器端的一小段文本數(shù)據(jù),由服務(wù)器通過HTTP響應(yīng)頭的Set-Cookie字段發(fā)送給瀏覽器,瀏覽器隨后會自動在每次請求中通過Cookie頭將其帶回給服務(wù)器。
工作原理:
圖片
2.2 Cookie實戰(zhàn)代碼
// 服務(wù)器設(shè)置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訪問  | 必須設(shè)置為true,防XSS  | 
Secure  | 僅通過HTTPS傳輸  | 生產(chǎn)環(huán)境必須設(shè)置為true  | 
SameSite  | 控制跨站請求時是否發(fā)送Cookie  | 建議設(shè)置為Strict或Lax  | 
Max-Age  | 設(shè)置Cookie有效期  | 根據(jù)業(yè)務(wù)安全性要求設(shè)置  | 
三、Session:服務(wù)端的用戶檔案
3.1 什么是Session?
Session是存儲在服務(wù)器端的用戶狀態(tài)信息。服務(wù)器為每個用戶創(chuàng)建一個唯一的Session ID,并通過Cookie將這個ID傳遞給瀏覽器,瀏覽器后續(xù)請求時帶上這個ID,服務(wù)器就能識別用戶身份。
Session存儲結(jié)構(gòu):
// 典型的Session數(shù)據(jù)結(jié)構(gòu)
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實戰(zhàn)代碼
// 基于Spring Session的實現(xiàn)
@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. 內(nèi)存存儲(默認(rèn))
# 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是一種自包含的身份憑證,服務(wù)器不需要在服務(wù)端存儲會話狀態(tài),所有必要信息都包含在Token本身中。
Token vs Session 核心區(qū)別:
圖片
4.2 Token實戰(zhàn)代碼
// 生成Token
public String generateToken(User user) {
    long currentTime = System.currentTimeMillis();
    return JWT.create()
            .withIssuer("myapp") // 簽發(fā)者
            .withSubject(user.getId()) // 用戶ID
            .withClaim("username", user.getUsername())
            .withClaim("role", user.getRole())
            .withIssuedAt(new Date(currentTime)) // 簽發(fā)時間
            .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:現(xiàn)代化的Token標(biāo)準(zhǔn)
5.1 什么是JWT?
JWT(JSON Web Token)是一種開放標(biāo)準(zhǔn)(RFC 7519),用于在各方之間安全地傳輸信息作為JSON對象。
這種信息可以被驗證和信任,因為它是數(shù)字簽名的。
JWT結(jié)構(gòu):
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實戰(zhàn)代碼
// 創(chuàng)建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)或內(nèi)存存儲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:授權(quán)框架之王
6.1 什么是OAuth 2.0?
OAuth 2.0是一個授權(quán)框架,允許第三方應(yīng)用在獲得用戶授權(quán)后,代表用戶訪問受保護(hù)的資源。
OAuth 2.0角色:
- 資源所有者(Resource Owner):用戶
 - 客戶端(Client):第三方應(yīng)用
 - 授權(quán)服務(wù)器(Authorization Server):頒發(fā)訪問令牌
 - 資源服務(wù)器(Resource Server):托管受保護(hù)資源
 
6.2 OAuth 2.0授權(quán)碼流程
圖片
6.3 OAuth 2.0實戰(zhàn)代碼
// 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());
    }
}
// 資源服務(wù)器配置
@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");
    }
}七、五大概念對比
為了讓大家更清晰地理解這五個概念的關(guān)系和區(qū)別,我準(zhǔn)備了以下對比表格:
7.1 功能定位對比
概念  | 本質(zhì)  | 存儲位置  | 主要用途  | 特點  | 
Cookie  | HTTP狀態(tài)管理機制  | 瀏覽器  | 維持會話狀態(tài)  | 自動攜帶,有大小限制  | 
Session  | 服務(wù)端會話信息  | 服務(wù)器  | 存儲用戶狀態(tài)  | 服務(wù)端狀態(tài),需要存儲管理  | 
Token  | 訪問憑證  | 客戶端/服務(wù)端  | 身份認(rèn)證  | 自包含,可驗證  | 
JWT  | Token的一種實現(xiàn)標(biāo)準(zhǔn)  | 客戶端/服務(wù)端  | 安全傳輸信息  | 標(biāo)準(zhǔn)化,自包含,可簽名  | 
OAuth2  | 授權(quán)框架  | 不直接存儲  | 第三方授權(quán)  | 標(biāo)準(zhǔn)化授權(quán)流程  | 
7.2 應(yīng)用場景對比
場景  | 推薦方案  | 原因說明  | 
傳統(tǒng)Web應(yīng)用  | Session + Cookie  | 簡單易用,生態(tài)成熟  | 
前后端分離應(yīng)用  | JWT  | 無狀態(tài),適合API認(rèn)證  | 
第三方登錄  | OAuth 2.0  | 標(biāo)準(zhǔn)化授權(quán),安全可靠  | 
微服務(wù)架構(gòu)  | JWT  | 分布式認(rèn)證,無需會話同步  | 
移動端應(yīng)用  | Token  | 輕量級,適合移動網(wǎng)絡(luò)  | 
7.3 安全考慮對比
安全威脅  | Cookie方案防護(hù)  | Token/JWT方案防護(hù)  | 
XSS攻擊  | HttpOnly Cookie  | 避免localStorage存儲  | 
CSRF攻擊  | SameSite Cookie  | 自定義Header+CSRF Token  | 
令牌泄露  | 短期有效+HTTPS  | 短期有效+HTTPS+刷新機制  | 
數(shù)據(jù)篡改  | 服務(wù)端驗證  | 簽名驗證  | 
總結(jié)
通過今天的深入探討,我們可以得出以下結(jié)論:
- Cookie是載體:HTTP協(xié)議的狀態(tài)管理機制,是Session和Token的傳輸媒介之一。
 - Session是狀態(tài):服務(wù)端維護(hù)的會話狀態(tài),需要借助Cookie或URL重寫來實現(xiàn)。
 - Token是憑證:認(rèn)證授權(quán)的憑證,可以放在Cookie、Header或URL中。
 - JWT是標(biāo)準(zhǔn):Token的一種標(biāo)準(zhǔn)化實現(xiàn),自包含、可驗證、可信任。
 - OAuth2是框架:授權(quán)框架,定義了完整的第三方授權(quán)流程。
 
最終建議:
- 簡單Web應(yīng)用:Session + Cookie
 - 前后端分離:JWT + HTTP Header
 - 第三方授權(quán):OAuth 2.0 + JWT
 
沒有最好的方案,只有最合適的方案。
理解每個技術(shù)的本質(zhì)和適用場景,才能做出正確的架構(gòu)決策。















 
 
 









 
 
 
 