Spring Boot 3.2.1集成GitHub OAuth2后如何检查用户所属组织/团队
实现Spring Boot GitHub OAuth2登录后的组织/团队权限校验
核心步骤
1. 配置GitHub OAuth2 Scope
要访问用户的组织和团队信息,必须在OAuth2客户端配置中添加read:org权限,否则GitHub API会返回权限不足的错误。在application.yml中配置:
spring: security: oauth2: client: registration: github: client-id: YOUR_CLIENT_ID client-secret: YOUR_CLIENT_SECRET scope: read:user, read:org # 新增read:org权限
2. 自定义OAuth2UserService扩展用户信息
Spring Security的OAuth2UserService负责处理登录后的用户信息,我们可以重写它来调用GitHub API获取组织和团队:
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.web.client.RestTemplate; import java.util.List; import java.util.Map; public class GitHubOAuth2UserService extends DefaultOAuth2UserService { private final RestTemplate restTemplate = new RestTemplate(); @Override public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { OAuth2User oAuth2User = super.loadUser(userRequest); String accessToken = userRequest.getAccessToken().getTokenValue(); // 调用GitHub API获取用户所属组织 List<Map<String, Object>> orgs = restTemplate.getForObject( "https://api.github.com/user/orgs?access_token=" + accessToken, List.class ); // 调用GitHub API获取用户所属团队 List<Map<String, Object>> teams = restTemplate.getForObject( "https://api.github.com/user/teams?access_token=" + accessToken, List.class ); // 将组织和团队信息加入用户属性,方便后续权限校验 oAuth2User.getAttributes().put("orgs", orgs); oAuth2User.getAttributes().put("teams", teams); return oAuth2User; } }
3. 配置Spring Security使用自定义UserService
在Security配置类中,替换默认的OAuth2UserService:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .oauth2Login(oauth2 -> oauth2 .userInfoEndpoint(userInfo -> userInfo .userService(new GitHubOAuth2UserService()) ) ); return http.build(); } }
4. 实现权限校验
方法级权限校验
可以通过@PreAuthorize注解结合SpEL表达式,检查用户是否属于指定组织/团队:
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.oauth2.core.user.OAuth2User; @RestController public class ResourceController { @GetMapping("/admin") @PreAuthorize("#oauth2User.attributes['orgs'].stream().anyMatch(org -> org['login'] == 'your-target-org')") public String adminResource(@AuthenticationPrincipal OAuth2User oauth2User) { return "Admin Access Granted"; } @GetMapping("/team-member") @PreAuthorize("#oauth2User.attributes['teams'].stream().anyMatch(team -> team['name'] == 'your-target-team')") public String teamResource(@AuthenticationPrincipal OAuth2User oauth2User) { return "Team Member Access Granted"; } }
全局权限拦截(可选)
如果需要全局拦截特定路径,可以自定义Filter,检查用户的组织/团队信息:
import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; import java.util.List; import java.util.Map; public class GitHubOrgTeamFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null && auth.getPrincipal() instanceof OAuth2User oauth2User) { List<Map<String, Object>> orgs = (List<Map<String, Object>>) oauth2User.getAttributes().get("orgs"); boolean isInTargetOrg = orgs.stream().anyMatch(org -> "your-target-org".equals(org.get("login"))); if (!isInTargetOrg && request.getRequestURI().startsWith("/admin")) { response.sendError(HttpServletResponse.SC_FORBIDDEN, "Not authorized to access this resource"); return; } } filterChain.doFilter(request, response); } }
然后在Security配置中添加这个Filter:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .addFilterBefore(new GitHubOrgTeamFilter(), UsernamePasswordAuthenticationFilter.class) // 其他原有配置... return http.build(); }
注意事项
- API速率限制:GitHub API有速率限制,登录时调用两次API(组织+团队)在免费额度内完全够用,无需额外处理。
- 权限异常处理:如果用户授权时未同意
read:org权限,API调用会返回403,此时可以在自定义UserService中捕获异常,引导用户重新授权。 - 自定义UserDetails(可选):如果需要更灵活的权限管理,可以创建自定义UserDetails类,将组织/团队信息封装为GrantedAuthority,方便直接使用
hasAuthority注解。
内容的提问来源于stack exchange,提问作者Luiggi33
相关产品推荐
相关产品推荐

