博客
关于我
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/

    你可能感兴趣的文章
    vMotion 操作失败进度卡在14% ,报错: Operation Timed out
    查看>>
    重置UAG Application admin密码
    查看>>
    Horizon Daas租户管理平台扩展分配时报:内部错误
    查看>>
    嵌入式系统试题库(CSU)
    查看>>
    【自考】之信息资源管理(一)
    查看>>
    setup facatory9.0打包详细教程(含静默安装和卸载)
    查看>>
    ionic4 路由跳转传值
    查看>>
    pwn题shellcode收集
    查看>>
    java.security.InvalidKeyException: Illegal key size
    查看>>
    Linux kernel pwn --- CSAW2015 StringIPC
    查看>>
    配置jdk的环境变量
    查看>>
    编译android源代码(aosp)
    查看>>
    IDEA 找不到 Persistence窗口解决办法
    查看>>
    维基百科之AndroidRoot
    查看>>
    C++ Primer Plus读书笔记:循环读取(错误处理)
    查看>>
    skimage与cv2 安装失败的解决办法
    查看>>
    关于吴恩达的深度学习的一些授课视频里面英文翻译错误的实例展示
    查看>>
    伴随矩阵和逆矩阵的关系证明
    查看>>
    Form窗体属性
    查看>>
    解决Eclipse加载图片或网页出现404错误
    查看>>