我正在使用 Keycloak 作为我的OAuth2授权服务器,并按照GitHub上的此官方示例为多租户配置了OAuth2资源服务器。 考虑到 JWT 令牌的 Issuer 字段来解决当前租户。 因此,将根据在相应的 OpenID Connect 知名端点处公开的 JWKS 来验证令牌。
这是我的安全配置:
@EnableWebSecurity
@RequiredArgsConstructor
@EnableAutoConfiguration(exclude = UserDetailsServiceAutoConfiguration.class)
public class OrganizationSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final TenantService tenantService;
private List<Tenant> tenants;
@PostConstruct
public void init() {
this.tenants = this.tenantService.findAllWithRelationships();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.authenticationManagerResolver(new MultiTenantAuthenticationManagerResolver(this.tenants));
}
}
这是我的自定义 AuthenticationManagerResolver :
public class MultiTenantAuthenticationManagerResolver implements AuthenticationManagerResolver<HttpServletRequest> {
private final AuthenticationManagerResolver<HttpServletRequest> resolver;
private List<Tenant> tenants;
public MultiTenantAuthenticationManagerResolver(List<Tenant> tenants) {
this.tenants = tenants;
List<String> trustedIssuers = this.tenants.stream()
.map(Tenant::getIssuers)
.flatMap(urls -> urls.stream().map(URL::toString))
.collect(Collectors.toList());
this.resolver = new JwtIssuerAuthenticationManagerResolver(trustedIssuers);
}
@Override
public AuthenticationManager resolve(HttpServletRequest context) {
return this.resolver.resolve(context);
}
}
现在,由于org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver.TrustedIssuerJwtAuthenticationManagerResolver
的设计
这是私有,我想提取自定义主体的唯一方法是重新实现以下所有内容:
在我看来,很多重新发明轮子,我唯一需要的就是拥有一个自定义的Principal。
我发现的示例似乎不适合我的情况,因为它们引用了OAuth2Client或对Multitenancy而言并不困难。
我真的需要重新实现所有这些类/接口吗,还是有一种更聪明的方法?
答案 0 :(得分:0)
我就是这样做的,没有重新实现大量的类。然而,这没有使用 JwtAuthenticationToken。
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
@Override
protected void configure(HttpSecurity http) throws Exception {
http
...
.oauth2ResourceServer(oauth2 -> oauth2.authenticationManagerResolver(authenticationManagerResolver()));
}
@Bean
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver() {
List<String> issuers = ... // get this from list of tennants or config, whatever
Predicate<String> trustedIssuer = issuers::contains;
Map<String, AuthenticationManager> authenticationManagers = new ConcurrentHashMap<>();
AuthenticationManagerResolver<String> resolver = (String issuer) -> {
if (trustedIssuer.test(issuer)) {
return authenticationManagers.computeIfAbsent(issuer, k -> {
var jwtDecoder = JwtDecoders.fromIssuerLocation(issuer);
var provider = new JwtAuthenticationProvider(jwtDecoder);
provider.setJwtAuthenticationConverter(jwtAuthenticationService::loadUserByJwt);
return provider::authenticate;
});
}
return null;
};
return new JwtIssuerAuthenticationManagerResolver(resolver);
}
}
@Service
public class JwtAuthenticationService {
public AbstractAuthenticationToken loadUserByJwt(Jwt jwt) {
UserDetails userDetails = ... // or your choice of principal
List<GrantedAuthority> authorities = ... // extract from jwt or db
...
return new UsernamePasswordAuthenticationToken(userDetails, null, authorities);
}
}