摘要:Spring Security是一个安全框架,作为Spring家族的一员。
本文分享自华为云社区《【云驻共创】深入浅出Spring Security》,作者:香菜聊游戏。
一、前言
1.历史
Spring Security最早叫Acegi Security,这个名称并不是说它和Spring就没有关系,它依然是为Spring框架提供安全支持的。Acegi Security基于Spring,可以帮助我们为项目建立丰富的角色与权限管理系统。Acegi Security虽然好用,但是最为人诟病的则是它臃肿繁琐的配置,这一问题最终也遗传给了Spring Security。Acegi Security最终被并入Spring Security项目中,并于2008年4月发布了改名后的第一个版本Spring Security 2.0.0。
2.对比
和Shiro相比,Spring Security重量级并且配置繁琐。其实自从Spring Boot推出后,就彻底颠覆了传统了JavaEE开发,自动化配置让许多事情变得非常容易。在一个Spring Boot项目中,我们甚至只需要引入一个依赖,不需要任何额外配置,项目的所有接口就会被自动保护起来了。在Spring Cloud中很多涉及安全管理的问题,也是一个Spring Security依赖两行配置就能搞定,在和Spring家族的产品一起使用时,Spring Security的优势就非常明显了。因此在微服务时代,我们不需要纠结要不要学习Spring Security,我们要考虑的是如何快速掌握Spring Security,并且能够使用Spring Security实现我们微服务的安全管理。
3.为什么选择
不同于其他领域,在Java企业级开发中,安全管理方面的框架非常少,一般来说,主要有三种方案:
• Shiro
• Spring Security
• 开发者自己实现
Shiro本身是一个老牌的安全管理框架,有着众多的优点,例如轻量、简单、易于集成,可以在JavaSE环境中使用等。不过在微服务面前,它无法充分展示自己的优势。也有开发者选择自己实现安全管理,不过一个系统的安全,不仅仅是登录和权限控制这么简单,我们还要考虑各种各样可能存在的网络攻击以及防御策略,从这个角度来说,只有大公司才有足够的人力物力去支持这件事情。 Spring Security作为Spring家族的一员,在和Spring家族的其他成员进行整合时,具有其他框架无可比拟的优势,同时对OAuth2有着良好的支持,再加上Spring Cloud对Spring Security的不断加持,让Spring Security成为微服务项目的首选安全管理方案。
二、Spring Security简介
Spring Security的核心功能
对于一个安全管理框架而言,无论是Shiro还是Spring Security,最核心的功能,无非就是如下两方面认证和授权。
1.认证
认证就是身份验证(你是谁?),作为一个开放的平台,我们还可以通过引入第三方依赖来支持更多的认证方式,同时,如果这些认证方式无法满足我们的需求,我们也可以自定义认证逻辑,特别是当我们和一些“老破旧”的系统进行集成时,自定义 认证逻辑就显得非常重要了。
2.授权
授权就是访问控制(你可以做什么?),无论采用了哪种认证方式,都不影响在Spring Security中使用授权功能。Spring Security支持基于URL的请求授权、支持方法访问授权、支持SpEL访问控制、支持域对象安全(ACL),同时也支持动态权限配置、支持RBAC权限模型等,总之我们常见的权限管理需求,Spring Security基本上都是支持的。
3.其他
在认证和授权这两个核心功能之外,Spring Security还提供了很多安全管理的“周边功能”,这也是一个非常重要的特色,例如:
• 密码加密
• RememberMe
• 会话固定攻击防御
• CSRF防御
• Http防火墙
Spring Security 的整体架构
1.认证和授权
在Spring Security的架构设计中,认证(Authentication)和授权(Authorization)是分开的,无论使用什么样的认证方式,都不会影响授权,这是两个独立的存在,这种独立带来的好处之一,就是Spring Security可以非常方便地整合一些外部的认证方案。在Spring Security中,用户的认证信息主要由Authentication的实现类来保存,当用户使用用户名/密码登录或使用Remember-me登录时,都会对应一个不同的Authentication实例。Spring Security中的认证工作主要是由AuthenticationManager接口来负责,在该接口中通过authenticate方法来做认证。AuthenticationManager最主要的实现类是ProviderManager,ProviderManager管理了众多的 AuthenticationProvider实例。在一次完整的认证流程中,可能会同时存在多个AuthenticationProvider,多个AuthenticationProvider统一由ProviderManager来管理。同时,ProviderManager具有一个可选的parent,如果所有的AuthenticationProvider都认证失败,那么就会调用parent进行认证。
2.关键接口
在Spring Security的授权体系中,有两个关键接口: AccessDecisionManager 和AccessDecisionVoter。
AccessDecisionVoter是一个投票器,投票器会检查用户是否具备应有的角色,进而投出赞成、反对或者弃权票。
AccessDecisionManager则是一个决策器,来决定此次访问是否被允许。
3.Web安全
在Spring Security中,认证、授权等功能都是基于过滤器来完成的。开发者所见到的Spring Security提供的功能,都是通过这些过滤器来实现的,这些过滤器按照既定的优先级排列,最终形成一个过滤器链。开发者也可以自定义过滤器,并通过@Order注解去调整自定义过滤器在过滤器链中的位置。需要注意的是,默认过滤器并不是直接放在Web项目的原生过滤器链中,而是通过一个FilterChainProxy来统一管理。Spring Security中的过滤器链通过FilterChainProxy嵌入到Web项目的原生过滤器链中。在Spring Security中,这样的过滤器链不仅仅只有一个可能会有多个。当存在多个过滤器链时,多个过滤器链之间要指定优先级,当请求到达后,会从FilterChainProxy进行分发,先和哪个过滤器链匹配上,就用哪个过滤器链进行处理。
三、Spring Security认证流程分析
1.基本认证
在Spring Boot项目中使用Spring Security非常方便,创建一个新的SpringBoot项目,我们只需要引入web和Spring Security依赖即可。
Maven 项目加入下面的依赖
引入依赖后,项目中的所有接口就都被保护起来了,此时访问接口就可以看到登录页面了。
2.Spring Security认证流程分析
AuthenticationManafer是一个认证管理器。它定义了Spring Security过滤器要如何执行认证操作,在认证成功后,会返回一个Authentication对象,这个对象会被设置到SecurityContextHodler中。AuthenticationManafer是一个接口,它有着诸多的实现类,开发者可以自定义AuthenticationManafer的实现类,不过在实际应用中,我们使用最多的是ProviderManager,在Spring Security框架中,默认也是使用ProviderManager。
1)AuthentucationProvider
Spring Security支持多种不同的认证方式,不同的认证方式对应不同的身份类型,AuthentucationProvider就是针对不同的身份类型执行具体的身份认证。例如,常见的DaoAuthenticationProvider用来支持用户名密码登录认证, RememberMeAuthenticationProvider用来支持记住我的认证。
2)ProviderManager
在Spring Security中,由于系统可能同时支持多种不同的认证方式,例如同时支持用户名/密码认证、RememberMe认证、手机号码动态认证等,而不同的认证方式对应了不同的AuthenticationProvider,所以一个完整的认证流程可能由多个AuthenticationProvider来提供。多个AuthenticationProvider将组成一个列表这个列表将由ProviderManagerf代理。换句话说,在ProviderManager中存在一个AuthenticationProvider表在ProviderManager中遍历列表中的每一个AuthenticationProvider去执行身份认证,最终得到认证结果。ProviderManager本身也可以再配置一个AuthenticationManager作为parent,这样当ProviderManager认证失败之后,就可以进入到parent中再次进行认证。理论上来说,ProviderManager的parent可以是任意类型的AuthenticationManager,但是通常都是由ProviderManager来扮演parent的角色,也就是ProviderManager是ProviderManager的parent。
3)AbstractAuthenticationProcessingFilter
AbstractAuthenticationProcessingFilter用来处理任何提交给它的身份认证。
四、Spring Security密码加密
1.常见实现类
BcryptPasswordEncoder
Argon2PasswordEncoder
Pbkdf2PasswordEncoder
ScryptPasswordEncoder
2.DelegatingPasswordEncoder
DelegatingPasswordEncoder是一个代理类,而并非一种全新的密码加密方案。主要用来代理不同的密码加密方案。为什么采用而不是某一个具体加密方式作为默认的密码加密方案呢?主要考虑了如下三方面的因素:
(1)兼容性:使用DelegatingPasswordEncoder可以帮助许多使用旧密码加密方式的系统顺利迁移到中,它允许在同一个系统中同时存在多种不同的密码加密方案。
(2)便捷性:密码存储的最佳方案不可能一直不变,如果使用DelegatingPasswordEncoder作为默认的密码加密方案,当需要修改加密方案时,只需要修改很小一部分代码就可以实现。
(3)稳定性:作为一个框架,不能经常进行重大更改,而使用DelegatingPasswordEncoder可以方便地对密码进行升级(自动从一个加密方案升级到另外一个加密方案)。
五、Spring Security会话管理
1.什么是会话
当浏览器调用登录接口登录成功后,服务端会和浏览器之间建立一个会话(Session),浏览器在每次发送请求时都会携带一个SessionId,服务端则根据这个SessionId来判断用户身份。当浏览器关闭后,服务端的Session并不会自动销毁,需要开发者手动在服务端调用Session销毁方法,或者等Session过期时间到了自动销毁。在Spring Security中,与HttpSession相关的功能由 SessionManagementFilter和SessionAuthenticationStrategy接口来处理, 过滤器将Session相关操作委托给SessionAuthenticationStrategy接口去完成。
2.什么是会话并发管理?
会话并发管理就是指在当前系统中,同一个用户可以同时创建多少个会话,如果一台设备对应一个会话,那么也可以简单理解为同一个用户可以同时在多少台设备上进行登录。默认情况下,同一用户在多少台设备上登录并没有限制,不过开发者可以在Spring Security中对此进行配置。
3.挤下线
当会话并发数达到限制时,新的会话将之前旧的会话挤下线,旧的登录会话失效。配置如下
4.限制登录
当会话并发数达到限制时,新的会话将被限制创建,除非旧的会话主动退出登录。
5.什么是会话固定攻击
会话固定攻击(Session fixation attacks)是一种潜在的风险,恶意攻击者有可能通过访问当前应用程序来创建会话,然后诱导用户以相同的会话登录(通常是将会话作为参数放在请求链接中,然后诱导用户去单击),进而获取用户的登录身份。
1.会话固定攻击步骤
(1)攻击者自己可以正常访问javaboy网站,在访问的过程中,网站给攻击者分配了一个。
(2)攻击者利用自己拿到的sessionId构造一个javaboy网站的链接,并把该链接发送给受害者。
(3)受害者使用该链接登录javaboy网站(该链接中含有sessionId),登录成功后,一个合法的会话就成功建立了。
(4)攻击者利用手里的冒充受害者。
2.会话固定攻击防御策略
Spring Security中从三方面入手防范会话固定攻击:
(1)Spring Security中默认自带了Http防火墙,如果sessionId放在地址栏中,这个请求就会直接被拦截下来。
(2)在http响应的Set-Cookie字段中有HttpOnly属性,这样避免了通过XSS攻击来获取Cookie中的会话信息, 进而达成会话固定攻击。
(3)在用户登录成功后,改变SessionId, Spring Security中默认实现了该种方案。
六、Spring Security防火墙
1.什么是HttpFireWall
HttpFirewall是Spring Security提供的Http防火墙,它可以用于拒绝潜在的危险请求或者包装这些请求进而控制其行为。通过可以对各种非法请求提前进行拦截并处理,降低损失。
2.Spring Security 中的HttpFirewall两个实现类
• DefaultHttpFirewall虽然名字中包含Default,但这并不是框架默认使用的Http防火墙,它只是一个检查相对宽松的防火墙。
HttpFirewall普通模式就是使用DefaultHttpFirewall,该类的校验规则就要简单很多。一般来说,并不建议开发者在项目中使用DefaultHttpFirewall,因为相比于StrictHttp Firewal,DefaultHttpFirewall的安全性要差很多。
• StricHttpFirewall 这是一个检查严格的Http防火墙,也是框架默认使用的 Http防火墙
严格模式下对请求做出了诸多限制:
1) rejectForbiddenHttpMethod:校验请求方法是否合法。
2)rejectedBlacklistedUrls:校验请求中的非法字符。
3) rejectedUntrustedHosts:检验主机信息。
4)isNormalized:判断参数格式是否合法。
5)containsOnlyPrintableAsciCharacters: 判断请求字符是否合法。
总结
Spring Security是一个安全框架,作为Spring家族的一员,可以简单地认为 Spring Security是放在用户和Spring应用之间的一个安全屏障,每一个web请求都先要经过Spring Security 进行Authenticate和 Authoration验证,其核心就是一组过滤器链。