飞道的博客

[ 成为架构师系列 ] 2. 深入理解 Cookie 与 Session ,Facade 设计模式, 分布式 Session...

461人阅读  评论(0)

1.什么是 Cookie

Cookie,有时也用其复数形式 Cookies。类型为“小型文本文件”,是某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息

打开百度首页:https://www.baidu.com/

浏览器 console 中输入:

> document.cookie

"BAIDUID=40E9A74CB78CA05206FD128BEB27E014:FG=1; PSTM=1568883435; BIDUPSID=4927A15EB7E602CFABA425A910D5136C; BD_UPN=123253; BDSFRCVID=OvtOJeC62C6a1m7wlk3EjPnnWgA5FqRTH6aowb_SsXNp6D3o4q0aEG0P_U8g0Kub2VhkogKKKgOTHICF_2uxOjjg8UtVJeC6EG0Ptf8g0M5; H_BDCLCKID_SF=tJAj_D-btK03fP36qR6sMJ8thmT22-ustN5RQhcH0hOWsIOF3-Rj5U-q-PneQMc4WjrG-pLXbtQCqj69DUC0DjO-jaKOJjFsb5vfstbHatnjDb7GbKTjhPrM0HQiWMT-0bFHLRO_BxJofUoeDRQY3TkW0tjnbRof-Hn7_JjCbb5Mhq5oMPKh3TtObMrMWUQxtNR--CnjtpvhKJ3D3-oobUPUyUJ9LUvA02cdot5yBbc8eIna5hjkbfJBQttjQn3hfIkj2CKLtC8WhD_mDjRV5-JH-xQ0KnLXKKOLVb38Wh7keq8CDR76QU4q-lJQa5Jd2HRXWf3jJUn_jtQ2y5jHhnIDhUcRXJ3vtaRz0RjFKxTpsIJMMl_WbT8U5ecgJfRuaKviahvjBMb1OqODBT5h2M4qMxtOLR3pWDTm_q5TtUJMeCnTDMFhe6jyDNADJ6FDf5vfL5uat4bqqPbYh4t_hnDsePnq-URZ5mAqoq8KKCjVMP31MTbpMl8HhRJM36Ql-GrnaIQqa-3D_UORWMT2jx3yhNODJPo43bRTMMKy5KJvfJ_4347OhP-UyPRMWh37Wm7lMKoaMp78jR093JO4y4Ldj4oxJpOJ5JbMopCafJOKHICCDj82jUK; H_PS_PSSID=1428_21093_29568_29220; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; ZD_ENTRY=baidu; BD_HOME=1; delPer=0; BD_CK_SAM=1; PSINO=5; COOKIE_SESSION=735862_0_6_5_10_3_0_1_5_2_0_1_69129_0_0_0_1573784783_0_1574520625%7C9%23721633_42_1572833622%7C9; sug=3; sugstore=1; ORIGIN=0; bdime=0"

这就是 Cookie, 文本字符串是用 ; 分隔:

document.cookie.split(';')

["BAIDUID=40E9A74CB78CA05206FD128BEB27E014:FG=1", " PSTM=1568883435", " BIDUPSID=4927A15EB7E602CFABA425A910D5136C", " BD_UPN=123253", " BDSFRCVID=OvtOJeC62C6a1m7wlk3EjPnnWgA5FqRTH6aowb_…8g0Kub2VhkogKKKgOTHICF_2uxOjjg8UtVJeC6EG0Ptf8g0M5", " H_BDCLCKID_SF=tJAj_D-btK03fP36qR6sMJ8thmT22-ustN5…oaMp78jR093JO4y4Ldj4oxJpOJ5JbMopCafJOKHICCDj82jUK", " H_PS_PSSID=1428_21093_29568_29220", " BDORZ=B490B5EBF6F3CD402E515D22BCDA1598", " ZD_ENTRY=baidu", " BD_HOME=1", " delPer=0", " BD_CK_SAM=1", " PSINO=5", " COOKIE_SESSION=735862_0_6_5_10_3_0_1_5_2_0_1_6912…84783_0_1574520625%7C9%23721633_42_1572833622%7C9", " sug=3", " sugstore=1", " ORIGIN=0", " bdime=0"]

计算机 cookie 的目的是帮助网站跟踪您的访问和活动。这并不总是坏事。例如,许多在线零售商在浏览网站时使用 cookie 来跟踪用户购物车中的商品。如果没有 Cookie,每次点击网站上的新链接时,您的购物车都会重置为零。这将使在线购买任何东西变得困难!

网站也可能使用 cookie 来记录您最近的访问记录或记录您的登录信息。许多人发现这很有用,因此他们可以在常用网站上存储密码,或者只是让他们知道他们过去访问或下载的内容。

不同类型的 cookie 跟踪不同的活动。会话 cookie 仅在某人主动浏览网站时使用; 一旦您离开网站,会话 cookie 就会消失。跟踪 cookie 可用于创建对同一站点的多次访问的长期记录。身份验证 cookie 跟踪用户是否已登录,如果是,则以何种名称登录。

Cookie 何时创建?
将数据写入 cookie 通常在加载新网页时完成 – 例如,在按下“提交”按钮后,数据处理页面将负责将值存储在 cookie 中。如果用户已选择禁用 cookie,则写入操作将失败,并且依赖 cookie 的后续站点将必须采取默认操作,或者提示用户重新输入将存储在 cookie 中的信息。

为什么使用 Cookie?
Cookie 是一种方便的方式,可以将信息从网站上的一个会话传送到另一个会话,或者在相关网站上的会话之间,而不必为服务器机器带来大量数据存储负担。在不使用 cookie 的情况下将数据存储在服务器上也是有问题的,因为如果不需要在每次访问网站时登录就很难检索特定用户的信息。

如果存储大量信息,则可以简单地将 cookie 用作识别给定用户的手段,以便可以在服务器端数据库上查找进一步的相关信息。例如,当用户第一次访问网站时,他们可以选择存储在 cookie 中的用户名,然后提供密码,名称,地址,首选字体大小,页面布局等数据 – 这些信息都将被存储使用用户名作为密钥在数据库上。随后,当重新访问该站点时,服务器将读取 cookie 以查找用户名,然后从数据库中检索所有用户的信息,而无需重新输入。

Cookie 持续多久了?
可以在创建 cookie 时设置 cookie 到期的时间。默认情况下,cookie 在当前浏览器窗口关闭时被销毁,但在此之后可以使其持续任意长度的时间。

谁可以访问 Cookies?
创建 cookie 时,可以通过设置其“根域”来控制其可见性。然后,属于该根的任何 URL 都可以访问它。例如,root 可以设置为“leiue.com”,然后 cookie 将可用于“www.leiue.com”或“i.leiue.com”或“leiue.com”中的站点。这可以用于允许相关页面彼此“通信”。无法将根域设置为“顶级”域,例如“.com”或“.co.uk”,因为这样可以广泛访问 cookie。

默认情况下,cookie 对其域中的所有路径都是可见的,但在创建时,它们可以被限制到给定的子路径 – 例如“www.leiue.com/news”。

Cookies 的安全性如何?
关于互联网上的隐私和安全性存在很多问题。Cookie 本身不会对隐私构成威胁,因为它们只能用于存储用户自愿提供的信息或 Web 服务器已有的信息。虽然有可能将此信息提供给特定的第三方网站,但这并不比将其存储在中央数据库中更糟糕。如果您担心您提供给网络服务器的信息不会被视为机密信息,那么您应该质疑是否确实需要提供该信息。

什么是跟踪 Cookie?
一些商业网站包括从第三方网站提供的嵌入式广告材料,这些广告可能存储该第三方网站的 cookie,其中包含从包含网站提供给它的信息 – 这些信息可能包括网站名称,正在查看的特定产品,访问过的网页等。当用户稍后访问包含来自同一第三方网站的类似嵌入广告的其他网站时,广告客户将能够阅读该 Cookie 并使用它来确定某些有关用户浏览历史记录的信息。这使得发布者能够提供以用户兴趣为目标的广告,因此理论上更有可能与用户相关。但是,很多人看到这样的’追踪 Cookie’。

Cookie 的组成

Cookie是一段不超过4KB的小型文本数据,由一个名称(Name)、一个值(Value)和其它几个用于控制Cookie有效期、安全性、使用范围的可选属性组成。其中 [3]

(1)Name/Value:设置Cookie的名称及相对应的值,对于认证Cookie,Value值包括Web服务器所提供的访问令牌 [3]

(2)Expires属性:设置Cookie的生存期。有两种存储类型的Cookie:会话性与持久性。Expires属性缺省时,为会话性Cookie,仅保存在客户端内存中,并在用户关闭浏览器时失效;持久性Cookie会保存在用户的硬盘中,直至生存期到或用户直接在网页中单击“注销”等按钮结束会话时才会失效 [3]

(3)Path属性:定义了Web站点上可以访问该Cookie的目录 [3]

(4)Domain属性:指定了可以访问该 Cookie 的 Web 站点或域。Cookie 机制并未遵循严格的同源策略,允许一个子域可以设置或获取其父域的 Cookie。当需要实现单点登录方案时,Cookie 的上述特性非常有用,然而也增加了 Cookie受攻击的危险,比如攻击者可以借此发动会话定置攻击。因而,浏览器禁止在 Domain 属性中设置.org、.com 等通用顶级域名、以及在国家及地区顶级域下注册的二级域名,以减小攻击发生的范围 [3]

(5)Secure属性:指定是否使用HTTPS安全协议发送Cookie。使用HTTPS安全协议,可以保护Cookie在浏览器和Web服务器间的传输过程中不被窃取和篡改。该方法也可用于Web站点的身份鉴别,即在HTTPS的连接建立阶段,浏览器会检查Web网站的SSL证书的有效性。但是基于兼容性的原因(比如有些网站使用自签署的证书)在检测到SSL证书无效时,浏览器并不会立即终止用户的连接请求,而是显示安全风险信息,用户仍可以选择继续访问该站点。由于许多用户缺乏安全意识,因而仍可能连接到Pharming攻击所伪造的网站 [3]

(6)HTTPOnly 属性 :用于防止客户端脚本通过document.cookie属性访问Cookie,有助于保护Cookie不被跨站脚本攻击窃取或篡改。但是,HTTPOnly的应用仍存在局限性,一些浏览器可以阻止客户端脚本对Cookie的读操作,但允许写操作;此外大多数浏览器仍允许通过XMLHTTP对象读取HTTP响应中的Set-Cookie头 [3]

认证机制

在 Web认证中 ,因为HTTP协议本身的局限,必须采用其他技术将相关认证标记以某种方式持续传送,以免客户从一个页面跳转至另一个页面时重新输入认证信息 [5] 。基于Cookie的认证过程,主要由以下三个阶段组成:

(1)发布Cookie。当用户试图访问某Web站点中需要认证的资源时,Web服务器会检查用户是否提供了认证Cookie,如果没有,则将用户重定向到登录页面。在用户成功登录后,Web服务器会产生认证Cookie,并通过HTTP响应中的Set-Cookie头发送给客户端,

用于对用户随后的请求进行检查和验证,接着将用户重定向到初始请求的资源 [5]

(2)检索Cookie。在用户随后的访问请求中,客户端浏览器检索Path和Domain等属性与用户请求资源相匹配的Cookie,并将找到的Cookie通过HTTP请求中的Cookie头提交给Web服务器 [5]

(3)验证CookieWeb服务器提取客户端浏览器递交的Cookie,验证其中的访问令牌。若合法,则将访问请求的资源发送给客户端浏览器;反之则拒绝用户的访问请求。Cookie 认证技术简化了用户访问 Web 网站资源的过程,即用户只需在初次登录网站时输入身份信息进行认证,随后便可以访问被授权的所有站点资源,不再需要重复手工提交身份信息 [5]

2.什么是 Session

The servlet container uses this interface to create a session between an HTTP client and an HTTP server. The server can maintain a session in many ways such as using cookies or rewriting URLs.

几个问题:
1.session 是啥?
2.怎么保存的?
3.如何运行?
4.有生命周期吗?
5.关闭浏览器会过期吗?
6.Redis代替文件存储session
7.分布式session的同步问题

在计算机科学中,特别是在网络中,会话是两个或更多个通信设备之间或计算机和用户之间的临时和交互式信息交换。会话在某个时间点建立,然后在稍后的时间点拆除。建立的通信会话可以在每个方向上涉及多于一个消息。会话通常是有状态的,这意味着至少一个通信部分需要保存关于会话历史的信息以便能够进行通信,这与无状态通信相反,其中通信由具有响应的独立请求组成。

会话状态仅在支持cookie的浏览器中保留。

已建立的会话是执行面向连接的通信的基本要求。会话也是在无连接通信模式下传输的基本步骤。但是,任何单向传输都不会定义会话。

通信传输可以被实现为在协议和服务的一部分的应用层,在会话层或在传输层中的 OSI 模型。

如果说 Cookie 机制是通过检查客户身上的“通行证”来确定客户身份的话,那么 Session 机制就是通过检查服务器上的“客户明细表”来确认客户身份。Session 相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。

Session 是另一种记录客户状态的机制,不同的是 Cookie 保存在客户端浏览器中,而 Session 保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是 Session。客户端浏览器再次访问时只需要从该 Session 中查找该客户的状态就可以了。

Session的作用就是它在Web服务器上保持用户的状态信息供在任何时间从任何设备上的页面进行访问。因为浏览器不需要存储任何这种信息,所以可以使用任何浏览器,即使是像Pad或手机这样的浏览器设备。

持久性方法的限制

随着越来越多用户登录,Session所需要的服务器内存量也会不断增加。

访问Web应用程序的每个用户都生成一个单独的Session对象。每个Session对象的持续时间是用户访问的时间加上不活动的时间。

如果每个Session中保持许多对象,并且许多用户同时使用Web应用程序(创建许多Session),则用于 Session持久性的服务器内存量可能会很大,从而影响了可伸缩性。

在项目实践中,Jsp程序中很多参数需要从数据库中读取,有的参数实际读取一次就可以,如果设计成每个用户每产生一个页面都要读取数据库,很显然,数据库的负载很大,同时也浪费时间,虽然可能有数据库连接池优化,但是尽量少使用数据库是我们编程的原则。

JSP使用一个叫HttpSession的对象实现同样的功能。HTTPSession 建立在cookies 和URL-rewriting 上。Session的信息保存在服务器端,Session的id保存在客户机的cookie中。事实上,在许多服务器上,如果浏览器支持的话它们就使用cookies,但是如果不支持或废除了的话就自动转化为URL-rewriting,session自动为每个流程提供了方便地存储信息的方法。

3.Tomcat 源码分析

Cookie.java

源码注释说明:


  
  1. package javax .servlet .http;
  2. import java .io .Serializable;
  3. import java .security .AccessController;
  4. import java .security .PrivilegedAction;
  5. import java .text .MessageFormat;
  6. import java .util .BitSet;
  7. import java .util .Locale;
  8. import java .util .ResourceBundle;
  9. /**
  10. * Creates a cookie, a small amount of information sent by a servlet to a Web
  11. * browser, saved by the browser, and later sent back to the server. A cookie's
  12. * value can uniquely identify a client, so cookies are commonly used for
  13. * session management.
  14. * <p>
  15. * A cookie has a name, a single value, and optional attributes such as a
  16. * comment, path and domain qualifiers, a maximum age, and a version number.
  17. * Some Web browsers have bugs in how they handle the optional attributes, so
  18. * use them sparingly to improve the interoperability of your servlets.
  19. * <p>
  20. * The servlet sends cookies to the browser by using the
  21. * {@link HttpServletResponse#addCookie} method, which adds fields to HTTP
  22. * response headers to send cookies to the browser, one at a time. The browser
  23. * is expected to support 20 cookies for each Web server, 300 cookies total, and
  24. * may limit cookie size to 4 KB each.
  25. * <p>
  26. * The browser returns cookies to the servlet by adding fields to HTTP request
  27. * headers. Cookies can be retrieved from a request by using the
  28. * {@link HttpServletRequest#getCookies} method. Several cookies might have the
  29. * same name but different path attributes.
  30. * <p>
  31. * Cookies affect the caching of the Web pages that use them. HTTP 1.0 does not
  32. * cache pages that use cookies created with this class. This class does not
  33. * support the cache control defined with HTTP 1.1.
  34. * <p>
  35. * This class supports both the RFC 2109 and the RFC 6265 specifications.
  36. * By default, cookies are created using RFC 6265.
  37. */
  38. public class Cookie implements Cloneable, Serializable {}

Cookie 的模型属性与构造函数


  
  1. private static final long serialVersionUID = 1L;
  2. private final String name;
  3. private String value;
  4. private int version = 0; // ;Version=1 ... means RFC 2109 style
  5. //
  6. // Attributes encoded in the header's cookie fields.
  7. //
  8. private String comment; // ;Comment=VALUE ... describes cookie's use
  9. private String domain; // ;Domain=VALUE ... domain that sees cookie
  10. private int maxAge = - 1; // ;Max-Age=VALUE ... cookies auto-expire
  11. private String path; // ;Path=VALUE ... URLs that see the cookie
  12. private boolean secure; // ;Secure ... e.g. use SSL
  13. private boolean httpOnly; // Not in cookie specs, but supported by browsers
  14. /**
  15. * Constructs a cookie with a specified name and value.
  16. * <p>
  17. * The name must conform to RFC 2109. That means it can contain only ASCII
  18. * alphanumeric characters and cannot contain commas, semicolons, or white
  19. * space or begin with a $ character. The cookie's name cannot be changed
  20. * after creation.
  21. * <p>
  22. * The value can be anything the server chooses to send. Its value is
  23. * probably of interest only to the server. The cookie's value can be
  24. * changed after creation with the <code>setValue</code> method.
  25. * <p>
  26. * By default, cookies are created according to the Netscape cookie
  27. * specification. The version can be changed with the
  28. * <code>setVersion</code> method.
  29. *
  30. * @param name
  31. * a <code>String</code> specifying the name of the cookie
  32. * @param value
  33. * a <code>String</code> specifying the value of the cookie
  34. * @throws IllegalArgumentException
  35. * if the cookie name contains illegal characters (for example,
  36. * a comma, space, or semicolon) or it is one of the tokens
  37. * reserved for use by the cookie protocol
  38. * @see #setValue
  39. * @see #setVersion
  40. */
  41. public Cookie(String name, String value) {
  42. validation.validate(name);
  43. this.name = name;
  44. this.value = value;
  45. }

HttpSession.java


  
  1. package javax.servlet.http;
  2. import java.util.Enumeration;
  3. import javax.servlet.ServletContext;
  4. /**
  5. * Provides a way to identify a user across more than one page request or visit
  6. * to a Web site and to store information about that user.
  7. * <p>
  8. * The servlet container uses this interface to create a session between an HTTP
  9. * client and an HTTP server. The session persists for a specified time period,
  10. * across more than one connection or page request from the user. A session
  11. * usually corresponds to one user, who may visit a site many times. The server
  12. * can maintain a session in many ways such as using cookies or rewriting URLs.
  13. * <p>
  14. * This interface allows servlets to
  15. * <ul>
  16. * <li>View and manipulate information about a session, such as the session
  17. * identifier, creation time, and last accessed time
  18. * <li>Bind objects to sessions, allowing user information to persist across
  19. * multiple user connections
  20. * </ul>
  21. * <p>
  22. * When an application stores an object in or removes an object from a session,
  23. * the session checks whether the object implements
  24. * {@link HttpSessionBindingListener}. If it does, the servlet notifies the
  25. * object that it has been bound to or unbound from the session. Notifications
  26. * are sent after the binding methods complete. For session that are invalidated
  27. * or expire, notifications are sent after the session has been invalidated or
  28. * expired.
  29. * <p>
  30. * When container migrates a session between VMs in a distributed container
  31. * setting, all session attributes implementing the
  32. * {@link HttpSessionActivationListener} interface are notified.
  33. * <p>
  34. * A servlet should be able to handle cases in which the client does not choose
  35. * to join a session, such as when cookies are intentionally turned off. Until
  36. * the client joins the session, <code>isNew</code> returns <code>true</code>.
  37. * If the client chooses not to join the session, <code>getSession</code> will
  38. * return a different session on each request, and <code>isNew</code> will
  39. * always return <code>true</code>.
  40. * <p>
  41. * Session information is scoped only to the current web application (
  42. * <code>ServletContext</code>), so information stored in one context will not
  43. * be directly visible in another.
  44. *
  45. * @see HttpSessionBindingListener
  46. */
  47. public interface HttpSession {
  48. /**
  49. * Returns the time when this session was created, measured in milliseconds
  50. * since midnight January 1, 1970 GMT.
  51. *
  52. * @return a <code>long</code> specifying when this session was created,
  53. * expressed in milliseconds since 1/1/1970 GMT
  54. * @exception IllegalStateException
  55. * if this method is called on an invalidated session
  56. */
  57. public long getCreationTime();
  58. /**
  59. * Returns a string containing the unique identifier assigned to this
  60. * session. The identifier is assigned by the servlet container and is
  61. * implementation dependent.
  62. *
  63. * @return a string specifying the identifier assigned to this session
  64. * @exception IllegalStateException
  65. * if this method is called on an invalidated session
  66. */
  67. public String getId();
  68. /**
  69. * Returns the last time the client sent a request associated with this
  70. * session, as the number of milliseconds since midnight January 1, 1970
  71. * GMT, and marked by the time the container received the request.
  72. * <p>
  73. * Actions that your application takes, such as getting or setting a value
  74. * associated with the session, do not affect the access time.
  75. *
  76. * @return a <code>long</code> representing the last time the client sent a
  77. * request associated with this session, expressed in milliseconds
  78. * since 1/1/1970 GMT
  79. * @exception IllegalStateException
  80. * if this method is called on an invalidated session
  81. */
  82. public long getLastAccessedTime();
  83. /**
  84. * Returns the ServletContext to which this session belongs.
  85. *
  86. * @return The ServletContext object for the web application
  87. * @since 2.3
  88. */
  89. public ServletContext getServletContext();
  90. /**
  91. * Specifies the time, in seconds, between client requests before the
  92. * servlet container will invalidate this session. A zero or negative time
  93. * indicates that the session should never timeout.
  94. *
  95. * @param interval
  96. * An integer specifying the number of seconds
  97. */
  98. public void setMaxInactiveInterval(int interval);
  99. /**
  100. * Returns the maximum time interval, in seconds, that the servlet container
  101. * will keep this session open between client accesses. After this interval,
  102. * the servlet container will invalidate the session. The maximum time
  103. * interval can be set with the <code>setMaxInactiveInterval</code> method.
  104. * A zero or negative time indicates that the session should never timeout.
  105. *
  106. * @return an integer specifying the number of seconds this session remains
  107. * open between client requests
  108. * @see #setMaxInactiveInterval
  109. */
  110. public int getMaxInactiveInterval();
  111. /**
  112. * Do not use.
  113. * @return A dummy implementation of HttpSessionContext
  114. * @deprecated As of Version 2.1, this method is deprecated and has no
  115. * replacement. It will be removed in a future version of the
  116. * Java Servlet API.
  117. */
  118. @Deprecated
  119. public HttpSessionContext getSessionContext();
  120. /**
  121. * Returns the object bound with the specified name in this session, or
  122. * <code>null</code> if no object is bound under the name.
  123. *
  124. * @param name
  125. * a string specifying the name of the object
  126. * @return the object with the specified name
  127. * @exception IllegalStateException
  128. * if this method is called on an invalidated session
  129. */
  130. public Object getAttribute(String name);
  131. /**
  132. * @param name
  133. * a string specifying the name of the object
  134. * @return the object with the specified name
  135. * @exception IllegalStateException
  136. * if this method is called on an invalidated session
  137. * @deprecated As of Version 2.2, this method is replaced by
  138. * {@link #getAttribute}.
  139. */
  140. @Deprecated
  141. public Object getValue(String name);
  142. /**
  143. * Returns an <code>Enumeration</code> of <code>String</code> objects
  144. * containing the names of all the objects bound to this session.
  145. *
  146. * @return an <code>Enumeration</code> of <code>String</code> objects
  147. * specifying the names of all the objects bound to this session
  148. * @exception IllegalStateException
  149. * if this method is called on an invalidated session
  150. */
  151. public Enumeration<String> getAttributeNames();
  152. /**
  153. * @return an array of <code>String</code> objects specifying the names of
  154. * all the objects bound to this session
  155. * @exception IllegalStateException
  156. * if this method is called on an invalidated session
  157. * @deprecated As of Version 2.2, this method is replaced by
  158. * {@link #getAttributeNames}
  159. */
  160. @Deprecated
  161. public String[] getValueNames();
  162. /**
  163. * Binds an object to this session, using the name specified. If an object
  164. * of the same name is already bound to the session, the object is replaced.
  165. * <p>
  166. * After this method executes, and if the new object implements
  167. * <code>HttpSessionBindingListener</code>, the container calls
  168. * <code>HttpSessionBindingListener.valueBound</code>. The container then
  169. * notifies any <code>HttpSessionAttributeListener</code>s in the web
  170. * application.
  171. * <p>
  172. * If an object was already bound to this session of this name that
  173. * implements <code>HttpSessionBindingListener</code>, its
  174. * <code>HttpSessionBindingListener.valueUnbound</code> method is called.
  175. * <p>
  176. * If the value passed in is null, this has the same effect as calling
  177. * <code>removeAttribute()</code>.
  178. *
  179. * @param name
  180. * the name to which the object is bound; cannot be null
  181. * @param value
  182. * the object to be bound
  183. * @exception IllegalStateException
  184. * if this method is called on an invalidated session
  185. */
  186. public void setAttribute(String name, Object value);
  187. /**
  188. * @param name
  189. * the name to which the object is bound; cannot be null
  190. * @param value
  191. * the object to be bound; cannot be null
  192. * @exception IllegalStateException
  193. * if this method is called on an invalidated session
  194. * @deprecated As of Version 2.2, this method is replaced by
  195. * {@link #setAttribute}
  196. */
  197. @Deprecated
  198. public void putValue(String name, Object value);
  199. /**
  200. * Removes the object bound with the specified name from this session. If
  201. * the session does not have an object bound with the specified name, this
  202. * method does nothing.
  203. * <p>
  204. * After this method executes, and if the object implements
  205. * <code>HttpSessionBindingListener</code>, the container calls
  206. * <code>HttpSessionBindingListener.valueUnbound</code>. The container then
  207. * notifies any <code>HttpSessionAttributeListener</code>s in the web
  208. * application.
  209. *
  210. * @param name
  211. * the name of the object to remove from this session
  212. * @exception IllegalStateException
  213. * if this method is called on an invalidated session
  214. */
  215. public void removeAttribute(String name);
  216. /**
  217. * @param name
  218. * the name of the object to remove from this session
  219. * @exception IllegalStateException
  220. * if this method is called on an invalidated session
  221. * @deprecated As of Version 2.2, this method is replaced by
  222. * {@link #removeAttribute}
  223. */
  224. @Deprecated
  225. public void removeValue(String name);
  226. /**
  227. * Invalidates this session then unbinds any objects bound to it.
  228. *
  229. * @exception IllegalStateException
  230. * if this method is called on an already invalidated session
  231. */
  232. public void invalidate();
  233. /**
  234. * Returns <code>true</code> if the client does not yet know about the
  235. * session or if the client chooses not to join the session. For example, if
  236. * the server used only cookie-based sessions, and the client had disabled
  237. * the use of cookies, then a session would be new on each request.
  238. *
  239. * @return <code>true</code> if the server has created a session, but the
  240. * client has not yet joined
  241. * @exception IllegalStateException
  242. * if this method is called on an already invalidated session
  243. */
  244. public boolean isNew();
  245. }

放入Session 中的对象要序列化.

checks the value for serializability


  
  1. /**
  2. * {@inheritDoc}
  3. * <p>
  4. * This implementation simply checks the value for serializability.
  5. * Sub-classes might use other distribution technology not based on
  6. * serialization and can override this check.
  7. */
  8. @Override
  9. public boolean isAttributeDistributable(String name, Object value) {
  10. return value instanceof Serializable;
  11. }

StandardSession.java


  
  1. /**
  2. * Standard implementation of the <b>Session</b> interface. This object is
  3. * serializable, so that it can be stored in persistent storage or transferred
  4. * to a different JVM for distributable session support.
  5. * <p>
  6. * <b>IMPLEMENTATION NOTE</b>: An instance of this class represents both the
  7. * internal (Session) and application level (HttpSession) view of the session.
  8. * However, because the class itself is not declared public, Java logic outside
  9. * of the <code>org.apache.catalina.session</code> package cannot cast an
  10. * HttpSession view of this instance back to a Session view.
  11. * <p>
  12. * <b>IMPLEMENTATION NOTE</b>: If you add fields to this class, you must
  13. * make sure that you carry them over in the read/writeObject methods so
  14. * that this class is properly serialized.
  15. *
  16. * @author Craig R. McClanahan
  17. * @author Sean Legassick
  18. * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
  19. */
  20. public class StandardSession implements HttpSession, Session, Serializable {}
  21. private static final long serialVersionUID = 1L;
  22. ...
  23. /**
  24. * The collection of user data attributes associated with this Session.
  25. */
  26. protected ConcurrentMap<String, Object> attributes = new ConcurrentHashMap<>();
  27. /**
  28. * The authentication type used to authenticate our cached Principal,
  29. * if any. NOTE: This value is not included in the serialized
  30. * version of this object.
  31. */
  32. protected transient String authType = null;
  33. /**
  34. * The time this session was created, in milliseconds since midnight,
  35. * January 1, 1970 GMT.
  36. */
  37. protected long creationTime = 0L;
  38. /**
  39. * We are currently processing a session expiration, so bypass
  40. * certain IllegalStateException tests. NOTE: This value is not
  41. * included in the serialized version of this object.
  42. */
  43. protected transient volatile boolean expiring = false;
  44. /**
  45. * The facade associated with this session. NOTE: This value is not
  46. * included in the serialized version of this object.
  47. */
  48. protected transient StandardSessionFacade facade = null;
  49. ...
  50. }

StandardSessionFacade.java


  
  1. /**
  2. * Facade for the StandardSession object.
  3. *
  4. * @author Remy Maucherat
  5. */
  6. public class StandardSessionFacade implements HttpSession {
  7. // ----------------------------------------------------------- Constructors
  8. /**
  9. * Construct a new session facade.
  10. *
  11. * @param session The session instance to wrap
  12. */
  13. public StandardSessionFacade(HttpSession session) {
  14. this.session = session;
  15. }
  16. // ----------------------------------------------------- Instance Variables
  17. /**
  18. * Wrapped session object.
  19. */
  20. private final HttpSession session;
  21. // ---------------------------------------------------- HttpSession Methods
  22. @Override
  23. public long getCreationTime() {
  24. return session.getCreationTime();
  25. }
  26. @Override
  27. public String getId() {
  28. return session.getId();
  29. }
  30. @Override
  31. public long getLastAccessedTime() {
  32. return session.getLastAccessedTime();
  33. }
  34. @Override
  35. public ServletContext getServletContext() {
  36. // FIXME : Facade this object ?
  37. return session.getServletContext();
  38. }
  39. @Override
  40. public void setMaxInactiveInterval(int interval) {
  41. session.setMaxInactiveInterval(interval);
  42. }
  43. @Override
  44. public int getMaxInactiveInterval() {
  45. return session.getMaxInactiveInterval();
  46. }
  47. ...
  48. }

Facade 设计模式: facade外观模式是一种非常常用的模式,特别是在组织一些复杂的相互调用的逻辑的时候,为外界提供统一的接口(API),可以看到在设计模式中,最常用的应该就是模板方法和facade模式了,很多时候很多需求需要我们认真的取舍,人无远虑必有近忧,同样的,只有为以后的可复用性、可扩展性来考虑,我们的代码才是好的代码。

带着几个问题来看:

1.为什么 StandardSession 要搞一个外观模式?
答:因为他的功能实现了session,但是其中Session和Serializable接口的方法是内部处理的东西,无需对外界开放。而外观模式可以屏蔽不想让外界看到的东西。

2.为什么不把那些不让外界查看的东西设置为private呢?
答:因为接口中的函数必须为public,因此他实现的方法域必须为public。

3.外观模式怎么实现屏蔽的呢?
答:首先确定要对外开放的函数是哪些接口,确定HttpSession之后,则在StandardSession内部存放一个外观类,在对外获取Session的时候将StandardSessionFacade返回给外界的处理者,如代码:

StandardSession类中:


  
  1. /**
  2. * Return the <code>HttpSession</code> for which this object
  3. * is the facade.
  4. */
  5. @Override
  6. public HttpSession getSession() {
  7. if (facade == null) {
  8. if (SecurityUtil.isPackageProtectionEnabled()) {
  9. facade = AccessController.doPrivileged( new PrivilegedNewSessionFacade( this));
  10. } else {
  11. facade = new StandardSessionFacade( this);
  12. }
  13. }
  14. return facade;
  15. }

类图层次结构如下

Session.png

而外界的处理怎么能够用到StandardSession的处理呢,其中StandardSessionFacade跟StandardSession一样继承自HttpSession,同时我们查看StandardSessionFacade源码会发现里面有一个指向HttpSession的对象session,根据里氏替换原则其实就是StandardSession对象,以及下面的处理都是StandardSession的处理,这样就实现了StandardSession向外界需要看到的东西,如下:


  
  1. /**
  2. * Facade for the StandardSession object.
  3. *
  4. * @author Remy Maucherat
  5. */
  6. public class StandardSessionFacade implements HttpSession {
  7. // ----------------------------------------------------------- Constructors
  8. /**
  9. * Construct a new session facade.
  10. *
  11. * @param session The session instance to wrap
  12. */
  13. public StandardSessionFacade(HttpSession session) {
  14. this.session = session;
  15. }
  16. // ----------------------------------------------------- Instance Variables
  17. /**
  18. * Wrapped session object.
  19. */
  20. private final HttpSession session;
  21. // ---------------------------------------------------- HttpSession Methods
  22. @Override
  23. public long getCreationTime() {
  24. return session.getCreationTime();
  25. }
  26. @Override
  27. public String getId() {
  28. return session.getId();
  29. }
  30. ...
  31. }

通过这个我们可以感觉到其中StandardSession的getSession()其实就是用的双重派分的方式,让他人代替自己处理一些自己本身处理但是又不想处理的一些东西。

看到这里感觉到以后如果设计系统的时候就可以参考这个例子,通过外观模式和双重派分的方式来实现。

分布式 Session ( distributable session )

在搭建完集群环境后,不得不考虑的一个问题就是用户访问产生的session如何处理。如果不做任何处理的话,用户将出现频繁登录的现象,比如集群中存在A、B两台服务器,用户在第一次访问网站时,Nginx通过其负载均衡机制将用户请求转发到A服务器,这时A服务器就会给用户创建一个Session。当用户第二次发送请求时,Nginx将其负载均衡到B服务器,而这时候B服务器并不存在Session,所以就会将用户踢到登录页面。这将大大降低用户体验度,导致用户的流失,这种情况是项目绝不应该出现的。

session共享机制

使用分布式缓存方案比如memcached、Redis,但是要求Memcached或Redis必须是集群。

分布式情况下,如果每台服务器都session存在自己的内存中,不同服务器之间就会造成数据不一致问题,

这时候就需要session共享。单机情况下,不存在Session共享的情况。分布式情况下,

如果不进行Session共享会出现数据不一致,比如:会导致请求落到不同服务器要重复登录的情况。

分布式 session 解决方案

①tomcat+redis方案
这个其实还挺方便的,就是使用session的代码跟以前一样,还是基于tomcat原生的session支持即可,然后就是用一个叫做Tomcat RedisSessionManager的东西,让所有我们部署的tomcat都将session数据存储到redis即可。

在tomcat的配置文件中,配置一下


  
  1. <Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
  2. <Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
  3. host= "{redis.host}"
  4. port= "{redis.port}"
  5. database= "{redis.dbnum}"
  6. maxInactiveInterval= "60"/>

还可以用基于redis哨兵支持的redis高可用集群来保存session数据,都是ok的.


  
  1. <Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
  2. <Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
  3. sentinelMaster= "mymaster"
  4. sentinels= "<sentinel1-ip>:26379,<sentinel2-ip>:26379,<sentinel3-ip>:26379"
  5. maxInactiveInterval= "60"/>

②spring session+redis方案
分布式会话的东西和容器耦合在一起,比如要将tomcat容器迁移成jetty,就需要修改session同步的配置,这样会比较麻烦。

可以使用spring的解决方案spring session,使用方法如下
1.在pom中引入依赖


  
  1. <dependency>
  2. <groupId>org.springframework.session </groupId>
  3. <artifactId>spring-session-data-redis </artifactId>
  4. <version>1.2.1.RELEASE </version>
  5. </dependency>
  6. <dependency>
  7. <groupId>redis.clients </groupId>
  8. <artifactId>jedis </artifactId>
  9. <version>2.8.1 </version>
  10. </dependency>

2.配置spring配置文件


  
  1. <bean id= "redisHttpSessionConfiguration"
  2. class= "org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
  3. <property name= "maxInactiveIntervalInSeconds" value= "600"/>
  4. </bean>
  5. <bean id= "jedisPoolConfig" class= "redis.clients.jedis.JedisPoolConfig">
  6. <property name= "maxTotal" value= "100" />
  7. <property name= "maxIdle" value= "10" />
  8. </bean>
  9. <bean id= "jedisConnectionFactory"
  10. class= "org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method= "destroy">
  11. <property name= "hostName" value= "${redis_hostname}"/>
  12. <property name= "port" value= "${redis_port}"/>
  13. <property name= "password" value= "${redis_pwd}" />
  14. <property name= "timeout" value= "3000"/>
  15. <property name= "usePool" value= "true"/>
  16. <property name= "poolConfig" ref= "jedisPoolConfig"/>
  17. </bean>

3.修改web.xml


  
  1. < filter>
  2. < filter-name>springSessionRepositoryFilter</ filter-name>
  3. < filter- class>org.springframework.web. filter.DelegatingFilterProxy</ filter- class>
  4. </ filter>
  5. < filter-mapping>
  6. < filter-name>springSessionRepositoryFilter</ filter-name>
  7. <url-pattern>/*</url-pattern>
  8. </ filter-mapping>

使用示例:


  
  1. @Controller
  2. @RequestMapping("/test")
  3. public class TestController {
  4. @RequestMapping("/putIntoSession")
  5. @ResponseBody
  6. public String putIntoSession(HttpServletRequest request, String username){
  7. request.getSession().setAttribute( "name", “leo”);
  8. return "ok";
  9. }
  10. @RequestMapping("/getFromSession")
  11. @ResponseBody
  12. public String getFromSession(HttpServletRequest request, Model model){
  13. String name = request.getSession().getAttribute( "name");
  14. return name;
  15. }
  16. }

上面的代码就是ok的,给sping session配置基于redis来存储session数据,然后配置了一个spring session的过滤器,这样的话,session相关操作都会交给spring session来管了。接着在代码中,就用原生的session操作,就是直接基于spring sesion从redis中获取数据了。

实现分布式的会话,有很多种很多种方式,我说的只不过比较常见的两种方式,tomcat + redis早期比较常用;近些年,重耦合到tomcat中去,通过spring session来实现。

参考资料

https://www.cnblogs.com/daofaziran/p/10933221.html
https://blog.csdn.net/zhouzhenyong/article/details/52998760
https://www.leiue.com/what-is-cookie
https://www.leiue.com/what-is-session
https://blog.csdn.net/qq_20936333/article/details/86773664
https://blog.csdn.net/jack199504/article/details/90547185


Kotlin 开发者社区

国内第一Kotlin 开发者社区公众号,主要分享、交流 Kotlin 编程语言、Spring Boot、Android、React.js/Node.js、函数式编程、编程思想等相关主题。

越是喧嚣的世界,越需要宁静的思考。


转载:https://blog.csdn.net/universsky2015/article/details/103235290
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场