原文使用有道云笔记:为更好浏览体验移步有道云:
为什么有cookie/Session?
因为HTTP协议本身是无状态,HTTP无状态协议,是指协议对于事务处理没有记忆能力。
无状态是指Web浏览器与Web服务器之间不需要建立持久的连接,这意味着当一个客户端向服务器端发出请求,然后Web服务器返回响应(Response),连接就被关闭了,在服务器端不保留连接的有关信息。
也就是会话管理
Cookie技术:会话数据保存在浏览器客户端。
Session技术:会话数据保存在服务器端。
Cookie
Cookie实际上是一小段的文本信息(key-value格式)。它的原理如下:
1:由服务器产生一个cookie;
2:服务器发送给浏览器(响应头中包含set-cookie);
3:浏览器保存;
4:浏览器之后发送请求,请求头中会有cookie信息;
5:服务器接收到cookie。
通过一个示例说明:
新建项目,这里使用之前的springboot项目(outwebest)了,懒得建。
我这里使用servlet来写写看:新建两个servlet:
resp.addCookie(cookie);
}
}
}
}
}
}
}
}
}
需要注意要在启动类加上注解:@ServletComponentScan
否则404.
启动后,打开浏览器(这里使用chrome)控制台(F12)-》network:
查看刚才的请求:
可知,我们在响应中设置的cookie,实际是在响应头添加了:
Set-Cookie: userName=sunsas
浏览器会把它放到请求头中,下一次访问http://localhost:8080/getCookie
已经携带cookie了。后端也接收到了:
当然上面两个servlet也可以使用一个controller代替了:
}
}
}
}
}
只能说以前写代码真麻烦,servlet3以上才支持的@Webservlet,更久以前还得自己写xml中配置servlet,一个servlet至少6行,吐了。现在一个controller直接给你搞了,来再多的接口,也不虚。
这里还要说下setMaxAge这个方法:
/**
* Sets the maximum age of the cookie in seconds.
* <p>
* A positive value indicates that the cookie will expire after that many
* seconds have passed. Note that the value is the <i>maximum</i> age when
* the cookie will expire, not the cookie's current age.
* <p>
* A negative value means that the cookie is not stored persistently and
* will be deleted when the Web browser exits. A zero value causes the
* cookie to be deleted.
*
* Sets the maximum age of the cookie in seconds.
* <p>
* A positive value indicates that the cookie will expire after that many
* seconds have passed. Note that the value is the <i>maximum</i> age when
* the cookie will expire, not the cookie's current age.
* <p>
* A negative value means that the cookie is not stored persistently and
* will be deleted when the Web browser exits. A zero value causes the
* cookie to be deleted.
*
源码已经说的很清楚了,这个方法就是设置cookie的过期时间,单位是秒,
正值就是多少秒过期,0的话就是马上过期。没啥意义。
默认为-1,负值表示浏览器关闭就会丢失cookie。
我这里设置存活时间为10min:
cookie.setMaxAge(60*10);
响应头添加上了过期时间。
Demo:显示上次访问时间
思路就是每次访问去看看cookie中有没有访问时间,如果没有,是第一次访问,如果有,则打印时间,并更新时间:
}
return lastTime;
}
return lastTime;
}
注意addCookie时是不能使用空格的 而在ASCII码中32对应的就是空格。
否则会报错:
An invalid character [32] was present in the Cookie value
Cookie的局限:
1)Cookie只能存字符串类型。不能保存对象
2)只能存非中文。
3)1个Cookie的容量不超过4KB。
如果大小超过4kb,则使用session。
Session
会话数据保存在服务器端。(内存中)
当访问服务器否个网页的时候,会在服务器端的内存里开辟一块内存,这块内存就叫做session,而这个内存是跟浏览器关联在一起的。这个浏览器指的是浏览器窗口,或者是浏览器的子窗口,意思就是,只允许当前这个session对应的浏览器访问,就算是在同一个机器上新启的浏览器也是无法访问的。而另外一个浏览器也需要记录session的话,就会再启一个属于自己的session。
由于HTTP协议无状态,那么浏览器怎么知道自己的session,答案是sessionId
1:第一次访问创建session对象,分配一个sessionId;
2:把sessionId放在cookie中给浏览器;
3:第二次访问cookie就携带了sessionId;
4:服务器根据sessionId查找对应的session(缓存中);
5:如果找到,返回session对象;
6:没有找到则创建session。
新建servlet类:
}
}
}
}
}
}
}
}
注意req.getSession()方法。此方法默认传参true,如果为false,获得session为空也直接返回,为true则会创建一个session:
/**
* Return the session associated with this Request, creating one
* if necessary.
*/
@Override
public HttpSession getSession() {
return getSession(true);
}
* Return the session associated with this Request, creating one
* if necessary.
*/
@Override
public HttpSession getSession() {
return getSession(true);
}
@Override
public HttpSession getSession(boolean create) {
if (crossContext) {
// There cannot be a session if no context has been assigned yet
// crossContext为true表示配置的不同context共享一个session,这里省略
} else {
return super.getSession(create);
}
public HttpSession getSession(boolean create) {
if (crossContext) {
// There cannot be a session if no context has been assigned yet
// crossContext为true表示配置的不同context共享一个session,这里省略
} else {
return super.getSession(create);
}
* won't be provided for requests to all web applications.
*
* Any session ID provided by the client should be for a session
* that already exists somewhere on the host. Check if the context
* is configured for this to be confirmed.
*/
if (context.getValidateClientProvidedNewSessionId()) {
boolean found = false;
for (Container container : getHost().findChildren()) {
Manager m = ((Context) container).getManager();
if (m != null) {
try {
if (m.findSession(sessionId) != null) {
found = true;
break;
}
} catch (IOException e) {
// Ignore. Problems with this manager will be
// handled elsewhere.
}
}
}
if (!found) {
sessionId = null;
}
}
} else {
sessionId = null;
}
session = manager.createSession(sessionId);
// Creating a new session cookie based on that session
if (session != null && trackModesIncludesCookie) {
Cookie cookie = ApplicationSessionCookieConfig.createSessionCookie(
context, session.getIdInternal(), isSecure());
response.addSessionCookieInternal(cookie);
}
if (session == null) {
return null;
}
session.access();
return session;
}
*
* Any session ID provided by the client should be for a session
* that already exists somewhere on the host. Check if the context
* is configured for this to be confirmed.
*/
if (context.getValidateClientProvidedNewSessionId()) {
boolean found = false;
for (Container container : getHost().findChildren()) {
Manager m = ((Context) container).getManager();
if (m != null) {
try {
if (m.findSession(sessionId) != null) {
found = true;
break;
}
} catch (IOException e) {
// Ignore. Problems with this manager will be
// handled elsewhere.
}
}
}
if (!found) {
sessionId = null;
}
}
} else {
sessionId = null;
}
session = manager.createSession(sessionId);
// Creating a new session cookie based on that session
if (session != null && trackModesIncludesCookie) {
Cookie cookie = ApplicationSessionCookieConfig.createSessionCookie(
context, session.getIdInternal(), isSecure());
response.addSessionCookieInternal(cookie);
}
if (session == null) {
return null;
}
session.access();
return session;
}
控制台打印“
后访问
还是F12看一下浏览器请求:
可见里面存的叫JSessionId。
这里我把浏览器关闭了,再次创建session然后获得session,所以他们id不一样。
由于每次重新开启浏览器都会生成新的session,但这并不代表原来的session失效了。
例如我现在复制下原来的sessionId
A7439D2FFDF37F89C87AE5B6D4D64EAE
我们给项目打上断点(Request下的doGetSession方法中),debug运行:
右键此方法进行运行:
把老的sessionId传进去:
可见session仍然在。
session本身有一个存活时间,在tomcat中默认的是30分钟,
和浏览器是没有关系的
Token
在计算机身份认证中是令牌(临时)的意思。一般作为邀请、登录系统使用。
Token 是在服务端产生的。如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的合法地位。
这有一篇文章关于cookie,session,token的演化讲的很好:
简单来说,我们以前保存用户登录可能就是放在session,现在也可以,服务器返回的的sessionId相当于就是token,但是这样服务器要存很多的sessionId,来做验证。
由于sessionId增建了服务器压力,于是有人发明了token,token存在客户端,在请求时随请求头过来。
但是服务器端没有token,关键就是验证。就是加上比如userId+密钥,加密。这个只有服务器知道,有请求过来,就去解析,拿到userId。
1:客户端请求来拿到token;
2:服务器根据此用户id,加上签名,密钥加密,并返回给客户端;
3:客户端在请求头中保存token;
4:客户端下一次请求,服务器拿到token,并去解密;
5:根据结果做不同操作。
现在企业用JWT比较多,有机会再说这个东西。