Java 在Spring Security中捕获“记住我”身份验证事件

Java 在Spring Security中捕获“记住我”身份验证事件,java,spring-security,kml,remember-me,google-earth-plugin,Java,Spring Security,Kml,Remember Me,Google Earth Plugin,我正在开发一个应用程序,在这个应用程序中,我需要捕获并响应身份验证事件以采取适当的操作。目前,我很好地捕捉到了当用户手动登录时Spring抛出的AuthenticationSuccessEvent。我现在正在尝试实现记住我的功能。日志记录帮助我确定了我想要捕获的事件是InteractiveAuthenticationSuccessEvent。有人能看一下下面的代码,帮我对这个新事件做出反应吗 @Override public void onApplicationEvent(Application

我正在开发一个应用程序,在这个应用程序中,我需要捕获并响应身份验证事件以采取适当的操作。目前,我很好地捕捉到了当用户手动登录时Spring抛出的
AuthenticationSuccessEvent
。我现在正在尝试实现记住我的功能。日志记录帮助我确定了我想要捕获的事件是
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 Earth插件。GE充当一个独立的、不相关的用户代理,因此用户的会话信息永远不会被GE传递到服务器。出于这个原因,我重写了来自GE的请求URL,以包含用户的活动会话Id(来自前面提到的映射)作为参数,以便我们可以验证所述会话Id对于登录的用户确实有效。所有这些都到位了,因为我们有通用电气需要的KML,但我们不能允许用户通过Firebug或其他方式获取直接的、不受保护的URL

Spring配置:(抱歉,有点篡改了格式)


请阅读本文底部的更新内容

您是否尝试过根据“InteractiveAuthenticationSuccessEvent的事件实例”添加另一个“else if”

更新:您的问题基本上是,“如何让一个http客户端(即Google Earth插件)在我的站点上显示为使用另一个http客户端(用户的浏览器)登录的人的身份验证?”即使您可以让它工作,从安全角度看,这似乎不是一个好主意。另一个有趣的问题是,“除了让插件通过http请求KML文件之外,如何将KML加载到Google Earth插件中?”根据他们的文档,有一个方法parsekml(),它接受包含KML数据的字符串。因此,理论上,您可以使用用户浏览器中的JavaScript/AJAX调用加载受保护的KML数据,这将与您站点的正常安全设置兼容,然后将返回的KML传递给parsekml(),在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:自定义由
元素创建的
用户名密码身份验证筛选器
。特别是,将其放入
元素:

<sec:custom-filter position="FORM_LOGIN_FILTER" ref="myFilter" />
有关更多详细信息,请参阅。另请参阅文档


顺便说一句,您的问题让我想起了OAuth。本质上,您是由于资源所有者授权而向客户端发出访问令牌的。

我已经尝试过了。这种方法的问题是,
InteractiveAuthenticationSuccessEvent中的sessionId始终为空。
触发该事件时,会话id确实为空。你需要它做什么?你可能需要跳过一些障碍来获取会话id,比如当你处理InteractiveAuthenticationSuccessEvent时,你在某个地方设置了某种标志,以表明以后一旦会话id可用,你需要以某种方式在其他代码中获取会话id。也许你可以从InteractiveAuthenticationSuccessEvent中存储用户名nticationSuccessEvent,然后当ServletRequestHandledEvents进入时,检查传入事件中的用户名是否是您存储在某处的用户名,如果是,则从传入事件中获取会话id并执行您需要的操作。存储用户名的问题是,我们将非常容易受到只知道va的人的攻击lid用户名,并幸运地在该用户确实登录时访问我们的服务器。我曾考虑在会话中使用UUID,但后来我们一直在传递UUID,情况也不比当前的好多少。好的,我现在正在阅读您对jtoberon的解释,并开始了解,很抱歉我没有看到这些。我认为ink你和他正在寻找解决方案。我的建议是在不知道你问题的更大背景的情况下提出的,我认为这不适用于你的情况。如果你编辑原始问题以提供有关Google Earth插件、KML文件和你的安全请求的背景,它可能会帮助人们查看此线程我们之所以在地图中存储基于会话id的身份验证,是因为我们使用的是Google Earth插件,它充当自己的用户代理,不会从浏览器会话传递任何会话或cookie信息。因为我们必须保护GE检索的KML文档,所以我们重写了GE的请求URL以包含用户的会话ID作为参数。当请求来自GE时,我们必须从地图中获取身份验证对象,以确认用户确实有权访问我们的KML。如果没有,我们无法将数据发送回。有意义吗?P.s.我意识到这不是最安全的选项,但这是我们必须与您合作的直到我能想出一个更好的方法来保护KML。我们不能让别人观看请求(通过firebug或其他方式)并获取KML的URL,如果我们不以这种方式保护它,可以下载该URL。这是有道理的。我相信上面的响应仍然是稳定的
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);
    }
}
<sec:custom-filter position="FORM_LOGIN_FILTER" ref="myFilter" />
<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>