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

Spring Security认证授权后仍出现403 Forbidden错误排查

问题分析及解决方案

核心问题根源

  1. User实体未实现UserDetails接口:Spring Security的UserDetailsService返回的用户对象必须实现UserDetails接口,否则框架无法识别用户的权限信息,直接导致权限校验失败触发403。
  2. 角色权限前缀不匹配:hasRole("ADMIN")会自动匹配带ROLE_前缀的权限字符串(如ROLE_ADMIN),若User类返回的权限是ADMIN(无前缀),则无法匹配校验规则。
  3. 重复创建默认用户:每次调用注册接口都会重新创建Admin和Applicant用户,可能导致数据库中存在多个同名用户,登录时无法获取正确权限的用户实例。

分步修复方案

1. 让User实体实现UserDetails接口

修改User类,实现UserDetails并正确重写权限相关方法,确保Spring Security能识别用户权限:

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import jakarta.persistence.*;
import java.util.Collection;
import java.util.List;

@Entity
public class User implements UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long userId;
    private String userName;
    private String passwordHash;
    @Enumerated(EnumType.STRING)
    private UserType role;
    // 其他字段:email、address、profileHeadline等

    // 重写UserDetails核心方法
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        // 为角色添加ROLE_前缀,匹配hasRole的校验规则
        return List.of(new SimpleGrantedAuthority("ROLE_" + role.name()));
    }

    @Override
    public String getPassword() {
        return passwordHash; // 返回加密后的密码
    }

    @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;
    }

    // getter、setter、构造方法省略
}

2. 修复默认用户初始化逻辑

移除注册接口中重复创建Admin/Applicant的代码,改为项目启动时仅初始化一次:

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    // 项目启动时初始化默认用户(仅执行一次)
    @PostConstruct
    public void initDefaultUsers() {
        // 检查Admin是否存在,不存在则创建
        userRepository.findByUserName("Admin").ifPresentOrElse(
                user -> {},
                () -> {
                    User admin = new User();
                    admin.setUserName("Admin");
                    admin.setPasswordHash(passwordEncoder.encode("Admin"));
                    admin.setRole(UserType.ADMIN);
                    userRepository.save(admin);
                }
        );

        // 检查Applicant是否存在,不存在则创建
        userRepository.findByUserName("Applicant").ifPresentOrElse(
                user -> {},
                () -> {
                    User applicant = new User();
                    applicant.setUserName("Applicant");
                    applicant.setPasswordHash(passwordEncoder.encode("Applicant"));
                    applicant.setRole(UserType.APPLICANT);
                    userRepository.save(applicant);
                }
        );
    }

    public User registerUser(User user) {
        // 仅处理新用户注册,加密密码后保存
        user.setPasswordHash(passwordEncoder.encode(user.getPasswordHash()));
        return userRepository.save(user);
    }

    // 其他方法省略
}

3. 权限校验规则的可选调整(若无需ROLE_前缀)

如果不想在权限中添加ROLE_前缀,可将SecurityConfig中的hasRole改为hasAuthority:

// 原代码
.requestMatchers("/admin/**").hasRole("ADMIN")
// 修改为
.requestMatchers("/admin/**").hasAuthority("ADMIN")

同时修改User类的getAuthorities方法:

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    return List.of(new SimpleGrantedAuthority(role.name()));
}

验证测试

  1. 重启项目,确保默认Admin用户已正确初始化到数据库
  2. 使用Postman选择Basic Auth认证方式,输入用户名Admin、密码Admin
  3. 请求/admin/applicants接口,确认返回200状态码及正确数据

内容的提问来源于stack exchange,提问作者Waleed Najam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 13:50:16