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

如何为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 21:50:07