Grails 4.0.9集成spring security core grails plugin 4.0.3如何记录登录成败尝试
Grails 4.0.9 + Spring Security Core 4.0.3 登录事件捕获实现方案
你当前使用的版本完全支持登录成功、失败行为的捕获需求,无需重写核心认证逻辑、自定义登录接口,通过插件基于Spring Security原生提供的两类扩展点即可实现,两种方案可根据业务场景灵活选择。
方案一:Spring认证事件监听(零侵入,推荐优先使用)
Spring Security本身内置了完整的认证事件发布机制,该版本的Grails插件会自动在用户认证流程的对应节点发布事件,仅需注册对应的事件监听器即可捕获行为,不需要修改任何默认登录配置。
- 可监听的核心事件类型:
- 登录成功类:
InteractiveAuthenticationSuccessEvent(表单等交互式登录触发)、AuthenticationSuccessEvent(所有认证场景,包括记住我、程序级认证触发),可根据需要捕获的范围选择监听 - 登录失败类:
AuthenticationFailureEvent(所有认证失败场景的父类,可直接监听捕获所有失败类型,包括账号密码错误、账号锁定、账号禁用、密码过期等),也可按需单独监听AuthenticationFailureBadCredentialsEvent等细分失败事件
- 登录成功类:
将以下类放在src/main/groovy目录下,标注@Component即可被Grails自动扫描加载:
import org.springframework.security.authentication.event.AuthenticationFailureEvent import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent import org.springframework.stereotype.Component import org.springframework.web.context.request.RequestContextHolder import javax.servlet.http.HttpServletRequest @Component class LoginAuditListener { // 捕获交互式登录成功事件 @EventListener void handleLoginSuccess(InteractiveAuthenticationSuccessEvent event) { String username = event.authentication.name HttpServletRequest request = RequestContextHolder.requestAttributes.request // 适配反向代理场景的IP取值 String clientIp = request.getHeader("X-Forwarded-For") ?: request.remoteAddr // 在此处实现业务逻辑:记录审计日志、更新用户最后登录时间、触发登录成功通知等 log.info("用户[$username] 从IP[$clientIp]登录成功") } // 捕获所有类型的登录失败事件 @EventListener void handleLoginFailure(AuthenticationFailureEvent event) { // 失败场景下需兼容不同取值位置获取提交的用户名 String username = event.authentication?.name ?: event.authentication.principal HttpServletRequest request = RequestContextHolder.requestAttributes.request String clientIp = request.getHeader("X-Forwarded-For") ?: request.remoteAddr // 在此处实现业务逻辑:记录失败次数、触发账号锁定阈值校验、留存攻击痕迹等 log.info("用户[$username] 从IP[$clientIp]登录失败,原因:${event.exception.message}") } }
方案二:自定义认证成功/失败Handler(适合需修改登录流程的场景)
如果需要在捕获登录行为的同时,修改默认的登录成功跳转、失败跳转逻辑,可以选择自定义Handler,通过覆盖插件默认配置生效。
- 自定义成功Handler,继承插件默认的成功跳转逻辑类:
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse import org.springframework.security.core.Authentication class CustomLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { @Override void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { // 前置实现登录成功的捕获逻辑 String username = authentication.name log.info("用户[$username]登录成功") // 调用父类逻辑保留默认的跳转规则 super.onAuthenticationSuccess(request, response, authentication) } }
- 自定义失败Handler,继承插件默认的失败跳转逻辑类:
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse import org.springframework.security.core.AuthenticationException class CustomLoginFailureHandler extends SimpleUrlAuthenticationFailureHandler { @Override void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) { // 前置实现登录失败的捕获逻辑 String submittedUsername = request.getParameter("username") log.info("用户[$submittedUsername]登录失败,原因:${exception.message}") // 调用父类逻辑保留默认的失败跳转规则 super.onAuthenticationFailure(request, response, exception) } }
- 在
grails-app/conf/application.groovy的Spring Security配置块中替换默认的Handler:
grails.plugin.springsecurity.successHandler.authenticationSuccessHandler = new CustomLoginSuccessHandler() grails.plugin.springsecurity.failureHandler.authenticationFailureHandler = new CustomLoginFailureHandler()
实现注意事项
- 两种方案无冲突,可按需组合使用:事件监听方式耦合度低,适合做独立的审计、统计类需求;自定义Handler方式灵活度高,适合需要联动调整登录跳转、请求响应的场景
- 从事件/请求中获取用户名、IP信息时需做好空值判断,兼容反向代理场景的请求头取值逻辑,避免获取到代理服务器IP、空用户名的问题
- 无需额外引入第三方依赖,上述类全部基于Spring Security、Grails内置能力实现,和你当前使用的插件版本完全兼容
内容的提问来源于stack exchange,提问作者SShehab
相关产品推荐
相关产品推荐

