文章目录
什么是Spring Security验证?
让我们考虑一个大家都很熟悉的标准的验证场景。
- 提示用户输入用户名和密码进行登录。
- 该系统 (成功) 验证该用户名的密码正确。
- 获取该用户的环境信息 (他们的角色列表等).
- 为用户建立安全的环境。
- 用户进行,可能执行一些操作,这是潜在的保护的访问控制机制,检查所需权限,对当前的安全的环境信息的操作。
而对于Spring Security来说,假设是用户名密码登陆,那执行顺序就是:
- 用户通过url:/login 登录,该过滤器接收表单用户名密码
- 判断用户名密码是否为空
- 生成
UsernamePasswordAuthenticationToken
- 将 Authentiction 传给
AuthenticationManager#authenticate
方法进行认证处理 AuthenticationManager
默认是实现类为 ProviderManager ,ProviderManager 委托给AuthenticationProvider
进行处理UsernamePasswordAuthenticationFilter
继承了AbstractAuthenticationProcessingFilter
抽象类,AbstractAuthenticationProcessingFilter
在successfulAuthentication
方法中对登录成功进行了处理,通过SecurityContextHolder.getContext().setAuthentication()
方法将Authentication
认证信息对象绑定到SecurityContext
- 下次请求时,在过滤器链头的
SecurityContextPersistenceFilter
会从 Session 中取出用户信息并生成 Authentication(默认为UsernamePasswordAuthenticationToken
),并通过SecurityContextHolder.getContext().setAuthentication()
方法将Authentication
认证信息对象绑定到SecurityContext
- 需要权限才能访问的请求会从
SecurityContext
中获取用户的权限进行验证
下面来逐个分析一下每个类。
1.核心组件
这一节主要介绍一些Spring Security中核心的java类,他们之间的依赖,构建起了整个框架。
1.1 SecurityContextHolder
SecurityContextHolder 是最基本的对象,它负责存储当前安全上下文信息。即保存着当前用户是什么,是否已经通过认证,拥有哪些权限。。。等等。SecurityContextHolder默认使用**ThreadLocal
**策略来存储认证信息,意味着这是一种与线程绑定的策略。在Web场景下的使用Spring Security,在用户登录时自动绑定认证信息到当前线程,在用户退出时,自动清除当前线程的认证信息。而在非Web场景下,比如Swing环境下,Spring提供在启动时使用策略配置SecurityContextHolder。这部分具体的配置请自行翻阅官方文档。我就不在此赘述了,之后的讲解也将基于Web场景。
获取有关当前用户的信息
Spring Security使用一个Authentication对象来表示这些信息。通常不需要自己创建一个Authentication对象,但可以在程序的任何一个地方获取到用户信息,下面时官方提供的获取用户信息:
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
1.2 Authentication
首先看一下源码:
package org.springframework.security.core;
public interface Authentication extends Principal, Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
Object getCredentials();
Object getDetails();
Object getPrincipal();
boolean isAuthenticated();
void setAuthenticated(boolean var1) throws IllegalArgumentException;
}
从这个接口中,我们可以得到用户身份信息,密码,细节信息,认证信息,以及权限列表,具体的详细解读如下:
getAuthorities()
,权限列表,通常是代表权限的字符串列表;getCredentials()
,密码信息,由用户输入的密码凭证,认证之后会移出,来保证安全性;getDetails()
,细节信息,Web应用中一般是访问者的ip地址和sessionId;getPrincipal()
, 最重要的身份信息,一般返回UserDetails的实现类;
官方文档里说过,当用户提交登陆信息时,会将用户名和密码进行组合成一个实例UsernamePasswordAuthenticationToken
,而这个类是Authentication的一个常用的实现类,用来进行用户名和密码的认证,类似的还有RememberMeAuthenticationToken
,它用于记住我功能。
1.3 UserDetails
上文提到了UserDetails接口,它代表了最详细的用户信息,这个接口涵盖了一些必要的用户信息字段,具体的实现类对它进行了扩展,首先来看一下源码:
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
它和Authentication
接口类似,都包含了用户名,密码以及权限信息,而区别就是Authentication
中的getCredentials
来源于用户提交的密码凭证,而UserDetails中的getPassword取到的则是用户正确的密码信息,认证的第一步就是比较两者是否相同,除此之外,Authentication#getAuthorities
是认证用户名和密码成功之后,由UserDetails#getAuthorities
传递而来。而Authentication
中的getDetails信息是经过了AuthenticationProvider
认证之后填充的。
1.4 UserDetailsService