Spring Security

本文基于 Spring Security 5.3版本
官方文档

一、简介

Spring Security是一个提供身份验证,授权和保护以防止常见攻击的框架。 凭借对命令式和响应式应用程序的一流支持,它是用于保护基于Spring的应用程序的实际标准。
Spring Security通过标准的Filter与servlet容器进行集成,所以,只要是符合servlet规范的容器都可以用Spring Servlet进行保护。

Spring Security需要Java 8或更高版本的运行时环境。

二、引入Spring Security

文档中介绍了使用springboot和不使用springboot集成的方法,这里只介绍使用springboot的方式(采用maven)

新建springboot项目(版本2.2.4)引入maven坐标

<dependencies>
   <!-- ... other dependency elements ... -->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-security</artifactId>
   </dependency>
</dependencies>

三、Spring Security模块划分

在Spring Security 3.0中,代码库被细分为单独的jar,这些jar更清楚地区分了不同的功能区域和第三方依赖项。

1.Core — spring-security-core.jar

该模块包含核心身份验证和访问控制类和接口,远程支持和基本配置API。 使用Spring Security的任何应用程序都需要它。 它支持独立的应用程序,远程客户端,方法(服务层)安全性和JDBC用户配置。 它包含以下顶级程序包

  • org.springframework.security.core
  • org.springframework.security.access
  • org.springframework.security.authentication
  • org.springframework.security.provisioning

2.Remoting — spring-security-remoting.jar

该模块提供了与Spring Remoting的集成。 除非您要编写使用Spring Remoting的远程客户端,否则您不需要关注。

3.Web — spring-security-web.jar

该模块包含过滤器和相关的Web安全基础结构代码。 它包含任何与Servlet API相关的内容。 如果需要Spring Security Web认证服务和基于URL的访问控制,则需要它。 主要软件包是org.springframework.security.web。

4.Config — spring-security-config.jar

该模块包含安全名称空间解析代码和Java配置代码。 如果您使用Spring Security XML名称空间进行配置或Spring Security的Java配置支持,则需要它

5.LDAP — spring-security-ldap.jar

该模块提供LDAP身份验证和供应代码。 如果您需要使用LDAP认证或管理LDAP用户条目,则为必填项

6. OAuth 2.0 Core — spring-security-oauth2-core.jar

spring-security-oauth2-core.jar包含核心类和接口,这些类和接口提供对OAuth 2.0授权框架和OpenID Connect Core 1.0的支持。 使用OAuth 2.0或OpenID Connect Core 1.0的应用程序(例如客户端,资源服务器和授权服务器)需要它

7.OAuth 2.0 Client — spring-security-oauth2-client.jar

包含Spring Security对OAuth 2.0授权框架和OpenID Connect Core 1.0的客户端支持。 使用OAuth 2.0登录或OAuth客户端支持的应用程序需要使用它

8.OAuth 2.0 JOSE — spring-security-oauth2-jose.jar

包含Spring Security对JOSE(Javascript对象签名和加密)框架的支持。 JOSE框架旨在提供一种在各方之间安全地转移索赔的方法。 它是根据一系列规范构建的:

  • JSON Web Token (JWT)
  • JSON Web Signature (JWS)
  • JSON Web Encryption (JWE)
  • JSON Web Key (JWK)

9. OAuth 2.0 Resource Server — spring-security-oauth2-resource-server.jar

包含Spring Security对OAuth 2.0资源服务器的支持。 它用于通过OAuth 2.0承载令牌保护API

10.ACL — spring-security-acl.jar

该模块包含专门的域对象ACL实现。 它用于将安全性应用于应用程序中的特定域对象实例

11.CAS — spring-security-cas.jar

该模块包含Spring Security的CAS客户端集成。 如果要对CAS单点登录服务器使用Spring Security Web认证,则应该使用它。

12.OpenID — spring-security-openid.jar

该模块包含OpenID Web身份验证支持。 它用于根据外部OpenID服务器对用户进行身份验证

13.Test — spring-security-test.jar

该模块包含对使用Spring Security进行测试的支持

四、Spring Boot 关于Spring Security的自动配置

1. springSecurityFilterChain

自动配置会在servlet容器中创建一个名为springSecurityFilterChain的过滤器,负责应用程序内的所有安全性(保护应用程序URL,验证提交的用户名和密码,重定向到登录表单等)


image.png

2. UserDetailsS​​ervice

自动配置会创建一个UserDetailsS​​ervice实例,其中包含用户名user和一个随机生成的密码,该密码将记录到控制台

3. springSecurityFilterChain会作为标准的Filter注册到Servlet容器中,每个请求都会经过它被处理

4. 其他自动完成的配置

五、Spring Security整体架构设计

1.FilterChain

首先是spring security的FilterChain里面包含很多spring security内部的过滤器,并且过滤器的顺序是固定的

image.png
2.DelegatingFilterProxy

Spring提供了一个名为DelegatingFilterProxy的Filter实现,可以在Servlet容器的生命周期和Spring的ApplicationContext之间进行桥接。 Servlet容器允许使用其自己的标准注册Filters,但它不了解Spring定义的Bean。 DelegatingFilterProxy可以通过标准的Servlet容器机制进行注册,但是可以将所有工作委托给实现Filter的Spring Bean。


image.png

DelegatingFilterProxy的作用可以用下面的伪代码表示

public void doFilter(ServletRequest request, 
ServletResponse response, FilterChain chain) {
// Lazily get Filter that was registered as a Spring Bean 
 // For the example in DelegatingFilterProxy delegate is an instance of Bean Filter0
 Filter delegate = getFilterBean(someBeanName);
 // delegate work to the Spring Bean 
 delegate.doFilter(request, response);

}

DelegatingFilterProxy的另一个好处是它允许延迟查找Filter bean实例。 这很重要,因为容器需要在容器启动之前注册Filter实例。 但是,Spring通常使用ContextLoaderListener加载Spring Bean,直到需要注册Filter实例之后才可以加载。

3.FilterChainProxy

Spring Security的Servlet支持包含在FilterChainProxy中。 FilterChainProxy是Spring Security提供的特殊过滤器,允许通过SecurityFilterChain委派许多过滤器实例。 由于FilterChainProxy是Bean,因此通常将其包装在DelegatingFilterProxy中。


image.png

4.SecurityFilterChain

FilterChainProxy使用SecurityFilterChain确定应对此请求调用哪些Spring Security过滤器。


image.png

5. Security Filters

SecurityFilterChain 中包含很多个Filter,这些Filter装载到SecurityFilterChain中, SecurityFilterChain再装载到FilterChainProxy中,FilterChainProxy再被装载到连接Servlet容器和spring容器的DelegatingFilterProxy中。
包含以下这些过滤器:

  • ChannelProcessingFilter

  • ConcurrentSessionFilter

  • WebAsyncManagerIntegrationFilter

  • SecurityContextPersistenceFilter

  • HeaderWriterFilter

  • CorsFilter

  • CsrfFilter

  • LogoutFilter

  • OAuth2AuthorizationRequestRedirectFilter

  • Saml2WebSsoAuthenticationRequestFilter

  • X509AuthenticationFilter

  • AbstractPreAuthenticatedProcessingFilter

  • CasAuthenticationFilter

  • OAuth2LoginAuthenticationFilter

  • Saml2WebSsoAuthenticationFilter

  • UsernamePasswordAuthenticationFilter

  • ConcurrentSessionFilter

  • OpenIDAuthenticationFilter

  • DefaultLoginPageGeneratingFilter

  • DefaultLogoutPageGeneratingFilter

  • DigestAuthenticationFilter

  • BearerTokenAuthenticationFilter

  • BasicAuthenticationFilter

  • RequestCacheAwareFilter

  • SecurityContextHolderAwareRequestFilter

  • JaasApiIntegrationFilter

  • RememberMeAuthenticationFilter

  • AnonymousAuthenticationFilter

  • OAuth2AuthorizationCodeGrantFilter

  • SessionManagementFilter

  • ExceptionTranslationFilter

  • FilterSecurityInterceptor

  • SwitchUserFilter

6.Handling Security Exceptions

ExceptionTranslationFilter允许将AccessDeniedException和AuthenticationException转换为HTTP响应。它也是上面介绍的过滤器链中的一个


image.png

①开始处理
②如果用户未通过身份验证或抛出AuthenticationException,则启动身份验证。
③如果抛出AccessDeniedException,则拒绝访问。 调用AccessDeniedHandler以处理拒绝的访问

六、认证方式

1. 认证模块的组件

  • SecurityContextHolder:Spring Security在其中存储通过身份验证的人员的详细信息


    image.png
  • SecurityContext:从SecurityContextHolder获得,并包含当前经过身份验证的用户的身份验证

  • Authentication
    两个主要用途:
    1.作为AuthenticationManager的输入,用于提供用户提供的用于身份验证的凭据
    2.代表当前经过身份验证的用户。 可以从SecurityContext获得当前的身份验证

  • GrantedAuthority:授予身份验证主体的权限(即角色,作用域等)

  • AuthenticationManager:AuthenticationManager是用于定义Spring Security的过滤器如何执行身份验证的API。 然后,由调用AuthenticationManager的控制器(即Spring Security的Filters)在SecurityContextHolder上设置返回的身份验证。 如果您没有与Spring Security的过滤器集成,则可以直接设置
    SecurityContextHolder,并且不需要使用AuthenticationManager。虽然AuthenticationManager的实现可以是任何东西,但最常见的实现是ProviderManager。

  • ProviderManager:AuthenticationManager的最常见实现,ProviderManager将认证工作委托给AuthenticationProviders列表, 每个AuthenticationProvider都有机会指示认证应该成功,失败,或者表明它不能做出决定并允许下一个AuthenticationProvider进行决定。 如果没有一个已配置的AuthenticationProviders可以进行身份验证,则身份验证将失败,并显示ProviderNotFoundException,这是一个特殊的AuthenticationException,它指示未配置ProviderManager并支持传递给它的Authentication类型。


    image.png

七、 Username/Password Authentication(用户名密码认证方式)

由于这个认证组件部分内容笔记多,单独拉出来说明
Spring Security提供了以下内置机制,用于从HttpServletRequest中读取用户名和密码
1.Form Login
2.Basic Authentication
3.Digest Authentication

1.表单登录方式
image.png

该图基于我们的SecurityFilterChain图。

①用户对未经授权的资源/ private进行未经身份验证的请求。

②Spring Security的FilterSecurityInterceptor表示通过抛出AccessDeniedException拒绝了未经身份验证的请求。

③由于用户未通过身份验证,因此ExceptionTranslationFilter会启动“开始身份验证”,并使用配置的AuthenticationEntryPoint将重定向发送到登录页面。 在大多数情况下,AuthenticationEntryPoint是LoginUrlAuthenticationEntryPoint的实例。

④然后,浏览器将请求将其重定向到的登录页面。

⑤应用程序中的某些内容必须呈现登录页面。

提交用户名和密码后,UsernamePasswordAuthenticationFilter会对用户名和密码进行身份验证

image.png

①当用户提交其用户名和密码时,UsernamePasswordAuthenticationFilter通过从HttpServletRequest中提取用户名和密码来创建UsernamePasswordAuthenticationToken,这是一种身份验证类型。

②接下来,将UsernamePasswordAuthenticationToken传递到AuthenticationManager进行身份验证。 AuthenticationManager外观的详细信息取决于用户信息的存储方式。

③如果身份验证失败,则失败

  • 清除SecurityContextHolder。
  • RememberMeServices.loginFail被调用。如果记住我没有配置,这是一个禁忌。
  • AuthenticationFailureHandler被调用。

④如果身份验证成功,则为成功。

  • 新的登录通知SessionAuthenticationStrategy。
  • 身份验证是在SecurityContextHolder上设置的。
  • RememberMeServices.loginSuccess被调用。如果记住我没有配置,这是一个禁忌。
  • ApplicationEventPublisher发布一个
  • InteractiveAuthenticationSuccessEvent。
  • AuthenticationSuccessHandler被调用。通常,这是一个SimpleUrlAuthenticationSuccessHandler,当我们重定向到登录页面时,它将重定向到ExceptionTranslationFilter保存的请求。
  • AuthenticationProvider:
    可以将多个AuthenticationProviders注入ProviderManager。 每个AuthenticationProvider执行特定的身份验证类型。
    例如,DaoAuthenticationProvider支持基于用户名/密码的身份验证,而JwtAuthenticationProvider支持对JWT令牌的身份验证。
  • 带AuthenticationEntryPoint的请求凭据:用于从客户端请求凭据(即,重定向到登录页面,发送WWW身份验证响应等)
  • AbstractAuthenticationProcessingFilter:用于身份验证的基本过滤器


    image.png
2.Basic Authentication
image.png

该图基于我们的SecurityFilterChain图。

①用户对未经授权的资源发起请求。
②Spring Security的FilterSecurityInterceptor表示通过抛出AccessDeniedException拒绝了未经身份验证的请求。
③由于用户未通过身份验证,因此ExceptionTranslationFilter会启动“开始身份验证”。 配置的AuthenticationEntryPoint是BasicAuthenticationEntryPoint的实例,该实例响应WWW-Authenticate头。 RequestCache通常是一个NullRequestCache,它不保存请求,因为客户端能够重播它最初请求的请求。
当客户端收到WWW-Authenticate响应头时,它知道应该使用用户名和密码重试。 以下是正在处理的用户名和密码的流程。


image.png

①当用户提交其用户名和密码时,BasicAuthenticationFilter通过从HttpServletRequest中提取用户名和密码来创建UsernamePasswordAuthenticationToken,这是一种身份验证类型。
②接下来,将UsernamePasswordAuthenticationToken传递到AuthenticationManager进行身份验证。
③如果身份验证失败,则失败,清除SecurityContextHolder存储,RememberMeServices.loginFail被调用。
调用AuthenticationEntryPoint触发WWW-Authenticate重新发送。
④如果身份验证成功,则为成功,向SecurityContextHolder存储登录成功信息
RememberMeServices.loginSuccess被调用
BasicAuthenticationFilter调用FilterChain.doFilter(request,response)继续进行其余的应用程序逻辑。

Spring Security的HTTP基本身份验证支持默认为启用。 但是,一旦提供了任何基于servlet的配置,则必须显式提供HTTP Basic
如下

protected void configure(HttpSecurity http) {
    http
        // ...
        .httpBasic(withDefaults());
}

3.Digest Authentication

4.In-Memory Authentication

springsecurity支持在内存中临时设置用户信息完成认证
可以按实例进行设置,定义两个用户并设置权限

@Bean
public UserDetailsService users() {
    UserDetails user = User.builder()
        .username("user")
        .password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
        .roles("USER")
        .build();
    UserDetails admin = User.builder()
        .username("admin")
        .password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
        .roles("USER", "ADMIN")
        .build();
    return new InMemoryUserDetailsManager(user, admin);
}

5. JDBC Authentication

支持从数据库查询用户信息完成认证

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342