第九期读书会深入浅出Spring Security读书笔记整理

花溪 发表于 2021/06/19 11:59:17 2021/06/19
【摘要】 一、Spring Security简介缘起:在 Web 应用开发中,安全一直是非常重要的一个方面。安全虽然属于应用的非功能性需求,但是应该在应用开发的初期就考虑进来。如果在应用开发的后期才考虑安全的问题,就可能陷入一个两难的境地:一方面,应用存在严重的安全漏洞,无法满足用户的要求,并可能造成用户的隐私数据被攻击者窃取;另一方面,应用的基本架构已经确定,要修复安全漏洞,可能需要对系统的架构做出...

一、Spring Security简介

缘起:

在 Web 应用开发中,安全一直是非常重要的一个方面。安全虽然属于应用的非功能性需求,但是应该在应用开发的初期就考虑进来。如果在应用开发的后期才考虑安全的问题,就可能陷入一个两难的境地:一方面,应用存在严重的安全漏洞,无法满足用户的要求,并可能造成用户的隐私数据被攻击者窃取;另一方面,应用的基本架构已经确定,要修复安全漏洞,可能需要对系统的架构做出比较重大的调整,因而需要更多的开发时间,影响应用的发布进程。因此,从应用开发的第一天就应该把安全相关的因素考虑进来,并在整个应用的开发过程中。

Spring Security 简介:

为什么选择 Spring Security?

Shiro本身是一个老牌的安全管理框架(一个强大且易用的Java安全框架),能够非常清晰的处理身份验证、授权、管理会话以及密码加密。有着众多的优点,例如轻量、简单、易于集成、可以在JavaSE环境中使用等。不过在微服务面前,它无法充分展示自己的优势。

Spring Security作为Spring家族的一员,在和Spring家族的其他成员进行整合时,具有其他框架无可比拟的优势,同时对OAuth2有着良好的支持,再加上Spring Cloud对Spring Security的不断加持,让Spring Security成为微服务项目的首选安全管理方案。除了不能脱离Spring,shiro的功能它都有。

所以推荐选择Spring Security。

Spring Security 核心功能:

认证 (你是谁)(Authentication):

Authentication 是一个接口,用来表示用户认证信息的,在用户登录认证之前相关信息会封装为一个 Authentication 具体实现类的对象,在登录认证成功之后又会生成一个信息更全面,包含用户权限等信息的 Authentication 对象,然后把它保存在 SecurityContextHolder 所持有的 SecurityContext 中,供后续的程序进行调用,如访问权限的鉴定等。

授权 (你能干什么)(Authorization):

也叫访问控制,即在应用中控制谁访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。

Spring Security 整体架构:

认证和授权:在Spring Security的授权体系中,有两个关键接口:AccessDecisionManager和AccessDecisionVoter

Web 安全:在Spring Security中,认证、授权等功能都是基于过滤器来完成的。开发者所见到的Spring Security提供的功能,都是通过这些过滤器来实现的,这些过滤器按照既定的优先级排列,最终形成一个过滤器链。开发者也可以自定义过滤器,并通过@Order注解去调整自定义过滤器在过滤器链中的位置。需要注意的是,默认过滤器并不是直接放在Web项目的原生过滤器链中,而是通过一个FilterChainProxy来统一管理。

总结:

Spring 是一个非常流行和成功的java应用开发框架。 

Spring Security 基于Spring 框架,提供了一套web应用安全性的完整解决方案。

Spring Security 核心功能是:认证和授权。

Spring安全的所有内部过滤器对于容器来说都是透明的。

二、Spring Security 认证流程分析

Spring Security是一款强大的安全认证服务框架,它的原理就是在访问我们的系统前加了一系列的过滤器,可以称为过滤器链。它的两大核心就是认证和授权

1.Spring Security 基本认证

在 Spring Boot项目中使用 SpringSecurity非方便,创建一个新的 Spring Boot项目,我们只需要引入Web和 Spring Security依赖即可。

新建一个springboot工程,pom里添加springsecurity的依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

引入 Spring Security依赖后,项目中的所有接口就都被保护起来了,此时访问接口就可以看到登录页面了。

在不进行配置的情况下,默认拦截所有的请求

2.Spring Security 认证流程分析

image.png 

AuthenticationManager

AuthenticationManager是一个认证管理器,它定义了 Spring Security过滤器要如何执行认证操作。 AuthenticationManager是一个接口,它有着诸多的实现类,开发者也可以自定义 Authentication Manager的实现类,不过在实际应用中,我们使用最多的是 ProviderManager.在 Spring Security框架中,默认也是使用 ProviderManager.

添加用户自己的处理逻辑,需要实现UserDetailsService接口

自定义登录成功处理需要实现AuthenticationSuccessHandler接口,AuthenticationSuccessHandler里面只有一个onAuthenticationSuccess方法,参数有个Authentication,我们可以拿到这个Authentication 然后自定义自己的处理逻辑。springsecurity里面也是有一个默认的成功处理类 SavedRequestAwareAuthenticationSuccessHandler
自定以失败处理同样需要实现AuthenticationFailureHandler接口,里面有一个onAuthenticationFailure方法,不同的是参数是一个AuthenticationException
而springsecurity默认的登录失败处理类是SimpleUrlAuthenticationFailureHandler

ProviderManager

常见的 DaoAuthenticationProvider用来支持用户名/密码

登录认证, RememberMeAuthenticationProvider用来支持记住我"的认证。



image.png


3.配置多数据源

同时支持多张用户表

多个数据源是指在同一个系统中,用户数据来自不同的表,在认证时,如果第一张表没有查找到用户,那就去第二张表中查询,依次类推。

要实现这个需求就很容易,认证要经过 Authenticatic,每一个 AuthenticationProvid中都配置了一个 UserDetailsService,而不同的 UserDetailsService则可以代表不同的数据源。所以我们只需要手动配置多个 AuthenticationProvider,并为不同的 Authenticatio Provider提供不同的userDetailsService即可。

4.添加登录验证码

登录验证码也是项目中一个常见的需求,但是 Spring Security对此并未提供自动化配置方案,需要开发者自行定义。一般来说,有两种实现登录验证码的思路:

(1)自定义过滤器。

(2)自定义认证逻辑。

总结:

image.png

三、Spring Security 密码加密

1.为什么需要密码加密

由于很多用户是多个网站共用一个密码,因此一个网站密码泄漏就会造成很大的安全隐患。由于有了这么多前车之鉴,我们现在做系统时,密码都要加密处理。

2.加密方案进化史

Hash算法

  1. 原始密码经哈希函数计算后得到一个哈希值

  2. 改变原始密码,哈希函数计算出的哈希值也会相应改变

  3. 同样的密码,哈希值也是相同的

  4. 哈希函数是单向、不可逆的。也就是说从哈希值,你无法推算出原始的密码是多少

密码加盐

为了防止黑客通过彩虹表根据哈希值反推原始口令,在计算哈希的时候,不能仅针对原始输入计算,需要增加一个salt来使得相同的输入也能得到不同的哈希,这样,大大增加了黑客破解的难度。

自适应单向函数

现在鼓励开发人员利用自适应单向函数来存储密码。开发人员来设定不同的worker factor满足不同的安全需要,在安全和效率之间做出权衡。例如: bcrypt , PBKDF2 , scrypt , and Argon2 .

3.PasswordEncoder

常见实现类

  1.  BCryptPasswordEncoder

  2.  Argon2PasswordEncoder

  3.  Pbkdf2PasswordEncoder

  4.  SCryptPasswordEncoder

为什么采用  Delegatin“g-P-a-s-s”wordEncoder而不是某一个具体加密方式作为默认的密码加密方案呢?主要考虑了如下三方面的因素:

  1. 兼容性

  2. 便捷性

  3. 稳定性

4.加密方案自动升级

如果开发者使用了 Delegatin“g-P-a-s-s”wordEncoder,只要数据库中存储的密码加密方案不是 Delegatin“g-P-a-s-s”wordEncoder中默认的BCryptPassword Encoder,在登录成功之后,都会自动升级为 BCryptPassword Encoder加密。在同一种密码加密方案中,也有可能存在升级的情况。

总结:

Delegating-P-a-s-swordEncoder(spring5.0后的新特性)

  1. 确保使用当前密码存储建议对密码进行编码

  2. 允许验证现代和传统格式的密码

  3. 允许将来升级编码

  4. 将编码方式存入密码文本

四、Spring Security 会话管理

由于HTTP协议是无状态协议,对于服务器而言每个请求都一样,缺少一个状态去区分请求是否来自于不同的用户,以便服务器提供不同的服务。所以我们需要利用某种机制来记录不同用户的标识信息。这个机制就是Session,这个时候cookie就体现了它的重要作用。当客户端首次请求服务端时,服务端为该用户生成一个sessionId,并保存在cookie中,带回客户端,客户端保存这个cookie。之后客户端每次请求都带上这个cookie,服务端可以很容易区分是来自哪个用户的请求。但出于安全考虑,有时用户在浏览器中禁用cookie,这个时候可以利用URL重写,将sessionId拼接在重写的URL后面返回给已经授权的用户。

1.会话简介

当浏览器调用登录接口成功后,服务端会和浏览器直接建立一个会话(session),浏览器每次发送请求都会携带sessionId,服务端会根据这个id判断身份,浏览器关闭后服务器的session不会自动销毁,需要手动在服务端调用销毁方法,或者等待过期时间自动销毁。

2.会话并发管理

会话并发管理就是当前系统中同一个用户可以创建多个会话,一台设备对应一个会话,一个用户可以在多台设备上登录,这个并没有限制,但在spring security中可以配置。

当会话并发数达到限制时,新的会话将之前旧的会话 挤下线 ,旧的登录会话失效。

当会话并发数达到限制时,新的会话将被限制创建,除非旧的会话主动退出登录。

3.会话固定攻击与防御

会话固定攻击是一种潜在的风险,恶意攻击者有可能通过访问当前应用程序来创建会话,然后诱导用户以相同的会话ID登录(通常是将会话 作为参数放在请求链接中,然后诱导用户去单击),进而获取用户的登录身份。

攻击者自己正常访问系统,系统给攻击者分配了一个sessionId,攻击者拿着自己手上的sessionId伪造一个系统登录链接,受害者利用链接登陆了,那么sessionId绑定了用户的信息,攻击者可以利用手里的sessionId冒充受害者,这就是会话固定攻击

当我们登录成功之后重新生成新的session,即可避免。

Spring Security自带该功能,并且自带的HTTP防火墙会帮我们拦截掉哪些拼接的不合法的URL

sessionManagement是一个会话管理的配置器,其中,防御会话固定攻击的策略有四种

  • none:不做任何变动,登录之后沿用旧的session。

  • newSession:登录之后创建一个新的session。

  • migrateSession:登录之后创建一个新的session,并将旧的session中的数据复制过来。

  • changeSessionId:不创建新的会话,而是使用由Servlet容器提供的会话固定保护。

默认已经启用migrateSession策略,如有必要,可以做出修改。

Spring Security从三方面防范会话固定攻击:

(1)默认自带Http防火墙

(2)在Http响应的Set-Cookie字段中有httpOnly属性,这样避免了通过xss攻击来获取Cookie中的会话信息,

进而达成会话固定攻击。

(3)登录成功后改变sessionId, Spring Security中默认实现了该种方案。

总结:

在 Spring Security 中,即便没有配置,也大可不必担心会话固定攻击。这是因为Spring Security的HTTP防火墙会帮助我们拦截不合法的URL,当我们试图访问带session的URL时,实际上会被重定向到一个错误页。

五、Spring Security 防火墙

各种各样的web攻击每天都在发生,什么xss攻击、sql注入等等,如果不了解这些攻击,那么做出来的系统肯定也不能防御这些攻击。使用 Spring Security 的好处就是,即使不了解这些攻击,也不用担心这些攻击,因为 Spring Security 已经帮你做好防御工作了。

1.HttpFirewall 简介

HttpFirewall 是Spring Security提供的Http防火墙,它可以用于拒绝潜在的危险请求或者包装这些请求进而控制其行为。通过HttpFirewall 可以对各种非法请求提前进行拦截并处理,降低损失。

在 servlet容器规范中,为Httpervletrequest定义了一些属性,如contextPath、servletath、pathinfo、 queryString等,这些属性都可以通过get方法获取。

HttpFirewall 目前一共有两个实现类:一个是严格模式的防火墙设置,还有一个默认防火墙设置。

  • DefaultHttpFirewall:虽然名字中包含Default,但这并不是框架默认使用的Http防火墙,它只是一个检查相对宽松的防火墙。

  • StrictHttpFirewall:这是一个检查严格的Http防火墙,也是框架默认使用的Http防火墙。

DefaultHttpFirewall 的限制相对于 StrictHttpFirewall 要宽松一些,当然也意味着安全性不如 StrictHttpFirewall。Spring Security 中默认使用的是 StrictHttpFirewall。

2.HttpFirewall 严格模式

StrictHttpFirewall是Spring Security Web提供的一个HTTP防火墙(对应概念模型接口HttpFirewall)实现,该实现采用了严格模式,遇到任何可疑的请求,会通过抛出异常RequestRejectedException拒绝该请求。StrictHttpFirewall也是Spring Security Web在安全过滤器代理FilterChainProxy内置缺省使用的HTTP防火墙机制。

  • rejectForbiddenHttpMethod:校验请求方法是否合法。

  • rejectedBlacklistedUrls:校验请求中的非法字符。

  • rejectedUntrustedHosts:检验主机信息。

  • isNormalized:判断参数格式是否合法。

  • containsOnlyPrintableAsciiCharacters:判断请求字符是否合法。

3.HttpFirewall 普通模式

HttpFirewall普通模式就是使用DefaultHttpFirewall,该类的校验规则就要简单很多。

一般来说,并不建议开发者在项目中使用DefaultHttpFirewall,因为相比于StrictHttpFirewall,DefaultHttpFirewall的安全性要差很多。

总结:

不在HTTP method许可清单的请求会被拒绝。

标准化的URL必须符合以下条件 :在其requestURI/contextPath/servletPath/pathInfo中,必须不能包含以下字符串序列之一 :["//","./","/…/","/."]

每一条限制都有它的原由,每放开一个限制,就会带来未知的安全风险。尤其是如果请求URL 中包含不可打印ASCII字符则该请求会被拒绝。该规则不可关闭,因为对应风极高。

江南一点雨老师博客1:江南一点雨-分享 Java 点滴

江南一点雨老师博客2:江南一点雨

开源社区:江南一点雨 (lenve) - Gitee.com

学习任重而道远,加油!感谢老师!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区),文章链接,文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:cloudbbs@huaweicloud.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。