Spring+Angular应用用户登录请求JSON解析失败问题求助
问题解决思路与方案
核心问题分析
- 权限配置未生效:你当前使用的
configure(HttpSecurity)方法在Spring Security 6+版本中不会被自动执行(WebSecurityConfigurerAdapter已被官方弃用),导致/users的permitAll规则未生效,访问该接口时被重定向到默认的HTML登录页面。 - 登录响应格式不匹配:默认的
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); } }
关键说明
- 新配置使用
SecurityFilterChainBean替代旧的configure方法,确保权限规则正确生效,解决/users接口被重定向的问题。 - 自定义的成功/失败处理器强制返回JSON格式响应,适配前端解析需求,消除JSON解析错误。
- 登录接口统一改为
/api/login,与默认的HTML登录页面路径区分开,避免混淆。
内容的提问来源于stack exchange,提问作者GotaKev
相关产品推荐
相关产品推荐

