Catching Remember-Me Authentication Events in Spring Security

I am developing an application in which I need to catch and respond to authentication events in order to take appropriate action. Currently, I understand perfectly what AuthenticationSuccessEvent Spring issues when a user logs in manually. Now I am trying to implement the Remember-Me function. Logging helped me figure out the event I want to catch is InteractiveAuthenticationSuccessEvent . Can someone take gander into the code below and help me answer this new event?

 @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(); } } } 

Additional Information:

I did not mention in the original publication that the requirement to save the session identifier and authentication object on the map is due to the fact that I use the Google Earth plugin. GE acts as a separate, unrelated user agent, and thus, user session information is never transmitted to the GE server. For this reason, I am rewriting the request URL from GE to contain the active user session identifier (from the aforementioned Map) as a parameter, so we can verify that the specified session identifier is indeed valid for the registered user. All this happens because we have the KML that GE needs, but we cannot allow the user to receive a direct, insecure URL through Firebug or whatever you have.

Spring Configuration: (sorry, SO seems to have falsified the formatting)

 <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> 
+7
source share
2 answers

According to spring docs , "In Spring Security 3, the user first authenticates with the AuthenticationManager, and after it is successfully completed, the session is created."

Instead, you can implement your own AuthenticationSuccessHandler (perhaps by subclassing SavedRequestAwareAuthenticationSuccessHandler ). You can put any logic in the onAuthenticationSuccess method, so move the existing logic there:

 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); } } 

Then update your configs so that Spring Security calls this class during the authentication process. Here's how:

Step 1: Configure the UsernamePasswordAuthenticationFilter that is created by the <form-login> element. In particular, put this in your <http> element:

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

Step 2: Define myFilter and hook 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> 

See http://static.springsource.org/spring-security/site/docs/3.0.x/reference/ns-config.html for more details. Also see AbstractAuthenticationProcessingFilter .

By the way, your problem reminds me of OAuth. In fact, you issue an access token to the client as a result of authorization of the resource owner.

+2
source

Read the update at the bottom of this post.

Have you tried just adding another "else if" based on an "instance of the InteractiveAuthenticationSuccessEvent event"?

  @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(); } } } 

UPDATE:. Your question is basically: "How can I get one http client (i.e. Google Earth plugin) for authentication on my site as someone who logged in using another http client (user browser)?" Even if you can make it work, it doesn't seem like a good idea from a security point of view. Another interesting question: "How to download KML to the Google Earth plugin, except how to request a KML plugin file via http?" According to their docs, there is a parsekml () method that takes a string containing KML data. Thus, theoretically, you can load protected KML data by calling JavaScript / AJAX from the user's browser, which will be compatible with the normal security setting of your site, and then pass the returned KML to parsekml ().

+4
source

All Articles