Spring Security--Authentication认证

SpringSecurity-Authentication认证

1.认证

首先我们了解SpringSecurity的各个接口和类之间的关系,只有理清了这个组件之间的关系,才能为后续的应用打下基础。

image-20210716110926714

1.1 SecurityContextHolder 安全上下文持有者

image-20210716111211822

SecurityContext context = SecurityContextHolder.createEmptyContext(); 
Authentication authentication = new TestingAuthenticationToken("username", "password", "ROLE_USER"); 
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context); 

如果想获取认证过的用户信息,可以从SecurityContextHolder中获取

SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

1.2 Authentication 认证

在Spring Security中,身份验证有两个主要目的:

  • AuthenticationManager的输入,用于提供用户为进行身份验证而提供的凭据。 在此场景中使用时,isAuthenticated()返回false。

  • 表示当前通过身份验证的用户。 当前的认证可以从SecurityContext中获取。

身份验证包含:

  • Principal—标识用户。 当使用用户名/密码进行身份验证时,这通常是UserDetails的一个实例。

  • credentials—通常是密码。 在许多情况下,这将在用户身份验证后清除,以确保不会泄漏。

  • authorys—grantauthorys为用户被授予的高级权限。 比如是角色或权限。

从源码上可以看到Authentication接口主要有如下6个方法

image-20210716120200738

实现了Authentication接口的主要有以下8个类,其中做登录认证最常用的就是UsernamePasswordAuthenticcationToken

image-20210716120354051

1.3 AuthenticationManager 认证管理器

AuthenticationManager是定义 Spring Security 的过滤器如何执行Authentication 身份验证的 API .

image-20210716112644125

从源码可以看出AuthenticationManager接口只包含一个authenticate()方法,它的实现类主要包括以下4个,ProviderManager是AuthenticationManager最重要的一个实现类

image-20210716113013312

1.3.1 ProviderManager

image-20210716114038394

ProviderManager是AuthenticationManager最常用的实现。 ProviderManager委托给AuthenticationProviders列表。 每个AuthenticationProvider都有机会表明身份验证应该是成功的,失败的,或者表明它不能做出决定,并允许下游的AuthenticationProvider来做出决定。 如果配置的AuthenticationProviders中没有一个可以进行身份验证,那么身份验证将会失败,并会出现一个ProviderNotFoundException异常,这是一个特殊的AuthenticationException,表明ProviderManager没有被配置为支持所传入的身份验证类型

从源码中可以看到 ProviderManager委托给AuthenticationProviders列表,即providers为ProviderManager的一个list集合的成员变量。

image-20210716125632198

身份提供者AuthenticationProvider接口有如下的实现类

image-20210716132153275

多个AuthenticationProviders可以被注入到ProviderManager中。 每个AuthenticationProvider执行特定类型的身份验证。 例如,DaoAuthenticationProvider支持基于用户名/密码的身份验证,而JwtAuthenticationProvider支持验证JWT令牌。

综上所述,以上接口与类的关系如图所示

image-20210716133058872

1.4 AuthenticationEntryPoint

AuthenticationEntryPoint是Spring Security Web一个概念模型接口,顾名思义,他所建模的概念是:“认证入口点”。它在用户请求处理过程中遇到认证异常时,被ExceptionTranslationFilter用于开启特定认证方案(authentication schema)的认证流程。

AuthenticationEntryPoint 用来解决匿名用户访问无权限资源时的异常

AccessDeineHandler 用来解决认证过的用户访问无权限资源时的异常

1.5 AbstractAuthenticationProcessingFilter 抽象认证处理过滤器

image-20210716141709985

  • 当用户提交他们的凭据时,AbstractAuthenticationProcessingFilterAuthenticationHttpServletRequest要进行身份验证的 中创建一个。Authenticationcreated的类型取决于 的子类AbstractAuthenticationProcessingFilter。例如,从.csv文件中提交的用户名密码UsernamePasswordAuthenticationFilter创建一个。UsernamePasswordAuthenticationToken``HttpServletRequest

  • 接下来,将Authentication传递给AuthenticationManager要进行身份验证的

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

    • SecurityContextHolder中被清除出去。
    • RememberMeServices.loginFail被调用。如果记住我没有配置,这是一个空操作。
    • AuthenticationFailureHandler 被调用。
  • If authentication is successful, then Success.

    • SessionAuthenticationStrategy 收到新登录通知。
    • 认证被设置在SecurityContextHolder中。稍后将SecurityContextPersistenceFilter保存SecurityContextHttpSession.
    • RememberMeServices.loginSuccess被调用。如果记住我没有配置,这是一个空操作。
    • ApplicationEventPublisher发布一个InteractiveAuthenticationSuccessEvent.
    • AuthenticationSuccessHandler 被调用。

1.6 用户名和密码认证

1.6.1 表单登陆

基于表单的登录在 Spring Security 中是如何工作的

image-20210716143651861

首先客户端发送一个没有认证的请求到服务器端,过滤器链SecurityFilterChain中的FilterSecurityInterceptor拒绝并抛出AccessDeniedException异常,异常处理过滤器ExceptionTranslationFilter拦截,进入到LoginURLAuthentictionEntryPoint处理,重定向到/login的登陆页面,然后客户端重新发送登陆请求,服务器返回登陆页面。当用户提交用户民和密码提交后会进入到UsernamPasswordAuthenticationFilter认证过滤器中进行处理

image-20210716144727336

以上的就是我们看到用户如何被重定向到spring security默认的登录表单的过程。

但是在实际的项目中,很多时候我们需要使用我们自定义的登陆表单,不会使用默认的登陆表单,该如何设置呢?

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        // ...
        .formLogin(form -> form
            .loginPage("/login")
            .permitAll()
        );
}

在自定义的登陆表单中请求路径必须为POST /login请求,然后用户名和密码名称必须用username和password.如果使用的是 Spring MVC,您将需要一个映射GET /login到我们创建的登录模板的控制器.

1.6.2UserDetailsService

UserDetailsService所使用的DaoAuthenticationProvider用于检索的用户名,密码和其他属性与用户名和密码进行认证

image-20210716150927030

通过UserDetailService从数据库中通过userName获取用数据后,如何进行对比确认认证成功的?

image-20210716153615366

AbstractAuthenticationProcessingFilter

调用 requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。
如果需要验证,则会调用 attemptAuthentication(HttpServletRequest, HttpServletResponse) 方法。
有三种结果:
1、返回一个 Authentication 对象.配置的 SessionAuthenticationStrategy 将被调用,然后调用 successfulAuthentication(HttpServletRequest,HttpServletResponse,FilterChain,Authentication) 方法。
2、验证时发生 AuthenticationException。unsuccessfulAuthentication(HttpServletRequest, HttpServletResponse, AuthenticationException) 方法将被调用。
3、返回Null,表示身份验证不完整。假设子类做了一些必要的工作(如重定向)来继续处理验证,方法将立即返回.假设后一个请求将被这种方法接收,其中返回的Authentication对象不为空。

image-20210719164239954

UsernamePasswordAuthenticationFilter

attemptAuthentication 方法是AbstractAuthenticationProcessingFilter抽象类中的一个抽象方法,而UsernamePasswordAuthenticationFilter类是它的一个实现类,实现了该方法attemptAuthentication

image-20210719170414516

把form表单中的username和password封装成一个UsernamePasswordAuthenticationToken对象,然后调用AuthenticationManager的authenticate放进行认证

ProviderManager

Spring Security中进行身份验证的是AuthenticationManager接口,ProviderManager是它的一个默认实现,但它并不用来处理身份认证,而是委托给配置好的AuthenticationProvider,每个AuthenticationProvider会轮流检查身份认证。检查后或者返回Authentication对象或者抛出异常。

image-20210719173345512

ProviderManager委托给AuthenticationProvider列表,AuthenticationProvider是一个接口,有认证的方法authenticate进行认证

image-20210719170131076

AuthenticationProvider

image-20210719174125602

实现了AuthenticationProvider接口的类有如图所示的11个类,AbstractUserDetailsAuthenticationProvider实现了给接口的认证方法

AbstractUserDetailsAuthenticationProvider

image-20210719171653402

在该方法中首先从缓存中查询,如果没有通过username调用我们实现的接口UserDetailsService,获取数据库中的用户信息;然后进行数据库用户和输入用户数据的passwrod的比较

DaoAuthenticationProvider

登录时用到了 DaoAuthenticationProvider ,它有一个方法additionalAuthenticationChecks(UserDetails userDetails,UsernamePasswordAuthenticationToken authentication),此方法用来校验从数据库取得的用户信息和用户输入的信息是否匹配。

image-20210719162808393

通过以上流程就完成了用户数据的认证过程。

原文地址:https://www.cnblogs.com/seanRay/p/15031417.html