微服务实战(十五)看完不信你还不懂单点登录:SSO,CAS,OAuth,Jwt,Spring Security,Shiro

本文主要是介绍微服务实战(十五)看完不信你还不懂单点登录:SSO,CAS,OAuth,Jwt,Spring Security,Shiro,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

本篇主要就单点登录的概念、各类相关技术(比如CAS、OAUTH、JWT、SpringSecurity)的用途以及思路进行整体介绍。

单点登录(SSO)

单点登录,顾名思义,就是在一个点登录。

我们看看它的定义,单点登录SSO(Single Sign On ,以前我总记成 SOS)。百科上的解释是:“多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。”

其实我感觉这不够精准,并没有把“单点”解释到位,所谓单点登录,应是在多个应用系统环境下,用户只需要在统一的一个单点进行登录,然后就可以畅通无阻地访问该应用系统群组里的每个应用。

 

举个例子,像我们熟知的购物网站,虽然给人们的感觉这就是“一个”网站,但是实际上,它是一个系统集群,每个频道都是一个独立的系统。

而每个系统的登录入口,其实都是指向同一个地址,而且只要在这个地址登录过后,然后再访问这个网站的下属系统(比如在金融模块页面登录后,再进入首页或者 秒杀、优惠券等系统),你都已经是已登录状态咯!(如下图)

概括:单点登录不是一种具体的技术,而是一种问题解决方案,即在多个相关而又独立的系统环境下,如何避免每个系统节点都要登录;而解决方法各有不同,有多种技术实现,像CAS 就可以视作为实现单点登录的一种技术框架。

 

CAS  (Central Authentication Service)

https://apereo.github.io/cas

起源:CAS 是 Yale 大学发起的一个开源项目,在 2004 年 12 月正式成为 JA-SIG 的项目。

CAS是实现单点登录的技术框架,由客户端(CAS Client)和服务端(CAS Server)两部分组成,服务端部署后变身成为一个“登录页面”(当然还有它背后的逻辑处理);客户端要内嵌到具体应用中,用于和服务端的“登录页面”打交道。

多个系统通过CAS搭建好单点登录后,用户只需要在CAS的登录界面(界面和登录逻辑可以在CAS中自定义)登录,即可在其他应用系统里免登录进入。

在CAS实现的单点登录系统中,是这样的登录流程:

场景:有两个系统 : 小A和小B,他们通过 CAS(老C)搭建了单点登录系统。

用户要访问小A: "嗨,我的老伙计,我可以登录吗?"

小A心想:"这特么谁啊?",然后问老C :“这有个人要登录,我手头没记录,你接待一下”

老C表示不想说话,然后反手就丢了一个登录页面给用户

用户输入完账号密码,老C验证通过,“是你小子啊!发你一张通行证ST,你去找小A吧”

用户又屁颠屁颠地找到小A,“我这次有通行证ST了”

小A一看,这怕是假的吧,把这个通行证拿回给老C验证

老C给小A说:“是真的,你给他登录吧”

最后小A给用户贴了个标签(cookies),之后一段时间内,用户就可以拿着这个标签直接登录小A了。

当用户要登录小B时,又重蹈覆辙,不认识,打发到老C那去,老C一看,这货我记得啊!直接就发了去小B的另一张ST通行证,当然,小B也是拿着通行证再给老C验证,验证通过后,也给用户贴了标签( cookies),用户又可以在小B登录了。

 

以下是CAS的完整流程: 

 

OAuth(Open Authorization)

OAuth是一个关于授权(authorization)的开放网络标准,目前使用较广的是它的2.0版本,即 OAuth2。

OAuth主要用来给其他系统授权功能或者数据,按照一般的逻辑,当你去使用某个系统的功能或者数据时,第一步肯定是需要登录对吧,登录之后,功能才能开放给你使用。那如果系统里的功能,希望用户不登录,也能使用呢?

比如像我们经常在很多微信公众号的页面应用里,看到弹出需要授权的确认框,而一旦确认之后,该应用就获取到了你的微信头像、昵称等数据。 -- 这其实就是通过OAuth标准去实现的。

等等,感觉还是没明白,不是授权给其他系统么?  是的,其实微信公众号的页面应用对于微信确实属于其他应用,虽然都运行微信APP里,但是页面应用是部署在开发者自己的服务器上的,只是在微信浏览器中通过链接去访问。 换句话说, 微信是通过OAuth 标准,将微信用户的数据,授权给了开发者发布的微信公众号页面应用。

我们用上面这个 微信通过OAuth授权给H5应用 的例子来实际理解一下:

首先,OAuth 2.0定义了四种授权方式。

  • 授权码模式(authorization code)
  • 简化模式(implicit)
  • 密码模式(resource owner password credentials)
  • 客户端模式(client credentials)

我们这里主要是使用了 授权码模式

授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与"服务提供商"的认证服务器进行互动。

(A)用户访问客户端,后者将前者导向认证服务器。

(B)用户选择是否给予客户端授权。

(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。

(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。

(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

第一步,用户访问授权链接 ,具体场景就是 :用户在某公众号内的底部点击了某个菜单进入H5应用  (对应 A,B)

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect 

appid 为h5应用要挂接的微信公众号的标识

REDIRECT_URI 才是要访问的H5应用的地址

这一步中,通过appid,微信服务器就识别出这个appid是合法的应用(已在微信中认证为有效的开发者),然后将页面重定向到 REDIRECT_URI ,参数中附带生成好的授权码。

第二步,页面重定向至 REDIRECT_URI/?code=CODE&state=STATE (对应C)

此时授权码就发送给了 H5应用  (H5应用通过URL参数接收 code)

第三步,H5应用获取code后,请求以下链接获取access_token

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

(对应D,E)

H5应用拿到授权码后,在自己的后端去调用微信API获取access_token

如果参数均合法,微信API将会返回

{"access_token":"ACCESS_TOKEN","expires_in":7200,"refresh_token":"REFRESH_TOKEN","openid":"OPENID","scope":"SCOPE" 
}

实现OAuth授权之后,

拿着access_token, H5应用就可以调用微信API以及 openid,去获取操作者微信用户的数据(比如昵称,头像 等)

 

关于OAuth2.0的协议细节:

http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html

关于微信公众号授权文档:

https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html

 

Jwt(JSON Web Tokens)

JWT 全称 JSON Web Tokens ,是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息,是目前最流行的跨域认证解决方案。

Header 部分是一个 JSON 对象,描述 JWT 的元数据

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。

Signature 部分是对前两部分的签名,防止数据篡改。

我们可以简单粗暴地把jwt看做是一种数据结构,主要用来存储登录用户认证相关的数据。为什么要用jwt来存这份数据呢?咱慢慢一个个地捋一下吧。

这要从传统的用户登录说起,在前后不分离的大背景下,用户登录主要靠 session (cookies)实现,即后端验证完账号密码等信息后,将这个用户登录的凭证信息存入后端的一个专门内存空间:session,并把sessionID存入用户的cookies,由于前后端未分离,该用户在整个session有效期内发起请求,后端都能根据用户cookies里的sessionID获取到对应的用户信息(认为该用户一直是登录状态)。

随着前后端分离,并且后端集群化的流行,session机制的问题逐渐显现:

  • 因为session是在每个后端各自在内存中存储,所以多个后端难以实时共享session数据,用户请求到后端,通过负载均衡,可能某些请求能获取到对应session,有些请求获取不到。
  • 前端扩展到移动端,更多是采用了无状态的http请求,故而session+cookies的机制难以适应目前的完全前后端分离的需求。

而jwt则允许服务端将登录交互所需数据放到前端存储,前端在验证后的陆续请求中,都附带jwt token。以此来实现前后端分离场景下的无状态请求的认证问题; 另外由于放在前端存储,后端只需要负责处理token的验证以及从数据库中查找对应实际用户,所以后端也无需进行session共享。

JWT入门:http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

 

Spring Security/Shiro

Spring Security 和 Shiro 都是权限管理的技术实现框架,主要区别在于具体技术细节,Shiro相对轻量级,而Spring Security的功能更加全面。我们暂且以Spring Security作为权限管理的代表框架来进行下面的介绍吧。

何为权限管理呢?(官网文档中将其称为为 “认证”和“授权”两个部分)

我们在使用系统的本质是使用系统中的具体功能,而每个功能又可以细分成 查询,新增,修改等各种操作,权限管理就是对于系统中的功能以及操作进行统一的管理,控制操作者对于功能或操作的使用权限。

在 Spring Security中,我们按照框架的思路,也是去做“认证”和“授权”具体的实现。

(以最简化的代码可能更说得清楚

  • 认证

我们需要提供给框架 用户权限结构,即 用户-角色 的对应关系,

比如下面这段小代码,向框架提供了 两个账号:user (角色为USER)和admin (角色为 USER以及 ADMIN)

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("user").password("password").roles("USER").and().withUser("admin").password("password").roles("USER", "ADMIN");
}

当然,实际使用中,肯定不会直接把账号密码配置在这儿

Spring Security 支持多种验证的配置方法:https://www.springcloud.cc/spring-security-zhcn.html#jc-form

  • 授权

我们还需要给配置好的账号赋予具体的权限,

比如下面的小代码,定义了:

"/resources/**", "/signup", "/about"  这三类接口URl是所有用户都可以访问的

/admin/**    这类接口只能是 ADMIN 角色才可访问

/db/**         这类接口ADMIN角色并且同时是DBA角色 才可以访问

protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests()                                                                .antMatchers("/resources/**", "/signup", "/about").permitAll()                  .antMatchers("/admin/**").hasRole("ADMIN")                                      .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")            .anyRequest().authenticated()                                                   .and()// ....formLogin();
}

 

以上就是权限管理框架所负责的工作了,本质上就是将系统中 用户角色权限之间的关系 以及 如何进行访问控制 等问题从具体业务中抽离出来,单独使用一个框架去管理,当然框架本身的功能远不止这么简单,我们先只要对它的作用以及范围有个初步的认识。

 

Spring Security 入门:https://www.cnblogs.com/lenve/p/11242055.html

 

区别与联系

好了,讲了这么多,我们把这些知识点串起来

1、单点登录(SSO) 是一个多系统登录问题的解决方案,具体可以由CAS技术框架实现,实现效果是:部署一个独立的登录系统,在此系统登录成功后,所有集成进来的业务系统可免登录进入。

2、OAUTH 是一个授权开放协议,主要用于将系统内部的功能或者数据授权给外部系统调用或者使用。OAUTH也可以实现单点登录的效果,像我们常见的大型网站除了账号密码登录之外,还提供了 QQ登录、微信登录、微博登录,实际上是通过OAUTH获取到了openid (比如:微信openid) 直接在系统中创建了一个和openid绑定的账号,间接实现了单点登录。

3、Jwt 是一种Token的数据格式标准,主要用于前后端分离(面向http无状态),不借助cookies情况下的前后端用户身份标识的传递(默认不加密但可进行非对称加密)。另外在 单点登录/OAUTH 的集成过程中可以考虑将其中需要传递的相关信息以Jwt的形式代替。

4、Spring Security/Shiro 是权限管理框架,关注 用户-角色-权限 关系,以及权限控制逻辑。主要用于系统内部的权限管理,如果系统要接入单点登录,需要和内部已有的权限管理框架结合。

微服务和单点登录

在微服务架构下,和上述技术点有哪些相关性呢?

1、后端微服务通过网关统一提供接口,前端有多个:网关可以把分散的微服务组装成一套大的接口集,但是前端拆分为多个系统,这样其实用户在使用时,每个系统还是需要重复登录,所以还是需要用单点登录将不同的系统进行集成。

(可以考虑使用CAS框架,或者OAUTH授权的模式实现统一登录)

2、后端微服务通过网关统一提供接口,前端只有1个:即无需使用像CAS的单点登录框架,但是每个微服务之间如何进行用户身份的认证呢?有以下几种思路:

  • 单独开发一个微服务专门做用户认证,可以结合Jwt来作为认证信息的传递,而所有需要做用户认证的微服务可用Feign、rpc等远程调用方式去调用该认证服务。
  • 单独开发一个认证的API,作为JAR包提供给其他微服务进行引用

 

其中场景1里也需要考虑场景2的问题。

 

 

这篇关于微服务实战(十五)看完不信你还不懂单点登录:SSO,CAS,OAuth,Jwt,Spring Security,Shiro的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1019189

相关文章

Spring Boot @RestControllerAdvice全局异常处理最佳实践

《SpringBoot@RestControllerAdvice全局异常处理最佳实践》本文详解SpringBoot中通过@RestControllerAdvice实现全局异常处理,强调代码复用、统... 目录前言一、为什么要使用全局异常处理?二、核心注解解析1. @RestControllerAdvice2

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

Spring事务传播机制最佳实践

《Spring事务传播机制最佳实践》Spring的事务传播机制为我们提供了优雅的解决方案,本文将带您深入理解这一机制,掌握不同场景下的最佳实践,感兴趣的朋友一起看看吧... 目录1. 什么是事务传播行为2. Spring支持的七种事务传播行为2.1 REQUIRED(默认)2.2 SUPPORTS2

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

java中新生代和老生代的关系说明

《java中新生代和老生代的关系说明》:本文主要介绍java中新生代和老生代的关系说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、内存区域划分新生代老年代二、对象生命周期与晋升流程三、新生代与老年代的协作机制1. 跨代引用处理2. 动态年龄判定3. 空间分

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

深度解析Java DTO(最新推荐)

《深度解析JavaDTO(最新推荐)》DTO(DataTransferObject)是一种用于在不同层(如Controller层、Service层)之间传输数据的对象设计模式,其核心目的是封装数据,... 目录一、什么是DTO?DTO的核心特点:二、为什么需要DTO?(对比Entity)三、实际应用场景解析

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操