认证授权
- 什么是认证(系统知道你是谁)
参考:认证就是我们在使用系统的时候,让系统能够识别用户身份的过程,比如我们在火车站买票之前刷身份证,刷脸等都是认证,在我们的系统中,认证就是资源服务器解析jwt获取其中的人userId username的过程
- 什么是授权(系统根据身份信息决定你能干什么)
参考:授权就是根据用户的身份信息判断用户可以使用操作的资源,比如我们在买完火车票后,根据车票能够坐的座位就是授权判断,二等座车票不能做一等座.在我们系统中解析JWT之后是否能后去资源就是权限在判断拦截.
session认证授权
session认证授权流程
绑定客户端cookie使用的.在服务器保存session会话对象.session生命周期(存活的时间)持续到会话结束(打开浏览器,访问服务端开启了一个会话,关闭浏览器会话就结束了)
这里的认证是什么时候:登录成功 cookie携带这sessionId让服务器端拿到session中登录时存放的数据,数据保存着用户user信息.
这里的授权是什么时候:cookie携带sessionId,服务器通过user信息判断可以还是不可应访问这个资源过程.
这种使用session解决认证授权逻辑的方案,后续的开发结构发生变动,出现了问题--session集群共享问题.
session集群共享
单体架构中,程序承载了资源的访问,用户的认证和授权逻辑.系统发布之后,单节点(一个springboot,一个web应用,一个tomcat进程)并发上限不高.所以引入集群,session就出现了集群共享问题.
所以有很多种解决session共享问题的方案: 例如session第三方容器持久化.
但是由于session外化存储,性能要比之前内存存储降低.还要考虑很多提升性能的方案,比如使用redis存储session对象,就要比mysql存储session对象快.
后来又出现微服务多系统,多端登录(手机端,电脑端).整个系统结构早就不再是之前的单体架构.所以认证授权的方案也要随之变化更新.所以才引出我们今天的主题SSO(落地方案是前后端分离的JWT使用)
JWT落地的SSO
单点登录的概念: 多系统中,独立一个登录系统,让用户只在登录系统做一次登录,用户认证授权,就可以全系统都能解析.
当前项目认证登录流程
- 用户直接访问资源服务器,未登录状态
- 资源服务器校验JWT 失败,返回未登录信息
- 访问登录 提交username password
- 登录认证服务器校验用户信息,
-
- 校验用户名是否存在
- 校验密码是否正确
- 校验其他必要条件:锁定 开启 过期等
- 返回JWT,其中jwt携带信息
-
- 用户名
- 用户id
- 用户权限
- 用户浏览器携带JWT访问资源服务器
- 资源服务器校验JWT
-
- 解析用户信息
- 解析用户权限
- 判断授权是否允许
- 数据返回
-
- 如果解析成功授权成功返回正确数据json
- 如果解析成功授权失败返回权限不足
- 如果解析失败返回未认证
JWT详解
JWT是什么
- JWT是什么?
参考: JWT JSON WEB TOKEN 本质是token票据,在认证登录过程中表明用户的登录标识.之所以出现JWT,是因为这是一种常用的数据格式标准.
JWT内容详解
JWT本身就是一个字符串,这个字符串携带数据,并且配合编码加密,保证传输过程不被篡改.
- 包含一个JWT的头
{ "typ":"JWT",
"alg":"HS256"
}
typ:当前jwt类型
alg:表示jwt的签名算法 默认HS256(HMAC SHA256)
- 包含一个body
有效负载,可以理解为一个有固定格式的json,同时可以携带自定义数据的body.
{
"iss":"#发行人",
"exp":"#到期时间",
"sub":"主题(自定义数据)",
"aud":"用户",
"nbf":"在此之前不可用",
"iat":"发布时间"
}
是jwt主要携带的信息.以一个json格式存在
- 第三部分是签名计算
JWT生成时,会把前两部分做BASE64编码
{
"typ":"JWT",
"alg":"HS256"
}
编码字符串:
ewogICAgInR5cCI6IkpXVCIsCiAgICAiYWxnIjoiSFMyNTYiCn0=
body也会进行base64编码
编码字符串:
ewogICAgImV4cCI6IiPliLDmnJ/ml7bpl7QiLAogICAgInN1YiI6IuS4u+mimCjoh6rlrprkuYnmlbDmja4pIgp9
将会在签名之前使用"."分开 头.body.合并到一起的结果:
ewogICAgInR5cCI6IkpXVCIsCiAgICAiYWxnIjoiSFMyNTYiCn0=.ewogICAgImV4cCI6IiPliLDmnJ/ml7bpl7QiLAogICAgInN1YiI6IuS4u+mimCjoh6rlrprkuYnmlbDmja4pIgp9
签名本质是hash算法,原值和加密值是一一对应的.只要原值发生变动,加密值不一样,而且不能反向计算.
签名对前面2部分的组合进行签名计算,使用salt(敏感的,不对外暴露的)
得到一个三列字符串:
57eea5498cacf69512e80b301b9dd59d2ac20a29fe271b55a3626602b9807fe9
然后再继续对字符串做base64编码,拼接到之前的2个部分
公式 base64(头json).base64(负载json).base64(sha256(base64(头json).base64(负载)+salt)
例如当前案例:
ewogICAgInR5cCI6IkpXVCIsCiAgICAiYWxnIjoiSFMyNTYiCn0=. ewogICAgImV4cCI6IiPliLDmnJ/ml7bpl7QiLAogICAgInN1YiI6IuS4u+mimCjoh6rlrprkuYnmlbDmja4pIgp9. NTdlZWE1NDk4Y2FjZjY5NTEyZTgwYjMwMWI5ZGQ1OWQyYWMyMGEyOWZlMjcxYjU1YTM2MjY2MDJiOTgwN2ZlOQ==
JWT作用
- JWT作用是什么(数据格式三部分 头,有效载荷,签名)
参考:
-
- 携带用户的身份信息,一般包括id username.但是不会携带一些敏感信息(password不会携带),例如:
sub:"{'userId':'1','username','王翠花'}"
-
- 保证携带的信息不会在传输过程中遭到篡改(解析端使用同样的算法对比签名)
- JWT携带的信息是什么(网络传输的数据,所以他不易过大)
-
- userId(必带)
- username(展示哪个带哪个)
- nickname(展示哪个带哪个,一般都是nickname)
- autorities:用户的权限信息(取决于权限系统是否独立)
- JWT在传输过程中泄露传递信息怎么办
参考: JWT本身不是加密安全的,基本上是明文的传递,如果需要信息安全,可以对JWT在生成时进行加密,在解析时进行解密操作.(RSA非对称加密.生成jwt一端加密,解析jwt一端去解密,外界破解的可能性微乎其微)
授权系统
引入授权系统的流程,为了让授权实时后去数据库的权限信息,jwt不在携带任何权限数据了,减少jwt的大小,提升传输性能.解析速度也非常快,一般来讲企业的认证授权就是这个流程.
和没有授权系统对比,加入授权系统的流程仅仅是在单体架构系统拿到JWT之后有变化
- 解析JWT 获取用户信息,携带userId访问授权系统
- 授权系统根据userId从数据库或者缓存拿到权限信息
-
- 这时候的权限如果是从数据库读的就是实时的,如果是引入缓存需要考虑缓存一致性问题
- 授权系统返回权限信息
- 单体架构系统授权判断
- 根据判断结果返回给用户数据
-
- 授权通过,返回正确数据
- 授权失败,返回权限不足信息
面试问题
这个项目你是怎么实现的登录认证
我们这个项目呢,使用的是SSO单点登录的架构,然后使用的是JWT来携带我们的用户信息去解析做认证授权。
什么是SSO单点登录
SSO,单点登录的意思就是说在多系统的架构当中,我们希望通过独立实现的一个登录系统,完成的一次登录,在其他所有系统中可以进行认证和授权用户,实现功能的访问.
JWT是什么
JWT叫做Json web token,它主要作用是token票据,一般是在我们登录之后生成的,就像我们买的这个火车票,还有这个电影票,代表我们的个人身份,并且也能关联授权的信息。
JWT原理是什么
JWT它是由三部分组成的,第一部分是头,第二部分呢是它携带的信息,第三部分呢是它的签名,头的作用呢,主要是定义这个JWT的类型,还有就是定义签名使用的算法。第二部分携带的信息呢,就是我们自定义的一个Json字符串,第三部分呢,就是利用前两部分做的加密计算,它的作用呢,主要就是帮助我们在解析的时候确定前两部分的内容呢有没有被篡改,因为它是一个哈希计算。
JWT是否能够携带敏感信息,比如密码.
JWT一般是不建议去携带一些敏感信息的,比如这个密码,因为我们这个JWT的前两部分,包括它的头和携带的信息呢,可以看成是一个名文啊,它仅仅使用了BASE64的编码,完全可以去反编码得到它的原文的。
如果我们要想在这个JWT传递的过程当中呢,不被别人去解析到这个名文,就需要再多加一层加密的计算啊,比如说引入rsa这种非对称加密算法,让我们生成这个JWT一端的去加密啊,然后去解析JWT的一端的去解密,这样的传递过程当中是相对比较安全的。
JWT的签名作用是什么
他签名呢,主要是哈希算法做了一个比较.生成端生成的签名,然后由这个解析端去解析这个签名,在解析过程当中呢,如果他发现和我们携带过来传递过来这个签名的解析结果是不一样的话,那就证明他传递的头或者是他携带的内容在传递过程当中是被人篡改了,所以签名的作用就是确定传递的信息有没有被篡改,保证这个信息是我们生成JWT的原数据。
你的登录认证过程中JWT携带了什么数据.
我们在登录的时候呢,去查询数据库,从数据库里边查到用户的基本信息,包括他的userID username,还有昵称啊或者是邮箱,还有电话号码,然后呢,查到的角色和权限信息都是我这个JWT里面携带的数据。
权限如何保证实时,如果权限变动,后端系统会不会解析出最新权限?
目前在我们这种落地方案当中的JWT里边已经保存的权限是不会变动的,所以如果数据库变动的话呢,JWT登录代表的用户,他的权限也不会在我的后端系统解析出来最新的权限数据。因为我们现在这个系统,它权限变动的频率是极其的小的,所以这种情况如果出现的话呢,就没有考虑到它的实时性。
那如果需要保证权限数据的及时更新,怎么做呢?
那如果我们需要保证这种权限变动的及时更新和实时性呢.我们就不能够在JWT里边直接携带它的权限数据了,就需要在JWT里边只携带用户的信息,必须携带的一定有这个USERID,然后呢,再去做权限访问的一个接口,这样的话呢,我们的后端系统在拿到JWT里边的信息之后呢,用这个USERID去访问我们的权限的一个接口,从数据库里边拿到正确的实时的数据就可以了。
访问权限接口的话,查询数据库性能怎么保证呢?
啊,是的,相比于JWT直接携带权限数据,我们使用JWT里边里边的用户信息去访问一个权限的接口的话,性能是会稍微低一点的,但是我们可以引入缓存的逻辑,也就是查询的权限是保存在缓存里边的,而不是从数据库里面直接查。