Как [SpringSecurity]]Controller получает текущую информацию о вошедшем в систему пользователе

Spring Boot

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通常是不包含验证敏感信息的Форма аутентификации.

登陆验证使用的载体也是一种Authentication

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. Нам также необходимо выбрать соответствующие стратегии для различных сценариев развертывания приложений.