Spring Security AnonymousAuthFilter с PreAuthenticationFilter, разрешающим несанкционированные запросы

У меня довольно упрощенная конфигурация Spring Security в моем веб-приложении Spring MVC. Я получаю REST-запросы от удаленного веб-приложения, которые аутентифицируются с помощью моего фильтра предварительной аутентификации. Я просто ищу заголовок в запросе и, если он присутствует, возвращаю это строковое значение (которое является именем пользователя) в качестве объекта аутентификации. Это отлично работает, и когда приходит запрос, пользователь проходит аутентификацию.

По какой-либо причине, каждый раз, когда я пытаюсь применить аннотированную безопасность antMatcher или метода контроллера на основе шаблона, это приводит к тому, что клиент получает ошибку CORS. Я должен держать доступ полностью открытым, чтобы фактический запрос мог выполняться в этой междоменной среде.

Моя проблема в том, что через некоторое время сеанс, кажется, истекает. Когда пользователь нажимает на один из моих контроллеров, и я пытаюсь получить имя пользователя из объекта Principal, я получаю исключение NullPointerException, потому что Principal имеет значение null. По истечении срока действия похоже, что AnonymousAuthFilter срабатывает и просто позволяет пользователю подключаться как анонимному.

Я предполагаю, что мне не нужен полностью сеанс аутентификации без сохранения состояния, потому что я также использую сеанс для веб-сокетов, и когда я использую свой шаблон обмена сообщениями для «convertAndSendToUser», мне нужно указать имя пользователя, а Spring должен найти сеанс веб-сокета. Тем не менее, я хотел бы заставить цепочку безопасности распознать, не назначен ли входящий запрос принципалу, чтобы заставить запрос вернуться через мой PreAuthFilter и восстановить сеанс.

Это возможно?

Вот краткое изложение моего метода настройки в моем файле конфигурации безопасности:

@Override
    protected void configure(HttpSecurity http) throws Exception {
        // Create authentication providers and authentication manager
        List<AuthenticationProvider> authenticationProviders = new ArrayList<>(1);
        AuthenticationProvider authProvider = preAuthenticatedAuthenticationProvider();
        authenticationProviders.add(authProvider);
        AuthenticationManager authenticationManager = authenticationManager(authenticationProviders);

        // Create the pre-authentication filter
        MyPreAuthFilter myPreAuthFilter = myPreAuthFilter(authenticationManager);
        myPreAuthFilter.setAuthenticationManager(authenticationManager);

        // configure http security
        http
                .csrf().disable()
                .headers().addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN))
                .and()
                .authenticationProvider(authProvider)
                .addFilter(myPreAuthFilter)
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .antMatchers("/stomp/**").permitAll()
                .antMatchers("/**").permitAll();
    }

ОБНОВЛЕНИЕ: Итак, я только что узнал об этом методе на объекте http:

.anonymous().disable()

Однако, когда я использую это, все мои запросы CORS снова терпят неудачу. Я думаю, что проблема именно в предварительных запросах OPTIONS. Это не может быть аутентифицировано — оно должно быть анонимно доступно. Поэтому я думаю, что для получения нужного поведения от моей междоменной аутентификации мне нужно отключить анонимный фильтр, но это нарушает один из ключевых аспектов возможности выполнения междоменных запросов. Фу. Кто-нибудь знает, как отключить анонимность для всех запросов, кроме OPTIONS?


person Bal    schedule 15.07.2015    source источник


Ответы (1)


Вставьте CORS-фильтр перед цепочкой фильтров безопасности spring в инициализаторе вашего веб-приложения (или web.xml), чтобы устранить проблему CORS (разрешите источник запроса REST, установив Access-Control-Allow-Origin в соответствии с вашим запрос происхождения).

Затем вы можете использовать авторизацию на основе ролей (через средства сопоставления муравьев и аннотации JSR-250). Отключение анонимных пользователей не кажется хорошей идеей для предоставленного вами варианта использования.

person Abhinav Rai    schedule 16.07.2015
comment
Мы используем встроенный в Tomcat фильтр CORS, поэтому сервер всегда будет отвечать соответствующими заголовками в предварительном запросе OPTIONS. При установленном фильтре CORS у нас не возникнет проблем с CORS, если только я не попытаюсь реализовать элементы управления безопасностью для методов или шаблонов путей. - person Bal; 16.07.2015
comment
Ой! Извините за опечатку. Имелось в виду, что зарегистрируйте CorsFilter перед springSecurityFilterChain. Позвольте мне объяснить, когда вы реализуете свои элементы управления безопасностью, избиратели выражений безопасности Spring возвращают -1, и генерируется исключение AccessDeniedException (поскольку пользователь является анонимным после исключения NullPointerException). Это приводит к тому, что ваш запрос перенаправляется на точку входа аутентификации, которая отвечает 401 в вашем браузере. Это означает, что CorsFilter никогда не запускается, поскольку он находится за заголовком DelegatingFilterProxy, а Access-Control-Allow-Origin никогда не внедряется в ответ. - person Abhinav Rai; 16.07.2015
comment
Поэтому я не использую фильтр CORS уровня приложения, я использую встроенный фильтр CORS Tomcat на уровне контейнера, настроенный в файле Tomcat web.xml. При этом я поклялся, что пробовал это раньше, и это не сработало, но я просто изменил свой последний antMatcher с allowAll() на hasRole(USER). Теперь запросы OPTIONS проходят, но пользователь получит 403, если у него нет токена предварительной аутентификации в заголовке. Будет ли этот сценарий принудительно возвращать запрос через мой фильтр предварительной аутентификации после истечения срока действия сеанса? - person Bal; 17.07.2015
comment
Ой! Никогда не понимал, что вы будете использовать CorsFilter на уровне контейнера. Виноват. Отвечая на ваш вопрос, обычно AuthenticationEntryPoint вызывается ExceptionTranslationFilter, но все можно настроить для Сценарий предварительной аутентификации. Аутентификация заголовка запроса ( Siteminder) похоже на то, что вы ищете. Надеюсь это поможет :) - person Abhinav Rai; 17.07.2015
comment
Очень интересно... Итак, я расширяю AbstractPreAuthenticatedProcessingFilter и сам ищу нужный заголовок, что, похоже, именно то, что делает RequestHeaderAuthenticationFilter в этом примере. Итак, мой конфиг почти точно совпадает с этим примером... надеюсь, теперь у меня все будет хорошо! Большое спасибо за ваше понимание :) - person Bal; 17.07.2015