会话技术

一次会话包括若干次连接,一次连接即一次请求和响应。

1. Cookie

优点:HTTP协议自有的技术,嵌入在请求头(键cookie)和响应头(键set-cookie)中,所有的键值对都存储在浏览器端。
缺点:
a. 移动端app无法使用cookie
b. 不安全,用户可以自行禁止cookie,这样就无法使用cookie保持会话了
c. cookie不能在跨域的场合中使用

示例代码:

import edu.recipePost.pojo.Result;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class SessionController {

    // 设置cookie
    @GetMapping("/get-cookie")
    public Result getCookie(HttpServletResponse response) { // 从HttpServletResponse获取响应头
        Cookie cookie = new Cookie("cookie_key_name", "value"); // 创建cookie
        cookie.setMaxAge(3600*24*7); //cookie设置生命周期,单位是秒
        cookie.setPath("/cookie/age"); //cookie设置路径
        response.addCookie(cookie); //response对象添加cookie
        return Result.success();
    }

    // 获取cookie
    @GetMapping("/other")
    public Result setCookie(HttpServletRequest request) { // 从HttpServletRequest获取请求头
        Cookie[] cookies = request.getCookies(); //获取cookie对象
        for (Cookie cookie:cookies) {
            if(cookie.getName().equals("cookie_key_name")) {
                log.info("cookie_key_name:"+cookie.getValue()); // 数值cookie对应的键值
            }
        }
        return Result.success();
    }

}

2. Session

概述:浏览器的cookie中存储SessionID,请求头的cookie和响应头的set-cookie传输的都是SessionID,所有的键值对都存储在服务器端,服务器通过浏览器传来的SessionID来找到Session对象,Session对象中存储有一次会话的键值对。

优点:数据(键值对)存储在服务器端,比较安全
缺点:
a. 目前大型项目都是服务器集群,服务器集群环境下无法直接使用Session。原因:两次访问会负载均衡到不同的服务器,导致Session对象分布在各个服务器,各服务器之间的Session对象不同步。
b. Session技术依靠HTTP协议的Cookie实现,所以Cookie的缺点也是Session的缺点。

与Cookie区别:
a. Cookie技术键值对存储在浏览器,Session技术键值对存储在服务器;
b. Session技术浏览器的cookie里只存有SessionID,通过SessionID请求服务器,找到服务器的Session对象,从而找到此次会话的键值对。

示例代码:

import edu.recipePost.pojo.Result;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class SessionController {

    // 第一次访问时,往HttpSession存储值
    @GetMapping("/set-session")
    public Result getSession(HttpSession session) { // 获取会话对象session,若不存在则创建session;存在就获取当前请求对应的session
        log.info("HttpSession-test1: "+session.hashCode()); // 同次会话HttpSession-test1: 1675666366
        session.setAttribute("loginUser", "tom"); // session存值
        return Result.success();
    }

    // 从HttpSession获取值
    @GetMapping("/get-session")
    public Result setCookie(HttpServletRequest request) { // 通过请求头获取会话对象,也可以通过HttpSession获取会话对象
        HttpSession session = request.getSession(); // 通过请求头获取会话对象
        log.info("HttpSession-test2: "+session.hashCode()); // 同次会话HttpSession-test1: 1675666366
        Object loginUser = session.getAttribute("loginUser");
        log.info("loginUser: "+loginUser); // loginUser: tom
        return Result.success(loginUser);
    }

}

3. JWT令牌

JSON Web Token
JWT_structure.png
结构:
由“.”分成三部分:
第一部分:描述令牌类型、签名算法的Header => 经过JSON封装 => 经过base64编码
第二部分:自定义信息和默认信息的Payload(有效载荷) => 经过JSON封装 => 经过base64编码
第三部分:数字签名,前两部分和指定密钥经过签名算法得到。防止被篡改。

相比于Cookie和Session,有以下优点:
a. 不仅支持Web,还支持PC端、移动端
b. 解决集群服务器环境下的认证问题
c. 减轻服务器端的存储压力
缺点:非HTTP协议天然生成,需要自己实现令牌的生成、存储与传输

Maven依赖:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version></version>
</dependency>

实例代码:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;

import java.util.Date;
import java.util.Map;

public class JWTUtils {

    @Value("${JWT.web.login.sign-key}")
    private static String signKey;

    @Value("${JWT.web.login.expire}")
    private static Long expire;

    public static String generateJWT(Map<String, Object> claims) {
        return Jwts.builder()
                .addClaims(claims) // 添加JSON实体数据
                .signWith(SignatureAlgorithm.HS256, signKey) // 签名算法,签名密钥
                .setExpiration(new Date(System.currentTimeMillis()+expire)) // token过期时间
                .compact();
    }

    public static Claims parseJWT(String jwt) {
        return Jwts.parser() // JWT解析器
                .setSigningKey(signKey) // 添加签名密钥
                .parseClaimsJws(jwt) // 解析JWT令牌
                .getBody();
    }

}

标签: none

添加新评论