#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;