# 8.4.3. OAuth2

[OAuth2](https://oauth.net/2/)是 Spring 支持的广泛使用的授权框架。

**客户端**

如果您的类路径上有`spring-security-oauth2-client`，您可以利用一些自动配置来设置 OAuth2/Open ID Connect 客户端。此配置利用`OAuth2ClientProperties` 下的属性。相同的属性适用于 servlet 和反应式应用程序。

您可以在`spring.security.oauth2.client`前缀下注册多个 OAuth2 客户端和提供程序，如下例所示：

```properties
spring.security.oauth2.client.registration.my-login-client.client-id=abcd
spring.security.oauth2.client.registration.my-login-client.client-secret=password
spring.security.oauth2.client.registration.my-login-client.client-name=Client for OpenID Connect
spring.security.oauth2.client.registration.my-login-client.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-login-client.scope=openid,profile,email,phone,address
spring.security.oauth2.client.registration.my-login-client.redirect-uri={baseUrl}/login/oauth2/code/{registrationId}
spring.security.oauth2.client.registration.my-login-client.client-authentication-method=client_secret_basic
spring.security.oauth2.client.registration.my-login-client.authorization-grant-type=authorization_code

spring.security.oauth2.client.registration.my-client-1.client-id=abcd
spring.security.oauth2.client.registration.my-client-1.client-secret=password
spring.security.oauth2.client.registration.my-client-1.client-name=Client for user scope
spring.security.oauth2.client.registration.my-client-1.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-client-1.scope=user
spring.security.oauth2.client.registration.my-client-1.redirect-uri={baseUrl}/authorized/user
spring.security.oauth2.client.registration.my-client-1.client-authentication-method=client_secret_basic
spring.security.oauth2.client.registration.my-client-1.authorization-grant-type=authorization_code

spring.security.oauth2.client.registration.my-client-2.client-id=abcd
spring.security.oauth2.client.registration.my-client-2.client-secret=password
spring.security.oauth2.client.registration.my-client-2.client-name=Client for email scope
spring.security.oauth2.client.registration.my-client-2.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-client-2.scope=email
spring.security.oauth2.client.registration.my-client-2.redirect-uri={baseUrl}/authorized/email
spring.security.oauth2.client.registration.my-client-2.client-authentication-method=client_secret_basic
spring.security.oauth2.client.registration.my-client-2.authorization-grant-type=authorization_code

spring.security.oauth2.client.provider.my-oauth-provider.authorization-uri=https://my-auth-server.com/oauth2/authorize
spring.security.oauth2.client.provider.my-oauth-provider.token-uri=https://my-auth-server.com/oauth2/token
spring.security.oauth2.client.provider.my-oauth-provider.user-info-uri=https://my-auth-server.com/userinfo
spring.security.oauth2.client.provider.my-oauth-provider.user-info-authentication-method=header
spring.security.oauth2.client.provider.my-oauth-provider.jwk-set-uri=https://my-auth-server.com/oauth2/jwks
spring.security.oauth2.client.provider.my-oauth-provider.user-name-attribute=name
```

对于支持 [OpenID Connect discovery 的](https://openid.net/specs/openid-connect-discovery-1_0.html)OpenID Connect 提供商，可以进一步简化配置。提供者需要配置一个`issuer-uri`，该 URI 被断言为其发行者标识符。例如，如果`issuer-uri`提供的是“<https://example.com”，则将向“https://example.com/.well-known/openid-configuration”发出“OpenID> 提供商配置请求”。结果预计是“OpenID 提供商配置响应”。以下示例显示了如何使用以下命令配置 OpenID Connect 提供程序`issuer-uri`：

```properties
spring.security.oauth2.client.provider.oidc-provider.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/
```

默认情况下，Spring Security的`OAuth2LoginAuthenticationFilter`仅处理匹配`/login/oauth2/code/*`的URL. 如果您想自定义`redirect-uri`以使用不同的模式，则需要提供配置来处理该自定义模式。例如，对于 Servlet 应用程序，您可以添加自己的应用`SecurityFilterChain`程序，如下所示：

```java
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
public class MyOAuthClientConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((requests) -> requests
                .anyRequest().authenticated()
            )
            .oauth2Login((login) -> login
                .redirectionEndpoint((endpoint) -> endpoint
                    .baseUri("/login/oauth2/callback/*")
                )
            );
        return http.build();
    }

}
```

> Spring Boot 自动配置一个`InMemoryOAuth2AuthorizedClientService`以便Spring Security 用来管理客户端注册的文件。`InMemoryOAuth2AuthorizedClientService`功能有限，我们建议仅将其用于开发环境。对于生产环境，请考虑使用`JdbcOAuth2AuthorizedClientService`或`OAuth2AuthorizedClientService`创建您自己的.

**常见提供商的 OAuth2 客户端注册**

对于常见的 OAuth2 和 OpenID 提供商（包括 Google、Github、Facebook 和 Okta），我们提供了一组提供商默认值（分别为`google`、`github`、`facebook`、 和`okta`）。

如果您不需要自定义这些提供程序，则可以将`provider`属性设置为需要推断默认值的提供程序。此外，如果客户端注册的密钥与默认支持的提供程序匹配，Spring Boot 也会推断出这一点。

换句话说，以下示例中的两个配置都使用 Google 提供程序：

```properties
spring.security.oauth2.client.registration.my-client.client-id=abcd
spring.security.oauth2.client.registration.my-client.client-secret=password
spring.security.oauth2.client.registration.my-client.provider=google
spring.security.oauth2.client.registration.google.client-id=abcd
spring.security.oauth2.client.registration.google.client-secret=password
```

**资源服务器**

如果您的类路径上有`spring-security-oauth2-resource-server`，Spring Boot 可以设置 OAuth2 资源服务器。对于 JWT 配置，需要指定 JWK Set URI 或 OIDC Issuer URI，如以下示例所示：

```properties
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://example.com/oauth2/default/v1/keys
```

```properties
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/
```

> 如果授权服务器不支持 JWK Set URI，您可以使用用于验证 JWT 签名的公钥配置资源服务器。这可以使用`spring.security.oauth2.resourceserver.jwt.public-key-location` 属性来完成，其中值需要指向包含 PEM 编码的 x509 格式的公钥的文件。

`spring.security.oauth2.resourceserver.jwt.audiences`属性可用于指定 JWT 中 aud 声明的预期值。例如，要求 JWT 包含值为 `my-audience`的 aud 声明：

```properties
spring.security.oauth2.resourceserver.jwt.audiences[0]=my-audience
```

相同的属性适用于 servlet 和反应式应用程序。或者，您可以为 servlet 应用程序或`ReactiveJwtDecoder`反应式应用程序定义自己的`JwtDecoder` bean。

如果使用不透明令牌而不是 JWT，您可以配置以下属性以通过内省验证令牌：

```properties
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://example.com/check-token
spring.security.oauth2.resourceserver.opaquetoken.client-id=my-client-id
spring.security.oauth2.resourceserver.opaquetoken.client-secret=my-client-secret
```

同样，相同的属性适用于 servlet 和反应式应用程序。或者，您可以为 servlet 应用程序或`ReactiveOpaqueTokenIntrospector`反应式应用程序定义自己的`OpaqueTokenIntrospector` bean。

**授权服务器**

如果您的类路径上有`spring-security-oauth2-authorization-server`，则可以利用一些自动配置来设置基于 Servlet 的 OAuth2 授权服务器。

您可以在`spring.security.oauth2.authorizationserver.client`前缀下注册多个 OAuth2 客户端，如下例所示：

```properties
spring.security.oauth2.authorizationserver.client.my-client-1.registration.client-id=abcd
spring.security.oauth2.authorizationserver.client.my-client-1.registration.client-secret={noop}secret1
spring.security.oauth2.authorizationserver.client.my-client-1.registration.client-authentication-methods[0]=client_secret_basic
spring.security.oauth2.authorizationserver.client.my-client-1.registration.authorization-grant-types[0]=authorization_code
spring.security.oauth2.authorizationserver.client.my-client-1.registration.authorization-grant-types[1]=refresh_token
spring.security.oauth2.authorizationserver.client.my-client-1.registration.redirect-uris[0]=https://my-client-1.com/login/oauth2/code/abcd
spring.security.oauth2.authorizationserver.client.my-client-1.registration.redirect-uris[1]=https://my-client-1.com/authorized
spring.security.oauth2.authorizationserver.client.my-client-1.registration.scopes[0]=openid
spring.security.oauth2.authorizationserver.client.my-client-1.registration.scopes[1]=profile
spring.security.oauth2.authorizationserver.client.my-client-1.registration.scopes[2]=email
spring.security.oauth2.authorizationserver.client.my-client-1.registration.scopes[3]=phone
spring.security.oauth2.authorizationserver.client.my-client-1.registration.scopes[4]=address
spring.security.oauth2.authorizationserver.client.my-client-1.require-authorization-consent=true
spring.security.oauth2.authorizationserver.client.my-client-2.registration.client-id=efgh
spring.security.oauth2.authorizationserver.client.my-client-2.registration.client-secret={noop}secret2
spring.security.oauth2.authorizationserver.client.my-client-2.registration.client-authentication-methods[0]=client_secret_jwt
spring.security.oauth2.authorizationserver.client.my-client-2.registration.authorization-grant-types[0]=client_credentials
spring.security.oauth2.authorizationserver.client.my-client-2.registration.scopes[0]=user.read
spring.security.oauth2.authorizationserver.client.my-client-2.registration.scopes[1]=user.write
spring.security.oauth2.authorizationserver.client.my-client-2.jwk-set-uri=https://my-client-2.com/jwks
spring.security.oauth2.authorizationserver.client.my-client-2.token-endpoint-authentication-signing-algorithm=RS256
```

> `client-secret` 属性的格式必须能够与配置的 `PasswordEncoder` 匹配。 `PasswordEncoder` 的默认实例是通过`PasswordEncoderFactories.createDelegatingPasswordEncoder()` 创建的。

Spring Boot 为 Spring Authorization Server 提供的自动配置旨在快速入门。大多数应用程序都需要定制，并且需要定义多个 bean 来覆盖自动配置。

以下组件可以定义为 bean 来覆盖特定于 Spring Authorization Server 的自动配置：

* `RegisteredClientRepository`
* `AuthorizationServerSettings`
* `SecurityFilterChain`
* `com.nimbusds.jose.jwk.source.JWKSource<com.nimbusds.jose.proc.SecurityContext>`
* `JwtDecoder`

  Spring Boot 自动配置的`InMemoryRegisteredClientRepository`其只能被Spring 授权服务器用于管理注册客户端的权限。`InMemoryRegisteredClientRepository`功能有限，我们建议仅将其用于开发环境。对于生产环境，请考虑使用`JdbcRegisteredClientRepository`或创建您自己的`RegisteredClientRepository`.

[其他信息可以在《Spring 授权服务器参考指南》](https://docs.spring.io/spring-authorization-server/reference/1.2/index.html)的[“入门”](https://docs.spring.io/spring-authorization-server/reference/1.2/getting-started.html)一章中找到。
