版本
spring boot 3.2.1 spring seciruty 6.2.1
配置
- OAuth2 客户端配置文件 application.yml
spring:
security:
oauth2:
client:
registration:
auth-client:
provider: auth-server # 授权服务器(如果不配置,则provider需要使用auth-client作为key)
client-id: oidc-client # 客户端ID
client-name: 客户端 # 客户端名称
client-secret: secret # 客户端密码
authorization-grant-type: authorization_code # 授权方式
redirect-uri: http://localhost:8080/login/oauth2/code/auth-server # 授权码模式完成授权后跳转的地址(最后一节为provider.key)
scope: openid,profile # 请求的scope
provider:
auth-server:
# issuer-uri: http://localhost:8081 自动从授权服务器获取元数据
authorization-uri: http://localhost:8081/oauth2/authorize # 授权端点
token-uri: http://localhost:8081/oauth2/token # 令牌获取端点
user-info-uri: http://localhost:8081/userinfo # 用户信息端点
jwk-set-uri: http://localhost:8081/oauth2/jwks # JWS端点
user-name-attribute: sub # 用户名属性(OpenID Connect 默认使用sub声明字段保存用户名,如果自定义用户信息结构则需要改为对应声明字段名)
注: 如果授权服务器开放了元数据端点,可通过配置issuer-uri,自动通过授权服务器获取元数据,无需配置provider中的各端点uri
源码
- OAuth2客户端属性映射 org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesMapper
public final class OAuth2ClientPropertiesMapper {
private static ClientRegistration getClientRegistration(String registrationId,
OAuth2ClientProperties.Registration properties, Map<String, Provider> providers) {
// 尝试通过issuer配置获取客户端注册信息(授权服务器元数据)
Builder builder = getBuilderFromIssuerIfPossible(registrationId, properties.getProvider(), providers);
if (builder == null) {
// 如果获取失败则根据配置构造
builder = getBuilder(registrationId, properties.getProvider(), providers);
}
// 使用配置覆盖构造器默认值(没有配置或空值则不应用)
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(properties::getClientId).to(builder::clientId);
map.from(properties::getClientSecret).to(builder::clientSecret);
map.from(properties::getClientAuthenticationMethod)
.as(ClientAuthenticationMethod::new)
.to(builder::clientAuthenticationMethod);
map.from(properties::getAuthorizationGrantType)
.as(AuthorizationGrantType::new)
.to(builder::authorizationGrantType);
map.from(properties::getRedirectUri).to(builder::redirectUri);
map.from(properties::getScope).as(StringUtils::toStringArray).to(builder::scope);
map.from(properties::getClientName).to(builder::clientName);
return builder.build();
}
// 尝试通过issuer获取客户端注册信息构造器
private static Builder getBuilderFromIssuerIfPossible(String registrationId, String configuredProviderId,
Map<String, Provider> providers) {
// 如果客户端注册配置中没有配置providerId,则使用客户端注册ID作为providerId
String providerId = (configuredProviderId != null) ? configuredProviderId : registrationId;
// provider配置了issuerUri,则通过issuer查询客户端注册信息
if (providers.containsKey(providerId)) {
Provider provider = providers.get(providerId);
String issuer = provider.getIssuerUri();
if (issuer != null) {
Builder builder = ClientRegistrations.fromIssuerLocation(issuer).registrationId(registrationId);
return getBuilder(builder, provider);
}
}
return null;
}
// 获取客户端注册信息构造器
private static Builder getBuilder(String registrationId, String configuredProviderId,
Map<String, Provider> providers) {
// 如果客户端注册配置中没有配置providerId,则使用客户端注册ID作为providerId
String providerId = (configuredProviderId != null) ? configuredProviderId : registrationId;
// 获取通用provider(GOOGLE,GITHUB,FACEBOOK,OKTA)
CommonOAuth2Provider provider = getCommonProvider(providerId);
if (provider == null && !providers.containsKey(providerId)) {
throw new IllegalStateException(getErrorMessage(configuredProviderId, registrationId));
}
// 如果获取到通用provider, 则使用通用provider创建builder,否则使用客户端注册信息创建builder
Builder builder = (provider != null) ? provider.getBuilder(registrationId)
: ClientRegistration.withRegistrationId(registrationId);
// 如果配置信息中包含了provider配置,则将配置信息应用到builder中
if (providers.containsKey(providerId)) {
return getBuilder(builder, providers.get(providerId));
}
return builder;
}
}
- 客户端注册构造器工具类 org.springframework.security.oauth2.client.registration.ClientRegistrations
public final class ClientRegistrations {
...
// 从issuer获取客户端注册配置信息
public static ClientRegistration.Builder fromIssuerLocation(String issuer) {
Assert.hasText(issuer, "issuer cannot be empty");
URI uri = URI.create(issuer);
// 可支持从 OpenID Connect, OAuth授权服务器获取元数据
return getBuilder(issuer, oidc(uri), oidcRfc8414(uri), oauth(uri));
}
...
}