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

Spring+Angular应用用户登录请求JSON解析失败问题求助

问题解决思路与方案

核心问题分析

  1. 权限配置未生效:你当前使用的configure(HttpSecurity)方法在Spring Security 6+版本中不会被自动执行(WebSecurityConfigurerAdapter已被官方弃用),导致/users的permitAll规则未生效,访问该接口时被重定向到默认的HTML登录页面。
  2. 登录响应格式不匹配:默认的formLogin组件会返回HTML登录页面,但你的Angular前端期望JSON格式响应,因此出现JSON解析错误。

修正后的Spring Security配置

替换原SecurityConfig类的代码,使用Spring Security 6推荐的SecurityFilterChain Bean方式配置:

package com.tracker.EsportTracker.config;

import java.util.Arrays;
import java.util.Collections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import com.tracker.EsportTracker.service.impl.AppUserDetailsService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.databind.ObjectMapper;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private AppUserDetailsService appUserDetailsService;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private ObjectMapper objectMapper;

    // 定义AuthenticationManager Bean
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }

    // 核心安全过滤链配置
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .cors(cors -> cors.configurationSource(corsConfigurationSource()))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/users").permitAll() // 确保/users接口无需认证
                .requestMatchers("/api/login").permitAll() // 开放登录接口
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginProcessingUrl("/api/login") // 指定登录请求接口路径
                .usernameParameter("username") // 匹配前端表单的用户名参数名
                .passwordParameter("password") // 匹配前端表单的密码参数名
                .successHandler(customSuccessHandler()) // 登录成功返回JSON
                .failureHandler(customFailureHandler()) // 登录失败返回JSON
                .permitAll()
            )
            .logout(logout -> logout.permitAll());

        return http.build();
    }

    // 自定义登录成功处理器,返回JSON响应
    private AuthenticationSuccessHandler customSuccessHandler() {
        return (HttpServletRequest request, HttpServletResponse response, Authentication authentication) -> {
            response.setStatus(HttpStatus.OK.value());
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            objectMapper.writeValue(response.getWriter(), Collections.singletonMap("message", "登录成功"));
        };
    }

    // 自定义登录失败处理器,返回JSON响应
    private AuthenticationFailureHandler customFailureHandler() {
        return (HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) -> {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            objectMapper.writeValue(response.getWriter(), Collections.singletonMap("error", exception.getMessage()));
        };
    }

    // CORS配置整合到Security过滤链
    private UrlBasedCorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
        config.setAllowedMethods(Collections.singletonList("*"));
        config.setAllowedHeaders(Collections.singletonList("*"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }
}

补充配置

确保项目中已定义BCryptPasswordEncoder的Bean,可在配置类中添加:

@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
    return new BCryptPasswordEncoder();
}

前端调整

修改Angular登录请求代码,发送表单格式数据到/api/login接口:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private loginUrl = 'http://localhost:7070/api/login';

  constructor(private http: HttpClient) { }

  login(username: string, password: string): Observable<any> {
    const formData = new FormData();
    formData.append('username', username);
    formData.append('password', password);
    return this.http.post(this.loginUrl, formData);
  }
}

关键说明

  • 新配置使用SecurityFilterChain Bean替代旧的configure方法,确保权限规则正确生效,解决/users接口被重定向的问题。
  • 自定义的成功/失败处理器强制返回JSON格式响应,适配前端解析需求,消除JSON解析错误。
  • 登录接口统一改为/api/login,与默认的HTML登录页面路径区分开,避免混淆。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 13:44:56