Postman调用Spring后端出现401 Unauthorized错误求助
问题
基于Spring框架搭建后端服务,用户注册API可正常运行且数据已成功写入数据库,但发起GET /api/users/{username}请求时持续收到401 Unauthorized错误。已在Postman中启用Basic Authentication并填入已存在用户的凭证,错误仍未解决;尝试不加密密码后问题依旧,仅当允许未授权用户访问时请求可正常通过。
相关代码
1. SecurityConfig
package com.example.new_messager.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; @Configuration public class SecurityConfig { @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf().disable() // 测试阶段禁用CSRF(生产环境需正确配置) .authorizeHttpRequests(authorize -> authorize .requestMatchers("/api/users/register").permitAll() // 注册接口无需认证 .anyRequest().authenticated() // 其他接口需认证 ) .httpBasic(); // 使用Basic认证 return http.build(); } }
2. UserController
package com.example.new_messager.controller; import com.example.new_messager.model.User; import com.example.new_messager.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/users") public class UserController { private final UserService userService; @Autowired public UserController(UserService userService) { this.userService = userService; } @PostMapping("/register") public void registerUser(@RequestBody User user) { userService.register(user); } @GetMapping("/{username}") public User findByUsername(@PathVariable String username) { return userService.findByUsername(username); } }
3. User(实体类)
package com.example.new_messager.model; import jakarta.persistence.*; @Entity @Table(name = "app_user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true, nullable = false) private String username; @Column(nullable = false) private String password; public void setPassword(String encode) { this.password = encode; } public String getUsername() { return username; } public String getPassword() { return password; } }
4. UserService
package com.example.new_messager.service; import com.example.new_messager.model.User; import com.example.new_messager.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; @Service public class UserService { private final UserRepository userRepository; private final BCryptPasswordEncoder passwordEncoder; @Autowired public UserService(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder) { this.userRepository = userRepository; this.passwordEncoder = passwordEncoder; } public void register(User user) { if (user.getPassword() == null || user.getPassword().isEmpty()) { throw new IllegalArgumentException("密码不能为空"); } if (user.getUsername() == null || user.getPassword() == null) { throw new IllegalArgumentException("用户名和密码均不能为空"); } String encodedPassword = passwordEncoder.encode(user.getPassword()); user.setPassword(encodedPassword); userRepository.save(user); } public boolean validatePassword(String rawPassword, String encodedPassword) { return passwordEncoder.matches(rawPassword, encodedPassword); } public User findByUsername(String username) { return userRepository.findByUsername(username) .orElseThrow(() -> new RuntimeException("用户不存在")); } }
5. UserRepository
package com.example.new_messager.repository; import com.example.new_messager.model.User; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; public interface UserRepository extends JpaRepository<User, Long> { Optional<User> findByUsername(String username); }
问题原因及修复方案
核心问题是缺少Spring Security认证所需的用户信息加载逻辑——Spring Security无法从数据库中获取用户的认证信息来验证Basic Auth凭证,导致认证失败返回401。
1. 修改User实体类,实现UserDetails接口
Spring Security通过UserDetails接口获取用户认证的核心信息,修改User类:
package com.example.new_messager.model; import jakarta.persistence.*; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.Collections; @Entity @Table(name = "app_user") public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true, nullable = false) private String username; @Column(nullable = false) private String password; // 实现UserDetails接口方法 @Override public Collection<? extends GrantedAuthority> getAuthorities() { // 暂返回空集合,如需权限控制可添加角色逻辑 return Collections.emptyList(); } @Override public String getPassword() { return password; } @Override public String getUsername() { return username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } // 原有setter方法 public void setPassword(String password) { this.password = password; } public Long getId() { return id; } }
2. 修改UserService,实现UserDetailsService接口
让UserService实现UserDetailsService,提供Spring Security加载用户信息的入口:
package com.example.new_messager.service; import com.example.new_messager.model.User; import com.example.new_messager.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; @Service public class UserService implements UserDetailsService { private final UserRepository userRepository; private final BCryptPasswordEncoder passwordEncoder; @Autowired public UserService(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder) { this.userRepository = userRepository; this.passwordEncoder = passwordEncoder; } // 原有register、validatePassword、findByUsername方法保持不变 @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("用户 " + username + " 不存在")); } }
3. 验证修复效果
- 重启服务,通过注册接口创建新用户
- 在Postman中启用Basic Auth,填入注册的用户名和密码
- 发起
GET /api/users/{username}请求,此时应正常返回用户信息,不再出现401错误
内容的提问来源于stack exchange,提问作者Даниил Матюшин
相关产品推荐
相关产品推荐

