- предисловие
- Spring Security Core
- Spring Security Web
- Spring Security OAuth2
- Spring Security JWT
- Эпилог
- Ссылка на ссылку
предисловие
Spring Security — многомодульный проект, в котором разобрались доПроцесс аутентификации Spring Security, только сейчас я обнаружил, что часть гребенки — это больше контента в основном модуле Spring Security Core.
При повседневном использовании он будет включать в себя больше вещей в Spring Security Web и Spring Security OAuth 2. Основное содержание этого блога — разобраться во взаимоотношениях между ними и понять их соответствующие роли.
Spring Security Core
Spring Security Core играет важную роль во всей структуре Spring Security, предоставляя абстракции, связанные с аутентификацией и контролем разрешений.
Однако в процессе использования мы можем столкнуться с дополнительными абстракциями, связанными с аутентификацией, такими как:
- пройти через
AuthenticationManager
Обеспечивает абстракцию для методов аутентификации пользователей, позволяяProviderManager
иAuthenticationProvider
собрать и реализовать собственный метод аутентификации - пройти через
UserDetails
иUserDetailsService
Предоставляет абстракцию для сведений о пользователе и способ получения сведений о пользователе. - пройти через
Authentication
Обеспечивает абстракцию информации об аутентификации пользователя и результатов аутентификации. - пройти через
SecurityContext
иSecurityContextHolder
Предоставляет способ сохранить результаты аутентификации - ...
Эти вещи на самом деле абстрагируют ключевые компоненты традиционного процесса аутентификации. В сочетании с традиционным процессом аутентификации легко понять взаимосвязь между этими компонентами. Вы также можете увидеть это изображение изSpring Security (1) — Обзор архитектуры | Исходный код Taro — Блог о чистом анализе исходного кодаКартина:
Абстракция части контроля разрешений в основномAccessDecisionManager
иAccessDecisionVoter
Теперь я не управлял этими двумя вещами вручную.Я могу только сказать, что услуги, предоставляемые Spring Security Web, слишком интимны, а реализация части контроля разрешений не требует от меня слишком много беспокойства.
Для получения дополнительной информации о модуле Spring Security Core см.:
- Spring Security (1) — Обзор архитектуры | Исходный код Taro — Блог о чистом анализе исходного кода
- Архитектура безопасности Spring | Блог LeeReindeer
- Разбираем процесс аутентификации Spring Security
Spring Security Web
Если Spring Security Core предоставляет только абстракции, связанные с аутентификацией и контролем разрешений, Spring Security Web предоставляет нам конкретную реализацию и применение этих абстракций.
Spring Security Web черезцепочка фильтровЧтобы реализовать ряд функций, связанных с веб-безопасностью, аутентификация пользователя и контроль разрешений являются лишь частью этого. В этой части реализации фильтр действует как идентификатор вызывающего объекта Spring Security Core. Общий процесс таков:
- Информация об аутентификации в запросе извлечения фильтра инкапсулируется как
Authentication
Перейти кAuthenticationManager
Аутентифицируйтесь, а затем поместите результат аутентификации вSecurityContext
для последующих фильтров - Фильтр используется на основе результата аутентификации до того, как запрос поступит в конечную точку.
AccessDecisionManager
Определите, есть ли у вас соответствующие разрешения
Здесь Spring Security Core — это только часть функциональности, которую использует Spring Security Web, и, что более важно, вся цепочка фильтров.
Построение цепочки фильтров
Раньше я просто хотел понять процесс вызова цепочки фильтров, но посмотрев его, я перешел к исходному коду. Когда я отреагировал, я обнаружил, что было бы немного невыгодно останавливаться после стольких усилий, поэтому я просто рационализировал логику построения цепочки фильтров.
Разобравшись с построением и логикой вызова цепочки построителей, я чувствую, что логика построения цепочки фильтров не кажется очень полезной, лучше смотреть непосредственно на логику вызова цепочки фильтров...
Процесс разбора этой части логики немного сложен, так или иначе, когда я отлаживал, точка останова была наbuild()
Повторяющиеся горизонтальные прыжки рядом с методом, здесь для простоты сразу ставим результат1:
Временная диаграмма не очень стандартна, достаточно общего смысла ( ̄▽ ̄), анализ следующий:
- Конструкция цепочки фильтров в Spring Security Web в основном состоит из
WebSecurity
иHttpSecurity
Законченный -
WebSecurity
в зависимости от контекстаWebSecurityConfigurer
строить изHttpSecurity
объект, а затем передатьHttpSecurity
строить изSecurityFilterChain
После этогоSecurityFilterChain
ставитьFilterChainProxy
середина. Среди них распространенной реализацией WebSecurityConfigurer являетсяWebMvcConfigurerAdapter
, иSecurityFilterChain
Обычная реализацияDefaultSecurityFilterChain
-
HttpSecurity
Согласно непосредственно добавленномуFilter
и черезAbstractHttpConfigurer
Реализация конструкции классаFilter
Создать цепочку фильтров
В этой части логики ключевыми объектами являютсяWebSecurity
и классы конфигурации, от которых это зависитWebSecurityConfigurer
, HttpSecurity
и классы конфигурации, от которых это зависитAbstractHttpConfigurer
.
В реальном использовании мы обычно наследуемWebMvcConfigurerAdapter
этоWebSecurityConfigurer
класс реализации, а затем переопределить егоconfigure(HttpSecurity)
метод:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests()
.antMatchers("/oauth/**")
.authenticated()
.and()
.requestMatchers()
.antMatchers("/oauth/**","/login/**","/logout/**")
.and()
.csrf()
.disable()
.formLogin()
.permitAll();
// @formatter:on
}
}
В приведенном выше классе мы наследуемWebSecurityConfigurerAdapter
Этот класс, когда мы помещаем пользовательский класс в контекст Spring, может использоваться WebSecurity для построения HttpSecurity, а переписанныйconfigure(HttpSecurity)
Он будет вызываться до того, как HttpSecurity создаст фильтр для завершения настройки цепочки фильтров.
Среди них такие какcsrf()
такие методы, какAbstractHttpConfigurer
реализация, позволяющая нам настраивать определенные фильтры.
В конце HttpSecurity может завершить построение цепочки фильтров согласно соответствующей конфигурации, а затем WebSecurity поставит их вFilterChainProxy
вернулся в экземпляре.
Вызов цепочки фильтров
Вызов цепочки фильтров в основном включает в себя два объекта: FilterChainProxy и DefaultSecurityFilterChain, ключ находится на самом FilterChainProxy.
Однако исходный код этих двух объектов достаточно прост, поэтому я не буду его здесь выкладывать, если интересно, можете глянуть, вот краткое описание результатов:
- FilterChainProxy будет использоваться в качестве фильтра в цепочке фильтров контейнера сервлетов.После получения запроса он определит, есть ли соответствующая цепочка фильтров в удерживаемой цепочке фильтров.
- При наличии совпадающей цепочки фильтров запрос обрабатывается непосредственно с первым совпадением.
- Отсутствует совпадающая цепочка фильтров или после того, как совпадающая цепочка фильтров будет завершена, она вернется к цепочке фильтров контейнера сервлета для продолжения выполнения.
Ключевым моментом здесь является то, что существует несколько цепочек фильтров, и каждая цепочка фильтров соответствует определенному запросу. Я не был осторожен, когда читал документ раньше, я этого не осознавал и сэкономил много обходных путей QAQ
Прикрепил:
Использование цепочек фильтров
Использование веб-фильтров Spring Security в основном предназначено для настройки цепочки фильтров. Цепочка фильтров по умолчанию добавит некоторые фильтры, поставляемые с Spring Security Web. При ее использовании вам необходимо решить, следует ли удалить некоторые из фильтров по умолчанию (или не использовать их).Конфигурация по умолчанию) и добавьте пользовательский фильтр в подходящее место в цепочке фильтров.
Вот краткое введение в роль некоторых встроенных фильтров и порядок фильтров, в первую очередь встроенных фильтров:
- фильтр
SecurityContextPersistenceFilter
Информацию об аутентифицированных пользователях можно получить из сеанса. - фильтр
AnonymousAuthenticationFilter
в открытииSecurityContextHolder
Когда вSecurityContextHolder
- фильтр
ExceptionTranslationFilter
может справитьсяFilterSecurityInterceptor
Возникающие исключения, перенаправление, вывод сообщений об ошибках и т. д. - фильтр
FilterSecurityInterceptor
Оценка полномочий аутентификационной информации, выдача исключения, когда полномочий недостаточно.
При настройке фильтров (обычно фильтров проверки подлинности) нам необходимо учитывать расположение пользовательских фильтров, например, мы не должны помещать настраиваемые фильтры проверки подлинности вAnonymousAuthenticationFilter
После официальная документация объясняет порядок фильтров: После удаления некоторых фильтров приблизительный порядок такой:
Среди них AuthenticationProcessingFilter относится к реализации фильтра аутентификации, такой как обычно используемыйUsernamePasswordAuthenticationFilter
этот фильтр.
Полная последовательность может относиться к:
В этом порядке, посколькуSecurityContextPersistenceFilter
Можно получить информацию об аутентифицированном пользователе из сеанса, поэтому при настройке фильтра следует учитывать, содержит ли SecurityContextHolder информацию об аутентификации пользователя. Или установите фильтр для аутентификации пароля учетной записи пользователя в цепочке фильтров URL-адреса, связанного с входом/регистрацией, и установите фильтр для токена аутентификации в другой цепочке фильтров.
Spring Security OAuth2
Spring Security OAUTH2 создает на основе Spring Security Core и Spring Security Security, обеспечивая поддержку Framework для авторизации OAUTT2.
Среди них наиболее сложной частью являетсяСервер авторизацииНапротив, сервер ресурсов в основном повторно использует цепочку фильтров, предоставляемую Spring Security Web, через фильтрOAuth2AuthenticationProcessingFilter
и запрос был выполненToken
Получите информацию об аутентификации, поэтому основное внимание здесь будет уделено серверу авторизации.
Сервер авторизации
Для традиционных методов аутентификации в принципе достаточно простой аутентификации информации о пользователе, но для OAuth2 этого недостаточно.Для серверов авторизации OAuth2, помимо аутентификации пользователя, также требуется аутентификация клиента.Также необходимо проверить Scope, запрошенный Таким образом, одной цепочки фильтров недостаточно для завершения аутентификации обоих, поскольку SecurityContextHolder может содержать только один результат аутентификации.
Поэтому стратегия аутентификации, принятая Spring Security OAuth2, такова: завершить аутентификацию клиента или пользователя в цепочке фильтров, а затем завершить проверку оставшейся информации во внутренней логике конечной точки. И эта стратегия аутентификации отличается в разных режимах.
В основном здесьРежим кода авторизацииирежим пароляСтратегия аутентификации вAuthorizationEndpoint
иTokenEndpoint
Две основные цепочки фильтров закрыты.
Режим кода авторизации
Первым является режим кода авторизации.Для режима кода авторизации процесс запроса обычно является первым./oauth/authorize
Получите код авторизации, а затем перейдите на/oauth/token
Получить токен, для/oauth/authorize
Для цепочки фильтров этой конечной точки аутентификацией является информация пользователя.После прохождения аутентификации он входит в конечную точку и запрашивает клиента.Scope
и пользователяApproval
После проверки будет сгенерирован код авторизации, который будет возвращен клиенту, если проверка пройдена.
Собственно, вот почему/oauth/authorize
Эта конечная точка должна аутентифицировать пользователя, потому что здесь нужно получитьПользовательавторизация.
Затем клиент берет код авторизации на/oauth/token
Когда конечная точка получает маркер, цепочка фильтров конечной точки аутентифицирует клиента. После прохождения аутентификации он войдет в конечную точку. В это время конечная точка проверит область действия, запрошенную клиентом. После прохождения проверки , это пройдет.TokenGranter
Создайте токен и верните его клиенту.
То есть для режима кода авторизации:
- конечная точка
/oauth/authorize
Полная аутентификация пользователя, проверка области действия по запросу клиента и проверки авторизации пользователя. - конечная точка
/oauth/token
Завершите аутентификацию клиента, проверку объема, запрошенного клиентом, и проверку кода авторизации клиента.
На самом деле это можно рассматривать как кодовую интерпретацию режима кода авторизации, потому что в режиме кода авторизации часто не клиент, управляемый пользователем, получает Токен, поэтому необходимо проверить, является ли клиент доверенным.
Соответствует логике, связанным со связанным исходным кодом, код удалена часть эффективности:
@RequestMapping(value = "/oauth/authorize")
public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<String, String> parameters, SessionStatus sessionStatus, Principal principal) {
AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters);
try {
// 未通过认证的请求会抛异常
if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
throw new InsufficientAuthenticationException("User must be authenticated with Spring Security before authorization can be completed.");
}
ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());
// 效验 Scope
oauth2RequestValidator.validateScope(authorizationRequest, client);
// 效验用户的授权
authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest, (Authentication) principal);
boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
authorizationRequest.setApproved(approved);
// Validation is all done, so we can check for auto approval...
if (authorizationRequest.isApproved()) {
if (responseTypes.contains("token")) {
return getImplicitGrantResponse(authorizationRequest);
}
if (responseTypes.contains("code")) {
return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest, (Authentication) principal));
}
}
return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
}
catch (RuntimeException e) {
sessionStatus.setComplete();
throw e;
}
}
@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam Map<String, String> parameters)
throws HttpRequestMethodNotSupportedException {
// 可以看到,通过效验的是客户端
String clientId = getClientId(principal);
ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
// 效验请求的 Scope
if (authenticatedClient != null) {
oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
}
if (isAuthCodeRequest(parameters)) {
// The scope was requested or determined during the authorization step
if (!tokenRequest.getScope().isEmpty()) {
tokenRequest.setScope(Collections.<String> emptySet());
}
}
// 调用 TokenGranter 进行授权
OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
if (token == null) {
throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
}
return getResponse(token);
}
Блок-схема режима кода авторизации:
режим пароля
Режим шифрования или упрощенный режим имеет только одну конечную точку, т.е./oauth/token
Эта конечная точка, то есть эта конечная точка выполняет аутентификацию как пользователя, так и клиента.
Однако для этой конечной точки невозможно одновременное наличие двух цепочек фильтров.Для поддержки режима кода авторизации определена ответственность цепочки фильтров этой конечной точки, то есть завершение аутентификации клиента . Следовательно, аутентификация пользователя может выполняться только в рамках внутренней логики конечной точки.
когдаTokenEndpoint
Модель лицензирования найденарежим пароля, будетResourceOwnerPasswordTokenGranter
положить вTokenGranter
, иResourceOwnerPasswordTokenGranter
Вызывается при авторизацииAuthenticationManager
Чтобы завершить аутентификацию пользователя, аутентификация пройдет только в том случае, если аутентификация прошла успешно.TokenService
Токен генерирует доход.
// AuthorizationServerEndpointsConfigurer.getDefaultTokenGranters
private List<TokenGranter> getDefaultTokenGranters() {
List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();
tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails, requestFactory));
tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory));
tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory));
tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory));
if (authenticationManager != null) {
tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory));
}
return tokenGranters;
}
Блок-схема режима пароля:
Аутентификация клиента
сквозьРежим кода авторизацииирежим пароляМы знаем, что аутентификация клиента выполняется в цепочке фильтров, и эта аутентификация может пройтиBasicAuthenticationFilter
Полный, но, вероятно, более распространенныйClientCredentialsTokenEndpointFilter
этот фильтр.
Его внутренний процесс аутентификации на самом деле очень прост, и наиболее важным моментом является то, что он по-прежнему использует набор Spring Security Core!
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
String clientId = request.getParameter("client_id");
String clientSecret = request.getParameter("client_secret");
// If the request is already authenticated we can assume that this filter is not needed
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
return authentication;
}
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(clientId, clientSecret);
// 通过 AuthenticationManager 完成认证
return this.getAuthenticationManager().authenticate(authRequest);
}
Мы знаем, что Spring Security OAuth2 предоставляет две абстракции, ClientDetails и ClientDetailsService, которые несовместимы с UserDetails и UserDetailsService.В настоящее время вы можете самостоятельно реализовать AuthenticationProvider для использования ClientDetails и ClientDetailsService, но вы также можете преобразовать ClientDetails и ClientDetailsService в UserDetails. и UserDetailsService, Spring Security OAuth2 выполняет это преобразование через ClientDetailsUserDetailsService:
public class ClientDetailsUserDetailsService implements UserDetailsService {
private final ClientDetailsService clientDetailsService;
public ClientDetailsUserDetailsService(ClientDetailsService clientDetailsService) {
this.clientDetailsService = clientDetailsService;
}
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
ClientDetails clientDetails;
try {
clientDetails = clientDetailsService.loadClientByClientId(username);
} catch (NoSuchClientException e) {
throw new UsernameNotFoundException(e.getMessage(), e);
}
String clientSecret = clientDetails.getClientSecret();
if (clientSecret== null || clientSecret.trim().length()==0) {
clientSecret = emptyPassword;
}
return new User(username, clientSecret, clientDetails.getAuthorities());
}
}
TokenGranter
Генерация кода авторизации в Spring Security OAuth2 выполняется через TokenGranter.Когда код авторизации генерируется, каждая принадлежащая реализация TokenGranter будет проходиться до тех пор, пока токен не будет успешно сгенерирован или все реализации TokenGranter не смогут сгенерировать токен.
Создание токена также является ссылкой, которую можно абстрагировать, поэтому Spring Security OAuth2 генерирует, получает и сохраняет токен через TokenService и TokenStore.
public abstract class AbstractTokenGranter implements TokenGranter {
private final AuthorizationServerTokenServices tokenServices;
private final ClientDetailsService clientDetailsService;
private final OAuth2RequestFactory requestFactory;
private final String grantType;
protected AbstractTokenGranter(AuthorizationServerTokenServices tokenServices,
ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
this.clientDetailsService = clientDetailsService;
this.grantType = grantType;
this.tokenServices = tokenServices;
this.requestFactory = requestFactory;
}
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
// 每个 TokenGranter 对应一种授权类型
if (!this.grantType.equals(grantType)) {
return null;
}
String clientId = tokenRequest.getClientId();
ClientDetails client = clientDetailsService.loadClientByClientId(clientId);
validateGrantType(grantType, client);
// 获取授权码
return getAccessToken(client, tokenRequest);
}
protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
return tokenServices.createAccessToken(getOAuth2Authentication(client, tokenRequest));
}
}
// 默认的 TokenServices 的部分代码
public class DefaultTokenServices {
@Transactional
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
// 首先从 TokenStore 中获取 Token
OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
OAuth2RefreshToken refreshToken = null;
if (existingAccessToken != null) {
if (existingAccessToken.isExpired()) {
if (existingAccessToken.getRefreshToken() != null) {
refreshToken = existingAccessToken.getRefreshToken();
tokenStore.removeRefreshToken(refreshToken);
}
tokenStore.removeAccessToken(existingAccessToken);
}
else {
// Re-store the access token in case the authentication has changed
tokenStore.storeAccessToken(existingAccessToken, authentication);
return existingAccessToken;
}
}
if (refreshToken == null) {
refreshToken = createRefreshToken(authentication);
}
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
// 保存 accessToken
tokenStore.storeAccessToken(accessToken, authentication);
refreshToken = accessToken.getRefreshToken();
if (refreshToken != null) {
tokenStore.storeRefreshToken(refreshToken, authentication);
}
return accessToken;
}
// 从 TokenStore 中获取 Token
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
return tokenStore.getAccessToken(authentication);
}
}
Проще говоря:
- Полная аутентификация клиента и пользователя и проверка области в цепочках фильтров и внутренней логике конечной точки
- Сгенерируйте токен через TokenGranter, а TokenGranter создаст токен через TokenService, TokenStore может сохранить токен
сервер ресурсов
Сервер ресурсов намного проще, чем сервер авторизации, аналогичный традиционному процессу, через фильтрOAuth2AuthenticationProcessingFilter
иOAuth2AuthenticationManager
Подтвердите токен и получите информацию для аутентификации:
public class OAuth2AuthenticationProcessingFilter implements Filter, InitializingBean {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
// 从请求头中提取 Token
Authentication authentication = tokenExtractor.extract(request);
Authentication authResult = authenticationManager.authenticate(authentication);
SecurityContextHolder.getContext().setAuthentication(authResult);
chain.doFilter(request, response);
}
}
public class OAuth2AuthenticationManager implements AuthenticationManager, InitializingBean {
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String token = (String) authentication.getPrincipal();
// 通过 TokenService 获取认证信息
OAuth2Authentication auth = tokenServices.loadAuthentication(token);
if (auth == null) {
throw new InvalidTokenException("Invalid token: " + token);
}
checkClientDetails(auth);
if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
// Guard against a cached copy of the same details
if (!details.equals(auth.getDetails())) {
// Preserve the authentication details from the one loaded by token services
details.setDecodedDetails(auth.getDetails());
}
}
auth.setDetails(authentication.getDetails());
auth.setAuthenticated(true);
return auth;
}
}
Spring Security JWT
Использование JWT в OAuth2 можно увидеть во многих местах Spring Security JWT играет роль TokenService и TokenStore в Spring Security OAuth2 для создания и проверки токенов.
Тем не менее, я все еще хочу разглагольствовать о JWT. Это было очень интересно, когда я впервые увидел это: использование JWT может напрямую передавать некоторую информацию в токене, и серверу не нужно хранить информацию о токене.
Однако в некоторых фактических целях использования может возникнуть необходимость недействительна действительного токена JWT, что невозможно для JWT. Для достижения этого требования только некоторая информация может быть сохранена на стороне сервера.
Однако, если вам нужно хранить информацию на сервере, зачем использовать JWT? Пока вам нужно хранить информацию на сервере, не имеет большого значения, используете ли вы JWT или нет...
Эпилог
Spring Security действительно очень сложный фреймворк. В настоящее время он предназначен только для применения в программах сервлетов. Однако я внезапно заинтересовался Spring WebFlux. Я не знаю, что такое Spring Security в Spring WebFlux...
Кроме того, что я хочу сказать, так это то, что официальный учебник по Spring Security действительно хорош, он ясно объясняет общую структуру, но жаль, что он страдает английским языком.T
Ссылка на ссылку
Информация о Spring Security в целом:
- TERASOLUNA Server Framework for Java (5.x) Development Guideline
- Архитектура безопасности Spring | Блог LeeReindeer
Информация, связанная с Spring Security Web:
- Анализ процесса Spring Security Verification и пользовательских методов проверки — Decouple — Blog Park
- Веб-приложение Spring Security и практика входа по отпечатку пальца
- Spring Security Reference
Информация, связанная с Spring Security OAuth2:
- Taro Road Spring Security OAuth2 Введение | Исходный код Taro Road - Блог анализа чистого исходного кода
- Spring Security OAuth
- OAuth2 Boot
Footnotes
1Если вас интересует подробный процесс, вы можете прочитать мои заметкиПостроение цепочки веб-фильтров Spring Security