Spring Security中AuthenticationManager的可用扩展点有哪些?
推荐的3种无侵入扩展方案,完全保留原生认证流程
方案1:直接在控制器层处理(最简单,适配你当前的场景)
你本身已经在控制器中拿到了HttpServletRequest对象,也能直接提取IP,只需要在authenticationManager.authenticate执行成功(无异常抛出)之后,直接执行IP记录逻辑即可:
@RequestMapping(value = "login", method = RequestMethod.POST) public void loginUser(@Valid @RequestBody LoginUserRequest request, HttpServletRequest httpServletRequest) { Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword())); SecurityContextHolder.getContext().setAuthentication(authentication); // 直接在这里追加自定义逻辑,只有认证成功才会执行到这一步 String clientIp = getClientIp(httpServletRequest); // 自行实现客户端IP提取逻辑 userRepository.addIp(request.getEmail(), clientIp); }
该方案没有任何侵入性,不需要修改任何Spring Security原生配置,100%复用现有认证流程。
方案2:监听认证成功事件(适合全局统一处理认证成功逻辑的场景)
Spring Security在认证成功后会自动发布AuthenticationSuccessEvent事件,你只需要实现一个事件监听器,即可全局捕获所有认证成功的请求:
- 首先在构造认证Token时把IP信息塞到Token的details属性中:
// 控制器中修改逻辑 UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword()); authToken.setDetails(getClientIp(httpServletRequest)); // 把IP存入details字段 Authentication authentication = authenticationManager.authenticate(authToken);
- 编写事件监听器:
@Component public class AuthenticationSuccessListener implements ApplicationListener<AuthenticationSuccessEvent> { private final UserRepository userRepository; @Autowired public AuthenticationSuccessListener(UserRepository userRepository) { this.userRepository = userRepository; } @Override public void onApplicationEvent(AuthenticationSuccessEvent event) { Authentication auth = event.getAuthentication(); String email = auth.getName(); String ip = (String) auth.getDetails(); userRepository.addIp(email, ip); } }
该方案适合存在多个认证入口的场景,所有认证成功的请求都会统一执行自定义逻辑,不需要每个入口单独加代码。
方案3:自定义AuthenticationProvider(适合需要在认证流程中插入逻辑的场景)
如果你需要在认证流程中间(比如密码校验通过后、返回认证对象前)插入逻辑,可以继承原生的DaoAuthenticationProvider,复用它所有的认证逻辑,仅追加自己的扩展:
public class CustomAuthenticationProvider extends DaoAuthenticationProvider { private final UserRepository userRepository; public CustomAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder, UserRepository userRepository) { setUserDetailsService(userDetailsService); setPasswordEncoder(passwordEncoder); this.userRepository = userRepository; } @Override protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) { // 调用父类方法生成原生认证对象,保留所有原生校验逻辑 Authentication successAuth = super.createSuccessAuthentication(principal, authentication, user); // 插入自定义逻辑 String ip = (String) authentication.getDetails(); userRepository.addIp(user.getUsername(), ip); return successAuth; } }
然后在你的Security配置类中替换默认Provider即可:
@Override public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { authenticationManagerBuilder.authenticationProvider( new CustomAuthenticationProvider(userDetailsService, passwordEncoder, userRepository) ); }
该方案完全保留Spring Security原生的认证校验逻辑,仅在认证成功的节点插入自定义操作,适合需要深度定制认证流程的场景。
内容的提问来源于stack exchange,提问作者Johnyb
相关产品推荐
相关产品推荐

