Spring-security: приложение делится веб-страницами и REST API - PullRequest
0 голосов
/ 10 апреля 2020

В настоящее время я работаю над приложением Spring- MVC, в котором размещены мои html веб-страницы, а также (своего рода) REST API. Проблема состоит в том, что в зависимости от того, что обращается к приложению, ошибка аутентификации должна обрабатываться по-разному:

  • , если пользователь заходит на страницы с помощью своего браузера, он должен быть перенаправлен на страницу входа (302, которой управляют браузером)
  • если это компонент внутри страницы (в моем случае это jquery с датой), который пытается загрузить следующую страницу результата через Ajax, я должен получить статус 401, чтобы разрешить мне интерпретировать его в моем JS и перенаправить на страницу входа.

Но на самом деле приложение всегда возвращает 302, который обрабатывается браузером, не давая моей Javascript возможности обрабатывать это правильно.

Из моего чтения я обнаружил, что мне нужно определить 2 WebSecurityConfigurerAdapter, я сделал это, один для / api, а другой для веб-страниц, но он продолжает отправлять перенаправление 302 в обоих случаях .

Моя конфигурация API:

@Configuration
@Slf4j
@Order(SecurityProperties.BASIC_AUTH_ORDER - 5)
public class RestSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(final HttpSecurity http) throws Exception {
    http
    .csrf()
    .disable()
    .authorizeRequests()
    .antMatchers("/api/**")
    .authenticated();

}
}

И веб-конфигурация:

@Configuration
@Slf4j
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(final HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
        .antMatchers("/action/**", "/login", "/css/**")
        .permitAll()
        .anyRequest().authenticated().and()
        .formLogin()
        .loginPage("/login")
        .and()
        .logout()
        .logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
}
...
}

Может ли кто-нибудь помочь мне разобраться? и что не так?

Я видел некоторые решения, которые читают содержание ответа Ajax и перенаправляют, если внутри есть какой-то html, но я думаю, что он должен обрабатываться правильно на стороне сервера.

Ответы [ 2 ]

1 голос
/ 11 апреля 2020

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

@Configuration
@Slf4j
@Order(99)
public class RestSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http
            .requestMatchers()
                .antMatchers("/api/**")
                .and()
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .httpBasic(); // <-- use HTTP basic
    }
}

Также обратите внимание на небольшую разницу между requestMatchers и authorizeRequests. Если у вас есть разные WebSecurityConfigurerAdapter s, они сегментируются по пути с использованием requestMatchers.

. Значение приведенного выше фрагмента кода «для запросов, которые соответствуют / api / **, аутентифицируют любой запрос с использованием HTTP-основы» 1025 *. "

Для веб-безопасности мы хотим обрабатывать« все остальное », поэтому нам не нужен requestMatchers(). Итак, чтобы сделать то же самое для вашей веб-безопасности, вы должны сделать:

@Configuration
@Slf4j
@Order(100)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/action/**", "/login", "/css/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .and()
        // ...
        ;
    }
}

Смысл приведенного выше фрагмента кода - «аутентифицировать любой запрос с помощью входа в систему».

Наконец, Spring Безопасность будет обрабатывать WebSecurityConfigurerAdapter с по возрастанию @Order. Итак, обратите внимание, что первый - «99», а второй - «100». Это означает, что RestSecurityConfig будет обработан до WebSecurityConfig. Вы можете думать об этом как о выражении if:

if (request matches /api) {
   check the `RestSecurityConfig` configuration
} else {
   check the `WebSecurityConfig` configuration
}
1 голос
/ 10 апреля 2020

Вам нужна пользовательская точка входа для аутентификации отдыха:

@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(
        HttpServletRequest request,
        HttpServletResponse response,
        AuthenticationException authException) throws IOException {

    response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized" );
   }
}

также зарегистрируйте ее в httpsecurity в вашей реализации WebSecurityConfigurerAdapter, например:

@Autowired
RestAuthenticationSuccessHandler restAuthenticationSuccessHandler;

@Override
protected void configure(HttpSecurity http) throws Exception{
       http.
          ...
          .exceptionHandling()
          .authenticationEntryPoint(restAuthenticationEntryPoint)
          .formLogin().loginProcessingUrl("/doLogin")
}

, поэтому вы будете обрабатывать 401 неавторизованным в фронт-приложении. Также нет необходимости в .loginPage("/login"), поскольку у вас есть REST, в противном случае вы будете перенаправлены и при указанной выше конфигурации вы не будете перенаправлены (302), вместо этого он отправляет 401 приложению, запущенному в вашем браузере. В вашем фронт-приложении вы опубликуете учетные данные на "/doLogin".

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