You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Grails Spring Security插件中自定义默认认证事件发布器的异常映射

问题描述

背景

项目中继承DaoAuthenticationProvider实现邮箱OTP双因素认证,自定义了BadOtpException、ExpiredOtpException、ConsumedOtpException等异常(均为BadCredentialsException子类),认证流程正常,但认证失败事件不再发布。

问题核心

替换自定义认证提供者后,原DaoAuthenticationProvider会触发的认证事件无法正常发布。

原因分析

Grails Spring Security Core插件依赖DefaultAuthenticationEventPublisher发布事件,该类通过构造方法中配置的「异常-事件」映射关系决定要发布的事件。由于自定义异常不在默认映射中,导致认证失败时无法触发对应的事件。

尝试过的无效方案

继承DefaultAuthenticationEventPublisher添加额外映射,但注册Bean后无效果,构造方法的打印语句无输出,疑似Bean未成功注册:

自定义发布器代码:

class CustomAuthenticationEventPublisher extends DefaultAuthenticationEventPublisher {
    CustomAuthenticationEventPublisher() {

    }

    CustomAuthenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        super(applicationEventPublisher)
        println('CustomAuthenticationEventPublisher')
        Properties exceptionMappings = new Properties()
        exceptionMappings.setProperty(BadOtpException.class.name, AuthenticationFailureBadCredentialsEvent.class.name)
        super.setAdditionalExceptionMappings(exceptionMappings)
    }
}

resources.groovy注册代码:

beans = {
   authenticationEventPublisher(CustomAuthenticationEventPublisher)
}

解决方案

1. 修复自定义发布器的Bean注册问题

Grails Spring Security插件默认已注册authenticationEventPublisher Bean,直接覆盖需要明确配置优先级。修改resources.groovy,强制替换原有Bean:

beans = {
    authenticationEventPublisher(CustomAuthenticationEventPublisher) { bean ->
        bean.autowire = true
        bean.destroyMethod = 'destroy'
    }
}

同时优化自定义发布器代码,统一初始化逻辑并确保无参/有参构造都能完成映射配置:

class CustomAuthenticationEventPublisher extends DefaultAuthenticationEventPublisher {

    CustomAuthenticationEventPublisher() {
        super()
        initExceptionMappings()
    }

    CustomAuthenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        super(applicationEventPublisher)
        initExceptionMappings()
    }

    private void initExceptionMappings() {
        Properties exceptionMappings = new Properties()
        // 为所有自定义OTP异常添加事件映射
        exceptionMappings.setProperty(BadOtpException.class.name, AuthenticationFailureBadCredentialsEvent.class.name)
        exceptionMappings.setProperty(ExpiredOtpException.class.name, AuthenticationFailureBadCredentialsEvent.class.name)
        exceptionMappings.setProperty(ConsumedOtpException.class.name, AuthenticationFailureBadCredentialsEvent.class.name)
        setAdditionalExceptionMappings(exceptionMappings)
        // 若需要覆盖默认映射而非追加,可调用setExceptionMappings()传入合并后的配置
    }
}

2. 更简洁的方案:直接修改默认发布器的映射

无需继承类,直接在Spring配置中为默认的authenticationEventPublisher注入额外映射:

beans = {
    authenticationEventPublisher(DefaultAuthenticationEventPublisher) { bean ->
        def additionalMappings = new Properties()
        additionalMappings.setProperty(BadOtpException.class.name, AuthenticationFailureBadCredentialsEvent.class.name)
        additionalMappings.setProperty(ExpiredOtpException.class.name, AuthenticationFailureBadCredentialsEvent.class.name)
        additionalMappings.setProperty(ConsumedOtpException.class.name, AuthenticationFailureBadCredentialsEvent.class.name)
        bean.additionalExceptionMappings = additionalMappings
    }
}

这种方式复用默认发布器,避免继承带来的Bean冲突问题,实现更简洁。

3. 验证Bean是否生效

在BootStrap.groovy中添加验证代码,确认发布器是否正确加载:

class BootStrap {
    def authenticationEventPublisher

    def init = { servletContext ->
        println("当前加载的认证事件发布器:${authenticationEventPublisher.getClass().name}")
    }
}

启动项目后查看控制台输出,确认是否为自定义类或已修改映射的默认类。


内容的提问来源于stack exchange,提问作者Manish Kapoor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 16:20:46