[appendix] = OAuth2 OAuth2 是一个开放标准,允许用户授权第三方应用程序访问他们存储在另外的服务提供者上的信息,不需要将用户名和密码提供给第三方应用。 OAuth2 协议允许用户将认证与授权交给一个独立的第三方进行担保。 OAuth2可以提供一个统一的认证服务。 == 模块构成 * Resource owner (资源拥有者): 拥有该资源的服务或用户,如我们自己或者资源网站 * Authorization server (认证服务器) : 即用来认证与颁发令牌(如token)的服务 * Resource server (资源服务器) : 拥有资源的服务,如我们要访问的网站 * Client (客户端) : 即访问的客户端,如我们自己用的访问网站 == 授权方式 * authorization_code (授权码模式) : 最正规的模式,客户端先将用户导向认证服务器,登录后获取授权码,然后进行授权,最后根据授权码获取访问令牌 * refresh_token (刷新模式) : 用刷新码获取 * client_credentials (客户端模式) : 第三方应用自己本身需要获取资源 == 认证方式 * client_secret_basic : 客户端认证信息会以 Basic Auth 的方式进行传递 * client_secret_post : 客户端认证信息放在请求体中进行传递 * client_secret_jwt : HMAC 算法生成 JWT 来传递客户端秘钥 * private_key_jwt : 客户端使用 RSA 和 EC 算法生成 JWT 传递客户端认证信息,需提供公钥给授权服务器 * none : 会开启 PKCE 功能以确保安全, 适应于开放型客户端 === client_secret_jwt OAuth2 客户端将自己的密钥作为 HMAC SHA256 算法的 key 生成 SecretKey [source,java] ---- byte[] pin =clientSecret.getBytes(StandardCharsets.UTF_8); SecretKeySpec secretKey =new SecretKeySpec(pin,"HmacSHA256"); ---- 然后通过 SecretKey 生成一个携带 OAuth2 客户端信息的 JWT,在授权码请求 Token 环节携带该 JWT 以便授权服务器进行客户端认证,请求的报文为: [source,html] ---- POST /oauth2/token HTTP/1.1 Host: oauth2_client.felord.cn Content-Type: application/x-www-form-urlencoded grant_type=authorization_code& code=n0esc3NRze7LTCu7iYzS6a5acc3f0ogp4& client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer& client_assertion=你的JWT ---- 授权服务器收到请求后通过 OAuth2 客户端的 client_secret 对 JWT 进行解码校验以认证客户端。这种方式能很好的保护 client_secret 在非 HTTPS 环境下的传输。 这里 OAuth2 客户端的密钥(client_secret)比特长度必须大于等于256。 === private_key_jwt private_key_jwt 和 client_secret_jwt 唯一的区别就是生成 JWT 的方式不同。 通过这种方式,OAuth2 客户端已经不需要 client_secret,只需要配置一对 RSA 或者 EC 密钥,通过密钥来生成 JWT, 另外还需要向授权服务器提供公钥,通常是一个 jwkSetUrl。这种方式让客户端的认证信息更加安全的传输。 == 请求点 |=== | endpoint | method | 说明 | /.well-known/oauth-authorization-server | GET | meta data | /.well-known/openid-configuration | GET | meta data | /oauth2/authorize | GET/POST | 获取授权码 | /oauth2/token | POST | 获取访问令牌 | /oauth2/introspect | POST | 内省 | /oauth2/revoke | POST | 收回令牌 | /userinfo | GET/POST | 用户信息 | /oauth2/jwks | GET | JWK+ JWS | /connect/register | POST | oidc 客户端注册 |=== === /oauth2/authorize | 参数名 | 参数类型 | 参数值 | 说明 | client_id | 请求 | | | response_type | 请求 | code | 授权码 | redirect_uri | 请求 | | | scope | 请求 | | | state | 请求 | | | code | 响应 | | | scope | 响应 | | | state | 响应 | | | access_token | 响应 | | | token_type | 响应 | | | expires_in | 响应 | | | error | 响应 | | | error_description | 响应 | | | error_uri | 响应 | | === /oauth2/token | 参数名 | 参数类型 | 参数值 | 说明 | client_id | 请求 | | | client_secret | 请求 | | | client_assertion_type | 请求 | | | client_assertion | 请求 | | | assertion | 请求 | | | grant_type | 请求 | | | redirect_uri | 请求 | | | scope | 请求 | | | code | 请求 | | | refresh_token | 请求 | | | username | 请求 | | | password | 请求 | | | scope | 响应 | | | access_token | 响应 | | | token_type | 响应 | | | expires_in | 响应 | | | refresh_token | 响应 | | | error | 响应 | | | error_description | 响应 | | | error_uri | 响应 | | === /oauth2/revoke | 参数名 | 参数类型 | 参数值 | 说明 | token | 请求 | | | token_type_hint | 请求 | | === 获取授权码 http://localhost:8080/oauth2/authorize?client_secret=secret&client_id=platform-oidc&response_type=code&redirect_uri=http://localhost:8080/oauth2/authorized-oidc |=== | 参数名 | 参数值 | 说明 | client_id | | 注册客户端 ID | response_type | code | 响应类型 | |=== org.springframework.security.web.session.DisableEncodeUrlFilter, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter, org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.AuthorizationServerContextFilter, org.springframework.security.web.context.SecurityContextPersistenceFilter, org.springframework.security.web.header.HeaderWriterFilter, org.springframework.security.web.csrf.CsrfFilter, org.springframework.security.web.authentication.logout.LogoutFilter, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter, org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter, org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter, org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter, org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter, org.springframework.security.web.savedrequest.RequestCacheAwareFilter, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter, org.springframework.security.web.authentication.AnonymousAuthenticationFilter, org.springframework.security.web.session.SessionManagementFilter, org.springframework.security.web.access.ExceptionTranslationFilter, org.springframework.security.web.access.intercept.FilterSecurityInterceptor, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter, org.springframework.security.oauth2.server.authorization.oidc.web.OidcUserInfoEndpointFilter