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

现有接口返回401 Unauthorized错误且服务端日志无报错如何解决

问题描述

我在现有Customer实体中新增了username、password字段,同时添加了自定义JWT过滤器、认证提供者,以及标注了@Order(2)的WebSecurityConfig配置。当我携带用户名和密码载荷发送POST请求时,接口返回401 Unauthorized错误,但服务端日志中没有任何报错信息,我已核查WebConfig文件配置无误,迟迟定位不到问题根源,以下为相关实现代码:

MyCustomerDetails(UserDetails实现类)

public class MyCustomerDetails implements UserDetails{

    private static final long serialVersionUID = -5087929420394311276L;

    private Long id;
    private String username;
    private String password;
    
    public MyCustomerDetails() {
        
    }
    
    public MyCustomerDetails(Long id, String username, String password) {
        super();
        this.id = id;
        this.username = username;
        this.password = password;
    }

   public static MyCustomerDetails build(Customer customer) {
        return new MyCustomerDetails(customer.getId(), 
                customer.getUserName(), customer.getPassword());
    }


   @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getPassword() {
        // TODO Auto-generated method stub
        return password;
    }

    @Override
    public String getUsername() {
        // TODO Auto-generated method stub
        return username;
    }

    ............
}

Controller类

@CrossOrigin(origins = {"http://localhost:3000"})
@RestController
public class CustomerController {
    
        @Autowired
        CustomerAccountService customerRepo;
        
         @Autowired
         private CustomerJwtTokenUtil customerJwtTokenUtil;
        
         @Autowired
         private AuthenticationManager authenticationManager;
         
    
         @PostMapping(value="/validateCustomer")
          public ResponseEntity <?> createAuthenticationToken( @RequestBody MyCustomerDetails
          authenticationRequest) throws Exception 
             {  
             authenticate(authenticationRequest.getUsername(),  
                authenticationRequest.getPassword()); 
        // Long userId = authenticationRequest.getId();
             final MyCustomerDetails userDetails =  
                 (MyCustomerDetails)   customerRepo.loadUserByUsername(authenticationRequest.getUsername());
           
           final String token = 
                   customerJwtTokenUtil.generateToken(userDetails);
          
          return new ResponseEntity<>(new JwtResponse(token), HttpStatus.OK) ;
                    
         
               }
         private void authenticate(String username, String password) throws Exception {
                    try {
                        authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
                    } catch (DisabledException e) {
                        throw new Exception("USER_DISABLED", e);
                    } catch (BadCredentialsException e) {
                        throw new Exception("INVALID_CREDENTIALS", e);
                    }
                }
}

WebSecurityConfig配置类

@Configuration
@Order(2)
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class CustomerSecurityConfiguration extends WebSecurityConfigurerAdapter {

    
    @Autowired
    private CustomerJwtAuthenticationEntryPoint customerJwtAuthenticationEntryPoint;
    
    @Autowired
    private UserDetailsService myCustomerDetailsService;
    
    @Autowired 
    private CustomerJwtRequestFilter customerJwtRequestFilter;
     
    
    @Bean
    public CustomerAccountService myCustomerAccountService() {
        return new CustomerAccountService();
    }
    
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    /*
     * @Bean public UserDetailsService myCustomerDetailsService() { return
     * myCustomerDetailsService(); }
     */
    
    
     @Bean
        public DaoAuthenticationProvider daoAuthenticationProvider(PasswordEncoder passwordEncoder,
                                                                   UserDetailsService userDetailsService){ 
            DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
            daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
            daoAuthenticationProvider.setUserDetailsService(userDetailsService);
            return daoAuthenticationProvider;
      }
     
     
     @Override
     @Bean
     public AuthenticationManager authenticationManagerBean() throws Exception {
         return super.authenticationManagerBean();
     }

     
     protected void configure(AuthenticationManagerBuilder auth ) throws Exception {
         auth.userDetailsService(myCustomerDetailsService).passwordEncoder(passwordEncoder());
         }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
      http.cors().and().csrf().disable()
      .authorizeRequests()
      .antMatchers("/***").permitAll()
    
     // .antMatchers("/customer/**").hasAuthority("CUSTOMER")
      .anyRequest().authenticated()
      .and()
      .exceptionHandling()
      .authenticationEntryPoint(customerJwtAuthenticationEntryPoint)
      
      .and()
      .formLogin().permitAll()
    //  .loginPage("/login")
     
      .and()
      .logout().logoutUrl("/logout").logoutSuccessUrl("/login")
     
      .and()
      .sessionManagement()
      .maximumSessions(1)
      .and()
      .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
      http.addFilterBefore(customerJwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

CustomerAccountService服务类

@Primary
public class CustomerAccountService implements UserDetailsService {
    
    
    @Autowired  
    private CustomerAccountRepo custRepo;
    
    @Qualifier("passwordEncoder")
    @Autowired
    private PasswordEncoder bCryptPasswordEncoder;
    
    
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Customer customer = custRepo.findByUserName(username);
        if(customer == null) {
            throw new UsernameNotFoundException("Customer not found");
        }
        
      return MyCustomerDetails.build(customer);
        
      }
}

项目启动类

@Configuration
@EnableWebMvc
//@ComponentScan(basePackages = "com.bethsaida.org.security")
@EnableJpaRepositories
@SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
public class BethsaidaApplication  {
    
     public static void main(String[] args) 
      {SpringApplication.run(BethsaidaApplication.class, args);}
 
     public class WebConfig implements WebMvcConfigurer 
        { 
         private static final long MAX_AGE_SECS = 3600;

        @Override
        public void addCorsMappings(CorsRegistry registry) 
         { registry.addMapping("/**")
             .allowedOrigins("*")
             .allowedMethods("HEAD", "OPTIONS", "GET", "POST", "PUT", "PATCH", "DELETE")
             .maxAge(MAX_AGE_SECS);}
        }
     
}
问题修复方案
  • 启动类排除了Spring Security自动配置,首先检查CustomerSecurityConfiguration所在包是否在启动类默认扫描路径下,若Security相关代码在com.bethsaida.org.security包下,放开启动类中注释的@ComponentScan(basePackages = "com.bethsaida.org.security")注解即可。
  • 检查自定义CustomerJwtRequestFilter的逻辑,确认登录接口/validateCustomer是否被排除了JWT校验:登录请求本身不会携带JWT,若过滤器没有对该接口放行,会直接因为缺少合法JWT返回401,在过滤器中添加路径判断逻辑,请求路径匹配登录接口时直接跳过校验即可。
  • 检查MyCustomerDetails类中省略的四个状态方法:isAccountNonExpired、isAccountNonLocked、isCredentialsNonExpired、isEnabled,这四个方法只要有一个返回false就会直接导致认证失败,无权限访问接口,当前业务无特殊要求时四个方法全部返回true即可。
  • 你同时注册了DaoAuthenticationProviderBean,又在configure(AuthenticationManagerBuilder auth)方法中手动配置了userDetailsService和密码编码器,重复配置会导致认证逻辑冲突,删除手动配置的auth.userDetailsService(myCustomerDetailsService).passwordEncoder(passwordEncoder());代码即可,Spring会自动识别你注册的DaoAuthenticationProvider完成认证逻辑。
  • 检查数据库中存储的用户密码是否是BCrypt加密后的密文:若存储的是明文密码,BCrypt密码校验会直接失败返回401,且你捕获异常后只抛出了自定义业务异常,日志级别未开启异常栈打印的情况下就不会输出报错信息,写入用户密码时必须使用你注册的BCryptPasswordEncoder对明文密码加密后再存入数据库。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 20:24:03