博客
关于我
Spring Security 认证成功 AuthenticationSuccessEvent多次调用问题
阅读量:644 次
发布时间:2019-03-15

本文共 4389 字,大约阅读时间需要 14 分钟。

我注意到了你在使用Spring Security Oauth2认证成功处理时遇到的问题,具体来说是如何在每次认证请求中避免多次执行AuthenticationSuccessEvent的回调操作。让我来仔细分析你的场景及解决方案,并尽量提供一些优化意见。

你的代码中使用了ThreadLocal来完成旁路控制,这是一个值得肯定的思路,但在实际应用中可能会遇到一些问题,比如潜在的线程安全问题或难以追踪的内存泄漏。让我们一起探讨一下如何更高效地解决这个问题。

问题分析:

在使用Spring Security Oauth2进行认证授权成功的回调时,确实需要在认证成功时执行一些日志记录和业务更新操作。然而,由于事件监听机制的特性,每次认证请求可能会触发多次回调处理,导致意外的副作用,比如重复执行业务逻辑或重复记录日志。这种情况下,如果不进行有效的控制,可能会影响系统的稳定性和性能。

你尝试了使用ThreadLocal来实现对回调操作的旁路控制,但似乎仍然遇到了问题。这可能是因为ThreadLocal在实际应用场景下难以完全避免多次触发,或者在事件监听机制的多个点上无法有效地滚backor取消处理。

优化方案:

我不确定你是否已经尝试过这些方法,但让我们探讨一些更有效的处理方式。

1. 使用唯一的AuthenticationSuccessHandler实现单次执行

Spring Security提供了AuthenticationSuccessHandler接口,可以通过实现它来指定认证成功的回调操作。这种方式比直接监听AuthenticationSuccessEvent更为直接,同时也能更好地控制回调操作的唯一性。

你可以使用DefaultAuthenticationSuccessHandler作为基础实现,并在需要的时候扩展它。这样做的优势在于:

  • 通过setAuthorizeRequest.shoppingCartOrder或其他方法唯一标识你的回调操作
  • 自动避免重复调用回调处理逻辑
  • 更直观地控制哪些操作应该在认证成功时执行

示例代码如下:

import org.springframework.security.authentication.AuthenticationSuccessHandler;import org.springframework.security.authentication.loaders.AccountNotFoundException;public class ApplicationSuccessHandler implements AuthenticationSuccessHandler {    private UserLoginRecordService userLoginRecordService;    private UserServiceImpl userService;    public ApplicationSuccessHandler(UserLoginRecordService userLoginRecordService, UserServiceImpl userService) {        this.userLoginRecordService = userLoginRecordService;        this.userService = userService;    }    @Override    public void onAuthenticationSuccess(AuthenticationEvent event) {        // 在这里,你可以执行唯一的认证成功操作        // 例如,记录用户登录日志或触发业务更新逻辑        // 注意:这个方法将只在认证成功的时候被调用    }}

这种方式可以有效避免多次触发的问题,因为每次认证成功都将触发onAuthenticationSuccess方法,但如果这个方法本身的逻辑只执行一次操作,比如记录一次日志或触发一次业务更新,那么就不会存在多次执行的问题。

2. 使用简单的标记控制机制

你可以选择在认证成功回调中使用简单的标记来控制唯一性。避免使用ThreadLocal,更多地依赖于request或session中的状态,或者使用简单的锁机制。

这种方法简单且难以被破坏,同时也减少了相互之间的依赖关系。例如:

public class ApplicationListenerAuthencationSuccess implements ApplicationListener
{ @Autowired private UserLoginRecordService userLoginRecordService; @Autowired private HttpServletRequest request; private boolean isExclusive = false; @Override public void onApplicationEvent(AuthenticationSuccessEvent event) { // 检查当前请求是否已经被标记为处理过 boolean isHandling = request.getServletContext().getAttribute("isHandlingAuthenticationSuccess") != null; if (!isHandling) { request.getServletContext().setAttribute("isHandlingAuthenticationSuccess", true); // 执行你的认证成功操作 this.handleSuccessfulAuthentication(event); // 恢复状态 request.getServletContext().removeAttribute("isHandlingAuthenticationSuccess"); } else { // 如果已经有处理,跳过后续操作 logger.info("Authentication success handler is already processing another request"); } } protected void handleSuccessfulAuthentication(AuthenticationSuccessEvent event) { // 这里可以实现你的具体业务逻辑 }}

这种方法通过显式的标记控制,能够有效地避免多次执行认证成功回调逻辑。使用request.getServletContext().getAttribute()来获取和设置标记,可以有效避免Token校验或者其他内部操作在多个点上的触发。

3. 检查事件源和调用链

有时候,事件源或调用链可能会被多次触发。可以通过检查事件的来源来限制回调操作的执行范围。例如,在你的示例代码中,你可以检查事件是否是来自特定的组件,而不是在所有地方都触发回调逻辑。

@Overridepublic void onApplicationEvent(AuthenticationSuccessEvent event) {    if (tl.get().intValue() <= 0) {        // 检查事件源是否符合预期        if (event.getSource().getClass().getName().equals("org.springframework.security.authentication.UsernamePasswordAuthenticationToken")) {            // 执行你的认证成功操作        }    } else {        //重置ThreadLocal    }}

这意味着在每次认证成功时,都会自动检查事件源是否符合预期,从而避免不必要的处理。如果你能确保事件源唯一性,那么可以明确地知道哪些回调操作是原本的预期,而哪些则是因为系统内部原因触发的副作用。

4. 使用事件优先级来控制处理顺序

有时候,多个事件监听器会被同时触发,因为没有合理地设置优先级。这时候,事件会以先到先执行的方式处理,但在某些场景中可能会导致重复操作。

可以尝试为你的事件监听器设置@Order注解,这样可以在多个事件监听器中指定执行顺序。例如:

@Overridepublic void onApplicationEvent(AuthenticationSuccessEvent event) {    // 你的业务逻辑}

每个事件监听器都可以添加@Order注解,例如@Order(1),这样可以确保在事件处理流程中,某些特定的逻辑优先执行,而其他逻辑可以在后续处理。

总结

关于你的问题,我有如下几点建议:

  • Avoid使用ThreadLocal:ThreadLocal虽然是一种常用的工具,但在高并发场景下可能会引发内存泄漏的风险或性能问题。可以尝试使用更简单的标记控制方式,例如基于请求的标记控制(如上面的第二种方案)。

  • 使用AuthenticationSuccessHandler:Spring Security提供的AuthenticationSuccessHandler接口可以直接实现你的需求,代码简洁且易于维护。你可以选择在需要的时候进行扩展,而不是通过事件监听。

  • 检查事件来源:在多个地方触发相同的事件时,通过检查事件来源可以帮助你识别哪些是系统主流的认证成功事件,哪些是由于其他原因导致的副事件。

  • 使用事件优先级控制:在复杂的事件监听情境中,合理使用@Order注解可以帮助你控制事件处理的顺序,从而减少潜在的副作用。

  • 通过这些优化,你可以更好地控制认证成功回调的唯一性,避免多次执行相关操作。同时,建议你阅读Spring Security官方文档,了解更多关于认证成功处理的最佳实践。

    如果你有其他问题或需要更详细的技术方案,请随时继续与我交流!

    转载地址:http://nukmz.baihongyu.com/

    你可能感兴趣的文章
    Android HUAWEI 使用安装包安装App时系统提示:文件打开失败
    查看>>
    EasyUI的简单介绍
    查看>>
    Idea代码统计工具
    查看>>
    HTTP 错误 500.21 - Internal Server Error 发布网站遇到这个错误
    查看>>
    MySQL查询---排序后取第一条数据
    查看>>
    初次安装webpack之后,提示安装webpack-cli
    查看>>
    Java后端服务明显变慢诊断思路
    查看>>
    java中带参数的try(){}语法——关闭资源
    查看>>
    JSuite 最新版下载试用2021版本
    查看>>
    使用FileZilla,FTP登录出现错误:FileZilla状态: 不安全的服务器,不支持 FTP over TLS
    查看>>
    Python模块学习--uuid
    查看>>
    kafka+storm+hbase整合试验(Wordcount)
    查看>>
    VMware克隆虚拟机后重启network失败
    查看>>
    Hbase压力测试
    查看>>
    StreamReader & StreamWriter
    查看>>
    C#中的类、方法和属性
    查看>>
    Python爬取清朝末年医书:《醉花窗医案》,看看病症情况
    查看>>
    Python爬虫训练:爬取酷燃网视频数据
    查看>>
    Python数据分析入门(十九):绘制散点图
    查看>>
    大佬谈接口自动化,我是这样做测试框架开发的……
    查看>>