Перехват событий проверки подлинности «Помни меня» в Spring Security - PullRequest
7 голосов
/ 16 августа 2011

Я разрабатываю приложение, в котором мне нужно перехватывать события аутентификации и отвечать на них, чтобы предпринять соответствующие действия. В настоящее время я отлично ловлю AuthenticationSuccessEvent Spring, когда пользователь входит в систему вручную. Сейчас я пытаюсь реализовать функциональность Remember-Me. Ведение журнала помогло мне определить событие, которое я хочу поймать, это InteractiveAuthenticationSuccessEvent. Может ли кто-нибудь взглянуть на приведенный ниже код и помочь мне ответить на это новое событие?

@Override
public void onApplicationEvent(ApplicationEvent event) {
    log.info(event.toString()); // debug only: keep track of all events
    if (event instanceof AuthenticationSuccessEvent) {
        AuthenticationSuccessEvent authEvent = (AuthenticationSuccessEvent)event;
        lock.writeLock().lock();
        try {
            sessionAuthMap.put(((WebAuthenticationDetails)authEvent.getAuthentication().getDetails()).getSessionId(), authEvent.getAuthentication());
        } finally {
            lock.writeLock().unlock();
        }
    } else if (event instanceof HttpSessionDestroyedEvent) {
        HttpSessionDestroyedEvent destroyEvent = (HttpSessionDestroyedEvent)event;
        lock.writeLock().lock();
        try {
            sessionAuthMap.remove(destroyEvent.getId());
        } finally {
            lock.writeLock().unlock();
        }
    }
}

Дополнительная информация:

Я не упомянул в исходной публикации, что требование сохранения объекта Session Id и Authentication на карте связано с тем, что я использую плагин Google Earth. GE действует как отдельный, не связанный пользовательский агент, и, таким образом, информация о сеансе пользователя никогда не передается на сервер GE. По этой причине я переписываю URL-адрес запроса от GE, чтобы в качестве параметра он содержал активный идентификатор сеанса пользователя (из вышеупомянутой карты), чтобы мы могли убедиться, что указанный идентификатор сеанса действительно действителен для вошедшего в систему пользователя. Все это на месте, потому что у нас есть KML, который нужен GE, но мы не можем позволить пользователю выбрать прямой, незащищенный URL-адрес через Firebug или что-то еще.

Spring Config: (извините, ТАК что-то подделало форматирование)

<sec:http use-expressions="true">
<sec:intercept-url pattern="/Login.html*" access="permitAll"/>
<sec:intercept-url pattern="/j_spring_security*" access="permitAll" method="POST"/>
<sec:intercept-url pattern="/main.css*" access="permitAll"/>
<sec:intercept-url pattern="/favicon.ico*" access="permitAll"/>
<sec:intercept-url pattern="/images/**" access="permitAll"/>
<sec:intercept-url pattern="/common/**" access="permitAll"/>
<sec:intercept-url pattern="/earth/**" access="permitAll"/>
<sec:intercept-url pattern="/earth/kml/**" access="permitAll"/>
<sec:intercept-url pattern="/earth/js/**" access="permitAll"/>
<sec:intercept-url pattern="/css/**" access="permitAll"/>   
<sec:intercept-url pattern="/resource*" access="permitAll"/>
<sec:intercept-url pattern="/geom*" access="hasRole('ROLE_SUPERUSER')"/>    
<sec:intercept-url pattern="/status/**" access="permitAll"/>    
<sec:intercept-url pattern="/index.html*" access="hasRole('ROLE_USER')"/>
<sec:intercept-url pattern="/project.html*" access="hasRole('ROLE_USER')"/>
<sec:intercept-url pattern="/js/**" access="hasRole('ROLE_USER')"/>
<sec:intercept-url pattern="/help/**" access="hasRole('ROLE_USER')"/>
<sec:intercept-url pattern="/app/**" access="hasRole('ROLE_USER')"/>
<sec:intercept-url pattern="/data/**" access="hasRole('ROLE_USER')"/>   
<sec:intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')"/> 
<sec:intercept-url pattern="/session/**" access="hasRole('ROLE_USER')"/>
<sec:intercept-url pattern="/" access="hasRole('ROLE_USER')"/>
<sec:intercept-url pattern="/**" access="denyAll"/>
<sec:intercept-url pattern="**" access="denyAll"/>

<sec:session-management session-fixation-protection="none" />

<sec:form-login login-page="/Login.html${dev.gwt.codesrv.htmlparam}" default-target-url="/index.html${dev.gwt.codesrv.htmlparam}" authentication-failure-url="/Login.html${dev.gwt.codesrv.htmlparam}"/>
<sec:http-basic/>
<sec:logout invalidate-session="true" logout-success-url="/Login.html${dev.gwt.codesrv.htmlparam}"/>
 <sec:remember-me key="[REMOVED]" />
 </sec:http>

<bean id="authenticationEventPublisher" class="org.springframework.security.authentication.DefaultAuthenticationEventPublisher" />

<bean id="org.springframework.security.authenticationManager" class="org.springframework.security.authentication.ProviderManager">
    <property name="authenticationEventPublisher" ref="authenticationEventPublisher"/>
    <property name="providers">
        <list>
            <ref bean="authenticationProvider" />
            <ref bean="anonymousProvider" />
        </list>
    </property>
</bean>

<bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
    <property name="passwordEncoder" ref="passwordEncoder"/>
    <property name="saltSource" ref="saltSource"/>
    <property name="userDetailsService" ref="userService" />
</bean>

<bean id="anonymousProvider" class="org.springframework.security.authentication.AnonymousAuthenticationProvider">
    <property name="key" value="[REMOVED]" />
</bean>

Ответы [ 2 ]

4 голосов
/ 17 августа 2011

Пожалуйста, прочтите обновление в нижней части этого поста

Вы пытались просто добавить еще одно «else if» на основе «экземпляра события InteractiveAuthenticationSuccessEvent»?

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
    log.info(event.toString()); // debug only: keep track of all events
    if (event instanceof AuthenticationSuccessEvent) {
        AuthenticationSuccessEvent authEvent = (AuthenticationSuccessEvent)event;
        lock.writeLock().lock();
        try {
            sessionAuthMap.put(((WebAuthenticationDetails)authEvent.getAuthentication().getDetails()).getSessionId(), authEvent.getAuthentication());
        } finally {
            lock.writeLock().unlock();
        }
    } else if (event instanceof InteractiveAuthenticationSuccessEvent) {
        InteractiveAuthenticationSuccessEvent authEvent = (InteractiveAuthenticationSuccessEvent)event;
        lock.writeLock().lock();
        try {
            sessionAuthMap.put(((WebAuthenticationDetails)authEvent.getAuthentication().getDetails()).getSessionId(), authEvent.getAuthentication());
        } finally {
            lock.writeLock().unlock();
        }
    } else if (event instanceof HttpSessionDestroyedEvent) {
        HttpSessionDestroyedEvent destroyEvent = (HttpSessionDestroyedEvent)event;
        lock.writeLock().lock();
        try {
            sessionAuthMap.remove(destroyEvent.getId());
        } finally {
            lock.writeLock().unlock();
        }
    }
}

ОБНОВЛЕНИЕ: Ваш вопрос в основном таков: «Как я могу заставить одного http-клиента (например, плагин Google Планета Земля) выглядеть аутентифицированным на моем сайте как пользователь, вошедший в систему с помощью другого http-клиента (браузер пользователя) )?» Даже если бы вы могли заставить это работать, это не кажется хорошей идеей с точки зрения безопасности. Еще один интересный вопрос: «Как я могу загрузить KML в плагин Google Планета Земля, если только плагин не запрашивает файл KML через http?» Согласно их документам, существует метод parsekml (), который принимает строку, содержащую данные KML. Таким образом, теоретически вы можете загрузить защищенные данные KML с помощью вызова JavaScript / AJAX из браузера пользователя, который будет совместим с обычными настройками безопасности вашего сайта, а затем передать возвращенный KML в parsekml ().

2 голосов
/ 24 августа 2011

Согласно Spring Docs , «В Spring Security 3 пользователь сначала проходит проверку подлинности с помощью AuthenticationManager, и после успешной проверки подлинности создается сеанс».

Вместо этого вы можете реализовать свой собственный AuthenticationSuccessHandler (возможно, создав подкласс SavedRequestAwareAuthenticationSuccessHandler). Вы можете поместить любую логику в метод onAuthenticationSuccess, поэтому перенесите туда существующую логику:

public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    // declare and initialize lock and sessionAuthMap at some point...
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, 
            HttpServletResponse response, Authentication authentication) 
            throws ServletException, IOException {
        lock.writeLock().lock();
        try {
            sessionAuthMap.put(request.getSession().getId(), authentication);
        } finally {
            lock.writeLock().unlock();
        }
        super.onAuthenticationSuccess(request, response, authentication);
    }
}

Затем обновите свои конфигурации, чтобы Spring Security вызывал этот класс во время процесса аутентификации. Вот как это сделать:

Шаг 1: настройте UsernamePasswordAuthenticationFilter, созданный элементом <form-login>. В частности, поместите это в ваш элемент <http>:

<sec:custom-filter position="FORM_LOGIN_FILTER" ref="myFilter" />

Шаг 2: определите myFilter и подключите к нему MyAuthenticationSuccessHandler.

<bean id="myFilter" 
    class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
    <property name="authenticationManager" ref="authenticationManager" />
    <property name="authenticationFailureHandler" ref="myAuthenticationSuccessHandler" />
    <property name="authenticationSuccessHandler" ref="myAuthenticationFailureHandler" />
</bean>

<bean id="myAuthenticationSuccessHandler" 
    class="my.MyAuthenticationSuccessHandler">
<!-- set properties here -->
</bean>

<!-- you can subclass this or one of its parents, too -->
<bean id="myAuthenticationFailureHandler" 
    class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
    <!-- set properties such as exceptionMappings here -->
</bean>

Подробнее см. http://static.springsource.org/spring-security/site/docs/3.0.x/reference/ns-config.html. Также см. AbstractAuthenticationProcessingFilter документы.

Кстати, ваша проблема напоминает мне об OAuth. По сути, вы выдаете клиенту токен доступа в результате авторизации владельца ресурса.

...