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

自定义LoginFilter认证成功后为何无法进入Controller层?

问题描述

自定义了LoginFilter继承UsernamePasswordAuthenticationFilter,完成认证后触发了登录成功回调,但请求无法进入/sso/login的Controller方法。Postman测试时仅返回认证成功回调的内容,无法执行Controller里生成Token并返回的逻辑。

原因分析

Spring Security的UsernamePasswordAuthenticationFilter(你的LoginFilter继承自它)的设计逻辑是:拦截指定路径的请求完成认证后,通过配置的AuthenticationSuccessHandler直接向客户端输出响应,会终止整个请求链的执行。你将LoginFilter的filterProcessesUrl设置为/sso/login,和Controller的登录路径完全一致,导致请求被Filter拦截处理后直接返回,不会流转到Controller层。

解决方案

方案一:将Token生成逻辑移至登录成功回调(推荐)

既然认证已经在Filter中完成,直接在MyAuthenticationSuccessHandler里调用服务生成Token并返回,无需再走Controller。这种方式更符合Spring Security的认证流程设计。

修改MyAuthenticationSuccessHandler:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Autowired
    private UmsAdminService umsAdminService; // 注入你的用户服务
    private String tokenHead = "Bearer "; // 根据你的配置调整

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        String username = authentication.getName();
        // 认证已通过,无需再次校验密码,直接生成Token
        String token = umsAdminService.login(username, null); 
        Map<String, String> tokenMap = new HashMap<>();
        tokenMap.put("token", token);
        tokenMap.put("tokenHead", tokenHead);
        
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        out.write(mapper.writeValueAsString(CommonResult.success(tokenMap)));
        out.flush();
        out.close();
    }
}

同时可以删除Controller中对应的/sso/login方法,避免路径冲突。

方案二:修改LoginFilter,让请求继续流转到Controller

如果坚持要通过Controller处理返回,需要重写LoginFilter的successfulAuthentication方法,不调用SuccessHandler直接返回,而是继续执行请求链:

public class LoginFilter extends UsernamePasswordAuthenticationFilter {
    // 保留原有的构造方法和attemptAuthentication方法...

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        // 将认证信息存入SecurityContext
        SecurityContextHolder.getContext().setAuthentication(authResult);
        // 继续执行后续Filter链,让请求到达Controller
        chain.doFilter(request, response);
    }
}

同时,移除MyAuthenticationSuccessHandler的配置:

修改MyCustomDsl中的loginFilter方法:

LoginFilter loginFilter(AuthenticationManager authenticationManager) throws Exception {
    LoginFilter loginFilter = new LoginFilter(authenticationManager);
    loginFilter.setFilterProcessesUrl("/sso/login");
    loginFilter.setUsernameParameter("username");
    loginFilter.setPasswordParameter("password");
    // 注释掉SuccessHandler配置
    // loginFilter.setAuthenticationSuccessHandler(new MyAuthenticationSuccessHandler());
    loginFilter.setAuthenticationFailureHandler(new MyAuthenticationFailureHandler());
    return loginFilter;
}

调整Controller方法,直接从SecurityContext获取已认证的用户信息:

@ResponseBody
@RequestMapping(value = "/sso/login", method = RequestMethod.POST)
public CommonResult login(){
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    String username = authentication.getName();
    String token = umsAdminService.login(username, null);
    if(token == null){
        return CommonResult.validateFailed("生成Token失败");
    }
    Map<String, String> tokenMap = new HashMap<>();
    tokenMap.put("token", token);
    tokenMap.put("tokenHead", tokenHead);
    return CommonResult.success(tokenMap);
}
备注

推荐使用方案一,因为Spring Security的Filter设计就是用来处理认证逻辑并返回响应,将Token生成放在回调中更符合认证流程的职责划分,也避免了请求链的冗余流转。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 01:07:48