Spring Security с перекрывающимися конфигурациями проверяет авторизацию в нескольких блоках

#spring #spring-boot #spring-security

Вопрос:

У меня есть приложение, использующее Spring Security 4.2.18 с защитой, настроенной в xml. Прямо сейчас я представляю Spring Boot 2.5.4 в проекте. После обновления у меня возникла проблема с настройкой безопасности для некоторых запросов.

У меня есть блок, соответствующий конкретным запросам, и один, соответствующий остальным всем запросам.

 lt;http pattern="/api/**" use-expressions="true" authentication-manager-ref="apiAuthenticationManager" gt;  lt;http-basic/gt;   lt;intercept-url pattern="/api/**" access="hasRole('ROLE_API')"/gt;  lt;csrf disabled="true"/gt; lt;/httpgt;   lt;http pattern="/**" auto-config="true" use-expressions="true" create-session="always" disable-url-rewriting="true"  authentication-manager-ref="authenticationManager"gt;  lt;form-login login-processing-url="/resources/j_spring_security_check" login-page="/login"  username-parameter="j_username"  password-parameter="j_password"  authentication-failure-url="/login?login_error=t" default-target-url="/redirectlogin"/gt;  lt;logout logout-url="/resources/j_spring_security_logout"/gt;   ...   lt;intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/gt;   ...  lt;/httpgt;  lt;authentication-manager id="apiAuthenticationManager"gt;  lt;authentication-provider user-service-ref="apiUserDetailsService"gt;  lt;/authentication-providergt; lt;/authentication-managergt;   

Далее ApiUserDetailsService следует спецификация:

 @Service @Transactional public class ApiUserDetailsService implements UserDetailsService {   ...    @Override  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {  boolean foundAccount = apiConfig.getUsername().equals(username);  if (foundAccount) {  return new User(username, apiConfig.getPassword(), singletonList(new SimpleGrantedAuthority("ROLE_API")));  }  throw new UsernameNotFoundException("Could not finder user with name "   username);  }  }  

Если я делаю запрос к чему-то ниже /api и использую неправильные базовые учетные данные аутентификации, я ранее получил 401 несанкционированный ответ. После обновления я перенаправляюсь /login и в конечном итоге попадаю в цикл перенаправления, так как там также используются учетные данные.

Если я удалю lt;intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/gt; во втором lt;httpgt; блоке, я получу ожидаемое поведение.

Как я могу настроить свое приложение так, чтобы мой запрос не пытался авторизоваться, используя правила во втором lt;httpgt; блоке? Я пробовал использовать an EntryPoint , но он не вызывается до того, как пользователь ошибочно авторизован с помощью метода универсального lt;httpgt; блока.

Комментарии:

1. Это может помочь отследить запрос по цепочке фильтров, включив ведение журнала трассировки для обеспечения безопасности Spring logging.level.org.springframework.security=TRACE . Я подозреваю, что запрос обрабатывается первым lt;httpgt; блоком, а затем перенаправление вызывает ввод во второй lt;httpgt; блок.

Ответ №1:

Вы пробовали ставить lt;intercept-url pattern="/login*" access="permitAll" /gt; прямо над своими lt;intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/gt; подобными:

Во втором блоке http:

 ... lt;intercept-url pattern="/login*" access="permitAll" /gt; lt;intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/gt; ...  

Комментарии:

1. Проблема не в том, что я не хочу видеть страницу /входа в систему, а в том, что мои вызовы api перенаправляются на /вход в систему, когда они должны получить 401 ответ.

Ответ №2:

Проблема заключалась не в том, что конфигурации перекрывались, а скорее в том, что неудачная аутентификация была перенаправлена, на /login которую, в свою очередь, была обработана второй lt;httpgt; конфигурацией.

Я решил эту проблему, создав реализацию AuthenticationEntryPoint , которая устанавливает 401 Unathorized статус ответа:

 @Component public class BasicAuthenticationEntryPoint implements AuthenticationEntryPoint {  @Override  public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) {  response.setStatus(HttpStatus.UNAUTHORIZED.value());  } }  

и, в свою очередь, ссылаюсь на это в моей базовой конфигурации аутентификации:

 lt;http pattern="/api/**" use-expressions="true" authentication-manager-ref="apiAuthenticationManager" gt;  lt;http-basic entry-point-ref="basicAuthenticationEntryPoint"/gt;   lt;intercept-url pattern="/api/**" access="hasRole('ROLE_API')"/gt;  lt;csrf disabled="true"/gt; lt;/httpgt;