使用自定义 UserDetailsS​​ervice 时 JWT 中不存在 OAuth2 GrantedAuthorities

OAuth2 GrantedAuthorities not present in JWT when using custom UserDetailsService

使用新的 spring-authorization-server 0.2.3 并遵循 https://github.com/spring-projects/spring-authorization-server/tree/main/samples 作为参考,我能够在使用 [=13] 时成功设置授权服务器、资源服务器和客户端=]如下


@EnableWebSecurity
public class DefaultSecurityConfig {

    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeRequests(authorizeRequests ->
                        authorizeRequests.anyRequest().authenticated()
                )
                .formLogin(Customizer.withDefaults())
                .build();
    }


    @Bean
    UserDetailsService users() {
        User.UserBuilder users = User.withDefaultPasswordEncoder();
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(users.username("user1").password("password").roles("USER").build());
        manager.createUser(users.username("admin").password("password").roles("USER", "ADMIN").authorities("r1","r2","r3").build());
        return manager;
    }

}

这很有效,在客户端中,我可以看到权限 Granted Authorities=["r1","r2","r3"]存在。

现在,当我尝试实现自己的 UserDetailsService 从 Mongo 数据库检索用户时,我不再看到传递给客户端的 GrantedAuthorities,而只看到 Granted Authorities=[ROLE_USER, SCOPE_openid]

这就是我现在在 DefaultSecurityConfig 中的内容


@EnableWebSecurity
public class DefaultSecurityConfig {

    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeRequests(authorizeRequests ->
                        authorizeRequests.anyRequest().authenticated()
                )
                .formLogin(Customizer.withDefaults())
                .build();
    }

    @Autowired
    private MongoTemplate mongoTemplate;

    @Bean
    UserDetailsService users() {
        return new CustomUserDetailsService(mongoTemplate);
    }
}

我的 CustomUserDetailsService 如下所示:


public class CustomUserDetailsService implements UserDetailsService {

    private final MongoTemplate mongoTemplate;

    public CustomUserDetailsService(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Criteria criteria = Criteria.where("email").is(username);
        CustomUser user = mongoTemplate.findOne(new Query(criteria), CustomUser.class, "vOAuthUser");
        if (user != null) {
            log.info("Found user {}", user.email());
            List<GrantedAuthority> authorities = getUserAuthority(user.groups());
            return buildUserForAuthentication(user, authorities);
        } else {
            throw new UsernameNotFoundException("username not found");
        }
    }

    private UserDetails buildUserForAuthentication(CustomUser user, List<GrantedAuthority> authorities) {
        return new org.springframework.security.core.userdetails.User(user.email(), user.password(), authorities);
    }

    private List<GrantedAuthority> getUserAuthority(Set<String> groups) {
        List<GrantedAuthority> authorities = new ArrayList<>();
        groups.forEach(s -> {
            Criteria criteria = Criteria.where("name").is(s);
            CustomRole role = mongoTemplate.findOne(new Query(criteria), CustomRole.class, "vRole");
            if (role != null) {
                authorities.addAll(role.grantedAuthorities());
            }
        });
        return authorities;
    }

}


Any help is greatly appreciated.

您是否在安全配置中定义了 OAuth2 Token Customizer bean?如果需要,您可以在那里添加 Granted Authorities,如以下代码所示:

   @Bean
   @SuppressWarnings("unused")
   OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {
      return context -> {
         JoseHeader.Builder headers = context.getHeaders();
         JwtClaimsSet.Builder claims = context.getClaims();

         Authentication principal = context.getPrincipal();
         Set<String> authorities = principal.getAuthorities().stream()
               .map(GrantedAuthority::getAuthority)
               .collect(Collectors.toSet());
         claims.claim("authorities", authorities);
      };
   }