微服务实战(十五)看完不信你还不懂单点登录: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的问题。

 

 

  • 10
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
单点登录(Single Sign-On,简称SSO)是一种身份验证和授权机制,允许用户使用一组凭据登录到多个应用程序和系统,而不需要在每个应用程序中单独输入用户名和密码。 Shiro是一个开源的Java安全框架,提供身份验证、授权、加密、会话管理等功能。Spring Security OAuth 2.0是Spring Security的一个扩展,用于实现OAuth 2.0协议,提供基于令牌的身份验证和授权。 集成ShiroSpring Security OAuth 2.0可以实现单点登录功能。首先,需要配置一个认证中心作为身份提供者,其他应用程序和系统将依赖该认证中心进行身份验证和用户信息获取。在集成过程中,需要在认证中心和其他应用程序中配置Shiro的过滤器链,并将相关的OAuth 2.0配置添加到Spring Security的配置中。 当用户在一个应用程序中登录时,该应用程序将重定向至认证中心,用户在认证中心完成身份验证后,会生成一个访问令牌(Access Token)。该访问令牌可以在其他应用程序之间传递,并由Shiro验证器进行验证。在其他应用程序中,用户使用访问令牌向认证中心进行验证,通过后就可以无需重新输入用户名和密码,直接访问其他应用程序。 集成ShiroSpring Security OAuth 2.0实现单点登录的好处是可以减少用户登录的次数,提高用户体验,同时也可以提高系统的安全性,减少密码泄露的风险。这样的集成方案可以适用于多个系统和应用程序之间的跨域身份验证和授权需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值