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

Spring Security自定义过滤器认证失败:请求用户名密码为空

问题分析与解决方案

核心问题

你通过http://username:password@localhost:8090/login访问时,这种格式属于HTTP Basic认证,凭证是通过请求头Authorization传递的(格式为Basic Base64编码的username:password),但你的自定义过滤器里用request.getParameter("username")和request.getParameter("password")去获取,这两种方式完全不匹配,导致拿不到凭证,最终触发403。

另外,过滤器逻辑存在漏洞:不管是否成功获取到用户名密码,都会直接调用authenticationManager.authenticate,空凭证会直接触发验证失败。

具体修复步骤

1. 修改自定义过滤器,正确解析HTTP Basic认证凭证

更新DtscCustomUsernamePasswordAuthenticationFilter的doFilterInternal方法,改为解析请求头中的Basic认证信息:

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
    String username = null;
    String password = null;

    // 解析HTTP Basic认证的请求头
    String authHeader = request.getHeader("Authorization");
    if (authHeader != null && authHeader.startsWith("Basic ")) {
        // 提取Base64编码的部分并解码
        String base64Credentials = authHeader.substring("Basic ".length()).trim();
        byte[] credDecoded = Base64.getDecoder().decode(base64Credentials);
        String credentials = new String(credDecoded, StandardCharsets.UTF_8);
        
        // 分割用户名和密码(格式为username:password)
        final String[] values = credentials.split(":", 2);
        if (values.length == 2) {
            username = values[0];
            password = values[1];
        }
    }

    System.out.println("Username: " + username);
    System.out.println("Password: " + password);

    // 只有拿到有效凭证时才进行认证
    if (username != null && password != null) {
        try {
            Authentication authRequest = new UsernamePasswordAuthenticationToken(username, password);
            Authentication authentication = authenticationManager.authenticate(authRequest);
            
            // 生成JWT并返回
            SecretKey key = Keys.hmacShaKeyFor(signingKey.getBytes(StandardCharsets.UTF_8));
            HashMap<String, Object> claims = new HashMap<>();
            claims.put("username", username);
            
            String jwt = Jwts.builder()
                    .setClaims(claims)
                    .signWith(key)
                    .compact();
            response.setHeader("Authorization", jwt);
        } catch (AuthenticationException e) {
            // 认证失败时返回401,而不是让后续流程处理
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }
    }

    chain.doFilter(request, response);
}

2. 优化过滤器的请求过滤规则

确保只过滤目标请求,避免无意义的资源消耗,修改shouldNotFilter方法:

@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
    String path = request.getServletPath();
    String method = request.getMethod();
    // 只处理POST方法的/login请求,可根据实际接口需求调整
    return !("/login".equals(path) && "POST".equalsIgnoreCase(method));
}

3. 避免默认Basic认证过滤器冲突

在WebSecurityConfig的configure(HttpSecurity http)方法中禁用默认Basic认证过滤器,防止与自定义过滤器冲突:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable();
    http.httpBasic().disable(); // 禁用默认Basic认证过滤器
    http.cors().and().authorizeRequests()
        .antMatchers("/login").permitAll()
        .antMatchers("/").permitAll()
        .anyRequest().authenticated();
    http.addFilterAt(dtscCustomerUsernamePasswordAutenticationFilter, BasicAuthenticationFilter.class);   
}

4. 测试验证

  • 浏览器访问:http://username:password@localhost:8090/login(若接口为POST,建议用Postman测试)
  • Postman测试:选择Basic Auth认证方式,输入用户名密码,发送POST请求到http://localhost:8090/login

额外注意事项

  • 如果前端React是通过表单提交或JSON请求体传递用户名密码,需要修改过滤器逻辑,解析表单参数或请求体内容,当前修复仅针对URL中的Basic认证场景。
  • 确保配置文件中jwt.signing.key的长度符合要求(HMAC SHA算法要求密钥至少256位,即32个字符以上)。
  • 认证失败时返回401状态码,便于前端准确识别认证失败场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 06:36:59