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

Spring Security权限配置异常:User/Admin接口403无权限访问

解决Spring Security中User/Admin接口403匿名认证问题

问题描述

开发包含三种API访问权限的程序:Public、User和Admin,要求Spring Security根据接口路径和用户角色控制访问权限:

  • /public/**路径接口无需凭证校验
  • User和Admin接口需对应凭证及角色

测试时Public接口正常,但User和Admin接口在到达Controller和UserService前被拦截,返回403错误(已确认数据库中用户/管理员凭证正确)。

日志分析

日志显示请求被AnonymousAuthenticationFilter处理,SecurityContext被设置为匿名上下文,系统未读取数据库用户凭证:

2023-11-03T15:40:03.904+01:00  INFO 8812 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-11-03T15:40:03.904+01:00  INFO 8812 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2023-11-03T15:40:03.906+01:00  INFO 8812 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Completed initialization in 2 ms
2023-11-03T15:40:03.978+01:00 DEBUG 8812 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : Securing GET /admin/users/
2023-11-03T15:40:04.022+01:00 DEBUG 8812 --- [nio-8080-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
2023-11-03T15:40:04.084+01:00 DEBUG 8812 --- [nio-8080-exec-2] o.s.s.w.s.HttpSessionRequestCache        : Saved request http://localhost:8080/admin/users/?continue to session
2023-11-03T15:40:04.085+01:00 DEBUG 8812 --- [nio-8080-exec-2] o.s.s.w.a.Http403ForbiddenEntryPoint     : Pre-authenticated entry point called. Rejecting access
2023-11-03T15:40:04.094+01:00 DEBUG 8812 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : Securing GET /error
2023-11-03T15:40:04.100+01:00 DEBUG 8812 --- [nio-8080-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
2023-11-03T15:40:04.103+01:00 DEBUG 8812 --- [nio-8080-exec-2] o.s.s.w.s.HttpSessionRequestCache        : Saved request http://localhost:8080/error?continue to session
2023-11-03T15:40:04.103+01:00 DEBUG 8812 --- [nio-8080-exec-2] o.s.s.w.a.Http403ForbiddenEntryPoint     : Pre-authenticated entry point called. Rejecting access

相关代码检查

UserService中认证方法

@Override //for Spring Security and authentication measures using username and password credentials.
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    logger.info("trying to load user info using username {}", username);
    User user= repository.findByUserName(username);
    if (user == null) {
        throw new UsernameNotFoundException("User not found with username: " + username);
    }
    logger.info("Loaded user: {} with role: {}", user.getUsername(), user.getRole());
    // Create and return a UserDetails object based on the retrieved User entity
    return new org.springframework.security.core.userdetails.User(
            user.getUsername(),
            user.getPassword(),
            AuthorityUtils.createAuthorityList("ROLE_" + user.getRole().name())
    );
}

SecurityConfig配置

package com.easytravel.userManagementService.config;

import com.easytravel.userManagementService.model.Role;
import com.easytravel.userManagementService.userService.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.EnableWebSecurity;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig{

    @Autowired
    @Lazy //lazy initialization to avoid circular dependency with springSecurity
    private UserService userService ;
    public static final Logger logger= LoggerFactory.getLogger(SecurityConfig.class);

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
    
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth, UserService userService) throws Exception{ //using configureGlobal which will use userService to configure authentication
      logger.info("configureGlobal method invoked");
      auth.userDetailsService(userService);
    }

    //Configuring access control
    @Bean
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        logger.info("securityFilterChain method invoked. checking for correct roles based on path.");
        http.csrf(x-> x.disable());  //Cross-site Request Forgery
        http
                .authorizeHttpRequests(authorize ->
                        authorize
                                .requestMatchers(HttpMethod.POST,  "/public/**").permitAll()
                               // .requestMatchers(HttpMethod.GET,  "/public/**").permitAll()
                                .requestMatchers("/user/users/**").hasAnyRole(Role.AUTH_USER.name(), Role.ADMIN.name())
                                .requestMatchers("/admin/users/**").hasRole(Role.ADMIN.name())
                                .anyRequest().authenticated()
                );
        return http.build();
    }

    @Bean         //setting authentication with user's username and password that configured in the userService class.
    public DaoAuthenticationProvider daoAuthenticationProvider(UserService userService){
        DaoAuthenticationProvider authenticationProvider= new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userService);
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return authenticationProvider;
    }
}

解决方案

1. 添加认证方式配置

当前SecurityFilterChain仅配置了权限控制,但未指定认证入口,导致Spring Security无法获取用户凭证进行校验,直接进入匿名认证流程。需添加HTTP Basic认证(或表单登录等方式):

修改defaultSecurityFilterChain方法,添加httpBasic配置:

@Bean
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
    logger.info("securityFilterChain method invoked. checking for correct roles based on path.");
    http.csrf(x-> x.disable())
        .httpBasic(withDefaults()) // 启用HTTP Basic认证
        .authorizeHttpRequests(authorize -> authorize
                .requestMatchers(HttpMethod.POST, "/public/**").permitAll()
                .requestMatchers("/user/users/**").hasAnyRole(Role.AUTH_USER.name(), Role.ADMIN.name())
                .requestMatchers("/admin/users/**").hasRole(Role.ADMIN.name())
                .anyRequest().authenticated()
        );
    return http.build();
}

2. 修复认证提供者关联

现有configureGlobal方法未指定PasswordEncoder,且与DaoAuthenticationProvider Bean存在配置冲突,建议删除configureGlobal方法,Spring会自动使用已定义的DaoAuthenticationProvider进行认证,确保密码加密校验正常。

3. 测试时携带认证凭证

在Postman测试User/Admin接口时,需添加Authorization头:

  • 选择Basic Auth类型
  • 输入数据库中已注册的用户名和密码

4. 验证角色格式一致性

loadUserByUsername中角色前缀为ROLE_,而hasRole方法会自动添加该前缀,需确保Role枚举的name()值(如AUTH_USER、ADMIN)与代码逻辑匹配,避免角色校验不通过。

核心原因

系统未配置认证方式,导致Spring Security无法从请求中提取用户凭证,只能将请求标记为匿名,最终触发403权限拦截。添加认证方式并确保认证提供者正确配置后,系统会读取数据库用户信息完成校验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 11:19:50