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

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. 验证修复效果

  1. 重启服务,通过注册接口创建新用户
  2. 在Postman中启用Basic Auth,填入注册的用户名和密码
  3. 发起GET /api/users/{username}请求,此时应正常返回用户信息,不再出现401错误

内容的提问来源于stack exchange,提问作者Даниил Матюшин

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 11:13:15