session和cookie
背景
由于http的无状态性,为了使某个域名下的所有网页能够共享某些数据,session和cookie出现了。
客户端访问服务器的流程
- 客户端发送一个http请求到服务器端。
- 服务器端接受客户端请求后,建立一个session,并发送一个http响应到客户端,这个响应头,其中就包含Set-Cookie头部。该头部包含了sessionId。Set-Cookie格式如下:
set-cookie:
value:
expires:date
path:path
- 在客户端发起的第二次请求,假如服务器给了set-Cookie,浏览器会自动在请求头中添加cookie
- 服务器接收请求,分解cookie,验证信息,核对成功后返回response给客户端
注意
- cookie只是实现session的其中一种方案。虽然是最常用的,但并不是唯一的方法。禁用cookie后还有其他方法存储,比如放在url中
- 现在大多都是Session + Cookie,但是只用session不用cookie,或是只用cookie,不用session在理论上都可以保持会话状态。可是实际中因为多种原因,一般不会单独使用
- 用session只需要在客户端保存一个id,实际上大量数据都是保存在服务端。如果全部用cookie,数据量大的时候客户端是没有那么多空间的。
- 如果只用cookie不用session,那么账户信息全部保存在客户端,一旦被劫持,全部信息都会泄露。并且客户端数据量变大,网络传输的数据量也会变大
token
组成
- uid: 用户唯一身份标识
- time: 当前时间的时间戳
- sign: 签名, 使用 hash/encrypt 压缩成定长的十六进制字符串,以防止第三方恶意拼接
- 固定参数(可选): 将一些常用的固定参数加入到 token 中是为了避免重复查库
存放
token在客户端一般存放于localStorage,cookie,或sessionStorage中。在服务器一般存于数据库中
token认证流程
-
用户登录,成功后服务器返回Token给客户端。
-
客户端收到数据后保存在客户端
-
客户端再次访问服务器,将token放入headers中
-
服务器端采用filter过滤器校验。校验成功则返回请求数据,校验失败则返回错误码
token可以抵抗csrf,cookie+session不行
假如用户正在登陆银行网页,同时登陆了攻击者的网页,并且银行网页未对csrf攻击进行防护。
攻击者就可以在网页放一个表单,该表单提交src为http://www.bank.com/api/transfer,body为count=1000&to=Tom。
倘若是session+cookie,用户打开网页的时候就已经转给Tom1000元了.
因为form 发起的 POST 请求并不受到浏览器同源策略的限制,因此可以任意地使用其他域的 Cookie 向其他域发送 POST 请求,形成 CSRF 攻击。
在post请求的瞬间,cookie会被浏览器自动添加到请求头中。
token是开发者为了防范csrf而特别设计的令牌,浏览器不会自动添加到headers里,攻击者也无法访问用户的token,所以提交的表单无法通过服务器过滤,也就无法形成攻击。
cookie
网页浏览器保存用户信息的文件
是服务器在本地机器上存储的小段文本并随每一个请求发送至同一服务器,是在客户端保持状态的方案。
Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。
意义
HTTP协议是无状态的协议,对于事务处理没有记忆能力。
缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。
在服务器不需要先前信息时它的应答就较快。
特性
记录用户访问次数
记录用户访问次数
-
Java中把Cookie封装成了javax.servlet.http.Cookie类。
-
每个Cookie都是该Cookie类的对象。
服务器通过操作Cookie类对象对客户端Cookie进行操作。
-
通过request.getCookie()获取客户端提交的所有Cookie(以Cookie[]数组形式返回)
-
通过response.addCookie(Cookie cookie)向客户端设置Cookie。
-
Cookie对象使用key-value属性对的形式保存用户状态
一个Cookie对象保存一个属性对
一个request或者response同时使用多个Cookie。
因为Cookie类位于包javax.servlet.http.*下面,所以JSP中不需要import该类。
Cookie的不可跨域名性
Cookie具有不可跨域名性。
根据Cookie规范,浏览器访问a网站只会携带a网站的Cookie,而不会携带b网站的Cookie。a网站也只能操作a网站的Cookie,而不能操作b网站的Cookie。
Cookie在客户端是由浏览器来管理的。
浏览器判断一个网站是否能操作另一个网站Cookie的依据是域名。
Unicode编码:保存中文
中文与英文字符不同,中文属于Unicode字符,在内存中占4个字符,而英文属于ASCII字符,内存中只占2个字节。
Cookie中使用Unicode字符时需要对Unicode字符进行编码,否则会乱码。
Cookie中保存中文只能编码。一般使用UTF-8编码即可。不推荐使用GBK等中文编码,因为浏览器不一定支持,而且JavaScript也不支持GBK编码。
BASE64编码:保存二进制图片
Cookie不仅可以使用ASCII字符与Unicode字符,还可以使用二进制数据。
例如在Cookie中使用数字证书,提供安全度。使用二进制数据时也需要进行编码。
属性
每个属性对应一个getter方法与一个setter方法。
名称
String name:该Cookie的名称。Cookie一旦创建,名称便不可更改。
值
Object value:该Cookie的值。如果值为Unicode字符,需要为字符编码。如果值为二进制数据,则需要使用BASE64编码。
用处说明
String comment:该Cookie的用处说明。浏览器显示Cookie信息的时候显示该说明。
版本号
int version:该Cookie使>用的版本号。0表示遵循Netscape的Cookie规范,1表示遵循W3C的RFC 2109规范。
有效期
Cookie在生成时就会被指定一个Expire值,这就是Cookie的生存周期
Cookie的maxAge决定着Cookie的有效期,单位为秒(Second)。
Cookie中通过getMaxAge()方法与setMaxAge(int maxAge)方法来读写maxAge属性。
- maxAge属性为正数,则表示该Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的Cookie持久化,即写到对应的Cookie文件中。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。
永久有效的cookie
Cookie cookie = new Cookie("username","helloweenvsfei"); // 新建Cookie
cookie.setMaxAge(Integer.MAX_VALUE); // 设置生命周期为MAX_VALUE
response.addCookie(cookie); // 输出到客户端
- maxAge为负数,则表示该Cookie仅在本浏览器窗口以及本窗口打开的子窗口内有效,关闭窗口后该Cookie即失效。maxAge为负数的Cookie,为临时性Cookie,不会被持久化,不会被写到Cookie文件中。Cookie信息保存在浏览器内存中,因此关闭浏览器该Cookie就消失了。
Cookie默认的maxAge值为–1。
- maxAge为0,则表示删除该Cookie。Cookie机制没有提供删除Cookie的方法,因此通过设置该Cookie即时失效实现删除Cookie的效果。
失效的Cookie会被浏览器从Cookie文件或者内存中删除
Cookie cookie = new Cookie("username","helloweenvsfei"); // 新建Cookie
cookie.setMaxAge(0); // 设置生命周期为0,不能为负数
response.addCookie(cookie); // 必须执行这一句
注意:从客户端读取Cookie时,包括maxAge在内的其他属性都是不可读的,也不会被提交。浏览器提交Cookie时只会提交name与value属性。maxAge属性只被浏览器用来判断Cookie是否过期。
修改、删除
response对象提供的Cookie操作方法只有一个添加操作add(Cookie cookie)。
要想修改Cookie只能使用一个同名的Cookie来覆盖原来的Cookie,达到修改的目的。删除时只需要把maxAge修改为0即可。
注意:修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,例如name、path、domain等,都要与原Cookie完全一样。否则,浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。
域名
路径与域合在一起就构成了cookie的作用范围。
Cookie是不可跨域名的。
隐私安全机制能够禁止网站非法获取其他网站的Cookie。
正常情况下,同一个一级域名xxx.com下的两个二级域名如aaa.xxx.com和bbb.xxx.com也不能交互使用Cookie,因为二者的域名并不严格相同。如果想所有xxx.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数
Domain参数
可以访问该Cookie的域名。
如果设置为“.xxx.com”,则所有以xxx.com结尾的域名都可以访问该Cookie。
注意第一个字符必须为“.”。
注意:domain参数必须以点(".")开始。另外,name相同但domain不同的两个Cookie是两个不同的Cookie。如果想要两个域名完全不同的网站共有Cookie,可以生成两个Cookie,domain属性分别为两个域名,输出到客户端。
Cookie cookie = new Cookie("time","20080808"); // 新建Cookie
cookie.setDomain(".helloweenvsfei.com"); // 设置域名
cookie.setPath("/"); // 设置路径
cookie.setMaxAge(Integer.MAX_VALUE); // 设置有效期
response.addCookie(cookie); // 输出到客户端
读者可以修改本机C:\WINDOWS\system32\drivers\etc下的hosts文件来配置多个临时域名,然后使用setCookie.jsp程序来设置跨域名Cookie验证domain属性。
路径
决定允许访问Cookie的路径(ContextPath)。
Cookie cookie = new Cookie("time","20080808"); // 新建Cookie
cookie.setPath("/session/"); // 设置路径
response.addCookie(cookie); // 输出到客户端
默认为“/”,允许所有路径使用Cookie。
path属性需要使用符号“/”结尾。
name相同但domain不同的两个Cookie也是两个不同的Cookie。
注意:页面只能获取它属于的Path的Cookie。
浏览器会将domain和path都相同的cookie保存在一个文件里,cookie间用*隔开。
安全属性
boolean secure:该Cookie是否仅被使用安全协议传输。安全协议。安全协议有HTTPS,SSL等,在网络>上传输数据之前先将数据加密。默认为false。
HTTP协议不仅是无状态的,而且是不安全的。
使用HTTP协议的数据不经过任何加密就直接在网络上传播,有被截获的可能。
使用HTTP协议传输很机密的内容是一种隐患。
如果不希望Cookie在HTTP等非安全协议中传输,可以设置Cookie的secure属性为true。
浏览器只会在HTTPS和SSL等安全协议中传输此类Cookie。
Cookie cookie = new Cookie("time", "20080808"); // 新建Cookie
cookie.setSecure(true); // 设置安全属性
response.addCookie(cookie); // 输出到客户端
提示:secure属性并不能对Cookie内容加密,因而不能保证绝对的安全性。如果需要高安全性,需要在程序中对Cookie内容加密、解密,以防泄密。
创建流程
-
用户会提供包括用户名在内的个人信息并且提交至服务器
-
服务器在向客户端回传相应的超文本的同时也会发回这些个人信息
当然这些信息并不是存放在HTTP响应体(Response Body)中的,而是存放于HTTP响应头(Response Header&#x