在 Spring Security 中捕获 Remember-Me 身份验证事件
我正在开发一个应用程序,我需要在其中捕获并响应身份验证事件以采取适当的操作。目前,我很好地捕获了用户手动登录时 Spring 抛出的 AuthenticationSuccessEvent
。我现在正在尝试实现 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();
}
}
}
其他信息:
我在原始帖子中没有提到在地图中存储会话 ID 和身份验证对象的要求是因为我正在使用 Google 地球插件。 GE 充当单独的、不相关的用户代理,因此用户的会话信息永远不会被GE 传递到服务器。因此,我重写了来自 GE 的请求 URL,以包含用户的活动会话 Id(来自上述映射)作为参数,以便我们可以验证所述会话 Id 对于登录用户确实有效。所有这些都已到位,因为我们有 GE 需要的 KML,但我们不能允许用户通过 Firebug 或其他方式获取直接的、不受保护的 URL。
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>
I'm developing an application in which I need to catch and respond to Authentication events to take appropriate action. Currently, I'm catching just fine the AuthenticationSuccessEvent
Spring throws when a user logs in manually. I'm now trying to implement Remember-Me functionality. Logging helped me to figure out the the event I want to catch is the InteractiveAuthenticationSuccessEvent
. Can someone take a gander at the code below and help me to respond to 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 didn't mention in the original posting that the requirement of storing the Session Id and Authentication object in a Map is due to the fact that I'm using the Google Earth plugin. GE acts as a separate, unrelated user agent, and thus the user's session information never gets passed to the server by GE. For this reason, I rewrite the request URL from GE to contain the user's active Session Id (from the aforementioned Map) as a parameter so we can verify that said Session Id is indeed valid for a logged in user. All of this is in place because we have KML which GE needs, but we can't allow a user to pick up a direct, unprotected URL via Firebug or what have you.
Spring Config: (sorry, SO kinda fudged 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>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
请阅读本文底部的更新
您是否尝试过基于“InteractiveAuthenticationSuccessEvent 的事件实例”添加另一个“else if”?
更新:您的问题基本上是:“如何让一个 http 客户端(即 Google 地球插件)在我的网站上显示为使用另一个 http 客户端(用户的浏览器)登录的用户的身份验证? ”即使你可以让它发挥作用,从安全角度来看,这似乎也不是一个好主意。另一个有趣的问题是,“除了让插件通过 http 请求 KML 文件之外,如何将 KML 加载到 Google Earth 插件中?”根据他们的文档,有一个方法,parskml(),它接受包含 KML 数据的字符串。因此,理论上您可以使用用户浏览器中的 JavaScript/AJAX 调用加载受保护的 KML 数据,这将与您网站的正常安全设置兼容,然后将返回的 KML 传递给 parsekml()。
Please read the update at bottom of this post
Have you tried just adding another "else if" based on "event instance of InteractiveAuthenticationSuccessEvent"?
UPDATE: Your question is basically, "How can I get one http client (i.e. the Google Earth plugin) to appear authenticated to my site as someone who logged in using another http client (the user's browser)?" Even if you could get that to work, it doesn't seem like a good idea, security-wise. Another interesting question would be, "How can I load KML into the Google Earth plugin other than by having the plugin request the KML file over http?" According to their docs, there is a method, parsekml(), which takes a String containing KML data. So in theory you could load the protected KML data using a JavaScript/AJAX call from the user's browser, which would be compatible with your site's normal security setup, then pass the returned KML to parsekml().
根据 spring 文档 ,“在 Spring Security 3 中,用户首先由 AuthenticationManager 进行身份验证,一旦成功通过身份验证,就会创建一个会话。”
相反,您可以实现自己的 AuthenticationSuccessHandler(可能通过子类化 SavedRequestAwareAuthenticationSuccessHandler)。您可以将任何您想要的逻辑放入
onAuthenticationSuccess
方法中,因此将现有逻辑移至此处:然后,更新您的配置,以便 Spring Security 在身份验证过程中调用此类。具体方法如下:
第 1 步:自定义由
元素创建的UsernamePasswordAuthenticationFilter
。特别是,将其放入
元素中:步骤 2:定义 myFilter,并将
MyAuthenticationSuccessHandler
挂钩到其中。有关更多详细信息,请参阅 http:// static.springsource.org/spring-security/site/docs/3.0.x/reference/ns-config.html。另请参阅 AbstractAuthenticationProcessingFilter 文档。
顺便说一句,你的问题让我想起了 OAuth。本质上,您是根据资源所有者授权向客户端颁发访问令牌的。
According to the spring docs, "In Spring Security 3, the user is first authenticated by the AuthenticationManager and once they are successfully authenticated, a session is created."
Instead, you could implement your own
AuthenticationSuccessHandler
(probably by subclassingSavedRequestAwareAuthenticationSuccessHandler
). You can put whatever logic you want in theonAuthenticationSuccess
method, so move your existing logic there:Then, update your configs so that Spring Security invokes this class during the authentication process. Here's how:
Step 1: customize the
UsernamePasswordAuthenticationFilter
which is created by the<form-login>
element. In particular, put this into your<http>
element:Step 2: define myFilter, and hook
MyAuthenticationSuccessHandler
into it.For more details, see http://static.springsource.org/spring-security/site/docs/3.0.x/reference/ns-config.html. Also see the AbstractAuthenticationProcessingFilter docs.
BTW your problem reminds me of OAuth. Essentially you're issuing an access token to the client as a result of the resource owner authorization.