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

Spring Security使用Postman测试注册接口返回401未授权问题求助

Spring Security注册接口返回401未授权问题解决思路

使用Spring Security开发新用户注册功能时,通过Postman测试接口持续返回401未授权响应。已逐一排查过滤器、Controller、Service、Repository层逻辑,也查阅了社区相关资料均未找到解决方案,以下是对应代码及核心排查点:


相关代码

Security配置代码

package app.gym.v1.Utility.Config;

import app.gym.v1.Utility.Filter.JwtAccessDeniedHandler;
import app.gym.v1.Utility.Filter.JwtAuthenticationEntryPoint;
import app.gym.v1.Utility.Filter.JwtAuthorizationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import static app.gym.v1.Utility.Constant.SecurityConstant.*;
import static org.springframework.security.config.http.SessionCreationPolicy.*;

public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private JwtAuthorizationFilter jwtAuthorizationFilter;
    private JwtAccessDeniedHandler jwtAccessDeniedHandler;
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    private UserDetailsService userDetailsService;
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    public SecurityConfig(
            JwtAuthorizationFilter jwtAuthorizationFilter,
            JwtAccessDeniedHandler jwtAccessDeniedHandler,
            JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint,
            @Qualifier("userDetailsService")UserDetailsService userDetailsService,
            BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.jwtAuthorizationFilter = jwtAuthorizationFilter;
        this.jwtAccessDeniedHandler = jwtAccessDeniedHandler;
        this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;
        this.userDetailsService = userDetailsService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().cors().and()
                .sessionManagement().sessionCreationPolicy(STATELESS)
                .and().authorizeRequests().antMatchers(PUBLIC_URLS).permitAll()
                .anyRequest().authenticated()
                .and()
                .exceptionHandling().accessDeniedHandler(jwtAccessDeniedHandler)
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .and()
                .addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManagerBean();
    }
}

接口层代码

package app.gym.v1.Resource;

import app.gym.v1.Model.User;
import app.gym.v1.Service.UserService;
import app.gym.v1.Utility.Exception.Domain.*;
import app.gym.v1.Utility.Exception.ExceptionHandling;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

import static org.springframework.http.HttpStatus.OK;

@RestController
@RequestMapping(path = {"/","/user"})
public class UserControl extends ExceptionHandling {
    private UserService userService;

    @Autowired
    public UserControl(UserService userService) {
        this.userService = userService;
    }

    @PostMapping("/register")
    public ResponseEntity<User> register(@RequestBody User user) throws UserNotFoundException, UsernameExistException, EmailExistException, IOException {
        User newUser = userService.register(user.getUsername(), user.getEmail(), user.getPassword(), user.getRole());
        return new ResponseEntity<>(newUser,  OK);
    }
}

服务实现层代码

package app.gym.v1.Utility.Impl;

import app.gym.v1.Model.User;
import app.gym.v1.Model.UserPrincipal;
import app.gym.v1.Repo.UserRepo;
import app.gym.v1.Service.UserService;
import app.gym.v1.Utility.Exception.Domain.*;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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;

import javax.mail.MessagingException;
import javax.transaction.Transactional;
import java.io.IOException;
import java.util.Date;
import java.util.List;

import static app.gym.v1.Utility.Constant.UserImplConstant.*;
import static app.gym.v1.Utility.Enums.Role.*;
import static org.apache.commons.lang3.StringUtils.*;

@Service
@Transactional
@Qualifier("UserDetailsService")
public class UserServiceImpl implements UserService, UserDetailsService {
    private Logger LOGGER = LoggerFactory.getLogger(getClass());
    private UserRepo userRepo;
    private BCryptPasswordEncoder passwordEncoder;

    @Autowired
    public UserServiceImpl(UserRepo userRepo, BCryptPasswordEncoder passwordEncoder) {
        this.userRepo = userRepo;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepo.findUserByUsername(username);
        if (user == null) {
            LOGGER.error("User with this phone number does not exist: " + username);
            throw new UsernameNotFoundException("User with this phone number does not exist: " + username);
        }else {
            user.setLastLoginDateDisplay(user.getLastLoginDate());
            user.setLastLoginDate(new Date());
            userRepo.save(user);
            UserPrincipal userPrincipal = new UserPrincipal(user);
            LOGGER.info("Retrieving user with this phone number" + username);
            return userPrincipal;
        }
    }

    @Override
    public User register(String username, String email, String password, String role) throws UserNotFoundException, UsernameExistException, EmailExistException {
        validateNewUsernameAndEmail(EMPTY, username, email);
        User user = new User();
        user.setUserId(generateUserId());
        user.setUsername(username);
        user.setEmail(email);
        user.setPassword(encodePassword(password));
        user.setRole(USER.name());
        user.setAuthorities(USER.getAuthorities());
        user.setJoinDate(new Date());
        user.setActive(true);
        user.setNotLocked(true);
        userRepo.save(user);
        return user;
    }

    private String encodePassword(String password) {
        return passwordEncoder.encode(password);
    }

    private String generateUserId() {
        return RandomStringUtils.randomNumeric(20);
    }


    private String generatePassword() {
        return RandomStringUtils.randomAlphanumeric(20);
    }

    private User validateNewUsernameAndEmail(String currentUsername, String newUsername, String newEmail) throws UserNotFoundException, UsernameExistException, EmailExistException {
        User userByNewUsername = findUserByUsername(newUsername);
        User userByNewEmail = findUserByEmail(newEmail);
        if(isNotBlank(currentUsername)) {
            User currentUser = findUserByUsername(currentUsername);
            if(currentUser == null) {
                throw new UserNotFoundException(NO_USER_FOUND_BY_USERNAME + currentUsername);
            }
            if(userByNewUsername != null && !currentUser.getId().equals(userByNewUsername.getId())) {
                throw new UsernameExistException(USERNAME_ALREADY_EXISTS);
            }
            if(userByNewEmail != null && !currentUser.getId().equals(userByNewEmail.getId())) {
                throw new EmailExistException(EMAIL_ALREADY_EXISTS);
            }
            return currentUser;
        } else {
            if(userByNewUsername != null) {
                throw new UsernameExistException(USERNAME_ALREADY_EXISTS);
            }
            if(userByNewEmail != null) {
                throw new EmailExistException(EMAIL_ALREADY_EXISTS);
            }
            return null;
        }
    }
}

核心排查点

  • 检查SecurityConfig是否添加@Configuration和@EnableWebSecurity注解:当前提供的配置类代码未标注这两个注解,会导致Spring不加载自定义安全配置,走默认拦截所有请求的逻辑,直接返回401。
  • 校验UserDetailsService的Bean名称匹配:SecurityConfig中注入UserDetailsService使用的@Qualifier("userDetailsService")首字母小写,而UserServiceImpl上标注的是@Qualifier("UserDetailsService")首字母大写,大小写不匹配会导致注入失败,触发认证异常。
  • 验证PUBLIC_URLS常量配置:确认SecurityConstant类中的PUBLIC_URLS数组正确配置了/register和/user/register路径,注意路径大小写、斜杠是否完整,Spring Security的antMatchers规则大小写敏感。
  • 检查JWT过滤器逻辑:公开路径的请求仍会经过自定义的JwtAuthorizationFilter,需确认过滤器中对无Token的公开路径请求没有直接抛出401,而是直接放行到后续过滤器链。

内容的提问来源于stack exchange,提问作者Mohamad J Alanbaki

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 10:54:04