Spring Security 是一个功能强大的框架,用于保护 Spring 应用程序。它专注于身份验证和授权,并可根据需求进行配置和自定义。本文将讨论用于身份验证的不同组件,在实际应用中,了解身份验证组件的概念对于使用 Spring Security 进行实现和自定义非常有帮助。
如果我们没有使用 Spring Security,那么请求将被 DispatcherServlet 拦截。DispatcherServlet 是前端控制器,它拦截任何 HTTP 请求并将其转发到正确的控制器。在 Spring Boot 中,DispatcherServlet 会被自动配置。
请求会被 DispatcherServlet 拦截
在深入了解 Spring Security 之前,让我们先了解一下 DispatcherServlet 如何分发请求。当我们在一个端点(比如 /helloworld)发出请求时,前端控制器(DispatcherServlet)会如何处理它?
DispatcherServlet 创建了一个 IOC 容器,它是 Spring Framework 的核心组件之一,用于管理 bean 的创建和依赖关系。DispatcherServlet 创建的是 WebApplicationContext,这是一个专门用于 Web 应用程序的 IOC 容器。WebApplicationContext 是根据配置文件由 DispatcherServlet 进行配置的。
IOC 容器会创建控制器 bean 的实例。当请求到达时,DispatcherServlet 会使用 IOC 容器查找适当的控制器 bean,并委托给它来处理请求。
当将 Spring Security 添加到 Spring Boot 应用程序中时,所有请求在到达 DispatcherServlet 和控制器之前都会被 Spring Security 机制拦截。
每当一个请求到达应用程序时,它首先被一系列过滤器拦截,这些过滤器称为 Filter ChAIn。除了 Spring Security 提供的过滤器之外,我们还可以添加自己的自定义过滤器。认证后的 FilterChain 将请求转发到 DisptacherServlet。
过滤器链拦截传入请求
身份验证请求和响应
虽然 Spring Security 可以与不同类型的身份验证方法一起使用,但在本文中,我们讨论的是用户名和密码身份验证,以便深入了解完整的身份验证流程。
我们了解一下前面部分讨论的不同组件。
Spring Filter 是一个组件,可以拦截任何传入请求,并在将其传递给 DispatcherServlet 之前执行某些操作。过滤器可以处理请求,然后将其转发到 Filter Chain 中的下一个过滤器,或者可以停止并发送回 HTTP 响应。其中一个过滤器是存在于 FilterChain 中的 UsernamePasswordAuthenticationFilter。此过滤器尝试查找 HTTP Post 请求中传递的用户名和密码,如果找到,则尝试使用凭据对用户进行身份验证。以下是此类中存在的身份验证方法的快照。
我们可以创建自己的 Filter 并将其添加到 SecurityFilterChain 中,以提供自己的逻辑来处理请求。
AuthenticationManager 是一个接口,用于处理身份验证的过程。它只有一个方法 authenticate(Authentication authentication),该方法将一个身份验证对象作为参数,并返回已经认证的身份验证对象。身份验证对象通常是一个包含用户名和密码等凭据的 AuthenticationToken 对象。
Authentication authenticate(Authentication authentication) throws AuthenticationException;
AuthenticationManager 的实现类是 ProviderManager 类,它提供了 authenticate() 方法的逻辑。我们可以提供我们自己的 AuthenticationProvider 实现类或使用默认实现。
该类实现了 AuthenticationManager 接口并覆盖了 authenticate() 方法。它使用一组 AuthenticationProvider 来验证 Authentication 对象中发送的凭据。如果 AuthenticationProvider 支持身份验证类型,则将用于验证用户。如果没有提供者支持身份验证类型,则将抛出 AuthenticationException。
每个身份验证提供程序的 supports() 方法用于检查它是否可以支持所需的身份验证类型。
AuthenticationProvider 是一个接口,用于定义验证用户的协议。它负责接收 Authentication 对象(表示用户凭据)并返回已经认证的 Authentication 对象,如果凭据有效。如果凭据无效,则 AuthenticationProvider 应该抛出 AuthenticationException。
AuthenticationProvider 接口有两个方法:
Spring Security 中的默认 AuthenticationProvider 是 DaoAuthenticationProvider。此提供程序使用 UserDetailsService 从数据库加载用户详细信息。如果用户凭据与数据库中的详细信息匹配,则 DaoAuthenticationProvider 将返回已经认证的 Authentication 对象。
我们可以添加我们自己的 AuthenticationProvider 实现来提供不同的身份验证方法。
Authentication 和 UsernamePasswordAuthenticationToken 是什么?
这是 Spring Security 中的一个接口,表示传入身份验证请求的令牌或已认证的主体(表示一个实体,比如个人)AuthenticationManager.authenticate() 方法。
提供的一些方法为:
Collection<? extends GrantedAuthority> getAuthorities();
Object getCredentials();
boolean isAuthenticated();
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
此类扩展了 AbstractAuthenticationToken 类(身份验证对象的基类),可用于用户名/密码身份验证请求。
此类有两个构造函数:
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
super((Collection)null);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(false);
}
public UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
第一个构造函数可用于传入请求以创建未经身份验证的 Authentication 对象。
Authentication authentication = new UsernamePasswordAuthenticationToken(username,password);
第二个构造函数可用于创建完全经过身份验证的 Authentication 对象。
Authentication authToken = new UsernamePasswordAuthenticationToken(username, password, userAuthorities);
然后,此完全经过身份验证的 Authentication 对象从 AuthenticationProvider/AuthenticationManager 返回并表示已认证的用户。然后将此已认证的对象设置在 SecurityContext 中。Spring Security 中的 SecurityContext 是当前执行线程的安全上下文的表示。它包含有关当前已认证用户的信息,例如其用户名、权限和会话信息。
本文提供了身份验证流程的简要描述,以及设置身份验证所需的不同组件。任何想要使用 Spring Security 的人在进行更多自定义和实现之前,必须清楚了解这些概念。