Spring Анализ безопасности (1) — Процесс авторизации

Spring Boot

Spring Анализ безопасности (1) — Процесс авторизации

При изучении Spring Cloud, когда я столкнулся с контентом, связанным со службой авторизации oauth, я всегда мало знал об этом, поэтому я решил сначала изучить и разобраться с контентом, связанным с авторизацией и аутентификацией, принципами и дизайном Spring Security и Spring Security Oauth2. . Данная серия статей написана для закрепления впечатления и понимания в процессе обучения.Если есть какие-то нарушения, просьба сообщить.

Окружение проекта:

  • JDK1.8
  • Spring boot 2.x
  • Spring Security 5.x

1. Простая демонстрация безопасности

1. Пользовательская реализация UserDetailsService

   Настройте класс MyUserDetailsUserService, реализуйте метод loadUserByUsername() интерфейса UserDetailsService и просто возвращайте объект User, предоставленный Spring Security. Чтобы облегчить демонстрацию управления разрешениями Spring Security позже, здесь используетсяAuthorityUtils.commaSeparatedStringToAuthorityList("admin")Для учетной записи пользователя настроена информация о правах администратора. В реальных проектах пользователей, их роли и разрешения можно получить, обратившись к базе данных здесь.

@Component
public class MyUserDetailsUserService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 不能直接使用 创建 BCryptPasswordEncoder 对象来加密, 这种加密方式 没有 {bcrypt}  前缀,
        // 会导致在  matches 时导致获取不到加密的算法出现
        // java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"  问题
        // 问题原因是 Spring Security5 使用 DelegatingPasswordEncoder(委托)  替代 NoOpPasswordEncoder,
        // 并且 默认使用  BCryptPasswordEncoder 加密(注意 DelegatingPasswordEncoder 委托加密方法BCryptPasswordEncoder  加密前  添加了加密类型的前缀)  https://blog.csdn.net/alinyua/article/details/80219500
        return new User("user",  PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }
}

Обратите внимание, что Spring Security 5 не используется в началеNoOpPasswordEncoderв качестве кодировщика паролей по умолчанию, но по умолчанию используетDelegatingPasswordEncoderВ качестве своего кодировщика шифра его метод кодирования использует имя кодировщика шифра в качестве префикса + поручает различным кодировщикам шифра реализовать кодирование.

public String encode(CharSequence rawPassword) {
        return "{" + this.idForEncode + "}" + this.passwordEncoderForEncode.encode(rawPassword);
    }

   Здесь idForEncode — это сокращенное имя кодировщика паролей, доступ к которому можно получить с помощьюPasswordEncoderFactories.createDelegatingPasswordEncoder()Внутренняя реализация видит, что по умолчанию используется префикс bcrypt, то есть BCryptPasswordEncoder.

public class PasswordEncoderFactories {
    public static PasswordEncoder createDelegatingPasswordEncoder() {
        String encodingId = "bcrypt";
        Map<String, PasswordEncoder> encoders = new HashMap();
        encoders.put(encodingId, new BCryptPasswordEncoder());
        encoders.put("ldap", new LdapShaPasswordEncoder());
        encoders.put("MD4", new Md4PasswordEncoder());
        encoders.put("MD5", new MessageDigestPasswordEncoder("MD5"));
        encoders.put("noop", NoOpPasswordEncoder.getInstance());
        encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
        encoders.put("scrypt", new SCryptPasswordEncoder());
        encoders.put("SHA-1", new MessageDigestPasswordEncoder("SHA-1"));
        encoders.put("SHA-256", new MessageDigestPasswordEncoder("SHA-256"));
        encoders.put("sha256", new StandardPasswordEncoder());
        return new DelegatingPasswordEncoder(encodingId, encoders);
    }
}

2. Настройте конфигурацию безопасности Spring

  Определить класс конфигурации SpringSecurityConfig и наследоватьWebSecurityConfigurerAdapterПереопределите его метод configure(HttpSecurity http).

@Configuration
@EnableWebSecurity //1
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()  //2
            .and()
                .authorizeRequests() //3
                .antMatchers("/index","/").permitAll() //4
                .anyRequest().authenticated(); //6
    }
}

Разбор конфигурации:

  • @EnableWebSecurity Проверьте исходный код аннотации, в основном обратитесь к WebSecurityConfiguration.class и добавьте аннотацию @EnableGlobalAuthentication, которая здесь не будет представлена, нам нужно только понять, что добавление аннотации @EnableWebSecurity активирует функцию безопасности.
  • formLogin() использует форму входа в систему (адрес запроса по умолчанию — /login).В Spring Security 5 значение по умолчанию httpBasic() старой версии было заменено на formLogin() , чтобы указать, что форма входа в систему все еще настроена один раз.
  • authorizeRequests() начинает запрашивать конфигурацию разрешений
  • antMatchers() использует сопоставление путей в стиле Ant, где конфигурация соответствует / и /index.
  • пользователи allowAll() могут свободно получать доступ
  • anyRequest() соответствует всем путям
  • authentication() доступна после входа пользователя в систему.

3. Настроить html и протестировать интерфейс

   Создайте новый index.html в каталоге resources/static и определите внутри него кнопку для доступа к тестовому интерфейсу.

<!DOCTYPE html>
<html lang="en" >
<head>
    <meta charset="UTF-8">
    <title>欢迎</title>
</head>
<body>
        Spring Security 欢迎你!
        <p> <a href="/get_user/test">测试验证Security 权限控制</a></p>
</body>
</html>

   Создать интерфейс в стиле отдыха для получения информации о пользователе

@RestController
public class TestController {

    @GetMapping("/get_user/{username}")
    public String getUser(@PathVariable  String username){
        return username;
    }
}

4. Запустите тест проекта

1. Доступ к localhost:8080 осуществляется напрямую без какой-либо блокировки

image

2. Нажмите кнопку Test Verification Permission Control, чтобы перейти на страницу входа в систему безопасности по умолчанию.

3, с помощью MyUserDetailsUserService определен пользователь учетной записи по умолчанию: 123456 Перейти после успешного входа в систему / интерфейс get_user


Во-вторых, анализ конфигурации @EnableWebSecurity.

   Помните, что @EnableWebSecurity относится к классу конфигурации WebSecurityConfiguration и аннотации @EnableGlobalAuthentication? Среди них WebSecurityConfiguration — это конфигурация, связанная с авторизацией, а @EnableGlobalAuthentication настраивает конфигурацию, связанную с аутентификацией, в следующем разделе.

   Сначала мы смотрим исходный код WebSecurityConfiguration, мы можем четко найтиspringSecurityFilterChain()метод.

    @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		boolean hasConfigurers = webSecurityConfigurers != null
				&& !webSecurityConfigurers.isEmpty();
		if (!hasConfigurers) {
			WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
					.postProcess(new WebSecurityConfigurerAdapter() {
					});
			webSecurity.apply(adapter);
		}
		return webSecurity.build(); //1
	}

Этот метод сначала определит, является ли webSecurityConfigurers пустым, и загрузит объект WebSecurityConfigurerAdapter по умолчанию, если он пуст.Поскольку сам пользовательский SpringSecurityConfig наследует объект WebSecurityConfigurerAdapter, наша пользовательская конфигурация безопасности обязательно будет загружена (если вы хотите знать, как ее загрузить и см. метод WebSecurityConfiguration.setFilterChainProxySecurityConfigurer()).

Мы смотрим на метод websecurity.build(), реализующий фактический вызов метода AbstractConfigureDSecurityBuilder.Dobuild(), который реализован внутри следующего:

@Override
	protected final O doBuild() throws Exception {
		synchronized (configurers) {
			buildState = BuildState.INITIALIZING;

			beforeInit();
			init();

			buildState = BuildState.CONFIGURING;

			beforeConfigure();
			configure();

			buildState = BuildState.BUILDING;

			O result = performBuild(); // 1 创建 DefaultSecurityFilterChain (Security Filter 责任链 ) 

			buildState = BuildState.BUILT;

			return result;
		}
	}

   Мы ориентируемся наperformBuild()метод, см. его реализацию подкласса метода HttpSecurity.performBuild(), его внутренние фильтры сортировки и создаетDefaultSecurityFilterChainобъект.

    @Override
	protected DefaultSecurityFilterChain performBuild() throws Exception {
		Collections.sort(filters, comparator);
		return new DefaultSecurityFilterChain(requestMatcher, filters);
	}

   Глядя на конструктор DefaultSecurityFilterChain, мы видим, что есть журнал.

public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {
		logger.info("Creating filter chain: " + requestMatcher + ", " + filters); // 按照正常情况,我们可以看到控制台输出 这条日志 
		this.requestMatcher = requestMatcher;
		this.filters = new ArrayList<>(filters);
	}

   Мы можем просмотреть журнал запуска проекта. Вы можете видеть, что следующий рисунок ясно печатает этот журнал и печатает все имена фильтров. ==(Обратите внимание на напечатанную здесь цепочку фильтров, все наши процессы авторизации основаны на расширении этой цепочки фильтров)==

   Тогда возникает другой вопрос: как загружаются фильтры в методе HttpSecurity.performBuild()? В это время вам нужно проверить метод WebSecurityConfigurerAdapter.init().Этот метод внутренне вызывает метод getHttp() для возврата объекта HttpSecurity (видя здесь, мы должны думать, что фильтры — это данные, добавляемые в этот метод), и как в нагрузку он вводиться не будет.

public void init(final WebSecurity web) throws Exception {
		final HttpSecurity http = getHttp(); // 1 
		web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
			public void run() {
				FilterSecurityInterceptor securityInterceptor = http
						.getSharedObject(FilterSecurityInterceptor.class);
				web.securityInterceptor(securityInterceptor);
			}
		});
	}

   Анализ @EnableWebSecurity занял так много времени,На самом деле наиболее важным моментом является создание DefaultSecurityFilterChain.То есть мы часто используем цепочку ответственности фильтра безопасности.Далее мы анализируем процесс авторизации вокруг фильтров в этой DefaultSecurityFilterChain.

3. Анализ процесса авторизации

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

   Здесь мы сосредоточимся только на следующих важных фильтрах:

  • SecurityContextPersistenceFilter
  • UsernamePasswordAuthenticationFilter (AbstractAuthenticationProcessingFilter)
  • BasicAuthenticationFilter
  • AnonymousAuthenticationFilter
  • ExceptionTranslationFilter
  • FilterSecurityInterceptor

1. SecurityContextPersistenceFilter

  SecurityContextPersistenceFilter Этот фильтр в основном отвечает за следующие вещи:

  • Получите объект SecurityContext (контекст безопасности, аналогичный ApplicaitonContext) из запрашивающего сеанса с помощью метода (SecurityContextRepository)repo.loadContext().Если запрашивающий сеанс не создает аутентификацию (ключевой объект аутентификации по умолчанию, так как только этот раздел говорит об авторизации, она пока не будет представлена. ) объект SecurityContext со свойством null
  • SecurityContextHolder.setContext() помещает объект SecurityContext в SecurityContextHolder для управления (SecurityContextHolder по умолчанию использует стратегию ThreadLocal для хранения информации аутентификации)
  • Поскольку это реализовано в finally, объект SecurityContext будет очищен от SecurityContextHolder через SecurityContextHolder.clearContext() в конце
  • Поскольку это реализовано в finally, объект SecurityContext будет помещен в сеанс через repo.saveContext() в конце
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,
				response);
		//从Session中获取SecurityContxt 对象,如果Session中没有则创建一个 authtication 属性为 null 的SecurityContext对象
		SecurityContext contextBeforeChainExecution = repo.loadContext(holder); 

		try {
		    // 将 SecurityContext 对象放入 SecurityContextHolder进行管理 (SecurityContextHolder默认使用ThreadLocal 策略来存储认证信息)
			 SecurityContextHolder.setContext(contextBeforeChainExecution);

			 chain.doFilter(holder.getRequest(), holder.getResponse());

		}
		finally {
			SecurityContext contextAfterChainExecution = SecurityContextHolder
					.getContext();
			
			// 将 SecurityContext 对象 从 SecurityContextHolder中清除
			SecurityContextHolder.clearContext();
			// 将 SecurityContext 对象 放入Session中
			repo.saveContext(contextAfterChainExecution, holder.getRequest(),
					holder.getResponse());
			request.removeAttribute(FILTER_APPLIED);

			if (debug) {
				logger.debug("SecurityContextHolder now cleared, as request processing completed");
			}
		}

   Мы устанавливаем точку останова в SecurityContextPersistenceFilter, запускаем проект, посещаем localhost:8080 и выполняем отладку, чтобы увидеть реализацию:

   Мы ясно видим, что создается объект SecurityContext с нулевой аутентификацией, и мы можем видеть конкретную цепочку фильтров, вызываемую запросом. Далее, давайте взглянем на внутреннюю обработку finally

   Вы обнаружите, что аутентификация в SecurityContxt здесь представляет собой информацию аутентификации с именем анонимный пользователь (анонимный пользователь), поскольку запрос вызывает AnonymousAuthenticationFilter , Security по умолчанию создает доступ анонимного пользователя.

2. UsernamePasswordAuthenticationFilter (AbstractAuthenticationProcessingFilter)

   Глядя на буквальное значение фильтра, вы можете видеть, что это фильтр, который авторизуется, получая пароль учетной записи в запросе.Согласно соглашению, обязанности этого фильтра разобраны:

  • Используйте requireAuthentication(), чтобы определить, запрашивать ли /login по POST
  • Вызовите метод tryAuthentication() для аутентификации, создайте объект UsernamePasswordAuthenticationToken с аутентифицированным атрибутом false (т. е. неавторизованным) внутри и передайте его методу AuthenticationManager().authenticate() для аутентификации, а затем верните аутентифицированный = истинный аутентификация прошла успешно (то есть авторизация прошла успешно). ) Объект UsernamePasswordAuthenticationToken
  • Поместите аутентификацию в сеанс через sessionStrategy.onAuthentication()
  • Вызовите интерфейс onAuthenticationSuccess AuthenticationSuccessHandler через successAuthentication() для успешной обработки (вы можете написать логику успешной обработки, унаследовав AuthenticationSuccessHandler) successAuthentication(request, response, chain, authResult);
  • Вызовите интерфейс onAuthenticationFailure AuthenticationFailureHandler с помощью failedAuthentication() для обработки сбоя (вы можете написать собственную логику обработки сбоев, унаследовав AuthenticationFailureHandler)

   Посмотрим на логику обработки официального исходного кода:

// 1 AbstractAuthenticationProcessingFilter 的 doFilter 方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {

		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;

        // 2 判断请求地址是否是  /login 和 请求方式为 POST  (UsernamePasswordAuthenticationFilter 构造方法 确定的)
		if (!requiresAuthentication(request, response)) {
			chain.doFilter(request, response);
			return;
		}
		Authentication authResult;
		try {
		    
		    // 3 调用 子类  UsernamePasswordAuthenticationFilter 的 attemptAuthentication 方法
		    // attemptAuthentication 方法内部创建了 authenticated 属性为 false (即未授权)的 UsernamePasswordAuthenticationToken 对象, 并传递给 AuthenticationManager().authenticate() 方法进行认证,
		    //认证成功后 返回一个 authenticated = true (即授权成功的) UsernamePasswordAuthenticationToken 对象 
			authResult = attemptAuthentication(request, response);
			if (authResult == null) {
				return;
			}
			// 4 将认证成功的 Authentication 存入Session中
			sessionStrategy.onAuthentication(authResult, request, response);
		}
		catch (InternalAuthenticationServiceException failed) {
		     // 5 认证失败后 调用 AuthenticationFailureHandler 的 onAuthenticationFailure 接口 进行失败处理( 可以 通过 继承 AuthenticationFailureHandler 自行编写失败处理逻辑 )
			unsuccessfulAuthentication(request, response, failed);
			return;
		}
		catch (AuthenticationException failed) {
		    // 5 认证失败后 调用 AuthenticationFailureHandler 的 onAuthenticationFailure 接口 进行失败处理( 可以 通过 继承 AuthenticationFailureHandler 自行编写失败处理逻辑 )
			unsuccessfulAuthentication(request, response, failed);
			return;
		}
		
        ......
         // 6 认证成功后 调用 AuthenticationSuccessHandler 的 onAuthenticationSuccess 接口 进行失败处理( 可以 通过 继承 AuthenticationSuccessHandler 自行编写成功处理逻辑 )
		successfulAuthentication(request, response, chain, authResult);
	}

   С точки зрения исходного кода весь процесс на самом деле очень ясен: от принятия решения о том, следует ли обрабатывать, до аутентификации и, наконец, оценки результата аутентификации, успех аутентификации и неудача аутентификации обрабатываются соответственно.

Отладьте и посмотрите результат, на этот раз мы запрашиваем localhast:8080/get_user/test , поскольку у нас нет разрешения, мы перейдем непосредственно к интерфейсу входа в систему, сначала мы вводим неверный пароль учетной записи, чтобы увидеть, является ли ошибка аутентификации последовательной. с нашим резюме.

image

   Результат такой же, как и ожидалось.Вы можете удивиться, почему подсказка здесь на китайском языке.Это означает, что Security 5 начал поддерживать китайский язык, что показывает, что китайские программисты становятся все более и более важными в мире! ! !

   На этот раз введите правильный пароль и просмотрите возвращенную информацию об объекте аутентификации:

image

   Вы можете видеть, что на этот раз он успешно вернул authticated = ture , информацию об учетной записи пользователя без пароля, а также содержит информацию о правах администратора, которую мы определили. Отпустите точку останова, поскольку обработчиком успеха по умолчанию в Security является SimpleUrlAuthenticationSuccessHandler, этот обработчик перенаправит на ранее доступный адрес, который является localhost:8080/get_user/test. На этом весь процесс заканчивается. Нет, мы еще на один шаг, Сессия, мы видим Сессию из куков браузера:

image

3. Базовый фильтр аутентификации

  BasicAuthenticationFilter похож на UsernameAuthenticationFilter, но разница все же очевидна,BasicAuthenticationFilter в основном получает информацию о параметре авторизации из заголовка, а затем вызывает аутентификацию.После успешной аутентификации он напрямую обращается к интерфейсу, в отличие от процесса UsernameAuthticationFilter, который переходит через AuthenticationSuccessHandler.. Я не буду публиковать код здесь.Студенты, которые хотят знать, могут посмотреть исходный код напрямую. Следует отметить, однако, что обработчик успеха onSuccessfulAuthentication() BasicAuthenticationFilter является пустым методом.

   Чтобы протестировать BasicAuthenticationFilter, нам нужно заменить formLogin() в SpringSecurityConfig на httpBasic() для поддержки BasicAuthenticationFilter, перезапустить проект и получить доступ к тому же localhast:8080/get_user/test. В настоящее время, поскольку у вас нет разрешения на доступ к этому адресу интерфейса, на странице появится окно входа в систему. Учащиеся, знакомые с Security4, должны быть знакомы с ним. Аналогичным образом, после того, как мы вводим пароль учетной записи, смотрим отладочные данные:

image

В это время мы можем получить параметр авторизации, а затем проанализировать полученную информацию об учетной записи и пароле для аутентификации.Мы проверяем, что информация об объекте аутентификации, возвращаемая после успешной аутентификации, на самом деле такая же, как и в UsernamePasswordAuthticationFilter, и, наконец, снова вызываем следующий фильтр Поскольку аутентификация прошла успешно, он напрямую войдет в FilterSecurityInterceptor для проверки авторизации.

4. Фильтр анонимной аутентификации

Почему здесь упоминается AnonymousAuthenticationFilter, в основном потому, что нет такого понятия как отсутствие учетной записи в Security (описание здесь может быть не очень понятное, но общий смысл такой), этот AnonymousAuthenticationFilter специально предназначен для этого сотрудника Security, который используется в Если все фильтры не проходят аутентификацию, автоматически создается анонимный пользователь по умолчанию с анонимными правами доступа. Помните анонимное сообщение авторизации, которое мы видели, когда говорили о SecurityContextPersistenceFilter? Если вы не помните, вы должны оглянуться назад, и я не буду описывать это здесь.

5. Фильтр перевода исключений

ExceptionTranslationFilter на самом деле не выполняет никакой фильтрации, но не стоит недооценивать его роль, его самая большая и мощная вещь заключается в том, что он захватывает AuthenticationException и AccessDeniedException.Если возникает исключение, это эти два исключения, для обработки будет вызван метод handleSpringSecurityException() . Мы моделируем ситуацию AccessDeniedException (нет разрешения, нет исключения доступа), сначала нам нужно изменить интерфейс /get_user:

  • Добавить в контроллер @EnableGlobalMethodSecurity(prePostEnabled =true) Включить контроль доступа на уровне метода безопасности
  • Добавьте @PreAuthorize("hasRole('user')") в интерфейс, чтобы разрешить доступ только к учетным записям с ролью пользователя (помните роль администратора, когда мы получили учетную запись пользователя по умолчанию?)
@RestController
@EnableGlobalMethodSecurity(prePostEnabled =true)  // 开启方法级别的权限控制
public class TestController {

    @PreAuthorize("hasRole('user')") //只允许user角色访问
    @GetMapping("/get_user/{username}")
    public String getUser(@PathVariable  String username){
        return username;
    }
}

   Перезапустите проект, вернитесь к интерфейсу /get_user, введите правильный пароль учетной записи и обнаружите, что возвращается страница ошибки со статусом 403, что аналогично процессу, который мы делали ранее. Отладьте, посмотрите на обработку:

image

Хорошо видно, что объектом исключения является AccessDeniedException, а информация об исключении заключается в том, что доступ запрещен. Давайте посмотрим на метод обработки accessDeniedHandler.handle() после исключения AccessDeniedException и введем метод handle() класса AccessDeniedHandlerImpl. метод сначала определит, настроена ли система. Если отображается errorPage (страница ошибки), если нет, установите код состояния 403 непосредственно в ответ.

image

6. Фильтрбезопасностиперехватчик

FilterSecurityInterceptor — последний и самый важный во всей цепочке фильтров Security, его основная функция — определить, есть ли у пользователя, успешно прошедшего аутентификацию, разрешение на доступ к интерфейсу, основной метод обработки — вызов super.beforeInvocation родительского класса ( AbstractSecurityInterceptor).(fi), давайте разберемся с потоком обработки этого метода:

  • Получите информацию о разрешении, требуемую текущим адресом доступа, с помощью getSecurityMetadataSource().getAttributes().
  • Получите информацию о разрешениях текущего пользователя доступа с помощью функции authenticationIfRequired().
  • Используйте механизм голосования, чтобы судить о правах через accessDecisionManager.decide(), если решение терпит неудачу, исключение AccessDeniedException вызывается напрямую.
protected InterceptorStatusToken beforeInvocation(Object object) {
	       
	    ......
	    
	    // 1 获取访问地址的权限信息 
		Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
				.getAttributes(object);

		if (attributes == null || attributes.isEmpty()) {
		
		    ......
		    
			return null;
		}

        ......

        // 2 获取当前访问用户权限信息
		Authentication authenticated = authenticateIfRequired();

	
		try {
		    // 3  默认调用AffirmativeBased.decide() 方法, 其内部 使用 AccessDecisionVoter 对象 进行投票机制判权,判权失败直接抛出 AccessDeniedException 异常 
			this.accessDecisionManager.decide(authenticated, object, attributes);
		}
		catch (AccessDeniedException accessDeniedException) {
			publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
					accessDeniedException));

			throw accessDeniedException;
		}

        ......
        return new InterceptorStatusToken(SecurityContextHolder.getContext(), false,
					attributes, object);
	}

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

4. Личное резюме

  整个授权流程核心的就在于这几次核心filter的处理,这里我用序列图来概况下这个授权流程

image
(PS: Если картинка не ясна, вы можете посетить адрес проекта на github)

   В этой статье описан код процесса авторизации, вы можете получить доступ к модулю безопасности в репозитории кода, адрес проекта на github:GitHub.com/bug9/весна…

        Если вы заинтересованы в них, добро пожаловать, пометьте, подпишитесь, добавьте в избранное и вперед, чтобы поддержать!


  Добро пожаловать в следующую статью   Spring Анализ безопасности (2) — Процесс аутентификации