SpringBoot中认证和授权学习笔记

1. 认证,Authentication

认证 :用户认证就是判断一个用户的身份是否合法的过程,用户去访问系统资源时系统要求验证用户的身份信息,身份合法方可继续访问,不合法则拒绝访问。

常见的用户身份认证方式有:用户名密码登录,二维码登录,手机验证码,邮箱链接,指纹认证,人脸识别等方式。

2. 授权,Authorization

授权是系统通过根据用户的权限来控制用户访问资源的过程,拥有资源的访问权限则正常访问,没有权限则拒绝访问。

认证是为了保证用户身份的合法性,授权则是为了更细粒度的对隐私数据进行划分,授权是在认证通过后发生的,控制不同的用户能够访问不同的资源。

3. 会话

HTTP 是无状态的协议(对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息):每个请求都是完全独立的,服务端无法确认当前访问者的身份信息,无法分辨上一次的请求发送者和这一次的发送者是不是同一个人。

用户认证通过后,为了避免用户的每次操作都进行认证可将用户的信息保证在会话中。会话就是系统为了保持当前用户的登录状态所提供的机制。

基于session方式

用户认证成功后,在服务端生成用户相关的数据保存在session(当前会话)中,发给客户端的sesssion_id 存放到 cookie 中,这样用户客户端请求时带上 session_id 就可以验证服务器端是否存在 session 数据,以此完成用户的合法校验,当用户退出系统或session过期销毁时,客户端的session_id也就无效了。

基于session的认证方式由Servlet规范定制,服务端要存储session信息需要占用内存资源,客户端需要支持cookie;

基于token方式

用户认证成功后,服务端生成一个token发给客户端,客户端可以放到 cookie 或 localStorage等存储中,每次请求时带上 token,服务端收到token通过验证后即可确认用户身份。

基于token的方式则一般不需要服务端存储token,并且不限制客户端的存储方式。

4. Cookie和Session

Cookie

  • Cookie 存放在客户端,一般用来保存用户信息
  • 在 Cookie 中保存已经登录过的用户信息,下次访问网站的时候页面可以自动帮你登录的一些基本信息给填了。除此之外,Cookie 还能保存用户首选项,主题和其他设置信息。
  • 使用Cookie 保存 session 或者 token ,向后端发送请求的时候带上 Cookie,这样后端就能取到session或者token了。这样就能记录用户当前的状态了,因为 HTTP 协议是无状态的。
  • 记录和分析用户行为。例如获取你在某个页面的停留状态或者看了哪些商品。

Session

  • 通过服务端记录用户的状态。
  • 很多时候我们都是通过 SessionID 来实现特定的用户,SessionID 一般会选择存放在 Redis 中。举个例子:用户成功登陆系统,然后返回给客户端具有 SessionID 的 Cookie,当用户向后端发起请求的时候会把 SessionID 带上,这样后端就知道你的身份状态了。

在这里插入图片描述

Cookie和Session的区别

  • Cookie 是一个实际存在的的东西,一个很具体的东西,就是一段数据,而 Session 是一个抽象概念,或者叫做模式方法,它有很多实现方案;
  • 比如 Tomcat 的实现方法:把状态保存在服务端,然后生成一个 JSESSIONID 放在 Cookie 中;请求过来之后,拿着 JSESSIONID 在服务器端查询并验证状态。

如果没有Cookie的话Session还能用吗?

  • 一般是通过 Cookie 来保存 SessionID ,假如你使用了 Cookie 保存 SessionID的方案的话, 如果客户端禁用了Cookie,那么Seesion就无法正常工作。

  • 但是,并不是没有 Cookie 之后就不能用 Session 了,比如你可以将SessionID放在请求的 url 里面https://xxx.cn/?session_id=xxx 。这种方案的话可行,但是安全性和用户体验感降低。当然,为了你也可以对 SessionID 进行一次加密之后再传入后端。

为什么Cookie 无法防止CSRF攻击,而token可以?

  • **CSRF(Cross Site Request Forgery)**一般被翻译为 跨站请求伪造 。说简单用你的身份去发送一些对你不友好的请求。
  • 如果别人通过 cookie拿到了 SessionId 后就可以代替你的身份访问系统了。
  • 我们使用 token 的话就不会存在这个问题,在我们登录成功获得 token 之后,一般会选择存放在 local storage 中。然后我们在前端通过某些方式会给每个发到后端的请求加上这个 token,这样就不会出现 CSRF 漏洞的问题。因为,即使有个你点击了非法链接发送了请求到服务端,这个非法请求是不会携带 token 的,所以这个请求将是非法的。
  • 不论是 Cookie 还是 token 都无法避免跨站脚本攻击(Cross Site Scripting)XSS。

跨站脚本攻击(Cross Site Scripting)缩写为 CSS 但这会与层叠样式表(Cascading Style Sheets,CSS)的缩写混淆。因此,有人将跨站脚本攻击缩写为XSS

5. Token

  • 为什么需要token?

当然,随着用户量的增加,保存在服务端的 Session 也不断增加,这给服务端带来了很大的压力,并且如果程序是集群或分布式方式部署,同一个用户第一次请求,访问到了 A 服务器,创建了 Session,但是第二次请求却发到了 B 服务器上,但是 B 服务器上并没有之前创建的 Session;这就是分布式架构中的 Session 共享问题。

针对这个问题,我们可以进行服务器之间的 Session 同步,或者干脆把 Session 保存到第三方的组件中,例如保存到 Redis 中;但是不管是哪种方案,都让 Session 变成了项目的负担。

这时候,服务端就会想,如果 Session 不保存在我这里多好,第一次发送用户名密码给我,验证通过后我给你一个通行证,以后客户端每次请求的时候就带着这个通行证;

这个通行证就是 token,当然这个验证结果中需要包含客户端信息,服务端需要知道请求是谁发过来的;还需要包含时间信息,因为通行证不可能永远有效;通行证还不能是明文的,否则会有被截获的风险。

6. JWT

  • JWT 本质上就一段签名的 JSON 格式的数据。由于它是带有签名的,因此接收者便可以验证它的真实性。

JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted. ——JSON Web Token (JWT)

  • JWT构成
  1. Header :描述 JWT 的元数据。定义了生成签名的算法以及 Token 的类型。
  2. Payload(负载):用来存放实际需要传递的数据
  3. Signature(签名):服务器通过PayloadHeader和一个密钥(secret)使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。
  • 在基于 Token 进行身份验证的的应用程序中,服务器通过PayloadHeader和一个密钥(secret)创建令牌(Token)并将 Token 发送给客户端,客户端将 Token 保存在 Cookie 或者 localStorage 里面,以后客户端发出的所有请求都会携带这个令牌。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP Header 的 Authorization字段中:Authorization: Bearer Token

在这里插入图片描述

7. RBAC

  • 解释一:Role-Based Access Control,基于角色的访问控制。
    即,你要能够删除产品,那么当前用户就必须拥有产品经理这个角色

  • 解释二:Resource-Based Access Control,基于资源的访问控制
    即,你要能够删除产品,那么当前用户就必须拥有删除产品这样的权限

  • 3 张基础表: 用户,角色,权限, 以及 2 张中间表来建立 用户与角色的多对多关系,角色与权限的多对多关系。 用户与权限之间也是多对多关系,但是是通过 角色间接建立的。

  • 一个用户可以有多种角色,一个角色也可以赋予多个用户。

  • 一个角色可以包含多种权限,一种权限也可以赋予多个角色。

8. 统一认证授权

基于session的认证方式

将Session存入分布式缓存中,所有服务器应用实例统一从分布式缓存中存取Session

基于token的认证方式

服务端不用存储认证数据,易维护扩展性强, 客户端可以把token 存在任意地方,并且可以实现web和app统一认证机制。其缺点也很明显,token由于自包含信息,因此一般数据量较大,而且每次请求
都需要传递,因此比较占带宽。另外,token的签名验签操作也会给cpu带来额外的处理负担。

9. OAuth2

  • OAuth 是一个行业的标准授权协议,主要用来授权第三方应用获取有限的权限
  • 它就是一种授权机制,它的最终目的是为第三方应用颁发一个有时效性的令牌 token,使得第三方应用能够通过该令牌获取相关的资源。

10. SSO

  • SSO(Single Sign On)即单点登录说的是用户登陆多个子系统的其中一个就有权访问与其相关的其他系统。举个例子我们在登陆了京东金融之后,我们同时也成功登陆京东的京东超市、京东家电等子系统。

在这里插入图片描述

实现方式

  • 父域cookie:将cookie的domain属性设置为父域的域名,将cookie的path属性设置为根路径,这样所有子域的应用都可以访问到这个cookie
  • 认证中心
  • LocalStorage跨域

10. 扫码登录

1)网页端与服务器的配合逻辑:

接下来就是对于这个服务的详细实现。

首先用户打开网站的登录页面的时候,向浏览器的服务器发送获取登录二维码的请求。服务器收到请求后,随机生成一个uuid,将这个id作为key值存入redis服务器,同时设置一个过期时间,再过期后,用户登录二维码需要进行刷新重新获取。

同时,将这个key值和本公司的验证字符串合在一起,通过二维码生成接口,生成一个二维码的图片(二维码生成,网上有很多现成的接口和源码,这里不再介绍)。然后,将二维码图片和uuid一起返回给用户浏览器。

浏览器拿到二维码和uuid后,会每隔一秒向浏览器发送一次,登录是否成功的请求。请求中携带有uuid作为当前页面的标识符。这里有的同学就会奇怪了,服务器只存了个uuid在redis中作为key值,怎么会有用户的id信息呢?

这里确实会有用户的id信息,这个id信息是由手机服务器存入redis中的。具体请继续阅读“手机端与服务器的配合逻辑”。

2)手机端与服务器的配合逻辑:

话说,浏览器拿到二维码后,将二维码展示到网页上,并给用户一个提示:请掏出您的手机,打开扫一扫进行登录。

用户拿出手机扫描二维码,就可以得到一个验证信息和一个uuid(扫描二维码获取字符串的功能在网上同样有很多demo,这里就不详细介绍了)。

由于手机端已经进行过了登录,在访问手机端的服务器的时候,参数中都会携带一个用户的token,手机端服务器可以从中解析到用户的userId(这里从token中取值而不是手机端直接传userid是为了安全,直接传userid可能会被截获和修改,token是加密的,被修改的风险会小很多)。手机端将解析到的数据和用户token一起作为参数,向服务器发送验证登录请求(这里的服务器是手机服务器,手机端的服务器跟网页端服务器不是同一台服务器)。

服务器收到请求后,首先对比参数中的验证信息,确定是否为用户登录请求接口。如果是,返回一个确认信息给手机端。

手机端收到返回后,将登录确认框显示给用户(防止用户误操作,同时使登录更加人性化)。用户确认是进行的登录操作后,手机再次发送请求。服务器拿到uuId和userId后,将用户的userid作为value值存入redis中以uuid作为key的键值对中。

3)登录成功时的逻辑:

然后,浏览器再次发送请求的时候,浏览器端的服务器就可以得到一个用户Id,并调用登录的方法,生成一个浏览器端的token,再浏览器再次发送请求的时候,将用户信息返回给浏览器,登录成功。这里存储用户id而不是直接存储用户信息是因为,手机端的用户信息,不一定是和浏览器端的用户信息完全一致。

在这里插入图片描述

在这里插入图片描述

11. 多账户的统一登录

用户名密码注册登陆

  • 前端将用户名、密码发送到服务器,服务器进行常规的判断,判断用户名、密码长度是否满足,用户名是否重复等条件,条件不通过直接返回对应错误码给到前端,这里密码字段,为了防止传输过程中被截胡,建议加密再上传,我们的传输密码默认都是会进行一个md5加密,然后记录到数据库再进行一层加密,就算是脱库也没事,密码不要明文存储。
  • 校验通过后,就将用户名密码写入数据库,并进行后面积分发放等操作,这里不展开。
  • 现在进行登录,前端将用户名,密码发送给到服务端,服务端首先会校验登录次数是否超过设置的阈值,如果超过只能继续等待被关小黑屋。
  • 如果未超过继续登录逻辑,判断用户名、密码是否正确,不正确密码则进行阈值的判断,如果超过则关小黑屋,记住小黑屋必须设置过期时间,要不然就会永久关上了,这个可以用redis的过期来做。
  • 登录成功后进行后续的一切后置逻辑,比如加积分。。。等操作。

手机号注册登陆

  • 首先输入手机号,然后发送到服务端,服务端将手机号记录在我们数据库中,然后生成随机验证码,并将手机号和验证码绑定到一个redis里面,然后记录过期时间,这个过期时间一般是10分钟左右,这就是我们一般手机验证码的有效期。
  • 手机接收到手机短信后,那么就在界面填写验证码发送服务端,服务端收到验证码后就会在redis里面查询到这个手机号对应的验证码,失败就返回错误码。
  • 成功后就进行登录操作。
  • 密码?在后续产品里面增加一个手机号码密码补录的功能即可

引入第三方账户方案

  • 客户端自己调起登录的界面,进行输入用户名、密码,这里的是第三方的用户名,密码,登录成功后,会返回access_token openid expire_in,这过程会使用到oauth2.0,不过在sdk里面进行内置回调获取了,后面我们会说明我们自身实现的oauth2.0
  • 客户端拿到access_token、openid、login_type(qq、wechat…)请求应用服务器,应用服务器拿到这些数据后就会根据对应的login_type去对应的用户中心进行access_token和openid进行校验。校验不通过则返回对应错误码
  • 校验通过后就会判断本地是否有这个login_type和openid是否存在,不存在则进行获取远程的用户名、头像等基础信息来作为本地基础数据,并且返回code值
  • 如果已经存在,那就是进行登录操作,返回code值。
  • 客户端拿到code值后进行token值的换取,这个完全遵照oauth2.0的协议来走的,后续每次请求必须带上token,token值在服务端的时间比较久,因为我们想要做的是那种永不下线的操作,所以每次请求我们都将token过期时间进行累加。

数据表

  • users表只是单纯针对我们业务侧的登录,主要是做自身业务的oauth2.0业务,
  • user_local_auth是做自己用户名、密码登录,手机号码登录信息记录,
  • user_third_auth是我们第三方用户体系的数据记录,
  • user_auth_rel是用来关联我们users表与user_local_auth、user_third_auth。
用户基础表(users)
字段备注
user_id用户id
token用户登陆的token
expire_intoken过期时间
try_times登录失败次数
用户验证关联表(user_auth_rel)
字段备注
id自增id
user_id用户id
auth_id验证表id
auth_type验证类型(local、third)
本地用户表(user_local_auth)
字段备注
auth_id认证id,自增id
user_name用户唯一标识
password用户密码
mobile用户手机
第三方用户表(user_third_auth)
字段备注
auth_id用户id
openid第三方用户唯一标识
login_type第三方平台标识(qq、wechat…)
access_token第三方获取的access_token,校验使用
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

令人作呕的溏心蛋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值