zoukankan      html  css  js  c++  java
  • 理解 Web 中的Session

    ===================================
    Session 工作原理是什么?
    ===================================
    因为 http 协议是无状态的, 对于服务器端来讲, 如何为不同的访问用户提供不一样的体验呢? 比如邮箱系统, 只有登录用户才能收发邮件.
    这就需要服务器能识别每一个客户端访问, 知道哪些访问是来自一个同一个客户端, 显然这个事情光靠服务器端是做不到的, 需要浏览器配合才行, 浏览器端 cookie 概念就这么产生了.
    Session 工作原理:
    (1) 当一个 Session 第一次被启动时, 服务器端先产生一个唯一的 session id, 并为该 session id 分配一个内存区, 用来保存该 session 相关的信息. 然后将该 id 存储到浏览器的 cookie 中, cookie name 为 JSESSIONID.
    (2) 浏览器每次访问, 都会带上本域下所有的 cookie, 包括这个 JSESSIONID cookie(应该是放到 Http Header 上的), 服务器端通过对比 JSESSIONID 和服务器内存中的 session id, 就能识别哪些访问是来自同一个客户端.
    (3) 服务器端在每次访问中, 都可以在 JSESSIONID 对应的 session 内存区中记录一些信息, 比如最后一次的访问时间, 比如访问 IP 值, 以满足业务需求.


    ===================================
    Session 的用途有哪些?
    ===================================
    1. 核心功能: 是识别每一个访问.
    2. 基本功能: 做登录验证, 然后就能得到登录 id, 进而能完成权限验证.
    核心功能和基本功能都由 Spring Web 框架提供, 或 Shiro 这样安全框架提供, 我们的代码不用直接处理这些事情.
    3. 利用 Session, 可以衍生出更多的功能, 比如保存访客上次访问的 IP 和上次访问时间, 比如保存 user Id 和 user name, 这里稍微要多想一下:
    (1). 这些数据既可以保存到 DB 中, 也可以保存到 Session 内存中, user id 和 user name 在 DB 中本来就保存着, 有必要在 session 中保存吗?
    Session 的优点是访问速度快, 缺点是用户量大的情况下内存占用是个问题.
    (2). 这些数据既可以保存到 cookie , 也可以保存到 Session 内存中, 哪个更适合?
    Cookie 的优点是: 不占用服务器端内存, Session 占服务器内存并且重启后就没了, cookie 可以被js访问到, 常用的网页埋点就是把一些标记信息保存到cookie, 然后由js提交到埋点服务器中.
    Cookie 的缺点是, cookie 数据如果较大, 会消耗较多的网络带宽; 用户随意修改 cookie 值, 这样的话, cookie的数据就不太可靠了; 另外, 用户换个浏览器或者浏览器清个缓存, 原有的cookie就没法用了.
    这些问题都没有标准答案, 按照具体情形做选择吧.

    ===================================
    JSESSIONID Cookie 是到底是在什么时候生成?
    ===================================
    JSESSIONID Cookie 并不是在第一次访问网站时候立即生成的, 只有访问 url 对应视图函数中有 Session 相应操作时, 浏览器端才会创建 JSESSIONID cookie 的.
    当然, 一般网站在首页访问的视图函数基本都会有 session 操作, 所以给我们的错觉是, 一旦新访问一个网站, JSESSIONID cookie 就自动产生了.

    比如访问 http://localhost:8080/ 主页,  访问第一个测试代码的首页, 并不会创建 JSESSIONID cookie. 

    // 该 url 请求没有任何与 session 相关的操作, 浏览器端不会创建该网站的 JSESSIONID cookie
    @GetMapping("/")
    public String index(HttpServletRequest request) throws SQLException {
        return "OK";
    }
    
    // 在 url 请求中, 有获取 session 对象的指令, 浏览器端会创建该网站的 JSESSIONID cookie
    @GetMapping("/")
    public String index(HttpServletRequest request) throws SQLException {
        request.getSession(); //获取 session 对象
        return "OK";
    }    
    
    // 在 url 请求中, 视图函数要注入 session 对象, 浏览器端会创建该网站的 JSESSIONID cookie
    @GetMapping("/")
    public String index(HttpSession session) throws SQLException {
        return "OK";
    }   

    ===================================
    JSESSIONID 的 ID 值会登录后会变化吗?
    ===================================
    假设在登录之前, 浏览器端已经有了 JSESSIONID cookie. 现在问题来了, 在用户登录之后, 该 JSESSIONID ID 值是否变化?
    答案是: 早期网站, 登录前后 JSESSIONID 是不变的, 但后来发现, 如果登录前后 JSESSIONID 不变, 会有中间人攻击风险. 所以现在的安全框架在登录完成后, 会自动修改 JSESSIONID 值. 我们开发网站不需要再专门修改 JSESSIONID.

    和登录不一样, 登录退出 JSESSIONID 的 Id 值并不会改变.


    ===================================
    Session 和 cookie 之间的关系?
    ===================================
    前面已经提到为了标识同一个客户端访问, 光靠服务器端的 Session 是不够的, 还需要客户端 JSESSIONID Cookie 支持. 这体现了 Session 和 cookie 之间最重要的一个关系.
    另外, 在 Web 开发中 Session 和 Cookie 有一些很类似的方法, 往往会引起混淆, 有必要区别一下.
    1. Session:

    •  保存在服务器端内存中, 服务器重启所有的 session 都没了.
    • session.setAttribute() 是在服务端内存中保存一个属性, 并不会将它保存到浏览器端的 Cookie 中.
    • 可以在一个 session id 下新建多个属性.
    • session 的有效期是针对所有 session id 全局级别的, 不是单个 session id 级, 也不是属性级的.
    • Session 中 id 和对应的属性, 可以大致理解为从属关系, 这些属性属于一个 id.

    2. Cookie:

    • 保存在浏览器本地存储中, 服务器重启并不会影响 cookie.
    • 一个网站下可以包含多个 cookie , 最重要的那个是 JSESSIONID Cookie. 新建一个 cookie 并不会在服务器端生成一个对应的属性.
    • 每个 cookie 都可以设置自己的有效期, 自己的 HttpOnly 属性.
    • 一个网站的 JSESSIONID Cookie 和其他 cookie 并没有从属关系

    ===================================
    Spring 中 Session 和 cookie 的操作方式
    ===================================

    Spring Session操作很简单:

    • //获取 session 对象, 或者视图函数中直接注入 HttpSession 对象
    • request.getSession()
    • //设置一个 session 属性
    • session.setAttribute()
    • //读取一个 session 属性
    • session.getAttribute()

    1. 在视图函数中使用 @CookieValue 修饰形参, 可注入一个 cookie 属性值.

        @RequestMapping("/read")
        @ResponseBody
        public String read(@CookieValue(value = "foo", defaultValue = "hello") String fooCookie) {
            System.out.println(fooCookie);
            return fooCookie;
        }

    2. 使用 HttpServletResponse.addCookie() 完成 cookie 的写入. 

        @RequestMapping("/write")
        @ResponseBody
        public String write(HttpServletResponse response) {
            Cookie foo = new Cookie("foo", "bar"); //bake cookie
            foo.setMaxAge(1000); //set expire time to 1000 sec
            response.addCookie(foo); //put cookie in response
            return "write done";
        }

    ==================================
    ajax 调用过程中是否会附加cookie信息?
    ==================================
    如果 web 应用通过 jquery ajax 调用后台 api url, 调用过程是否会附加cookie信息, 要看情况:
    1. 如果是同域, ajax 请求会自动带上本域的cookie, 这台web服务器本身也有相应的 session 信息, 所以ajax请求自然能通过 session-cookie 完成身份验证, 整个过程非常自然.
    2. 如果 ajax调用的api 是另一个域下的url, 因为不是同域, 需要手动在ajax调用时加上cookie, 可参考:https://blog.csdn.net/wzl002/article/details/51441704, 服务器端返回response也需要做相应的处理, 另外也需要引入分布式session存储. 

    ==================================
    SSO/JWT/Session/OAuth2和web项目安全检查的关系
    ==================================
    1. OAuth2: 它是一个第三方认证机制, 适合To C的应用场景, 比如我们开发一个app, 可以借用微信/微博用户认证开放接口, 达到免注册登陆, 企业内部系统一般没必要引入该技术.
    2. SSO: 有两个作用, 1: 保持登陆状态, 2: 识别 username
    3. JWT: 它是一个 token 生成技术, 可以包含 username 等信息.
    4. Session: Session 一般用在web application中, 用来识别访问者.

    一般项目认证方式:
    1. 对于 web application 项目, 一般使用 Session 来识别访问者, 开发容易, 可以采用普通的 Login form 登陆, 也可以采用SSO登陆.
    SSO 登陆详细过程, 可参考张开涛博客<<shiro 单点登录>> https://www.vxzsk.com/1197.html

    2. 对于微服务项目, 一般和 OAuth2/Session 无关, 多数基于 jwt 的无状态认证机制, 通过 jwt 来识别访问者, 不过还是推荐将jwt前移到api 网关较好, 微服务仅仅关注业务逻辑.
    完整微服务认证过程是: SSO登陆验证通过后, SSO回调到统一auth服务来生成一个jwt token, 并传给客户端, 客户端可将 jwt token保存到本地(web 客户端可保存到 cookie 中), 每次客户端请求需要将 jwt token 加到 http Authorization header上, 服务器端仅需要 jwt 算法就可以完成验证(通常不会再查询数据库验证), 并能提取 username 信息.
    详细过程和源码, 可参考下面几个文章:
        张开涛博客<<shiro 无状态Web集成>>
            https://www.vxzsk.com/1233.html
        详细shiro jwt 构建无状态分布式鉴权体系
            https://www.jianshu.com/p/0a5d3d07a151
            https://www.jianshu.com/p/b0a577708a7b
        Shiro+JWT+Spring Boot Restful简易教程
            https://www.jianshu.com/p/f37f8c295057

  • 相关阅读:
    总结7.19 laravel验证码
    java学习day74--Redis常用命令
    java学习day74--JT项目12(Redis缓存/spring boot整合redis)
    java学习day73-JT项目11(数据库高可用/读写分离/负载均衡)
    疯狂学java的第27天
    xtrabackup全备+binlog模拟slave恢复到某一时间点-启用GTID
    读写文件
    goroutine、chan、select、互斥、读写互斥
    接口

  • 原文地址:https://www.cnblogs.com/harrychinese/p/web_session_cookie.html
Copyright © 2011-2022 走看看