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
相关产品推荐
相关产品推荐

