分布式高级篇(六) - 认证服务

646 阅读12分钟
  • 微服务商城项目地址

  • 简单认证 - 短信验证码

    • 验证码防刷
    • 验证码校验
  • 社交登录

    • 微博账户登录
    • 分布式session 一致性解决
    • spring session 原理
  • 单点登录

    • 简单实现
    • 单点登录流程
    • 原理

简单认证服务

基础环境搭建

  • 拷贝登录、注册的html文件 到项目的tenplates目录下,并修改文件名字

    image-20210122092849651

  • 修改本地hosts文件

    image-20210122092416725

  • 将静态资源放入nginx中

    image-20210122092712775

    • 修改 login.html 和 register.html 中引用的静态资源地址为nginx所在位置

      image-20210122093840982

  • 网关服务添加路由配置(重启)

    image-20210122101441963

  • 页面最新效果

    浏览器输入 auth.mall.com

    image-20210122101539056

注册功能

完善首页、登录、注册之间的跳转

  • 首页注册登录跳转最终效果

倒计时效果

  • 发送验证码后,60秒倒计时

    image-20210122131848587

视图映射

  • 原先有几个页面需要显示,我们就需要写几个空方法的controller,来实现页面跳转

    image-20210122132558806

  • SpringMVC viewController:将请求和页面映射进来,添加自定义的视图映射

    image-20210122132649591

短信验证

  • 短信第三方服务开通(阿里云有0元5次的体验)

    阿里云--云市场--短信服务

    image-20210122133650833

    操作官网的API接口文档,测试短信接口

  • 短信服务整合进 touch-air-mall-third-part服务

    • 测试效果

      image-20210122143346960

      image-20210122143322662

验证码接口防刷

  • 前端页面写的60s,并不能完全限制60s多次发送短信,60s能多次刷新注册页面,仍然可以发送短信验证码请求,服务器端引入redis临时存储,来进行防刷限制

    image-20210122152648748

    image-20210122194655306

验证码校验

  • 远程调用注册服务之前,校验验证码是否正确

    image-20210122194737615

注册请求

后端参数校验,并回显校验结果
  • jsr303

    image-20210122194823448

    image-20210122194906255

异常机制(用户名、手机号唯一校验)
  • 往上层抛出异常,controller捕获

    image-20210122194958191

    image-20210122195043756

    image-20210122195023981

密码加密

  • 两种(可逆/不可逆)
MD5
  • MD5(Message Digest algorithm 5)信息摘要算法
    • 压缩性:任意长度的数据,算出的MD5长度都是固定的
    • 容易计算:从原数据计算出MD5值很容易
    • 抗修改性:对原数据进行任何改动,哪怕只修改一个字节,所得到的MD5值区别都很大
    • 强抗碰撞:想找到两个不同的数据,使他们具有相同的MD5值,是非常困难的
    • 不可逆,但容易被暴力破解(彩虹表)
  • Md5Crypt 加盐
    • 通过生成随机数与MD5生成字符串进行组合
    • 数据库同时存储MD5值与salt值,验证正确性时使用salt进行MD5即可
BCrypt
  • BCryptPasswordEncoder

    BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
    String encode = bCryptPasswordEncoder.encode("123456");
    bCryptPasswordEncoder.matches("encode", "xxxx");
    

    注册时,密码加密image-20210122195128011

    登录时,密码匹配image-20210122195146350

登录功能

  • 简单实现,待后续所有认证完成,再完善

    输入用户名/手机号 + 密码,登录成功跳转商城首页,失败页面提示“用户名或密码错误”

    image-20210122195430238

社交登录

  • QQ、微信、微博、GitHub等网站的用户量非常大,别的网站为了简化自我网站的登录与注册逻辑,引入了社交登录功能

    image-20210123111424442

  • 步骤

    • 1、用户点击微博按钮

    • 2、引导跳转到微博授权页面

    • 3、用户主动点击授权,跳回之前网页

      image-20210123111506031

OAuth2.0

  • OAuth:OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容
  • OAuth2.0:对于用户相关的OpenAPI(例如获取用户信息,动态同步、照片、日志、分享等),为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的像用户征求授权

OAuth2.0 运行流程

  • 运行流程

    image-20210123113722593

    (A)用户打开客户端以后,客户端要求用户给予授权

    (B)用户同意给予客户端授权

    (C)客户端使用上一步获得的授权,向认证服务器申请令牌

    (D)认证服务器对客户端进行认证后,确认无误,同意发放令牌

    (E)客户端使用令牌,向资源服务区申请获取资源

    (F)资源服务器确认令牌无误,同意向客户端开放资源

    不难看出,上面六个步骤,B是关键,用户同意授权,有了授权才可以请求令牌,凭着令牌才能访问资源

    image-20210123121844012

微博登录准备工作

进入微博开放平台

  • 开放平台地址

    微博开放平台

  • 登录个人微博(进行身份认证 一个工作日)

微博网站接入

  • 微连接--->网站接入

    image-20210123122907341

创建自己的应用

  • 第一步:微连接-->网站接入-->创建新应用

    image-20210125162253703

  • 第二步:创建成功后,进入我的应用,后续开发需要使用的 App Key和App Secret

    image-20210125162529685

  • 第三步:进入高级设置,配置授权成功后,跳转的地址

    image-20210125162717586

微博OAuth2授权流程

Web网站的授权
  • 流程图

    image-20210123135137535

    (A)用户访问客户端,客户端引导用户到授权

    (B)用户对认证服务器进行授权

    (C)授权通过,认证服务器将用户导向客户端事先指定的“重定向URI(Redirection URI)”,同时附上一个 授权码

    (D)客户端收到授权码,附上先前的“重定向URI”,向认证服务器申请令牌,这一步是在后台服务器上完成的,用户无感知

    (E)认证服务器核对授权码和重定向URI,通过就像客户端发送访问令牌和更新令牌

  • 详细操作步骤

    • 第一步:引导需要授权的用户到如下地址

      https://api.weibo.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI
      
    • 第二步:如果用户同意授权,页面跳转至 YOUR_REGISTERED_REDIRECT_URI/?code=CODE,获得授权码

    • 第三步:换取Access Token

      https://api.weibo.com/oauth2/access_token?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&grant_type=authorization_code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI&code=CODE
      
      其中client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET可以使用basic方式加入header
      

      image-20210123141958570

      返回值

      image-20210123140221690

      模拟测试数据image-20210123141825401

    • 第四步:使用获得的Access Token 调用API

      进入我的应用--->接口管理--->已有权限 查看允许通过 token 令牌获取哪些数据

      image-20210123142417148

      用户普通读取接口调用image-20210123142710297

      image-20210123143418507

    1、使用授权码Code获取Access Token,Code只能使用一次,Access Token获取开放信息,可以使用多次

    2、同一个用户的Access Token 一段时间是不会变化的,即使多次不同的授权码Code获取

    3、整个过程中 client_secret和access token 要完全保密,因此需要在后台服务中进行获取

整合微博登录

  • 整体流程

    image-20210123161830443

  • 处理授权成功之后,根据授权码code 获取 access_token

  • 修改数据库的会员信息表,新增社交信息字段

    更新touch_air_mall_ums sql 文件

  • 代码实现

    image-20210126140357243

  • 现有问题:登录成功,首页账号信息回显,无法共享

    认证服务器登录成功,用户信息存放进session,商品服务无法获取到

    image-20210127090633566

分布式session

Session共享问题

  • 第一个问题:不能跨不同域名共享

    session原理

    image-20210123171610071

  • 第二个问题:同一份服务,复制多份,session不同步问题;不同服务,session不能共享问题

    image-20210123171828960

    image-20210127091201168

分布式session解决方案原理

方案一:session复制(同步)

image-20210123172705464

  • 优点:web-server(Tomcat)原生支持,只需要修改配置文件
  • 缺点
    • session同步需要数据传输,占用大量网络带宽,降低了服务器集群的业务处理能力
    • 任意一台web-server保存的数据都是所有web-server的session总和,受到内存限制无法水平扩展更多的web-server
    • 大型分布式集群情况下,由于所有web-server都全量保存数据,所以此方案不可取

方案二:客户端存储

image-20210123173416332

  • 优点:服务器不需要存储session,用户保存自己的session信息到cookie,节省服务端资源
  • 缺点:
    • 都是缺点,这只是一种思路,并不可取
    • 每次http请求,携带用户在cookie中的完整信息,浪费网络带宽
    • session数据放在cookie中,cookie有长度限制4k,不能保存大量信息
    • session数据放在cookie中,存在泄漏、篡改、窃取等安全隐患

方案三:hash一致性

image-20210126091908436

  • 优点:
    • 只需要改nginx配置,不需要修改应用代码
    • 负载均衡,只要hash属性的值分布是均匀的,多台web-server的负载是均衡的
    • 可以支持web-server水平扩展(Session同步法是不行的,受内存限制)
  • 缺点:
    • session还是存在web-server中的,所以web-server重启可能导致部分session丢失,影响业务,如部分用户需要重新登录
    • 如果web-server水平扩展,rehash后session重新分布,也会有一部分用户路由不到正确的session
  • 但是以上缺点也不是很大,因为session本来都是存在有效期的,索引这两种反向代理方式都可以使用

方案四:统一存储

image-20210126135632681

  • 优点:
    • 没有安全隐患
    • 可以水平扩展,数据库/缓存水平切分即可
    • web-server 重启或者扩容都不会有session丢失
  • 缺点:
    • 增加了一次网络调用,并且需要修改应用代码;如将所有的getSession方法替换为从Redis查数据的方法,redis获取数据比应用内存慢很多
    • 上面缺点可以用springSession完美解决

整合SpringSession

不同服务,子域共享

image-20210127091950408

SpringSession

  • Spring Session官方文档

  • Spring Session 提供了一个 API 和实现,用于管理用户的会话信息,同时支持集群会话,而不需要绑定到特定于应用程序容器的解决方案。它还提供透明的整合,包括:

    • HttpSession: 允许以与应用程序容器无关的方式替换 HttpSession,并支持在头文件中提供会话 id 以使用 RESTful api。
    • 提供在接收 WebSocket 消息时保持 HttpSession 处于活动状态的能力
    • WebSession: 允许以与应用程序容器无关的方式替换 Spring WebFlux 的 WebSession

整合

  • 第一步:引入依赖

    <dependency>
    	<groupId>org.springframework.session</groupId>
    	<artifactId>spring-session-data-redis</artifactId>
    </dependency>
    

    添加yml配置

    image-20210127093347562

  • 第二步:配置redis的连接信息

  • 第三步:servlet 容器初始化(不需要自己做)

    image-20210127093859869

  • 第四步:开启Spring Session功能

    image-20210127111409693

测试共享

  • 在auth认证服务,认证成功的,即使加上spring session 它的作用域依然只是在auth.mall.com 这个域名下,仍无法共享

    image-20210127112413940

  • 在商品服务 也配置上spring session 并开启功能,重启商品服务

  • 手动修改 session的作用域 为父域后,实现了session共享

    image-20210127140945478

自定义spring Session 完成子域共享

  • redis中存储的数据以json序列化的方式

    官方文档配置

  • 子域共享,修改cookie

  • 配置sessionConfig

    image-20210127144126248

  • 第三方登录完成后,跳转首页,并回显用户信息

    image-20210127144409070

Spring Session核心原理(*)

  • 装饰者模式

  • 1、@EnableRedisHttpSession 注解,导入了 RedisHttpSessionConfiguration.class

    • 1.1、给容器中添加了一个组件 RedisIndexedSessionRepository:redis操作Session,session的增删改查的封装类

      image-20210127150436541

    • 1.2、SessionRepositoryFilter : session 存储的过滤器;每个请求过来都必须经过filter

    ​ 创建的时候,就自动从容器中获取到了 sessionRepository

    ​ SessionRepositoryFilter 的核心代码:原生的 Request 和 Response 对象都被包装了

    image-20210127151834439

    ---> SessionRepositoryRequestWrapper 和 SessionRepositoryResponseWrapper

    ---> 之后要想获取 session, request.getSession()--> wrapperRequest.getSession()

    ---> wrapperRequest.getSession()===> 是从 SessionRepository 中获取到的 也就是从redis中获取的

    image-20210127152020910

社交登录最终效果

  • 第三方社交登录,分布式session一致效果演示

    登录完成跳转--商城首页,并回显微博账户昵称-->商品详情页和商品搜索页,都可以回显昵称

    涉及模块

    touch-air-mall-auth-server
    touch-air-mall-gateway
    touch-air-mall-member
    touch-air-mall-product
    touch-air-mall-search
    touch-air-mall-third-part
    

SSO(单点登录)

单点登录简介

  • 是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分;一次登录,处处可用

  • 如下场景:京东旗下有如下几个产品:京东商城 jdonline.mall.com 、京东教育 jd.stu.com 、京东生鲜 fresh.veg.com ...

    如果我们需要使用这几个应用,会不会需要每个不同的产品需要重复注册一次,登陆一次。这时候,如果想使用Spring Session 来解决 是不可行的。比如商城域名放大,最大也只能到 mall.com -->还是无法做到所有子域共享

单点登录框架&原理演示

  • XXL-SSO 是一个分布式单点登录框架,只需要登录一次就可以访问所有相互信任的应用系统,拥有轻量级、分布式、跨越、Cookie+Token 均支持、Web+App 均支持等特性,开箱即用

xxl-sso-server

  • 8080/xxl-sso-server

    修改配置文件中的redis连接地址

编排

  • 准备三个域名

    • ssoserver.com 登录认证服务器

    • client1.com 客户端1

    • client2.com 客户端2

    • 修改本地域名映射

      image-20210128091912798

  • 启动sso开源框架服务

    • 启动 oss-server ,浏览器访问:ssoserver.com:8080/xxl-sso-ser…

      image-20210128100143174

    • 修改两个客户端服务的redis连接地址,并配置你的登录服务器的地址

      image-20210128103608031

      然后客户端服务以8081和8083端口启动两个相同的服务

  • 客户端1,浏览器访问:client1.com:8081/xxl-sso-web…

    image-20210128103105715

    注意观察,URI的变化,重定向去了登录

    接着就点击客户端1的登录,再访问client2

    image-20210128103239363 浏览器输入:client2.com:8083/xxl-sso-web…

    image-20210128104940540

单点登录流程

  • 理论效果

    image-20210128110115501

  • 核心操作

    • 1、给登录服务器留下登录痕迹

    • 2、登录服务器要将token信息重定向的时候,带到url地址上

    • 3、其他系统要处理url地址上的关键token,只要有,将token对应的用户保存到自己的session中

    • 4、自己系统将用户保存在自己的会话中

      image-20210128155905510

      image-20210128160059470

      image-20210128170108971

      image-20210128170204629

单点登录效果演示

  • 涉及模块

    mall-test-sso-server
    mall-test-sso-client
    mall-test-sso-client2
    

    单点登录效果演示