Spring Boot OAuth登录后角色权限配置异常求助
问题排查
- 核心问题:
CustomOAuth2User.getUserRoles()尝试从GitHub OAuth返回的用户属性中读取roles字段,但GitHub的OAuth用户信息接口不会返回该字段,导致roles始终为空集合,触发默认逻辑返回ROLE_USER。 - 次要问题:
AuthService.loadUserByUsername()错误地将用户状态设置为accountExpired(true)、accountLocked(true)等,导致用户即使认证成功也会被判定为无效。
解决方案
1. 修改CustomOAuth2UserService,从数据库加载用户角色
注入UserRepository,在加载OAuth用户时查询数据库对应的用户记录,将角色传递给CustomOAuth2User:
@Service public class CustomOAuth2UserService extends DefaultOAuth2UserService { private final UserRepository userRepository; public CustomOAuth2UserService(UserRepository userRepository) { this.userRepository = userRepository; } @Override public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { OAuth2User oauth2User = super.loadUser(userRequest); String githubId = oauth2User.getAttribute("id").toString(); // 查询数据库中的用户角色 Set<Role> userRoles = userRepository.findByIdentifier(githubId) .map(user -> Set.of(user.getRole())) .orElse(Collections.emptySet()); return new CustomOAuth2User(oauth2User, userRoles); } }
2. 修改CustomOAuth2User,接收并使用数据库中的角色
添加角色字段,修改构造方法和getUserRoles()方法:
public class CustomOAuth2User implements OAuth2User { private OAuth2User oauth2User; private Set<Role> roles; // 新增字段存储数据库中的角色 public CustomOAuth2User(OAuth2User oauth2User, Set<Role> roles) { this.oauth2User = oauth2User; this.roles = roles; } @Override public Map<String, Object> getAttributes() { return oauth2User.getAttributes(); } @Override public Collection<? extends GrantedAuthority> getAuthorities() { System.out.println("User roles:"); for (Role role : roles) { System.out.println(role.name()); } if (!roles.isEmpty()) { return roles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role.name())) .collect(Collectors.toList()); } else { return Collections.singleton(new SimpleGrantedAuthority("ROLE_USER")); } } @Override public String getName() { return oauth2User.getAttribute("name"); } private Set<Role> getUserRoles() { return this.roles; // 直接返回数据库中加载的角色 } public String getEmail() { return oauth2User.<String>getAttribute("email"); } }
3. 修复AuthService中的用户状态错误
将用户状态全部改为false,确保用户有效:
@Service public class AuthService implements UserDetailsService{ private final UserRepository userRepository; public AuthService(UserRepository userRepository) { this.userRepository = userRepository; } @Override public UserDetails loadUserByUsername(String identifier) throws UsernameNotFoundException { User user = userRepository.findByIdentifier(identifier) .orElseThrow(() -> new UsernameNotFoundException("User not found with GitHub user ID: " + identifier)); return org.springframework.security.core.userdetails.User .withUsername(String.valueOf(user.getIdentifier())) .password(user.getPassword()) .authorities(user.getAuthorities()) .accountExpired(false) // 改为false .accountLocked(false) // 改为false .credentialsExpired(false) // 改为false .disabled(false) // 改为false .build(); } }
4. 可选优化:调整用户创建逻辑到CustomOAuth2UserService
确保加载用户时数据库已有记录,避免第一次登录时角色为空:
@Service public class CustomOAuth2UserService extends DefaultOAuth2UserService { private final UserRepository userRepository; private final UserService userService; public CustomOAuth2UserService(UserRepository userRepository, UserService userService) { this.userRepository = userRepository; this.userService = userService; } @Override public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { OAuth2User oauth2User = super.loadUser(userRequest); String githubId = oauth2User.getAttribute("id").toString(); // 检查用户是否存在,不存在则创建 User user = userRepository.findByIdentifier(githubId) .orElseGet(() -> { UserDTO userDTO = new UserDTO( oauth2User.getName(), "username", githubId, oauth2User.getAttribute("avatar_url").toString(), Role.ADMIN ); return userService.create(userDTO); }); Set<Role> userRoles = Set.of(user.getRole()); return new CustomOAuth2User(oauth2User, userRoles); } }
同时简化SecurityConfig的successHandler:
@Configuration @EnableWebSecurity public class SecurityConfig { private final CustomOAuth2UserService oauthUserService; public SecurityConfig(CustomOAuth2UserService oauthUserService) { this.oauthUserService = oauthUserService; } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http .authorizeHttpRequests(auth -> { auth.requestMatchers(HttpMethod.GET, "/userinfo").hasRole("ADMIN"); auth.requestMatchers(HttpMethod.GET, "/").hasRole("USER"); auth.anyRequest().authenticated(); }) .oauth2Login(oauth2 -> oauth2 .userInfoEndpoint(userInfo -> userInfo.userService(oauthUserService)) .successHandler((request, response, authentication) -> { response.sendRedirect("/"); }) ) .build(); } }
验证逻辑
- 第一次登录时,系统自动创建ADMIN角色的用户,
CustomOAuth2User加载到该角色,返回ROLE_ADMIN权限,可访问/userinfo路由。 - 后续登录直接从数据库读取角色,权限正确生效。
- 普通用户(ROLE_USER)无法访问
/userinfo,仅能访问/路由。
内容的提问来源于stack exchange,提问作者PauloRamos
相关产品推荐
相关产品推荐

