1. 参考文献
    1. 慕课网 - 架构师 - 分布式会话
    2. 慕课网 - 手记 - 漫谈jwt - Allen
    3. 慕课网 - 架构师 - 配置中心 - 加密

1.1 会话的概念

1.1.1 基础定义

  • 会话session代表着客户端和服务器的一次交互过程,这个过程可以是连续的,也可以是时断时续的

    1.1.2 Jsp/servlet时代的会话

  1. 一旦用户和服务器交互,服务器(tomcat)就会为用户创建一个session,同时前端会创建一个jessionid,每次交互都会携带
  2. 如此一来服务器只要接收到用户请求的时候,就可以拿到jessionid,并根据id在内存中找到对应的会话session,当拿到session会话后,那么我们就可以操作会话了
  3. 会话存活期间,我们就能认为用户正一直使用着网站状态,一旦session超时过期,就可以判断用户已经离开网站
  4. 相互之间的判断就是通过jessionid判断的,session中可以保存不同用户的信息

    1.1.3 无状态会话

    1.1.4 有状态会话

    1.1.5 Tomcat会话

    2. 分布式会话

    2.1 流程分析

    2.2 实现

  • 前后逻辑
    • 前端: 使用cookie记录token信息(小程序和app使用本地缓存)
    • 后端: Redis按照uid - token类似的逻辑存储
  • 令牌(token)思考
    • token可以让用户在一段时间内是免登录的
    • 前端是放在cookie中,随着http请求发送到后端进行验证
  • 令牌的失效:时效短2个小时左右,不断刷新(通过双令牌机制)

    3. 单点登录(SSO)

  • https://www.imooc.com/article/288331

  • https://blog.csdn.net/xiaoguan_liu/article/details/91492110

    3.1 同级顶级域名

    同级域名的单点登录 = 分布式会话

    3.2 不同顶级域名

    CAS:Central Authentication Service 单点登录/中央认证服务

    4. 鉴权

    4.1 权限的概念

    4.1.1 什么是权限

    (1) 权限的定义

    用户在被授权之后能访问的api范围

    (2) 静态权限

  • 静态权限:例如 普通用户、vip用户、特级用户,如果我们想增加一个用户必须要修改代码,也就是说分组是在项目设计之初就定制好了

    (3) 动态权限

  • 动态权限:没有任何分组,我们创建分组,动态分配权限

    (4) 超权概念

  1. 横向越权: 横向越权指的是攻击者尝试访问与他拥有相同权限的用户的资源,比如: 通过知道别的用户的id,修改别的用户的个人信息
  2. 纵向越权: 纵向越权指的是一个低级别攻击者尝试访问高级别用户的资源
  3. 用户的id不能在前台显示的传递: 不能在header、path路径、查询参数中传递,可以使用Threadlocal / redis 存储

    4.2 权限的设计

  4. 三个方面:用户、分组(角色)、权限

  5. 用户属于某一个分组,分组被赋予相关的权限

    4.3 登录鉴权

    4.3.1 单令牌机制

  6. 单令牌机制已经满足web程序的登录和权限交互,但是双令牌的体验更好

  7. 小程序只需要单令牌即可,因为小程序的token一旦过期,小程序会再次尝试登录,刷新token,而且这个流程是静默的,且用户无感知
  8. 单令牌的缺陷:当token的时间过了,我们在发起请求,会直接跳转到登录的界面

    4.3.2 双令牌机制

  9. 两个令牌的标志:access_tokenrefresh_token

  10. 双令牌帮助无感知登录的流程
    1. 在token快过之后,访问被拒接
    2. 检测是否存在refresh_token,存在则判断refresh_token是否过期
    3. refresh_token没问题,则调用刷新方法,重新获取一个access_token
    4. 系统重发被决绝的请求,并且更新refresh_token的有效期
  11. 一般情况下refresh_token时间远大于access_token的时间
  12. 例如:access_token 2小时 refresh_token 7天

    5. 鉴权实战

    5.1 鉴权的实战理解

  13. 登录是最简单的鉴权,例如系统内的所有接口,只有登录才能访问,后续访问也不分什么角色,这种比较简单,适合自己写着玩,所以这里也做记录

  14. 真正的权限管理,登录只是第一步,包含整体的权限划分,异常权限处理…..
  15. 后面的实战如果是鉴权,则表明有登录也有权限划分,如果是登录,就是简单的登录访问接口

    5.2 小程序的鉴权

  • 登录验证
    • 自己的小程序像微信获取到一个code码
    • 带着code访问自己项目的api
    • 调用微信的服务器验证
    • 生成token,存储在小程序的本地缓存中
  • 权限验证: 实现方式还是单令牌实现
    • token随着http请求发送到后端进行验证
  • 判断token是否正确(是否过期、是否正确….)
  • 根据结果判断是否通过

    5.3 web登录

    5.4 web鉴权

    6. 令牌的常见实现

  • 核心:只要能保证唯一的字符串就行

    6.1 UUID

  1. Jdk自带的方法::UUID.randomUUID().toString();

    6.2 雪花算法

    6.2.1 基本原理

  2. 一个64bitlong类型的数字

  3. 引入了时间戳,保持自增
  4. 雪花算法位数解析

    1. [1-1]:固定值0,改表该数字是一个正数
    2. (1-42]41位的时间戳,这个时间戳是一段时间(当前时间-设置的时间)
    3. (42-47]5位机房序号
    4. (47-52]5位机器序号
    5. (52-64]12位序号,能保证在并发情况下生成4096个不同的数字(毫秒级别的)

      6.2.2 前后端交互精度丢失

      6.2.2.1 现象

  5. 雪花算法得到的数字较长,传到前端后,精度丢失

  6. 库中存的值:23754851322302474
  7. 后端取的值:23754851322302474
  8. 前端得到值:23754851322302470,数据被四舍五入了

    6.2.2.2 解决方式

    ```java
  9. 单个字段 @JsonSerialize(using= ToStringSerializer.class) private Long id;

  10. 统一配置类 package com.jiawa.wiki.config; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

@Configuration public class JacksonConfig { @Bean public ObjectMapper jacksonObjectMapper (Jackson2ObjectMapperBuilder builder) { ObjectMapper objectMapper = builder.createXmlMapper(false).build(); SimpleModule simpleModule = new SimpleModule(); simpleModule.addSerializer(Long.class, ToStringSerializer.instance); objectMapper.registerModule(simpleModule); return objectMapper; } }

  1. <a name="tJcBI"></a>
  2. ## 6.3 Jwt
  3. 1. 全称为`json web token`,说白了是什么呢? 就仅仅只是一个字符串而已,例如:`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJhZG1pbiI6dHJ1ZX0.OLvs36KmqB9cmsUrMpUutfhV52_iSz4bQMYJjkI_TLQ`
  4. 1. jwt 包含了三个主要部分:` Header.Payload.Signature`,以" . "来进行分
  5. 1. `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9`
  6. 1. `eyJuYW1lIjoiSm9obiBEb2UiLCJhZG1pbiI6dHJ1ZX0`
  7. 1. `OLvs36KmqB9cmsUrMpUutfhV52_iSz4bQMYJjkI_TLQ`
  8. 3. **Header:头部声明token的类型,这部分主要存储关于签名算法的信息,通常包含两个部分:token类型和采用的加密算法,如下 👇 然后使用Base64Url编码组成了Header部分**
  9. ```json
  10. {
  11. "alg": "HS256",
  12. "typ": "JWT"
  13. }
  1. Payload: 是存储一些数据的地方

    1. 它其实就是一个数据实体,俗称Claim,jwt并不强制使用,它默认这一部分数据为业务数据,是系统业务需要的数据,可有可无,可多可少,然后使用Base64Url编码组成了Payload部分
    2. 你可以定义token的签发者、签发过期时间、生效时间等一系列属性,以及auth0包对应的方法名称主要包含如下👇
    3. 还可以添加自定义的属性,服务端收到Token的时候也可以对Payload中的信息验证,例如: 某个token的签发者是”feign-api”,加入某个接口只能允许”gateway-api”签发的token,那么在做权限验证的时候就可以加入lssuer的判断逻辑
      1. iss(签发者、发行方):issued
      2. exp(过期时间戳)
      3. sub(面向的用户)
      4. aud(接收方)
      5. iat(签发时间)
      6. {
      7. "sub": "1234567890",
      8. "name": "John Doe",
      9. "admin": true
      10. }
  2. Signature

    1. 创建签名需要使用编码后的header和payload以及一个秘钥,
    2. 组成的公式:编码后的header、编码后的payload、一个secret进行加密
    3. HMACSHA256( base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret)

      7. 数据加密

      7.1 加密的三个环节

  • 引入一个数据安全机制,减少敏感数据的暴露程度,因此需要对三个环节做严格控制
  1. 数据端加密:就像颁发CA证书一样,必须由一个中心机构用密钥对明文进行加密。这里也一样,需要在生产环境中创建一个特定的服务,专门做加密操作。最重要的是,加密所用的密钥只能保存在那台机器里,不能对外暴漏。
  2. 数据存放端:数据存放(如GitHub、db)只能保存在加密过后的数据
  3. 数据解密端:由于密钥都保存在数据加密端,因此数据解密和数据加密必须放在同一个地方进行,加密数据在传给客户端之前要经过解密,不能将密钥下方到客户端机器上进行解密

    7.2 对称加密(对称密钥)

  4. 对称加密算法要求发送者和接收者在进行信息交换之前,商定一个密钥,即加密和解密都使用同一个密钥来进行

  5. 如果大家上学的时候选修过密码学就会知道,对称加密算法运算速度快,比较高效
  6. 潜在风险:密钥只能分发给绝对信任的 client(授信服务),否则任何获得密钥的人都可以使用该密钥进行通信

    7.3 非对称加密(非对称密钥)

    7.3.1 基本原理

  7. 非对称加密算法需要生成一个密钥对,包括(公钥 & 私钥)

  8. 它是成对出现的,就像一把锁和一把钥匙,只有特定的钥匙才能开启对应的锁
  9. 使用: 公钥用来对特定的信息进行加密,使用对应的私钥进行解密
  10. 非对称算法复杂度强,加解密速度没有对称快,但是解决了密钥分发安全性问题

    7.3.2 使用技巧

  11. 流程: 服务A ——> 数据发送 ——> 服务B ——> 数据返回 ——> 服务A

  12. 谁接受服务,谁出一对密钥(公钥 & 私钥)
  13. 首先协商出两队公钥和私钥
  14. A向B发送数据时候,A采用B提供的公钥进行加密,B采用自己保留的私钥进行解密

    7.3.3 非对称的公钥是公开的吗?

  15. 首先公钥也是密钥的一部分,它所谓的”公开”是针对授信访问者来说的,在这个圈子里面是公开的

  16. 假如你的服务可以被任何人调用,那么这个公钥则是可以被任何人知道的

    7.3.4 频繁调用鉴权服务的令牌校验接口产生的问题?

  17. 一般的鉴权服务不是集成在网关层的,那么每一次的令牌的校验都需要发送一个http请求到鉴权服务。那么每一次方法的调用都会触发请求,并发 + RT(响应时间)都会增加

  18. 解决方式:令牌的颁发采用非对称加密,然后令牌的校验下放到网关层,网关层拿着私钥进行解密,然后直接在网关层校验令牌