美文网首页个人学习
Redis 分布式session

Redis 分布式session

作者: 香沙小熊 | 来源:发表于2020-02-12 18:21 被阅读0次

为什么需要session

http协议是一个无状态协议,某些场景我需要session和cookie去记录一些数据。

Session生成时间

第一次使用时,第一次获取如果Session不存在,会生成1个新的Session
第一次访问JSP,访问JSP时会注入九大域对象,其中包括Session

cookie与session

cookie是什么

存储在用户主机浏览器中的一小段文本文件
有服务器生成,发送给浏览器,KV形式存储

session是什么

字面理解为“会话”
保存在服务端的数据结构

   protected Map<String, Session> sessions = new ConcurrentHashMap<>();
   //...
    public Session findSession(String id) throws IOException {
        if (id == null) {
            return null;
        }
        return sessions.get(id);
    }
  //...
服务器通过请求中的Cookies识别回话

Tomcat Request

    protected Session doGetSession(boolean create) {

        // There cannot be a session if no context has been assigned yet
        Context context = getContext();
        if (context == null) {
            return null;
        }

        // Return the current session if it exists and is valid
        if ((session != null) && !session.isValid()) {
            session = null;
        }
        if (session != null) {
            return session;
        }

        // Return the requested session if it exists and is valid
        Manager manager = context.getManager();
        if (manager == null) {
            return null;      // Sessions are not supported
        }
        if (requestedSessionId != null) {
            try {
                session = manager.findSession(requestedSessionId);
            } catch (IOException e) {
                session = null;
            }
            if ((session != null) && !session.isValid()) {
                session = null;
            }
            if (session != null) {
                session.access();
                return session;
            }
        }
//....
    }


Tomcat CoyoteAdapter

    /**
     * Parse session id in Cookie.
     *
     * @param request The Servlet request object
     */
    protected void parseSessionCookiesId(Request request) {

        // If session tracking via cookies has been disabled for the current
        // context, don't go looking for a session ID in a cookie as a cookie
        // from a parent context with a session ID may be present which would
        // overwrite the valid session ID encoded in the URL
        Context context = request.getMappingData().context;
        if (context != null && !context.getServletContext()
                .getEffectiveSessionTrackingModes().contains(
                        SessionTrackingMode.COOKIE)) {
            return;
        }

        // Parse session id from cookies
        ServerCookies serverCookies = request.getServerCookies();
        int count = serverCookies.getCookieCount();
        if (count <= 0) {
            return;
        }

        String sessionCookieName = SessionConfig.getSessionCookieName(context);

        for (int i = 0; i < count; i++) {
            ServerCookie scookie = serverCookies.getCookie(i);
            if (scookie.getName().equals(sessionCookieName)) {
                // Override anything requested in the URL
                if (!request.isRequestedSessionIdFromCookie()) {
                    // Accept only the first session id cookie
                    convertMB(scookie.getValue());
                    request.setRequestedSessionId
                        (scookie.getValue().toString());
                    request.setRequestedSessionCookie(true);
                    request.setRequestedSessionURL(false);
                    if (log.isDebugEnabled()) {
                        log.debug(" Requested cookie session id is " +
                            request.getRequestedSessionId());
                    }
                } else {
                    if (!request.isRequestedSessionIdValid()) {
                        // Replace the session id until one is valid
                        convertMB(scookie.getValue());
                        request.setRequestedSessionId
                            (scookie.getValue().toString());
                    }
                }
            }
        }

    }
单体Session生命周期
单体Session生命周期
  1. 创建:第1次使用request.getSession() 会创建新的会话(并且将id写入到浏览器Cookies中)
  2. 更新:每次请求都会携带id获取相同的Session,并且更新其最后的访问时间
  3. 销毁:容器启动时会开启线程时(默认10s)检测Session是否过期,如果过期将会销毁

cookie与session的区别

数据 存储位置 大小限制 是否安全
cookie 用户浏览器 50个/4K
(httponly/https)
session 服务器
(内存、文件、缓存)
取决于存储介质
@RestController
public class LoginController {


    static Map<String, String> passwordMap = new HashMap<>();

    static {
        passwordMap.put("zhangsan", "123");
        passwordMap.put("lisi", "321");
    }

    @RequestMapping("/index")
    @ResponseBody
    public String index(HttpServletRequest request) {
        HttpSession httpSession = request.getSession();
        Boolean isLogin = (Boolean) httpSession.getAttribute("isLogin");
        if (isLogin != null && isLogin) {
            return "欢迎 " + httpSession.getAttribute("username") + " 进入首页";
        } else {
            return "请登录!";
        }

    }

    @RequestMapping("/login")
    @ResponseBody
    public String login(HttpServletRequest request,
                        @RequestParam("username") String username,
                        @RequestParam("password") String password) {
        //1、验证用户名和密码
        if (!passwordMap.containsKey(username)) {
            return "请先注册";
        }

        if (!passwordMap.get(username).equals(password)) {
            return "密码错误";
        }

        //2.生成session
        HttpSession httpSession = request.getSession();
        //3.将登陆信息写入session
        httpSession.setAttribute("isLogin", Boolean.TRUE);
        httpSession.setAttribute("username", username);
        //4.返回
        return "welcome " + username;
    }
}

http://localhost:8081/login?username=lisi&password=321

分布式session
分布式环境session失效问题
session粘粘
session复制
session集中式存储

redis实现分布式session

1.session 粘粘(固定分配)
session 粘粘

优点:实现简单
缺点:可扩展性差、分配不均衡

2.session 复制(会话同步)
session 复制

缺点:
每台机器存储全量数据,比较占机器内存
session同步当机器较多时,可能造成广播风暴
session同步占用一定的网络带宽

3.session 集中式存储(会话共享)
session 集中式存储
        <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>
            <version>2.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>
                org.springframework.security
            </groupId>
            <artifactId>spring-security-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>

        </dependency>
        <dependency>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </dependency>

本质上利用Tomcat的Filter的实现类SpringSessionRepositoryFilter实现了对每一次请求的拦截,拦截之后把Session放到Redis里面

允许跨域访问
    /**
     * 因为服务端返回给客户端的set-cookie中带有samesite=lax,
     * 这就是问题的根源,它表示不能携带cookie进行跨域post访问,
     *    然而我们是需要携带cookie的
     *
     * @return
     */
    @Bean
    public CookieSerializer httpSessionIdResolver() {
        DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();

        cookieSerializer.setCookieName("SESSION");

        cookieSerializer.setUseHttpOnlyCookie(false);

        cookieSerializer.setSameSite(null);

        return cookieSerializer;
    }
4.session 统一认证中心(token)

相关文章

网友评论

    本文标题:Redis 分布式session

    本文链接:https://www.haomeiwen.com/subject/mgssxhtx.html