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

Spring Boot自定义Token认证问题:AuthenticationProvider未触发

Spring Boot自定义Token认证问题:AuthenticationProvider未触发排查

嘿,我看了你的代码,问题出在你的认证流程根本没走到AuthenticationProvider那一步,下面给你拆解原因和解决办法:

为什么AuthenticationProvider没启动?

你现在的逻辑是:

  • /login请求直接permitAll放行,完全没触发Spring Security的认证流程;
  • 其他请求靠CredentialsFilter直接从Token里解析信息,手动设置SecurityContext,全程没用到AuthenticationManager——而AuthenticationProvider是被AuthenticationManager调用的,没有这个触发动作,它当然不会跑起来。

简单说:你把AuthenticationProvider定义好了,但从来没调用过它。

正确的流程应该是这样的

  1. 用户调用/login接口,传入用户名密码;
  2. 后端调用AuthenticationManager.authenticate(),触发BokiAuthenticationProvider做用户名密码校验;
  3. 校验通过后,生成Token返回给前端;
  4. 后续请求前端带Token,CredentialsFilter解析Token并设置认证信息到SecurityContext。

具体修改步骤

1. 先在SecurityConfig里暴露AuthenticationManager为Bean

默认情况下AuthenticationManager不能直接注入,需要在MyWebSecurityConfigurerAdapter里添加:

@Configuration 
@EnableWebSecurity 
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled=true) 
public class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { 
    // ... 你的其他代码 ...

    // 暴露AuthenticationManager为Bean,方便在Controller/Service中注入调用
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

2. 编写Login接口,触发AuthenticationProvider

新增一个LoginController(或者在现有Controller里加),主动调用认证逻辑:

@RestController
public class LoginController {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private MyCriptoService myCriptoService;

    @PostMapping("/login")
    public ResponseEntity<String> login(@RequestBody UserLoginRequest loginRequest) {
        // 构造用户名密码的认证请求
        UsernamePasswordAuthenticationToken authRequest = 
            new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
        
        try {
            // 这一步会触发BokiAuthenticationProvider的authenticate方法!
            Authentication authenticated = authenticationManager.authenticate(authRequest);
            
            // 生成Token(需要你在MyCriptoService里实现generateToken方法,把用户名、权限存进去)
            String token = myCriptoService.generateToken(authenticated);
            
            return ResponseEntity.ok(token);
        } catch (AuthenticationException e) {
            // 认证失败返回错误
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
        }
    }

    // 定义请求参数的DTO
    static class UserLoginRequest {
        private String username;
        private String password;
        // 省略getter/setter
    }
}

3. 修正CredentialsFilter的逻辑

你的过滤器里有几个小问题,比如判断URI用contains可能会匹配到其他带login的路径,还有Token解析失败的情况没处理,修改后:

@Component
public class CredentialsFilter extends OncePerRequestFilter{
    @Autowired 
    private MyCriptoService myCriptoService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        // 精确匹配/login请求,直接放行
        if ("/login".equals(request.getRequestURI())) {
            chain.doFilter(request, response);
            return;
        }

        String token = request.getHeader("MyTokenHeader");
        // 只有Token存在且当前没有认证信息时,才处理
        if (token != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            try {
                String username = myCriptoService.getUsernameFromToken(token);
                List<GrantedAuthority> authorities = myCriptoService.getAuthoritiesFromToken(token);
                
                // 构造已认证的Authentication对象,密码设为null(因为是Token认证,不需要密码)
                UsernamePasswordAuthenticationToken authentication = 
                    new UsernamePasswordAuthenticationToken(username, null, authorities);
                // 设置到SecurityContext,后续接口就能拿到认证信息了
                SecurityContextHolder.getContext().setAuthentication(authentication);
            } catch (Exception e) {
                // Token无效,返回401
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                response.getWriter().write("Invalid or expired token");
                return;
            }
        }
        chain.doFilter(request, response);
    }
}

4. 确保MyCriptoService的Token生成/解析逻辑正确

你需要在MyCriptoService里实现generateToken方法,把认证后的用户名和权限信息加密到Token里,比如用JWT的话,就把这些信息作为Claim存进去,这样getUsernameFromToken和getAuthoritiesFromToken才能正确解析出来。

最后再验证一下

启动项目后,先调用/login接口传入正确的用户名密码,这时候你应该能看到BokiAuthenticationProvider的authenticate方法被触发;拿到Token后,带着MyTokenHeader请求其他需要权限的接口,CredentialsFilter会解析Token并设置认证信息,接口就能正常访问了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:07:59