Контекст безопасности Spring не обновляется в AuthenticationManager.authenticate - PullRequest
1 голос
/ 27 января 2020

Я пытаюсь реализовать пользовательский процесс аутентификации для серверной части REST API. Некоторые детали реализации:

  1. Я реализовал пользовательский AuthenticationProvider, который успешно аутентифицирует пользователя и возвращает Authentication объект.
  2. Я зарегистрировал совершенно новый AuthenticationManager, чтобы пропустить весеннюю загрузку автоматическая настройка:
    @Bean
    fun authenticationManager() {
        return AuthenticationManager()
    }
В моем AuthController у меня есть /login конечная точка. Логика c этого довольно проста: она преобразует DTO в пользовательский Authentication объект и вызывает AuthenticationManager.authenticate(my_custom_authentication).

Примечание: я не напрямую манипулирую SecurityContextHolder.getContext() где-нибудь в моем коде.

Проблема в том, что контекст безопасности не обновляется после успешной аутентификации, и когда я пытаюсь получить доступ к SecurityContextHolder.getContext() в методе постобработки весеннего перехватчика, я вижу, что это не мой Authentication объект из пользовательского AuthenticationProvider Я получил после успешной аутентификации.

Вот несколько вопросов:

  1. Можно ли получить прямой доступ к SecurityContextHolder.getContext() для редактирования? Это неправильно и глупо делать что-то подобное.
  2. Я думал, что AuthenticationManager самостоятельно обрабатывает обновления контекста безопасности. Я имею в виду, что мы вызываем AuthenticationManager.authenticate(my_custom_authentication), и это обновляет контекст при успешной аутентификации. Если это не так, и мы должны поддерживать контекст безопасности в актуальном состоянии вручную, почему нам даже нужно иметь AuthenticationManager?

Можете ли вы указать мне на мои ошибки, если таковые имеются?
Также, если есть классическая реализация входа в систему и безопасности на основе rest api spring, было бы очень приятно взглянуть на это, как кажется, что есть Многое нужно сделать, чтобы это работало должным образом.

РЕДАКТИРОВАТЬ Вот пример проекта , представляющий мою проблему.

Ответы [ 2 ]

1 голос
/ 28 января 2020

Если вы хотите пропустить автоматическую настройку Spring Security, добавьте класс @Configuration с @EnableWebSecurity. Не регистрируйте пустое AuthenticationManager.

Добавьте компонент для своего провайдера и зарегистрируйте его с помощью AuthenticationManager через AuthenticationManagerBuilder.

@Configuration
@EnableWebSecurity
public void WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

  public void configure(HttpSecurity http) { ... // Security config here}

  @Bean
  public CustomAuthenticationProvider customProvider() {
    return new CustomAuthenticationProvider();
  }

  @Autowired
  public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(customProvider());
  }

}

Это настроит AuthenticationManager на использование настроенного вами AuthenticationProvider. Если вы используете последнюю версию Spring Security (не уверен, какая версия включает это), достаточно просто настроить свой пользовательский AuthenticationProvider, который будет автоматически обнаружен AuthenticationManagerBuilder. См. Также Spring Security Documentation .

SecurityContext устанавливается цепочкой фильтров (например, через UsernamePasswordAuthenticationFilter, который делегирует аутентификацию AuthenticationManager.

Обновление

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

public CustomAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

  private final ObjectMapper mapper;

  public CustomAuthenticationFilter(ObjectMapper mapper) {
    super(new AntPathRequestMatcher("/auth/login", "POST"));
    this.mapper=mapper;
  }

  public Authentication attemptAuthentication(HttpServletRequest request,
            HttpServletResponse response) throws AuthenticationException, IOException,
            ServletException {

      MyPrincipal principal = mapper.readValue(request.getInputStream(), MyPrincipal.class);
      MyAuthentication authentication = new MyAuthentication(principal);
      setDetails(request, authentication); //assuming you are extending AbstractAuthenticationToken
      return getAuthenticationManager().authenticate(authentication);

   }

    protected void setDetails(HttpServletRequest request,
            MyAuthentication authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

}

В вашей конфигурации добавьте этот фильтр в Spring Security и настройте фильтр соответствующим образом.

@Bean
public CustomAuthenticationFilter customAuthenticationFilter(ObjectMapper mapper) {
    CustomAuthenticationFilter filter = new CustomAuthenticationFilter(mapper);
    filter.setAuthenticationManager(authenticationManagerBean());
    return filter;
}

// To prevent registering the filter in the default filter chain!
@Bean
public FilterRegistrationbean customAuthenticationFilterRegistration() {
    FilterRegistrationBean filterReg = new FilterRegistrationBean(customAuthenticationFilter());
    filterReg.setEnabled(false);
    return filterReg;
}

@Override
public void configure(HttpSecurity http) {

    http.addFilterBefore(customerAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    // other config here
}

Теперь ваша аутентификация является частью Spring Security.

1 голос
/ 27 января 2020

У вас есть два варианта.

1 - Обновление SecurityContext В точке входа (Вход в систему)

  • Вы можете обновите SecurityContext в вашем сервисе. (Когда процесс аутентификации завершится, обновите SecurityContext.

    Я закодировал этот сценарий следующим образом

    @Service
    public class CustomLoginService {
    
    @Autowired
    private AuthenticationManager authenticateManager;
    
    public void customLogin(HttpServletRequest request, String username, String password) {
    
        //Custom authentication via your provider
        UsernamePasswordAuthenticationToken authReq = new UsernamePasswordAuthenticationToken(username, password);
        Authentication auth = authenticateManager.authenticate(authReq);
    
        //Update SecurityContext with authentication information (auth)
        SecurityContext securityContext = SecurityContextHolder.getContext();
        securityContext.setAuthentication(auth);
    
        //Set updated securityContext into session of user
        HttpSession session = request.getSession(true);
        session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, securityContext);
    
        }
    
    }
    

2 - SecurityContext обновляется автоматически в CustomAuthenticationProvider (отредактировано)

  • Когда CustomAuthenticationProvider возвращает UsernamePasswordAuthenticationToken, SecurityContext обновляется автоматически (например, сохраняются принципы пользователь)

    Я закодировал этот сценарий следующим образом

    @Component
    public class CustomAuthenticationProvider implements AuthenticationProvider {
    
    
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    
    
        //Get inputs
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();
    
    
        //Authenticate against the third-party system (VIA LDAP, WEB SERVICE ,...)
        if (AuthenticateAgainstThirdPartySystem()) {
    
            //Authentication is OK then create token
            //SecurityContext get updated automatically
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password, new ArrayList<>());
    
            return authenticationToken;
    
        } else {
            return null;
        }
    
    }
    
    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
       }
     }
    

Также Эта ссылка может быть полезно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...