1. Проблема
В процессе проверки кода этого проекта я обнаружил, что многим коллегам нравится напрямую работать с SecurityContextHolder на уровне Contoller или даже на уровне Service для получения информации о вошедшем в данный момент пользователе, то есть объекта Authentication.
public static JwtUser getUserFromContext() {
Object details = SecurityContextHolder.getContext().getAuthentication().getDetails();
return details instanceof JwtUser ? (JwtUser) details : null;
}
В дизайне мы должны стараться избегать зависимости от внешних ресурсов, особенно веб-ресурсов, на уровне службы, что очень недружественно для кода модульного тестирования. Поэтому я рекомендую иметь дело с веб-ресурсами на уровне контроллера.SecurityContextHolder
По крайней мере, должны работать на уровне контроллера.
Обсуждаю, как работает рабочая работа контроллераAuthention
Когда мы впервые обсуждаем следующие вопросы:
- Что такое аутентификация
- Когда и как Аутентификация сохраняется в SecurityContext
- В какой части аутентификации хранится наш пользовательский объект?
- Как ввести нужную нам информацию в контроллер
Наконец, мы обсуждаем, как получить текущий объект аутентификации входа в Spring Controller.
2. Что такое аутентификация?
Аутентификация является носителем информации об аутентификации.В Spring Security имя пользователя и пароль, которые мы включаем при попытке войти в систему и отправить запрос на сервер, являются аутентификацией.В этом сценарии мы видим больше в виде токена с суффиксом;После успешного входа в систему , Аутентификация используется для идентификации нашей конкретной идентификационной информации в контексте.
МыAbstractAuthenticationToken
Для дальнейшего обсуждения,AbstractAuthenticationToken
Реализованы два основных интерфейса: Principal и Authentication для сохранения идентификационной информации и CredentialsContainer для сохранения учетных данных аутентификации, таких как пароли. Обычно жизненный цикл заканчивается в момент отправки проверки. После проверки сохраните в ``SecurityContext通常是不包含验证敏感信息的
Форма аутентификации.
3. Когда и как Аутентификация сохраняется в SecurityContext
Аутентификация — это успешная аутентификация после установки SecurityContext,AbstractAuthenticationProcessingFilter
В коде показана такая операция:
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authResult);
rememberMeServices.loginSuccess(request, response, authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
authResult, this.getClass()));
}
successHandler.onAuthenticationSuccess(request, response, authResult);
}
Через исходный код мы также обнаружили, что на самом деле после проверки успешного входа будет установлен не только Context, но и событие будет отправлено через механизм Event.InteractiveAuthenticationSuccessEvent
, обычно мы также можем настроить прослушиватель событий для обработки некоторой бизнес-логики, которую необходимо обработать после аутентификации входа, чтобы не расширятьAuthenticationFilter
.
4. В какой части аутентификации хранится наш пользовательский объект?
После понимания предпосылки «После успешной аутентификации информация об аутентификации будет храниться в SecurityContext», давайте решим, какая информация будет храниться в Authentication. Наиболее распространенный сценарий заключается в том, что мы храним информацию UserDetails в поле сведений аутентификации.
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
\\ ...省略
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
Если вы являетесь пользовательским расширением AuthenticationFilter, вы также можете настроить метод проверки подлинности в попытке проверки подлинности в соответствии с организационной структурой проверки подлинности, но обычно мы будем детализировать поля для размещения информации UserDetails. В зависимости от сценария аутентификации также может быть размещен любой последующий идентификационный код службы, может использоваться информационный код, такой как IP, такие как коды и тому подобное.
Здесь мы должны быть в состоянии понять, что вся проверка прошла успешно.Authentication
жизненный цикл и строение.
5. Как ввести нужную нам информацию в контроллер
В контроллере нам может потребоваться получить информацию о текущем аутентифицированном пользователе.Наиболее распространенным примером является то, что нам нужно получить информацию об имени пользователя, вошедшего в систему в данный момент.
Внедрить принципала, чтобы получить имя пользователя при входе в систему
Предполагая, что имя пользователя и пароль являются именем пользователя и паролем, когда мы отправляем логин, тогда поле имени пользователя сохраняется в Принципале нашего AuthenticationToken, В соответствии с логикой по умолчанию поле аутентификации после проверки входа будет соответствовать запросу на вход, затем в Аутентификации Принципал - это имя пользователя для входа. Следующий фрагмент кода контроллера предназначен для получения объекта Principal в контексте с помощью механизма внедрения Spring.
public UserModel findUser(Principal principal) {
UserModel userModel = userService.findOneByUsername(principal.getName());
return userModel;
}
Внедрить UserDetails, чтобы получить в данных информацию, относящуюся к UserDetails.
Этот сценарий обычно связан с тем, что информация о идентификации входа в систему, используемая при входе в систему, несовместима с информацией, которой нам нужно использовать. Наиболее распространенным сценарием является то, что система поддерживает использование номеров мобильных телефонов и имена пользователей для входа в систему, но логика запросов в База данных поддерживает только имя пользователя пользователей в таблице. Если он получен из основного, мы не можем сказать, хранятся ли номер телефона или имя пользователя. Тогда мы можем получить информацию о пользовательских документах в поле «Детали поля аутентификации» путем введения объекта UserDetails.
public UserModel findUser(UserDetails userDetails) {
UserModel userModel = userService.findOneByUsername(userDetails.getUsername());
return userModel;
}
Введите аутентификацию, чтобы получить больше информации
Если информации об идентификаторе пользователя в Principal и UserDetails недостаточно для соответствия текущим сценариям использования в бизнесе, например, вам необходимо проверить некоторую информацию о пользовательской структуре, хранящуюся в деталях в то время, тогда мы можем напрямую управлять аутентификацией в текущем контексте с помощью внедрение аутентификации в объект уровня контроллера. После того, как мы поймем, как работать с аутентификацией в контроллере, мы также можем перехватывать и преобразовывать аутентификацию в ControllerAdvice, чтобы быть ленивыми, и размещать четкий тип экземпляра класса реализации пользователя вверху и внизу, чтобы контроллер мог легко внедрить его:
@ControllerAdvice
public class CurrentUserAdvice {
@ModelAttribute()
public JwtUser currentUser(Authentication authentication) {
JwtUser jwtUser = null;
if(authentication!=null) {
jwtUser = (JwtUser)authentication.getDetails();
}
return jwtUser;
}
}
Затем нам нужно только ввести его через @ModelAttribute в контроллере.
public UserModel findUser(@ModelAttribute() JwtUser JwtUser) {
UserModel userModel = userService.findOneById(JwtUser.getId());
return userModel;
}
Суммировать
На самом деле существует множество способов реализации контекста SecurityContext, некоторые из которых полагаются на пул потоков на одном компьютере, а некоторые — на HttpSession. Нам также необходимо выбрать соответствующие стратегии для различных сценариев развертывания приложений.