深入理解Spring Cloud Security OAuth2及JWT
OAuth2是一个关于授权的开放标准,核心思路是通过各类认证手段(具体什么手段OAuth2不关心)认证用户身份,并颁发token(令牌),使得第三方应用可以使用该令牌在 限定时间 、 限定范围 访问指定资源。主要涉及的RFC规范有 RFC6749 (整体授权框架), RFC6750 (令牌使用), RFC6819 (威胁模型)这几个,一般我们需要了解的就是 RFC6749 。获取令牌的方式主要有四种,分别是 授权码模式 , 简单模式 , 密码模式 和 客户端模式 ,如何获取token不在本篇文章的讨论范围,我们这里假定客户端已经通过某种方式获取到了access_token,想了解具体的oauth2授权步骤可以移步阮一峰老师的 理解OAuth 2.0 ,里面有非常详细的说明。
这里要先明确几个OAuth2中的几个重要概念:
明确概念后,就可以看OAuth2的协议握手流程,摘自RFC6749
Spring Security是一套安全框架,可以基于RBAC(基于角色的权限控制)对用户的访问权限进行控制,核心思想是通过一系列的filter chain来进行拦截过滤,以下是ss中默认的内置过滤器列表,当然你也可以通过 custom-filter 来自定义扩展filter chain列表
这里面最核心的就是 FILTER_SECURITY_INTERCEPTOR ,通过 FilterInvocationSecurityMetadataSource 来进行资源权限的匹配, AccessDecisionManager 来执行访问策略。
一般意义来说的应用访问安全性,都是围绕认证(Authentication)和授权(Authorization)这两个核心概念来展开的。即首先需要确定用户身份,在确定这个用户是否有访问指定资源的权限。认证这块的解决方案很多,主流的有 CAS 、 SAML2 、 OAUTH2 等(不巧这几个都用过-_-),我们常说的单点登录方案(SSO)说的就是这块,授权的话主流的就是spring security和shiro。shiro我没用过,据说是比较轻量级,相比较而言spring security确实架构比较复杂。
将OAuth2和Spring Security集成,就可以得到一套完整的安全解决方案。
为了便于理解,现在假设有一个名叫“脸盆网”的社交网站,用户在首次登陆时会要求导入用户在facebook的好友列表,以便于快速建立社交关系。具体的授权流程如下:
不难看出,这个假设的场景中,脸盆网就是第三方应用(client),而facebook既充当了认证服务器,又充当了资源服务器。这个流程里面有几个比较重要的关键点,我需要重点说一下,而这也是其他的涉及spring security与OAuth2整合的文章中很少提及的,很容易云里雾里的地方。
细心的同学应该发现了,其实在标准的OAuth2授权过程中,5、6、8这几步都不是必须的,从上面贴的 RFC6749 规范来看,只要有1、2、3、4、7这几步,就完成了被保护资源访问的整个过程。事实上, RFC6749 协议规范本身也并不关心用户身份的部分,它只关心token如何颁发,如何续签,如何用token访问被保护资源(facebook只要保证返回给脸盆网的就是当前用户的好友,至于当前用户是谁脸盆网不需要关心)。那为什么spring security还要做5、6这两步呢?这是因为spring security是一套完整的安全框架,它必须关心用户身份!在实际的使用场景中,OAuth2一般不仅仅用来进行被保护资源的访问,还会被用来做单点登陆(SSO)。在SSO的场景中,用户身份无疑就是核心,而token本身是不携带用户信息的,这样client就没法知道认证服务器发的token到底对应的是哪个用户。设想一下这个场景,脸盆网不想自建用户体系了,想直接用facebook的用户体系,facebook的用户和脸盆网的用户一一对应(其实在很多中小网站现在都是这种模式,可以选择使用微信、QQ、微博等网站的用户直接登陆),这种情况下,脸盆网在通过OAuth2的认证后,就希望拿到用户信息了。所以现在一般主流的OAuth2认证实现,都会预留一个用户信息获取接口,就是上面提到的 https://api.facebook.com/user (虽然这不是OAuth2授权流程中必须的),这样client在拿到token后,就可以携带token通过这个接口获取用户信息,完成SSO的整个过程。另外从用户体验的角度来说,如果获取不到用户信息,则意味者每次要从脸盆网访问facebook的资源,都需要重定向一次进行认证,用户体验也不好。
首先要明确一点, OAuth2并不是一个SSO框架,但可以实现SSO功能 。以下是一个使用github作为OAuth2认证服务器的配置文件
可以看到 accessTokenUri 和 userAuthorizationUri 都是为了完成OAuth2的授权流程所必须的配置,而 userInfoUri 则是spring security框架为了完成SSO所必须要的。所以总结一下就是: 通过将用户信息这个资源设置为被保护资源,可以使用OAuth2技术实现单点登陆(SSO),而Spring Security OAuth2就是这种OAuth2 SSO方案的一个实现。
Spring Security在调用user接口成功后,会构造一个 OAuth2Authentication 对象,这个对象是我们通常使用的 UsernamePasswordAuthenticationToken 对象的一个超集,里面封装了一个标准的 UsernamePasswordAuthenticationToken ,同时在 detail 中还携带了OAuth2认证中需要用到的一些关键信息(比如 tokenValue , tokenType 等),这时候就完成了SSO的登陆认证过程。后续用户如果再想访问被保护资源,spring security只需要从principal中取出这个用户的token,再去访问资源服务器就行了,而不需要每次进行用户授权。这里要注意的一点是 此时浏览器与client之间仍然是通过传统的cookie-session机制来保持会话,而非通过token。实际上在SSO的过程中,使用到token访问的只有client与resource server之间获取user信息那一次,token的信息是保存在client的session中的,而不是在用户本地 。这也是之前我没搞清楚的地方,以为浏览器和client之间也是使用token,绕了不少弯路,对于Spring Security来说, 不管是用cas、saml2还是Oauth2来实现SSO,最后和用户建立会话保持的方式都是一样的 。
根据前面所说,大家不难看出,OAuth2的SSO方案和CAS、SAML2这样的纯SSO框架是有本质区别的。在CAS和SAML2中,没有资源服务器的概念,只有认证客户端(需要验证客户信息的应用)和认证服务器(提供认证服务的应用)的概念。在CAS中这叫做 cas-client 和 cas-server ,SAML2中这叫做 Service Providers 和 Identity Provider ,可以看出CAS、SAML2规范天生就是为SSO设计的,在报文结构上都考虑到了用户信息的问题(SAML2规范甚至还带了权限信息),而OAuth2本身不是专门为SSO设计的,主要是为了解决资源第三方授权访问的问题,所以在用户信息方面,还需要额外提供一个接口。
脸盆网的这个例子中,我们看到资源服务器和认证服务器是在一起的(都是facebook),在互联网场景下一般你很难找到一个独立的、权威的、第三方的认证中心(你很难想像腾讯的QQ空间通过支付宝的认证中心去授权,也很难想像使用谷歌服务要通过亚马逊去授权)。但是如果是在公司内部,这种场景其实是很多的,尤其在微服务架构下,有大量服务会对外提供资源访问,他们都需要做权限控制。那么最合理的当然就是建立一个统一的认证中心,而不是每个服务都做一个认证中心。我们前面也介绍了,token本身是不携带用户信息的,在分离后resouce server在收到请求后,如何检验token的真实性?又如何从token中获取对应的用户信息?这部分的介绍网上其实非常少,幸好我们可以直接从官方文档获取相关的蛛丝马迹,官方文档对于resouce server的配置是这样描述的:
寥寥数语,但已经足够我们分析了。从这个配置可以看出,client在访问resource server的被保护资源时,如果没有携带token,则资源服务器直接返回一个401未认证的错误
如果携带了token,则资源服务器会使用这个token向认证服务器发起一个用户查询的请求,若token错误或已经失效,则会返回
若token验证成功,则认证服务器向资源服务器返回对应的用户信息,此时resource server的spring security安全框架就可以按照标准的授权流程进行访问权限控制了。
从这个流程中我们可以看出,通过OAuth2进行SSO认证,有一个好处是做到了 认证与授权的解耦 。从日常的使用场景来说,认证比较容易做到统一和抽象,毕竟你就是你,走到哪里都是你,但是你在不同系统里面的角色,却可能千差万别(家里你是父亲,单位里你是员工,父母那里你是子女)。同时角色的设计,又是和资源服务器的设计强相关的。从前面的配置中不难发现,如果希望获得为不同资源服务器设计的角色,你只需要替换 https://api.facebook.com/user 这个配置就行了,这为我们的权限控制带来了更大的灵活性,而这是传统的比如SAML2这样的SSO框架做不到的。
终于来到了著名的JWT部分了,JWT全称为Json Web Token,最近随着微服务架构的流行而越来越火,号称新一代的认证技术。今天我们就来看一下,jwt的本质到底是什么。
我们先来看一下OAuth2的token技术有没有什么痛点,相信从之前的介绍中你也发现了,token技术最大的问题是 不携带用户信息 ,且资源服务器无法进行本地验证,每次对于资源的访问,资源服务器都需要向认证服务器发起请求,一是验证token的有效性,二是获取token对应的用户信息。如果有大量的此类请求,无疑处理效率是很低的,且认证服务器会变成一个中心节点,对于SLA和处理性能等均有很高的要求,这在分布式架构下是很要命的。
JWT就是在这样的背景下诞生的,从本质上来说,jwt就是一种 特殊格式 的token。普通的oauth2颁发的就是一串随机hash字符串,本身无意义,而jwt格式的token是有特定含义的,分为三部分:
这三部分均用base64进行编码,当中用 . 进行分隔,一个典型的jwt格式的token类似 xxxxx.yyyyy.zzzzz 。关于jwt格式的更多具体说明,不是本文讨论的重点,大家可以直接去官网查看 官方文档 ,这里不过多赘述。
相信看到签名大家都很熟悉了,没错,jwt其实并不是什么高深莫测的技术,相反非常简单。认证服务器通过对称或非对称的加密方式利用 payload 生成 signature ,并在 header 中申明签名方式,仅此而已。通过这种本质上极其传统的方式,jwt可以实现 分布式的token验证功能 ,即资源服务器通过事先维护好的对称或者非对称密钥(非对称的话就是认证服务器提供的公钥),直接在本地验证token,这种去中心化的验证机制无疑很对现在分布式架构的胃口。jwt相对于传统的token来说,解决以下两个痛点:
在上面的那个资源服务器和认证服务器分离的例子中,如果认证服务器颁发的是jwt格式的token,那么资源服务器就可以直接自己验证token的有效性并绑定用户,这无疑大大提升了处理效率且减少了单点隐患。
就像布鲁克斯在《人月神话》中所说的名言一样:“没有银弹”。JWT的使用上现在也有一种误区,认为传统的认证方式都应该被jwt取代。事实上,jwt也不能解决一切问题,它也有适用场景和不适用场景。
适用场景:
这些场景能充分发挥jwt无状态以及分布式验证的优势
不适用的场景:
不要试图用jwt去代替session。 这种模式下其实传统的session+cookie机制工作的更好,jwt因为其无状态和分布式,事实上只要在有效期内,是无法作废的,用户的签退更多是一个客户端的签退,服务端token仍然有效,你只要使用这个token,仍然可以登陆系统。另外一个问题是续签问题,使用token,无疑令续签变得十分麻烦,当然你也可以通过redis去记录token状态,并在用户访问后更新这个状态,但这就是硬生生把jwt的无状态搞成有状态了,而这些在传统的session+cookie机制中都是不需要去考虑的。这种场景下,考虑高可用,我更加推荐采用分布式的session机制,现在已经有很多的成熟框架可供选择了(比如spring session)。
深入理解spring-IOC<容器的初始化>
在深入理解Spring的容器初始化过程中,我们首先关注的是容器的初始化入口。通常,当Spring容器被创建时,会从一个名为refresh的方法开始。以FileSystemXmlApplicationContext为例,其构造函数调用了refresh方法,启动了容器初始化过程。初始化过程主要包括三个基本步骤:BeanDefinition的Resource定位、载入和注册。...
如何深入理解java的spring框架中的ioc容器?
模拟实现步骤:1. 准备beans.properties文件,模拟xml功能;2. 编写QfBeanFactory.java,实现工厂功能;3. 编写测试类测试bean创建。Spring框架创建bean对象流程:1. 创建spring-bean.xml配置文件;2. 加载配置文件获得Spring IoC容器;3. 通过容器获取特定bean对象。BeanFactory作为核心接口,负责生产和管理bea...
深入理解Spring Security认证流程
深入理解Spring Security认证流程,这个强大的框架主要负责保护Spring应用程序,尤其在身份验证和授权方面。它的核心组件包括过滤器、AuthenticationManager、ProviderManager和AuthenticationProvider,它们共同构建了认证过程的基石。在未使用Spring Security时,DispatcherServlet负责拦截和转发请求。当集成Spring Security后...
面试题:说说你对spring的理解
而使用依赖注入以后,类A只需要去调用实现功能F接口的一个实现类,这个实现类可能是类B,C等等,具体调用谁是有Spring的配置文件决定的,这样类A就不再依赖于类B。我们可以这样理解控制反转:资源不是由使用资源的双方进行管理,而是由不使用资源的第三方(即Spring容器)进行管理,这样做的好处是:资源集...
面试官:"说说你对Spring的理解"
举例来说,在哈尔滨一家叫环宇互娱科技的面试时候,我针对spring的优点是这么跟面试官说的:方便解耦,简化开发 Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护,交给Spring管理。AOP编程的支持 Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。声明式事务的支持 只...
如何学习spring?学习spring前要学习什么?
学习Spring前,你需要理解Spring的核心概念,如IoC(控制反转)和AOP(面向切面编程)。控制反转(IoC)意味着Spring将对象的创建和管理责任从应用代码中抽取出来,使得代码更加灵活和易于维护。当你需要使用某个对象时,只需使用@Autowired注解,Spring会自动为你注入相应的对象。这样,你的代码不再需要直接...
如何深入理解 Java 的 Spring 框架中的 IOC 容器?
c, fobj); \/\/自动注入完成,c是需要注入的对象实例 }}总之,Spring的IoC容器就像一个魔术师,将原本程序员需要自行管理的对象实例调度工作交给了它。通过依赖注入,代码的耦合度降低,可维护性和扩展性得到了显著提升。理解并熟练运用Spring的IoC和DI,无疑能让你的Java开发旅程更加得心应手。
深入理解 Spring 中的事件机制
Spring 的事件机制就是具体的观察者模式的实现。Spring 中事件机制中各角色:如下图所示是 Spring 中所有的事件继承关系类图:其中常用的事件有:这边我们以 ContextRefreshedEvent 为例来介绍一下 Spring 事件发布的流程。从容器启动调用 refresh() 方法进入到 finishRefresh() 方法如下:
深入理解Spring注解机制:合并注解的合成
在 java 中,元注解是指可以注解在其他注解上的注解,spring 中通过对这个机制进行了扩展,实现了一些原生 JDK 不支持的功能,比如允许在注解中让两个属性互为别名,或者将一个带有元注解的子注解直接作为元注解看待,或者在这个基础上,通过 @AliasFor 或者同名策略让子注解的值覆盖元注解的值。
Spring源码解析——事务的回滚和提交
事务状态的决定性作用在Spring中,只有当事务状态为新事务时,才会执行提交。对status的理解,是理解事务行为的关键。总结与资源分享无论事务处理过程如何,Spring始终保持着对数据的严谨把控。深入理解这些机制,可以帮助我们更好地管理事务,确保业务的顺利进行。如果你对计算机技术书籍感兴趣,这里有超过200...
仲长怎葡萄: Spring Cloud 是Pivotal提 供的用于简化分布式系统构建的工具集.Spring Cloud引入了云平台连接器(Cloud Connector)和服务连接器(Service Connector)的概念.云平台连接器是一个接口,需要由云平台提供者进行实现,以便库中的其他模块可以与该云平台协同工作.
大名县13452514142: 现在为什么越来越多的公司要用Spring Cloud? - ?
仲长怎葡萄: Spring作为企业应用构建的利器已深入人心,Spring Cloud提供一套分布式应用常见问题的解决方案,帮助企业应用迅速云化.华为在构建自己的微服务框架的过程中充分借鉴了Spring以及SpringCloud 的很多优秀思想.我们公司也在用,是和上海艾班仕合作的.
大名县13452514142: 如何使用Spring Cloud - ?
仲长怎葡萄: Spring Cloud项目的既定目标在于为Spring开发人员提供一整套易于使用的工具集,从而保证其轻松构建起自己需要的分布式系统方案.为了实现这一目标,Spring Cloud以Netflix OSS堆栈为基础将大量实现堆栈加以整合并打包.这些堆栈而后...
大名县13452514142: spring cloud,基于什么实现 - ?
仲长怎葡萄: Spring Cloud的子项目,大致可分成两类,一类是对现有成熟框架”Spring Boot化”的封装和抽象,也是数量最多的项目;第二类是开发了一部分分布式系统的基础设施的实现,如Spring Cloud Stream扮演的就是kafka, ActiveMQ这样的角色....
大名县13452514142: 求助,Spring Cloud是什么?还有Gradle - ?
仲长怎葡萄: Spring Cloud是一些东西的集合,基础是Spring Boot,学过Spring MVC应该比较好入门. Gradle是一个构建工具,跟Maven类似,你可以把它当做另一种用法的Maven就行了.
大名县13452514142: spring cloud 有哪些功能 - ?
仲长怎葡萄: 用了二个多月,理解的就这些:1. 注册中心(易分布,易扩充)2. 网关(保护服务,权限统一验证)3. 配置中心(配置统一管理)4. 基于http的服务调用(调用简单)5. 熔断器(容灾)6. 监控 坑很多,一步一个坑.
大名县13452514142: spring boot和Spring Cloud有什么区别? - ?
仲长怎葡萄: spring boot 下个项目会用,作为app的服务端框架,spring boot 我理解就是把spring spring mvc spring data jpa 等等的一些常用的常用的基础框架组合起来,提供默认的配置,然后提供可插拔的设计,就是各种starter,来方便开发者使用这一系...
大名县13452514142: 怎么看spring cloud 源码 - ?
仲长怎葡萄: 1、在你的MyEclipse上安装一个反编译插件,这样,所有的class文件都能看到它的源代码2、建议你用jad MyEclipse反编译插件.3、要相应jar包,我上传给你了.4、MyEclipse10是在(MyEclipse安装目录)\MyEclipse 10\dropins下建立一个文...
大名县13452514142: Spring boot与Spring cloud 是什么关系 - ?
仲长怎葡萄: spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等操作提供了一种简单的开发方式.spring boot 的优点是可以快速启动,快速构建应用程序,而不需要太多的配置文件.spring cloud 是分布式开发的解决方案,基于spring boot,在spring boot做较少的配置,便可成为 spring cloud 中的一个微服务
大名县13452514142: 面试官问你说下你了解的spring,要怎么回答才显得你比较了解spring,求比较深入spring的回答. - ?
仲长怎葡萄: 首先最核心的是告诉面试官:spring是一个高效的管理对象的容器,是一种规范,能大大提高项目的开发管理维护.开发人员通过xml配置就能完成对象的注册,剩下的工作就是spring使用IOC完成对象的注入,期间不需要程序员干预,如果需要...