认证鉴权框架SpringSecurity-2--重点组件和过滤器链篇

上面一篇我们介绍了安全相关的概念以及springSecurity基础原理。了解了什么是认证,授权和鉴权,以及springSecurity是怎么将自己的过滤器链加入到web过滤器链中的。这一篇我们重点介绍springSecurity的重点组件及概念,以及过滤器链的常见过滤器及功能。

1、重点组件

springSecurity主要是帮助我们完成项目中认证和鉴权的过程,首先了解一些常用的概念方便我们后续学习理解。

(1)、SecurityContextHolder

SecurityContextHolder 是一个静态类,用于存储当前线程的 SecurityContext。它使用线程局部变量(ThreadLocal)来实现这一点,这意味着每个线程都有自己独立的 SecurityContext 实例。这种设计使得 SecurityContext 在多线程环境下也能保持线程安全。在Web场景下的使用Spring Security,在用户登录时自动绑定认证信息到当前线程,在用户退出时,自动清除当前线程的认证信息。

常用方法:

java 复制代码
1、获取当前线程的 SecurityContext 实例
SecurityContext context = SecurityContextHolder.getContext();

2、设置当前线程的 SecurityContext 实例。
SecurityContext context = new SecurityContextImpl(); 
SecurityContextHolder.setContext(context);

3、清除当前线程的 SecurityContext 实例
SecurityContextHolder.clearContext();

4、设置存储策略名称。默认策略是 MODE_THREADLOCAL,表示使用线程局部变量。
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);

(2)、SecurityContext

SecurityContext是一个接口,用于存储当前用户的认证信息。即保存着当前用户是谁,是否已经通过认证,拥有哪些权限等等。最常用的实现类是 SecurityContextImpl。

常用方法:

java 复制代码
获取当前用户的认证信息
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
设置当前用户认证信息
SecurityContextHolder.getContext().setAuthentication(authentication);

(3)、Authentication

Authentication 是一个接口,表示用户的认证信息。它包含了一些基本的信息,如用户名、密码、权限等。

常用方法:

java 复制代码
返回用户的主体信息,通常是 UserDetails 对象
Object principal = authentication.getPrincipal();

返回用户的凭证信息,通常是密码。
Object credentials = authentication.getCredentials();

返回用户的权限列表
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

返回用户的用户名
String name = authentication.getName();

返回用户是否已经认证
boolean isAuthenticated = authentication.isAuthenticated();

设置用户是否已经认证。通常情况下,不应该手动调用此方法。
authentication.setAuthenticated(true);

(4)、UsernamePasswordAuthenticationToken

UsernamePasswordAuthenticationToken 是 Authentication 接口的一个实现类,用于表示基于用户名和密码的认证信息。它通常在用户登录时使用。

常用方法:

java 复制代码
构造方法,未认证时使用
UsernamePasswordAuthenticationToken unauthenticatedToken = new UsernamePasswordAuthenticationToken(username, password);

构造方法,已认证状态时使用
Collection<GrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")); 
UsernamePasswordAuthenticationToken authenticatedToken = new UsernamePasswordAuthenticationToken(username, password, authorities);

回用户的主体信息,通常是用户名
Object principal = token.getPrincipal();

返回用户的凭证信息,通常是密码。
Object credentials = token.getCredentials();

返回用户的权限列表
Collection<? extends GrantedAuthority> authorities = token.getAuthorities();

返回用户的用户名
String name = token.getName();

返回用户是否已经认证
boolean isAuthenticated = token.isAuthenticated();

以上为最基础的概念

(5)、UsernamePasswordAuthenticationFilter

账号名密码认证过滤器,认证用户登录信息的主入口。

主要完成:

1、拦截 /login 请求,从请求中提取用户名和密码信息。

2、封装用户名和密码信息生成 UsernamePasswordAuthenticationToken。

3、将UsernamePasswordAuthenticationToken 提交给 AuthenticationManager 进行认证。

注意,具体认证方式是由 AuthenticationManager 接口负责的。

(6)、AuthenticationManager

认证管理器,是认证相关的核心接口,也是发起认证的出发点。实际业务中可以根据不同的认证信息进行不同类型的认证,具体的实现是由ProviderManager完成的。

(7)、ProviderManager

认证提供者管理器,是 AuthenticationManager 的一个实现类,用于管理多个 AuthenticationProvider(认证提供者),它会依次调用每个 AuthenticationProvider,只要任何一个认证提供者提供的认证方式通过,认证就结束。

主要功能:

1、管理多个 AuthenticationProvider。

2、依次调用每个 AuthenticationProvider 进行认证。

3、返回第一个成功认证的结果。

(8)、AuthenticationProvider

认证提供者,这是一个接口,具体如何认证,就看如何实现该接口。Spring Security 提供了 DaoAuthenticationProvider 实现该接口,这个类就是使用数据库中数据进行认证。

主要功能:

1、接收 Authentication 对象。

2、执行具体的认证逻辑。

3、返回认证结果。

(9)、DaoAuthenticationProvider

DaoAuthenticationProvider 是 AuthenticationProvider 的一个实现类,用于基于数据库或其他数据源进行认证。它依赖于 UserDetailsService 来加载用户详细信息。

源码中通过retrieveUser方法调用调用了 this.getUserDetailsService().loadUserByUsername(username);实现

主要功能:

1、使用 UserDetailsService 加载用户详细信息。

2、比较用户提供的密码和数据库中的密码。

3、返回认证结果。

配置示例:

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(daoAuthenticationProvider());
    }

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(passwordEncoder());
        return provider;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

(10)、UserDetailsService

UserDetailsService 是一个接口,用于加载用户详细信息。它通常用于从数据库或其他数据源中获取用户信息。

主要功能:

1、根据用户名加载用户详细信息。

2、返回 UserDetails 对象。

代码示例:

java 复制代码
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 从数据库或其他数据源中加载用户信息
        if ("user".equals(username)) {
            return User.withUsername("user")
                       .password("{noop}password")
                       .roles("USER")
                       .build();
        } else if ("admin".equals(username)) {
            return User.withUsername("admin")
                       .password("{noop}admin")
                       .roles("ADMIN")
                       .build();
        } else {
            throw new UsernameNotFoundException("User not found");
        }
    }
}

(11)、UserDetails

是 Spring Security 中的一个接口,用于表示用户的详细信息。这个接口包含了用户的基本信息,如用户名、密码、权限等。通过实现 UserDetails 接口,你可以自定义用户对象,以便在认证过程中使用。

主要方法:

java 复制代码
返回用户拥有的权限列表
Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();

返回用户的密码
String password = userDetails.getPassword();

返回用户的用户名
String username = userDetails.getUsername();

返回账户是否未过期
boolean accountNonExpired = userDetails.isAccountNonExpired();

返回账户是否未锁定
boolean accountNonLocked = userDetails.isAccountNonLocked();

返回凭证(密码)是否未过期
boolean credentialsNonExpired = userDetails.isCredentialsNonExpired();

返回账户是否启用
boolean enabled = userDetails.isEnabled();

(12)、BasicAuthenticationFilter

是一个过滤器,拦截非登录接口,校验token是否合法。合法则放行并根据token解析出用户写到全局变量中。

主要功能:

1、拦截带有 Authorization 头(即token请求头)的请求。

2、从请求头中提取用户名和密码。

3、将用户名和密码封装成 UsernamePasswordAuthenticationToken。

4、将 UsernamePasswordAuthenticationToken 提交给 AuthenticationManager 进行认证。

5、如果认证成功,将认证信息存入 SecurityContext。

配置示例:

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .anyRequest().authenticated()
            .and()
            .httpBasic() // 启用 HTTP Basic 认证
            .and()
            .addFilterAfter(new CustomBasicAuthenticationFilter(), BasicAuthenticationFilter.class);
    }

    @Bean
    public CustomBasicAuthenticationFilter customBasicAuthenticationFilter() {
        return new CustomBasicAuthenticationFilter(authenticationManager());
    }
}

以上为认证主要流程概念

上面的5-12这几个概念是完成认证流程的主要概念,具体流程如下:

1、UsernamePasswordAuthenticationFilter:处理表单登录请求,提取用户名和密码,创建 UsernamePasswordAuthenticationToken。

2、AuthenticationManager:管理认证过程,委托给 ProviderManager。

3、ProviderManager:管理多个 AuthenticationProvider,依次调用进行认证。

4、AuthenticationProvider:是一个接口,由实现类完成具体的认证逻辑。

5、DaoAuthenticationProvider:是AuthenticationProvider的一个实现类,基于数据库或其他数据源进行认证,依赖于 UserDetailsService。

6、UserDetailsService:加载用户详细信息,返回UserDetails对象。

7、认证成功,生成token返回给客户端

8、客户端携带token再次访问非登录接口,会被BasicAuthenticationFilter拦截验证token

9、验证成功,将认证信息保存到SecurityContext中,继续向下执行。

(13)、AuthenticationEntryPoint

是一个接口,用于处理未认证用户的请求。当用户尝试访问需要认证的资源但未提供有效的认证信息时,Spring Security 会调用 AuthenticationEntryPoint。

主要功能:

1、处理未认证用户的请求。

2、通常用于返回 401 Unauthorized 响应。

实现类代码示例:

java 复制代码
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized: " + authException.getMessage());
    }
}

配置类示例:

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .anyRequest().authenticated()
            .and()
            .exceptionHandling()
                .authenticationEntryPoint(customAuthenticationEntryPoint);
    }
}

(14)、AccessDeniedHandler

是一个接口,用于处理已认证但没有足够权限访问某个资源的请求。当用户尝试访问需要特定角色或权限的资源但未满足条件时,Spring Security 会调用 AccessDeniedHandler。

主要功能:

1、处理已认证但没有足够权限的请求。

2、通常用于返回 403 Forbidden 响应。

实现类代码示例:

java 复制代码
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden: " + accessDeniedException.getMessage());
    }
}

配置类示例:

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomAccessDeniedHandler customAccessDeniedHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .anyRequest().authenticated()
            .and()
            .exceptionHandling()
                .accessDeniedHandler(customAccessDeniedHandler);
    }
}

(15)、AuthenticationSuccessHandler

是一个接口,用于处理认证成功的请求。当用户成功认证后,Spring Security 会调用 AuthenticationSuccessHandler。

主要功能:

1、处理认证成功的请求。

2、通常用于重定向到成功页面或返回成功响应。

实现类代码示例:

java 复制代码
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.sendRedirect("/success");
    }
}

配置类示例:

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .successHandler(customAuthenticationSuccessHandler)
                .permitAll()
            .and()
            .logout()
                .permitAll();
    }
}

(16)、AuthenticationFailureHandler

是一个接口,用于处理认证失败的请求。当用户认证失败时,Spring Security 会调用 AuthenticationFailureHandler。

主要功能:

1、处理认证失败的请求。

2、通常用于重定向到失败页面或返回失败响应。

实现类代码示例:

java 复制代码
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.sendRedirect("/login?error=true");
    }
}

配置类示例:

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .failureHandler(customAuthenticationFailureHandler)
                .permitAll()
            .and()
            .logout()
                .permitAll();
    }
}

以上为认证或授权过程中的一些自定义行为处理器

这4个接口中,每一个都是当认证或者授权过程中发生结果后触发,可以是失败的场景也可以是成功后触发。1个成功后执行,3个为失败后促发执行

(17)、FilterSecurityInterceptor

FilterSecurityInterceptor是Spring Security 中负责执行授权检查的核心组件之一。它在请求到达目标资源之前,检查用户是否具有访问该资源的权限。通常情况下,Spring Security 会自动配置 FilterSecurityInterceptor,但我们可以自定义其行为。

springSecurity核心配置类示例:

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.access.vote.UnanimousBased;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomFilterInvocationSecurityMetadataSource securityMetadataSource;

    @Autowired
    private CustomAccessDecisionManager accessDecisionManager;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
            .and()
            .logout()
                .permitAll()
            .and()
            .addFilterBefore(securityInterceptor(), FilterSecurityInterceptor.class);
    }

    @Bean
    public FilterSecurityInterceptor securityInterceptor() {
        FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
        filterSecurityInterceptor.setSecurityMetadataSource(securityMetadataSource);
        filterSecurityInterceptor.setAccessDecisionManager(accessDecisionManager);
        return filterSecurityInterceptor;
    }
}

(18)、FilterInvocationSecurityMetadataSource

FilterInvocationSecurityMetadataSource 负责为每个请求提供所需的权限信息。我们需要实现这个接口,以便根据请求的 URL 和方法返回相应的权限配置。

代码示例:

java 复制代码
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import java.util.*;

public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    private Map<String, Collection<ConfigAttribute>> resourceMap = new HashMap<>();

    public CustomFilterInvocationSecurityMetadataSource() {
        loadResourceDefine();
    }

    private void loadResourceDefine() {
        // 定义资源与权限的映射关系(根据实际业务查询)
        resourceMap.put("/admin/**", Collections.singletonList(new SecurityConfig("ROLE_ADMIN")));
        resourceMap.put("/user/**", Collections.singletonList(new SecurityConfig("ROLE_USER")));
    }

    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        FilterInvocation fi = (FilterInvocation) object;
        String url = fi.getRequestUrl();
        for (Map.Entry<String, Collection<ConfigAttribute>> entry : resourceMap.entrySet()) {
            if (url.matches(entry.getKey())) {
                return entry.getValue();
            }
        }
        return null; // 默认允许访问
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        Set<ConfigAttribute> allAttributes = new HashSet<>();
        for (Collection<ConfigAttribute> attributes : resourceMap.values()) {
            allAttributes.addAll(attributes);
        }
        return allAttributes;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

(19)、AccessDecisionManager

AccessDecisionManager 负责根据用户的角色和权限,决定用户是否有权访问请求的资源。我们需要实现这个接口,并提供具体的决策逻辑。

代码示例:

java 复制代码
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
import java.util.Iterator;

public class CustomAccessDecisionManager implements AccessDecisionManager {

    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        if (configAttributes == null || configAttributes.isEmpty()) {
            return;
        }
        for (ConfigAttribute attribute : configAttributes) {
            String role = attribute.getAttribute();
            for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
                if (role.equals(grantedAuthority.getAuthority())) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("Access denied - user does not have the required role");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

以上为授权验证过程中的相关概念

当用户请求一个受保护的资源时,FilterSecurityInterceptor 会按以下步骤进行授权校验:

1、获取请求的权限信息:

FilterSecurityInterceptor 调用 FilterInvocationSecurityMetadataSource 的 getAttributes 方法,获取请求所需的所有权限。实际上是从核心配置类HttpSecurity中读取的权限。如下图:

2、获取用户的权限信息:

FilterSecurityInterceptor 从 SecurityContext 中获取当前用户的 Authentication 对象,进而获取用户的权限信息。

3、决策是否授权:

FilterSecurityInterceptor 调用 AccessDecisionManager 的 decide 方法,传入用户的权限信息和请求所需的权限信息。

4、AccessDecisionManager 根据具体的决策逻辑,判断用户是否有权访问请求的资源。

如果用户有权限,请求继续处理;如果没有权限,抛出 AccessDeniedException。

2、过滤器链

过滤器是一种典型的AOP思想,Spring Security 使用一个过滤器链来处理请求,每个过滤器负责不同的安全任务。

(1)、SecurityContextPersistenceFilter

实现类:

org.springframework.security.web.context.SecurityContextPersistenceFilter,这是一个重点的过滤器。
工作过程:

1、初始化和清理 SecurityContext。

2、在请求开始时,从会话中恢复 SecurityContext。

3、在请求结束时,清理 SecurityContext,确保线程安全。
实现:

SecurityContextPersistenceFilter主要是使用SecurityContextRepository在session中保存或更新一个SecurityContext,并将SecurityContext给以后的过滤器使用,来为后续filter建立所需的上下文。 SecurityContext中存储了当前用户的认证以及权限信息。

(2)、WebAsyncManagerIntegrationFilter

实现类:

org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
作用:

确保在异步Web请求过程中,SecurityContext能正确传递。
功能:

集成springSecurity与springWeb的异步请求支持。

(3)、HeaderWriterFilter

实现类:

org.springframework.security.web.header.HeaderWriterFilter
作用:

添加安全相关的 HTTP 头部。
功能:

向请求的Header中添加相应的信息,例如,设置 X-Content-Type-Options、X-Frame-Options、X-XSS-Protection 头部来防止各种常见的攻击。

(4)、CsrfFilter

实现类:

org.springframework.security.web.csrf.CsrfFilter

csrf又称跨域请求伪造,SpringSecurity会对所有post请求验证是否包含系统生成的csrf的token信息。一般都会禁用掉。
作用:

防御跨站请求伪造(CSRF)攻击。
功能:

在每个请求中验证 CSRF 令牌,确保请求是由合法用户发出的。

(5)、LogoutFilter

实现类:

org.springframework.security.web.authentication.logout.LogoutFilter

匹配URL为/logout的请求,实现用户退出,清除认证信息。
作用:

处理用户的注销请求。
功能:

执行注销逻辑,清除用户的认证信息和会话。

(6)、UsernamePasswordAuthenticationFilter

实现类:

org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

认证操作全靠这个过滤器,默认匹配URL为/login且必须为POST请求。
作用:

处理基于表单的登录认证请求。
功能:

验证用户提交的用户名和密码,如果成功,生成认证信息token返回给客户端。

(7)、DefaultLoginPageGeneratingFilter

实现类:

org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter

如果没有在配置文件中指定认证页面,则由该过滤器生成一个默认认证页面。如果指定了登录页,该过滤器将不在生效。
作用:

生成默认的登录页面。
功能:

在没有自定义登录页面时,提供一个默认的登录页面。

(8)、DefaultLogoutPageGeneratingFilter

实现类:

org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter

由此过滤器可以生产一个默认的退出登录页面
作用:

生成默认的注销页面。
功能:

在没有自定义注销页面时,提供一个默认的注销页面。

(9)、BasicAuthenticationFilter

实现类:

org.springframework.security.web.authentication.www.BasicAuthenticationFilter

此过滤器会拦截所有的非登录接口,自动解析HTTP请求中头部Authentication(token),校验token是否合法。合法则放行并根据token解析出用户写到全局变量中。
作用:

处理 HTTP Basic 认证。
功能:

从 HTTP 请求头中提取用户名和密码,并进行认证。

(10)、RequestCacheAwareFilter

实现类:

org.springframework.security.web.savedrequest.RequestCacheAwareFilter

通过HttpSessionRequestCache内部维护了一个RequestCache,用于缓存HttpServletRequest
作用:

处理请求缓存。
功能:

在用户登录后,自动重定向到登录前尝试访问的受保护资源。

(11)、SecurityContextHolderAwareRequestFilter

实现类:

org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter

针对ServletRequest进行了一次包装,使得request具有更加丰富的API。
作用:

为 HttpServletRequest 提供一些辅助方法。
功能:

使得请求对象在安全上下文中更便于使用,例如提供获取当前用户的方法。

(12)、AnonymousAuthenticationFilter

实现类:

org.springframework.security.web.authentication.AnonymousAuthenticationFilter

当SecurityContextHolder中认证信息为空,则会创建一个匿名用户存入到SecurityContextHolder中。spring security为了兼容未登录的访问,也走了一套认证流程,只不过是一个匿名的身份。
作用:

为未认证的用户提供匿名身份。
功能:

如果用户没有登录,赋予其匿名身份,以便应用程序能够处理匿名用户的请求。

(13)、SessionManagementFilter

实现类:

org.springframework.security.web.session.SessionManagementFilter

SessionManagementFilter用于管理会话安全的重要组件。它通过防止会话固定攻击、管理会话的最大生命周期和限制并发会话数,确保应用程序的安全性。
作用:

处理会话管理相关的功能。
功能:

例如防止会话固定攻击,确保会话在用户认证之后安全。

(14)、ExceptionTranslationFilter

实现类:

org.springframework.security.web.access.ExceptionTranslationFilter

异常转换过滤器位于整个springSecurityFilterChain的后方,用来转换整个链路中出现的异常。
作用:

将认证异常转换为适当的 HTTP 响应。
功能:

处理认证异常(如 AccessDeniedException 和 AuthenticationException),并将其转换为适当的 HTTP 状态码和错误信息。

(15)、FilterSecurityInterceptor

实现类:

org.springframework.security.web.access.intercept.FilterSecurityInterceptor

获取所配置资源访问的授权信息,根据SecurityContextHolder中存储的用户信息来决定其是否有权限。
作用:

执行最终的访问控制决策。
功能:

根据配置的访问决策管理器和投票器,决定请求是否被允许访问目标资源。

学海无涯苦作舟!!!

相关推荐
xlsw_2 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
独行soc2 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍06-基于子查询的SQL注入(Subquery-Based SQL Injection)
数据库·sql·安全·web安全·漏洞挖掘·hw
神仙别闹3 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭3 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫4 小时前
泛型(2)
java
超爱吃士力架4 小时前
邀请逻辑
java·linux·后端
南宫生4 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石4 小时前
12/21java基础
java
李小白664 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp4 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea