【Spring Security】 认证
【Spring Security】 认证
Metadata
title: 【Spring Security】 认证
date: 2023-02-02 14:01
tags:
- 行动阶段/完成
- 主题场景/组件
- 笔记空间/KnowladgeSpace/ProgramSpace/ModuleSpace
- 细化主题/Module/SpringSecurity
categories:
- SpringSecurity
keywords:
- SpringSecurity
description: 【Spring Security】 认证
认证流程
认证流程即验证用户信息真实有效性,也就是登录。spring security 提供了很多扩展点,因此支持很多种认证方式,其默认采用的是基于内存的认证,即项目启动时会在内存中生成默认用户名 user,同时生成密码,且会将其打印在日志中,但这只是个例子,并不适用日常业务需求,大多时都是基于数据库验证,即用户信息存放在数据库,当用户名传入后,会根据数据库中的记录进行认证,以下时序图则是以基于数据库认证为背景。
如图所示,当用户输入用户名密码点击登录发起一个认证请求后,该请求会经过多层过滤器,最终在 AbstractAuthenticationProcessingFilter 过滤器中会被处理。具体处理流程是在执行 filter 的 doFilter 方法时进行处理,doFilter 方法的主要逻辑分为三步,即第一步是调用 attemptAuthenitcation 方法进行认证,并得到一个认证对象 Authentication;第二步是调用 successfulAuthentication方法进行授权成功后的一些信息存储等(若执行到此,则说明已经认证成功,因为在第一步当中若认证失败则会抛出 AuthenticationException 异常,即认证异常);第三步是捕获异常然后调用 unsuccessfulAuthentication 方法进行异常处理。
attemptAuthentication 方法被定义为抽象方法,也就是说具体认证流程可以自定义实现,而在基于数据库认证中是由子类 UsernamePasswordAuthenticationFilter 负责的。该方法中是先通过调用 obtainUsername 和 obtianPassword 方法获取传入的用户名和密码,实际上是调用 request.getParamter 方法,而默认参数即用户名和密码默认参数是 username 和 password,且该参数可以通过具体方法进行配置。拿到用户名和密码后会创建一个未认证的 Authenticaion 对象,然后调用 AuthenticationManager 的 authenicate 方法进行认证。ProviderManager 类是 AuthenticationManager 接口的主要实现类,所以调用的是 ProviderManager 的 authenticate 方法,实际上该方法并不是具体的认证处理,相当于是触发认证的动作。在 ProviderManager 中维护了一个 AuthenticaionProvider 列表,会遍历整个 provider 列表,若某一个 provider 支持该对象的认证,则会调用该 provider 的 authenicate 方法进行具体的认证,若该 provider 的认证结果不为空则会直接 break,即不会使用剩余的 provider 进行认证。在 provider 的 authenticate 方法中会先调用 DaoAuthenticationProvider 的 retrieveUser 方法检索用户信息(DaoAuthenticaionProvider 类是基于数据库认证的一个 AuthenticationProvider 接口的主要实现),在 retrieveUser 方法中主要是调用 UserDetailsService 接口的 loadUserByUsername 方法记载用户信息,UserDetailsService 接口需要我们自己实现,实现 loadUserByUsername 方法,根据用户名从数据库加载用户信息,返回一个 UserDetails 对象,此时,就从数据库中拿到了用户信息(若没拿到则会抛出 UsernameNotFoundException 异常,该异常是 AuthenticaitonException 的一个子类)。接着会调用 UserDetailsChecker 的 check 方法进行用户状态校验,用户状态即账号是否被锁定、是否启用、是否过期,验证密码时会调用 PasswordEncoder 的 matches 方法,若校验失败(即密码错误)则会抛出 BadCredentialsException 异常,即错误凭证异常。然后调用 UserDetailsChecker 接口的另一个实现的 check 方法进行密码状态校验,主要时校验密码是否过期。此时认证已通过,则调用 createSuccessAuthentication 方法创建一个认证成功的 Authentication 对象,主要是设置用户权限、擦除密码等。
通过上一步的操作已经认证成且拿到了认证成功的 Authentication 对象,接下来会调用 successfulAuthenticaion 方法设置安全上下文并存储用户信息。先创建一个 SecurityContext 对象并设置 Authenticaion 属性,然后将该 SecurityContext 放进 SecurityContextHolder 中,这个 holder 的主要实现实际上是一个 map,key 为 request 对象,value 为 SecurityContext,将 SecurityContext 放进 map 中的主要作用是为该请求的后续流程服务(如当用户访问一个受保护的资源,系统发现当前为登录就会跳转到登录页面,用户登陆成功后重定向到原来访问的页面,这时候就根据 map 中该用户的权限信息来判断改用到底有没有访问该资源的权限)。最后将 SecurityContext 进行存储,即登录信息持久化,spring security 默认的 SecurityContextRepository 即存储实现是 http session,即默认情况下用户登录后的信息是放在 session 中的,当然该接口可自定义实现,如若用户量较大则可以自定义实现将其存储在 redis 等存储介质中。SecurityContextHolder 和 SecurityContextRepository 的区别是,前者是临时的,后者是持久的。前者只针对某个请求,即当某个请求到达时,先从后者中拿到 SecurityContext(若没登录则为空),然后做授权使用,当该请求结束后该 SecurityContext 就会被从 holder 中清除。后者是持久的,并不是完全持久,如用户登录的有效时长为 7 天,则超过 7 天就会从 repository 中清除。
【Spring Security】 认证机制
undefined
【Spring Security】 用户 密码 认证
undefined
【Spring Security】 登录核心类
undefined
【Spring Security】 认证流程源码分析
undefined
【Spring Security】 PasswordEncoder
undefined
【Spring Security】 密码编码流程
undefined