先思考问题:
1、如何实现登陆?
(1)表单输入账号密码
(2)后台数据库验证
2、为什么平日登陆一次,隔几天打开浏览器进入网页,登录状态还在呢?
因为保存了用户的登陆状态。下次访问的时候,先查看是否保存登录状态,有则不需要重新登陆。
登录状态的处理:Cookie,Session,Token三种方式
接下来看看如何区分和实践
这样的方式,第一次打开浏览器访问网页:需要登录。然后关闭浏览器
第二次打卡浏览器访问网页:需要重新登陆
功能期望:
先了解一个概念:
用甲的账号密码登录成功后,服务器会自动创建一个Session对象 ,表明甲登陆过了
用乙的账号密码登录成功后,服务器也会自动创建一个Session对象 ,表明乙登陆过了
那怎么区分已经创建的Session对象,用唯一标识Session id区分
Session Id = 1111 就是 甲的Session对象
账号登陆成功后服务器会创建这样一个散列表保存登陆状态,这个表存在服务器的内存中
看一下我们如何利用Session实现登录一次,多次访问
可以看到,第二次访问的时候我们仅需要在访问路径上带上标识甲的Session id
”localhost:8080/B?SessionId = 1111“ 就可以匹配服务器内存中的Session id
那么每次都需要在请求路径上手写Session id,简直太麻烦了,有什么办法可以自动在发送请求路径的时候,顺便发送Session Id呢?
Cookie就可以实现这个功能
甲登陆成功后,服务器为甲创建 Session Id + Session 对象
然后服务器创建Cookie对象,并把Session Id存入Cookie对象
最后返回给浏览器,保存在浏览器的内存里
第二次访问时候,保存在浏览器内存里的Session Id会自动的 与 访问路径一起发送给服务器
大部分时候我们不仅仅在Cookie中保存Session Id,还保存一些其他的信息,以此来说明这是用户甲发起的请求,因此千万不可以被别人知道了我们的Cookie
CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。
有人就疑惑了,是不是必须在cookie中设置账号密码呢?
如果我们修改了账号密码,且只携带Session Id,那岂不是当Session Id通过时,就可以不用登陆,那密码不白改了。
所以我们需要携带账号密码,先验证Seesion Id是否一致,再验证账号密码是否一致,这样
这里有个实例:
可以看到服务器返回给浏览器的cookie,包含了JsessionId,name和pwd
从后端代码编写的角度:
传入账号密码登陆成功后,后端在跳转界面之前,需要实现两步骤
1. 因为登录成功了,创建一个Session对象(根据此Session对象可以识别不同的用户)
此Session对象保存在服务器内存中
2. 服务器会继续创建一个Cookie对象,将Session传给Cookie,然后把Cookie返回给浏览器
浏览器会把Cookie保存在浏览器内存中,如下图
第一次访问:先登陆,并且保存登录状态
第二次访问:利用登录状态
AServlet:
连接数据库,查看匹配信息
if(success){
1 Tomcat服务器自动创建session对象
2 request.getSession() //获取session对象
3 将session对象放入Session域中
//设置Cookie
Cookie cookie = new Cookie(设置Name属性,设置Value属性)
cookie.setMaxAge(时间)
cookie.path(路径)
response.addCookie(cookie) //将服务器生成的cookie返回给浏览器保存起来
4 跳转到A.jsp界面
}
BServlet:
1 request.getsession(false) //禁止服务器在B中自动创建session对象
2 获取Session对象
if(session不为空 && session.getAttribute(对象)不为空){
获取浏览器的cookies
3 进入B界面
}
Session Id保存在服务器的内存中,所以服务器的压力就会很大。有没有什么办法可以减轻服务器的压力呢?这个时候需要Token了
Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
使用Token的目的:Token的目的是为了减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。
这意思是不是和Cookie+Session类似,画个图分析一下
可以看到我们不再需要在服务器存储Session Id,极大地降低服务器的压力。
先看看第一次登陆成功后,服务器如何产生的Token
第一步:
第二步:
第三步:
这样就得到了返回浏览器的Token
再来看看怎么生成新的Token进行验证
如果善于观察会发现,这个Token也是可以被人截获的,只需要用Baes64解析,至少可以得到两个密文,而这两个密文就是我们的数据信息,如果里面有账号密码也是有泄露的风险。
拓展:
再思考一个问题:
既然Token是由 Header,Playloder,签名 三部分决定的。那这里面都存储一些什么信息区分不同的客户端。(也就是说,A,B浏览器向服务器发送Token,服务器是怎么区分的)
为了互联网的规范性,我们对这三部分应该存储什么信息有一个规范:JSON WEB TOKEN(简称JWT)意思只要你想要使用Token,可以考虑参考这个标准。
JWT规定:
第一部分(Header存放:声明类型typ,加密算法alg)
{
'typ': 'JWT',
'alg': 'HS256'
}
然后用Base64加密生成密文,这也是我们为何解密 Header密文 就可以知道 加密算法HS256
第二部分(Playloder存放:
标准中注册的声明: 包括
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
公共的声明:
自定义的一些内容:name:"王冰冰"
私有的声明:
虽然叫私有,但也不安全,不推荐放敏感信息
截取Token主要针对这一部分内容。
第三部分:叫做signature,为生成签名
signature组成:Header密文.Playloder密文.secret
这里可以看看前面介绍如何利用服务器的密钥生成签名,对比一下。
其实这个secret是一个私钥,存放在服务器中,也就是前面说的服务器密钥。