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坐标


   
   
       org.springframework.boot
       spring-boot-starter-security
   

三、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. 其他自动完成的配置

  • 要求经过身份验证的用户才能与应用程序进行任何交互
  • 生成一个默认的登录表单
  • 使用用户名user和打印到控制台的密码登录
  • 使用BCrypt保护密码存储
  • 用户注销
  • CSRF攻击预防
  • 会话固定保护
  • 安全请求头信息集成
    1. HTTP严格传输安全性用于安全请求
    2. X-Content-Type-Options集成
    3. 缓存控制(以后可以由您的应用程序覆盖,以允许缓存您的静态资源)
    4. X-XSS-Protection集成
    5. X-Frame-Options集成有助于防止Clickjacking
  • 集成servletAPI
    1. HttpServletRequest#getRemoteUser()

    2. HttpServletRequest.html#getUserPrincipal()

    3. HttpServletRequest.html#isUserInRole(java.lang.String)

    4. HttpServletRequest.html#login(java.lang.String, java.lang.String)

    5. HttpServletRequest.html#logout()

五、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

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

你可能感兴趣的:(Spring Security)