Spring Security (2) — конфигурация WebSecurityConfigurer и порядок фильтрации

Spring Boot

  Перед процессом аутентификации и авторизацией доступа необходимо понять, откуда Spring Security знает, что мы требуем аутентификации всех пользователей? Как Spring Security узнает, что мы хотим поддерживать аутентификацию на основе форм? Поэтому необходимо понимать, как работает класс конфигурации WebSecurityConfigurerAdapter. И вы также должны понимать порядок фильтра, чтобы лучше понять его рабочий процесс вызова.

1. WebSecurityConfigurerAdapter

  Перед использованием WebSecurityConfigurerAdapter сначала разберитесь с конфигурацией безопасности Spring.
  Конфигурация безопасности Spring имеет три модуля, всего три сборщика, AuthenticationManagerBuilder, связанный с аутентификацией, и WebSecurity, HttpSecurity, связанный с Интернетом.

  1. AuthenticationManagerBuilder: используется для настройки глобальной информации, связанной с аутентификацией, фактически AuthenticationProvider и UserDetailsService, первый из которых является поставщиком службы аутентификации, а второй — службой запроса сведений о пользователе;

  2. WebSecurity: глобальная конфигурация правила игнорирования запросов (например, статические файлы, такие как страница регистрации), глобальная конфигурация HttpFirewall, необходимость отладки конфигурации, глобальная конфигурация SecurityFilterChain, citationEvaluator, expressionHandler, securityInterceptor;

  3. HttpSecurity: конкретная конфигурация правила управления разрешениями. Эта конфигурация эквивалентна тегу в конфигурации xml. Связанные конфигурации различных конкретных механизмов аутентификации, OpenIDLoginConfigurer, AnonymousConfigurer, FormLoginConfigurer, HttpBasicConfigurer и т. д.

      WebSecurityConfigurerAdapter предоставляет краткий способ создания WebSecurityConfigurer.В качестве базового класса вы можете настроить класс конфигурации, реализуя этот класс, в основном переопределяя эти три метода:

     protected void configure(AuthenticationManagerBuilder auth) throws Exception {}
     public void configure(WebSecurity web) throws Exception {}
     protected void configure(HttpSecurity httpSecurity) throws Exception {}
    

       И он автоматически находит AbstractHttpConfigurer из SpringFactoriesLoader и позволяет нам расширить его.Для этого мы должны создать класс расширения AbstractHttpConfigurer и создать файл META-INF/spring.factories в пути к классам. Например:

    org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyClassThatExtendsAbstractHttpConfigurer

    Его анализ исходного кода:

    //1.init初始化:获取HttpSecurity和配置FilterSecurityInterceptor拦截器到WebSecurity
     public void init(final WebSecurity web) throws Exception {
             //获取HttpSecurity
        final HttpSecurity http = getHttp();
     	//配置FilterSecurityInterceptor拦截器到WebSecurity
         web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
         	public void run() {
     	    	FilterSecurityInterceptor securityInterceptor = http
     			    	.getSharedObject(FilterSecurityInterceptor.class);
     	    	web.securityInterceptor(securityInterceptor);
     	    }
         });
     }
     ......
     //2.获取HttpSecurity的过程
     protected final HttpSecurity getHttp() throws Exception {
     if (http != null) {
     	return http;
     }
    
     DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
     		.postProcess(new DefaultAuthenticationEventPublisher());
     localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
    
     AuthenticationManager authenticationManager = authenticationManager();
     authenticationBuilder.parentAuthenticationManager(authenticationManager);
     Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
    
     http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
     		sharedObjects);
     if (!disableDefaults) {
     	// 默认的HttpSecurity的配置
     	http
                     //添加 CSRF 支持,使用WebSecurityConfigurerAdapter时,默认启用,禁用csrf().disable()
     		.csrf().and() 
     		//添加WebAsyncManagerIntegrationFilter
     		.addFilter(new WebAsyncManagerIntegrationFilter())
     		//允许配置异常处理
     		.exceptionHandling().and()
     		//将安全标头添加到响应
     		.headers().and()
     		//允许配置会话管理
     		.sessionManagement().and()
     		//HttpServletRequest之间的SecurityContextHolder创建securityContext管理
     		.securityContext().and()
     		//允许配置请求缓存
     		.requestCache().and()
     		//允许配置匿名用户
     		.anonymous().and()
     		//HttpServletRequestd的方法和属性注册在SecurityContext中
     		.servletApi().and()
     		//使用默认登录页面
     		.apply(new DefaultLoginPageConfigurer<>()).and()
     		//提供注销支持
     		.logout();
     	// @formatter:on
     	ClassLoader classLoader = this.context.getClassLoader();
     	List<AbstractHttpConfigurer> defaultHttpConfigurers =
     			SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
    
     	for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
     		http.apply(configurer);
     	}
     }
     configure(http);
     return http;
     }
     ...
     //3.可重写方法实现自定义的HttpSecurity   
     protected void configure(HttpSecurity http) throws Exception {
     logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
    
     http
     	.authorizeRequests()
     		.anyRequest().authenticated()
     		.and()
     	.formLogin().and()
     	.httpBasic();
     }
     ....
    

       Как видно из «Получить HttpSecurity» и «Настроить FilterSecurityInterceptor на WebSecurity» в исходном модуле инициализации инициализации, как вы хотите, чтобы Spring Security знал, что мы требуем аутентификации всех пользователей? Как Spring Security узнает, что мы хотим поддерживать аутентификацию на основе форм? Просто переопределите метод protected void configure (HttpSecurity http) для исключения. Поэтому нам необходимо понять роль методов HttpSecurity и способы их настройки. В следующем разделе обсуждается HttpSecurity.

    2. HttpSecurity

  HttpSecurity Веб-безопасность позволяет настраивать определенные HTTP-запросы. Существует много методов, некоторые из которых обычно используются в следующей таблице:

метод инструкция Случаи применения
csrf() Добавлена ​​поддержка CSRF, включенная по умолчанию при использовании WebSecurityConfigurerAdapter. Отключить: csrf(). Отключить()
openidLogin() Для аутентификации на основе OpenId openidLogin().permitAll();
authorizeRequests() Включить ограничения доступа для запросов, сделанных с помощью HttpServletRequest. authorizeRequests().anyRequest().authenticated()
formLogin() Включить аутентификацию формы, если FormLoginConfigurer#loginPage(String) не указан, будет создана страница входа по умолчанию formLogin().loginPage("/authentication/login").failureUrl("/authentication/login?failed")
oauth2Login() Включить аутентификацию OAuth 2.0 или OpenID Connect 1.0 authorizeRequests()..anyRequest().authenticated()..and().oauth2Login()
rememberMe() Включите в конфигурации аутентификацию «запомнить меня». authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin().permitAll().and().rememberMe()
addFilter() Добавить пользовательский фильтр addFilter(new CustomFilter())
addFilterAt() Добавить пользовательский фильтр в то же место, что и указанный фильтр addFilterAt(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)
addFilterAfter() Добавить пользовательский фильтр после указания местоположения фильтра addFilterAfter(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)
requestMatchers() Включите конфигурацию HttpSecurity, только когда RequestMatcher соответствует requestMatchers().antMatchers("/api/**")
antMatchers() Его можно сопоставить с authorizeRequests(), RequestMatcher, например: requestMatchers().antMatchers("/api/**")
logout() Добавить поддержку выхода из системы. Это будет применяться автоматически при использовании WebSecurityConfigurerAdapter. По умолчанию доступ к URL-адресу "/logout", аннулирование сеанса HTTP для очистки пользователя, удаление всех настроенных аутентификаций #rememberMe(), удаление SecurityContextHolder и перенаправление на "/login?success" logout().deleteCookies("remove").invalidateHttpSession(false).logoutUrl("/custom-logout").logoutSuccessUrl("/logout-success");

HttpSecurity также имеет множество методов, которые мы можем использовать для настройки HttpSecurity. Так как их здесь слишком много, я не буду объяснять их по порядку, если вам интересно, вы можете их изучить.

3. Использование WebSecurityConfigurerAdapter

Пример WebSecurityConfigurerAdapter:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 @Autowired
 private MyFilterSecurityInterceptor myFilterSecurityInterceptor;
 protected void configure(HttpSecurity http) throws Exception {    
     http
     //request 设置
     .authorizeRequests()   //http.authorizeRequests() 方法中的自定义匹配
     .antMatchers("/resources/**", "/signup", "/about").permitAll() // 指定所有用户进行访问指定的url          
     .antMatchers("/admin/**").hasRole("ADMIN")  //指定具有特定权限的用户才能访问特定目录,hasRole()方法指定用户权限,且不需前缀 “ROLE_“  
     .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")//          
     .anyRequest().authenticated()  //任何请求没匹配的都需要进行验证                                           
     .and()        //login设置  自定义登录页面且允许所有用户登录
     .formLogin()      
     .loginPage("/login") //The updated configuration specifies the location of the log in page  指定自定义登录页面
     .permitAll(); // 允许所有用户访问登录页面. The formLogin().permitAll() 方法
     .and 
     .logout()  //logouts 设置                                                              
     .logoutUrl("/my/logout")  // 指定注销路径                                              
     .logoutSuccessUrl("/my/index") //指定成功注销后跳转到指定的页面                                        
     .logoutSuccessHandler(logoutSuccessHandler)  //指定成功注销后处理类 如果使用了logoutSuccessHandler()的话, logoutSuccessUrl()就会失效                                
     .invalidateHttpSession(true)  // httpSession是否有效时间,如果使用了 SecurityContextLogoutHandler,其将被覆盖                                        
     .addLogoutHandler(logoutHandler)  //在最后增加默认的注销处理类LogoutHandler                
     .deleteCookies(cookieNamesToClear);//指定注销成功后remove cookies
     //增加在FilterSecurityInterceptor前添加自定义的myFilterSecurityInterceptor
     http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);
   }

ПРИМЕЧАНИЕ. Этот пример приведен только для справки.

4. Порядок фильтрации

Порядок фильтра Spring Security:

Filter Class инструкция
ChannelProcessingFilter Фильтр управления протоколом доступа, который может перенаправить нас на другой протокол, конвертируя из http в https
SecurityContextPersistenceFilter Создайте информацию о контексте безопасности SecurityContext и очистите SecurityContextHolder в конце запроса.
ConcurrentSessionFilter Фильтр управления одновременным доступом, основная функция: получение информации о сеансе из SessionRegistry, чтобы определить, истек ли срок действия сеанса, чтобы реализовать одновременный контроль доступа.
HeaderWriterFilter Добавьте заголовок в ответ http
CsrfFilter Междоменный фильтр, защита от подделки межсайтовых запросов Фильтр
LogoutFilter Фильтр, который обрабатывает выход
X509AuthenticationFilter Добавлена ​​поддержка механизма обработки предварительной авторизации X509.
CasAuthenticationFilter Фильтры аутентификации.После этих фильтров SecurityContextHolder будет содержать полностью собранный объект Authentication, чтобы последующая аутентификация могла выполняться в обычном режиме.
UsernamePasswordAuthenticationFilter Фильтры аутентификации.После прохождения этих фильтров SecurityContextHolder будет содержать полностью собранный объект Authentication, чтобы последующая аутентификация могла выполняться в обычном режиме. Проверка подлинности с помощью формы является наиболее часто используемым методом проверки подлинности.
BasicAuthenticationFilter Фильтры аутентификации.После этих фильтров SecurityContextHolder будет содержать полностью собранный объект Authentication, чтобы последующая аутентификация могла выполняться в обычном режиме.
SecurityContextHolderAwareRequestFilter Этот фильтр оборачивает ServletRequest один раз, делая запрос более богатым API.
JaasApiIntegrationFilter (JAAS) фильтр метода аутентификации
RememberMeAuthenticationFilter Фильтр обработки аутентификации памяти, то есть, если предыдущий фильтр аутентификации не обрабатывает текущий запрос и включена функция RememberMe, пользователь будет проанализирован из файла cookie, и будет выполнен процесс аутентификации, а затем объект аутентификации будет храниться в SecurityContextHolder.
AnonymousAuthenticationFilter Фильтр обработки анонимной аутентификации, когда информация аутентификации в SecurityContextHolder пуста, анонимный пользователь будет создан и сохранен в SecurityContextHolder.
SessionManagementFilter Фильтр управления сеансом, в котором сохраняется информация для входа пользователя, которую можно сохранить в сеансе, в файлах cookie или redis.
ExceptionTranslationFilter Фильтр обработки исключений в основном перехватывает исключение, созданное в последующей операции фильтра (FilterSecurityInterceptor).
FilterSecurityInterceptor Класс фильтра перехвата безопасности получает ConfigAttribute, соответствующий текущему URL-адресу запроса, и вызывает accessDecisionManager для принятия решений об авторизации доступа.

Цепочка фильтров весенней безопасности по умолчанию:

 SecurityContextPersistenceFilter
->HeaderWriterFilter
->LogoutFilter
->UsernamePasswordAuthenticationFilter
->RequestCacheAwareFilter
->SecurityContextHolderAwareRequestFilter
->SessionManagementFilter
->ExceptionTranslationFilter
->FilterSecurityInterceptor

В предыдущем разделе мы проанализировали исходный код и функции основного фильтра. Вы можете вернуться к анализу исходного кода в предыдущем разделе для более глубокого понимания того, как работает каждый фильтр.

Суммировать:

Перед процессом аутентификации и авторизации доступа WebSecurityConfigurer должен сначала соответствовать конфигуратору безопасности своего собственного приложения, а последовательность цепочки фильтров должна быть четкой, чтобы лучше понять принцип работы весенней безопасности и местонахождение проблемы в проект. После понимания подготовительной работы следующим шагом будет изучение рабочего процесса модулей аутентификации и авторизации доступа и анализ примера проекта. Наконец, если есть какие-либо ошибки, пожалуйста, прокомментируйте.

Вы в порядке, офицеры? Если вам это нравится, проведите пальцем, чтобы нажать 💗, нажмите, чтобы подписаться! ! Спасибо за Вашу поддержку!

Добро пожаловать в публичный аккаунт【Технический блог Ccww], впервые была запущена оригинальная техническая статья