如何为Google JWT手动添加Spring Security权限?
解决Google Sign-on JWT无权限问题:手动添加权限与数据库角色跟踪
一、核心结论
Google生成的JWT确实不含scope或roles字段,要让@PreAuthorize注解生效,完全可以通过自定义JWT认证转换器,在认证流程中从数据库读取用户角色,手动注入到JwtAuthenticationToken的权限列表中,同时支持自行在数据库中跟踪用户实体的角色信息。
二、具体实现步骤
1. 实现用户角色的数据库查询逻辑
首先需要定义数据访问层和服务类,根据Google JWT中的sub字段(Google用户的唯一标识)查询数据库中存储的用户角色:
// 用户实体类,存储Google用户ID与对应角色 @Entity public class AppUser { @Id private String googleSub; // 对应Google JWT的sub字段 private List<String> roles; // 示例:["ROLE_USER", "ROLE_ADMIN"] // getter、setter、构造方法省略 } // 数据访问Repository public interface AppUserRepository extends JpaRepository<AppUser, String> { Optional<AppUser> findByGoogleSub(String googleSub); } // 角色查询服务 @Service public class UserRoleService { private final AppUserRepository userRepository; public UserRoleService(AppUserRepository userRepository) { this.userRepository = userRepository; } // 根据Google用户ID查询角色,转换为SimpleGrantedAuthority列表 public List<SimpleGrantedAuthority> getAuthoritiesByGoogleSub(String googleSub) { return userRepository.findByGoogleSub(googleSub) .map(user -> user.getRoles().stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList())) .orElse(Collections.emptyList()); // 无匹配用户时返回空权限,可改为默认权限 } }
2. 自定义JwtAuthenticationConverter
创建转换器,重写JWT到认证令牌的转换逻辑,将数据库查询到的角色注入到令牌中:
@Component public class CustomJwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> { private final UserRoleService userRoleService; // 默认转换器,可保留用于处理可能存在的默认权限(Google JWT中为空,可忽略) private final JwtGrantedAuthoritiesConverter defaultConverter = new JwtGrantedAuthoritiesConverter(); public CustomJwtAuthenticationConverter(UserRoleService userRoleService) { this.userRoleService = userRoleService; } @Override public AbstractAuthenticationToken convert(Jwt jwt) { // 获取默认权限(Google JWT中为空,此处仅为兼容逻辑) Collection<GrantedAuthority> authorities = defaultConverter.convert(jwt); // 从JWT中提取Google用户唯一标识 String googleSub = jwt.getSubject(); // 从数据库查询用户角色并添加到权限列表 List<SimpleGrantedAuthority> dbAuthorities = userRoleService.getAuthoritiesByGoogleSub(googleSub); authorities.addAll(dbAuthorities); // 构造带权限的JwtAuthenticationToken return new JwtAuthenticationToken(jwt, authorities); } }
3. 修改SecurityConfig配置
将自定义转换器配置到OAuth2资源服务器的JWT配置中:
package com.example.ressource_server.config; import com.example.ressource_server.converter.CustomJwtAuthenticationConverter; 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.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class SecurityConfig { private final CustomJwtAuthenticationConverter customJwtAuthenticationConverter; // 注入自定义转换器 public SecurityConfig(CustomJwtAuthenticationConverter customJwtAuthenticationConverter) { this.customJwtAuthenticationConverter = customJwtAuthenticationConverter; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception{ httpSecurity.oauth2ResourceServer(r -> r.jwt(jwtConfigurer -> jwtConfigurer.jwkSetUri("https://www.googleapis.com/oauth2/v3/certs") // 配置自定义转换器 .jwtAuthenticationConverter(customJwtAuthenticationConverter) ) ); httpSecurity.authorizeHttpRequests(auth -> auth.anyRequest().authenticated()); return httpSecurity.build(); } }
三、关键注意事项
- 需提前将用户的
googleSub(Google JWT的sub字段)与对应角色存入数据库,可在用户首次通过Google登录时(如OAuth2授权码流程回调)完成初始化。 - 使用
@PreAuthorize时注意角色匹配规则:若数据库存储的角色带ROLE_前缀(如ROLE_ADMIN),则用@PreAuthorize("hasRole('ADMIN')");若不带前缀,则用@PreAuthorize("hasAuthority('ADMIN')")。 - 若需为未找到的用户设置默认权限,可修改
getAuthoritiesByGoogleSub方法的返回值,例如返回Collections.singletonList(new SimpleGrantedAuthority("ROLE_GUEST"))。
内容的提问来源于stack exchange,提问作者Roronoa Zoro
相关产品推荐
相关产品推荐

