分布式场景下的无状态登录解决方案-JWT+RSA(无编码)

知识铺垫

有状态登录

即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session。
如:用户登录后,我们把登录者的信息保存在服务端session中,并且给用户一个cookie值,记录对应的session。
然后下次请求,用户携带cookie值来,我们就能识别到对应session,从而找到用户的信息。

缺点:

  • 服务端保存大量数据,增加服务端压力(占用内存或数据库空间)
  • 服务端保存用户状态,无法进行水平扩展(无法搭建集群)
  • 客户端请求依赖服务端,多次请求必须访问同一台服务器(防止出现多次请求用户状态不一致)

无状态登录

微服务集群中的每个服务,对外提供的都是Rest风格的接口。而Rest风格的一个最重要的规范就是:服务的无状态性,
即:	
服务端不保存任何客户端请求者信息
客户端的每次请求必须具备自描述信息,通过这些信息识别客户端身份

好处:

  • 客户端请求不依赖服务端的信息,任何多次请求不需要必须访问到同一台服务
  • 服务端的集群和状态对客户端透明
  • 服务端可以任意的迁移和伸缩
  • 减小服务端存储压力(不用再存储用户状态)

如何实现无状态登录

无状态登录流程:

  • 用户首次请求服务,服务端对用户信息进行登录认证
  • 登陆成功后,服务端将用户信息加密形成的token放入cookie中返回给客户端,作为登录凭证
  • 用户后续请求都携带token,服务端对token进行解密,并判断是否有效

流程图:
在这里插入图片描述

生成token所用的认证规范与加密算法

token是识别客户端身份的唯一标示,那么token的安全性至关重要。
本解决方案采用JWT+RSA非对称加密的方式实现token的生成。

JWT相关知识,点击这里:

  • 头部Header(采用base64加密):声明类型,加密算法
  • 载荷Payload(采用base64加密):用户身份信息,注册声明(签发时间,过期时间等)
  • 签名Signature:根据前两个数据,加上密钥通过加密算法生成

RSA非对称加密算法基本原理:

同时生成两把密钥:私钥和公钥,私钥隐秘保存,公钥可以下发给信任客户端
  • 私钥加密,持有私钥或公钥才可以解密
  • 公钥加密,持有私钥才可解密

结合Zuul的鉴权流程

secret是签名的关键,因此一定要保密,我们放到鉴权中心保存,其它任何服务中都不能获取secret。
无RSA的鉴权方案:
  • 用户请求登录

  • Zuul将请求转发到授权中心,请求授权

  • 授权中心校验完成,颁发JWT凭证

  • 客户端请求其它功能,携带JWT

  • Zuul将jwt交给授权中心校验,通过后放行

  • 用户请求到达微服务

  • 微服务将jwt交给鉴权中心,鉴权同时解析用户信息

  • 鉴权中心返回用户数据给微服务

  • 微服务处理请求,返回响应
    在这里插入图片描述

     缺点:每次鉴权都需要访问鉴权中心,系统间的网络请求频率过高,效率略差,鉴权中心的压力较大。
    
有RSA的鉴权方案:
  • 利用RSA生成公钥和私钥。私钥保存在授权中心,公钥保存在Zuul和各个微服务
  • 用户请求登录
  • 授权中心校验,通过后用私钥对JWT进行签名加密
  • 返回jwt给用户
  • 用户携带JWT访问
  • Zuul直接通过公钥解密JWT,进行验证,验证通过则放行
  • 请求到达微服务,微服务直接用公钥解析JWT,获取用户信息,无需访问授权中心

在这里插入图片描述

实战描述

创建授权中心

授权中心职责:

  • 用户鉴权:接收用户的登录请求,通过用户中心的接口进行校验,通过后使用私钥生成JWT返回

  • 服务鉴权:微服务间调用不经过Zuul会有风险,需要鉴权中心认证通过后,微服务再使用公钥解密

     授权中心中公钥私钥应在JWT相关承载对象实例化后就立即被读取,spring可以使用@PostConstruct实现。
     且如果公钥私钥不存在的话,要先使用密钥创建公钥私钥。
    

实际编码可能出现的问题

返回的信息不包含cookie
设置cookie跨域
因为cookie中包含domain属性,所以cookie是不能跨域的,所以要设置cookie跨域。
1·服务的响应头中需要携带Access-Control-Allow-Credentials并且为true。
2·响应头中的Access-Control-Allow-Origin一定不能为*,必须是指定的域名(安全性考虑)
3·浏览器发起ajax需要指定withCredentials 为true(携带cookie)	

在这里插入图片描述

配置反向代理Host
如果设置了cookie跨域后,返回信息依旧不存在cookie信息。则检查cookie的domain属性,cookie的domain必须和当前操作的域名一致。
通过读取cookie相关工具的源码得知,domain的取值是requestUrl中的serverName,也就是请求头中的host信息。
如果cookie的domain属性与域名不一致,则检查服务是否使用了代理服务器,如nginx反向代理。
默认情况下nginx反向代理是不会转发请求中的Host头部。

需要增加以下配置:

proxy_set_header Host $host;
配置zuul

但在使用springCloud做分布式服务时,除了使用nginx做反向代理外,还会使用zuul来实现服务路由,服务鉴权,请求过滤等操作。所以此时cookie应该还不会出现在响应中。此时如果使用Postman测试debug接口的话,则会发现,响求头中set-cookie消失了。
这是因为zuul的默认过滤器会对用户的请求头和响应头进行重组,过滤掉敏感头。
在这里插入图片描述
解决方案有两种:
全局设置:zuul.sensitive-headers=
指定路由设置:
zuul.routes..sensitive-headers=
zuul.routes..custom-sensitive-headers=true

此时还没有结束,因为zuul默认是过滤掉请求本身的host信息的,还要增加以下配置。
add-host-header: true

以上配置了,但响应依旧没有cookie信息

此时需要检查自己的zuul的版本,在spring-cloud-start-netflix-zuul:2.0.1.RELEASE版本的RibbonRoutingFilter过滤中存在一个bug,即对于host请求头无论如何都会被过滤,导致cookie的domain设置不上。源码如下。
解决方法只能更换版本。
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值