SpringSecurity中文文档(Servlet OAuth 2.0 Login)

OAuth 2.0 Login

OAuth 2.0 Login 功能允许应用程序让用户通过使用 OAuth 2.0 Provider (如 GitHub)或 OpenID Connect 1.0 Provider (如 Google)的现有帐户登录到应用程序。OAuth 2.0 Login 实现了两个用例: “ Login with Google”或“ Login with GitHub”。

OAuth 2.0 Login 是通过使用授权代码授权(Authorization Code Grant)实现的,正如 OAuth 2.0 Authorization Framework 和 OpenID Connect Core 1.0中指定的那样。

本节摘要

  • Core Configuration
  • Advanced Configuration
  • OIDC Logout

Core Configuration

Spring Boot Sample

Spring Boot 为 OAuth 2.0 Login 带来了完整的自动配置功能。

本节展示如何使用 Google 作为身份验证提供者来配置 OAuth 2.0 Login 示例,并涵盖以下主题:

  • Initial Setup
  • Setting the Redirect URI
  • Configure application.yml
  • Boot up the Application

Initial Setup

要使用 Google 的 OAuth 2.0身份验证系统进行登录,必须在 Google API Console 中设置一个项目,以获得 OAuth 2.0凭据。

Google 用于身份验证的 OAuth 2.0实现符合 OpenID Connect 1.0规范,并通过了 OpenID 认证。

按照 OpenID Connect 页面上的说明进行操作,从“设置 OAuth 2.0”部分开始。

在完成“获取 OAuth 2.0凭据”说明之后,您应该拥有新的 OAuth Client,其凭据包括客户端 ID 和客户端秘钥。

Setting the Redirect URI

重定向 URI 是应用程序中的路径,最终用户的用户代理在通过 Google 身份验证并授予对 Consent 页面上的 OAuth Client (在前一步中创建)的访问权限之后,将重定向回该路径。

在“ Set a redirect URI”小节中,确保 Authorized redirect URI 字段设置为 localhost: 8080/login/oauth2/code/google。

默认的重定向 URI 模板是{ baseUrl }/login/oauth2/code/{ registrationId }。 registrationId 是 ClientRegistry 的唯一标识符。

如果 OAuth 客户端运行在代理服务器之后,您应该检查代理服务器配置以确保应用程序配置正确。另外,请参阅 redirect-URI 支持的 URI 模板变量。

Configure application.yml

现在您已经有了一个新的 OAuth Client 和 Google,您需要将应用程序配置为使用 OAuth Client 进行身份验证流。这样做:

  1. 转到 application.yml 并设置以下配置:

    spring:security:oauth2:client:registration:	google:	client-id: google-client-idclient-secret: google-client-secret
    

    OAuth Client properties

    1. spring.security.oauth2.client.registration 是 OAuth Client 属性的基本属性前缀。
    2. 在基本属性前缀之后是 ClientRegistration的 ID,例如 Google。
  2. 用前面创建的 OAuth 2.0凭据替换 client-id 和 client-secret 属性中的值。

Boot up the Application

启动 Spring Boot 示例并转到 localhost: 8080。然后重定向到默认的自动生成登录页面,该页面显示 Google 的链接。

点击 Google 链接,然后你会被重定向到 Google 进行身份验证。

在使用您的 Google 帐户凭据进行身份验证之后,您将看到“同意”屏幕。Consent 屏幕要求您允许或拒绝访问先前创建的 OAuth Client。单击“允许”授权 OAuth 客户端访问您的电子邮件地址和基本配置文件信息。

此时,OAuth 客户机将从 UserInfo 端点检索您的电子邮件地址和基本配置文件信息,并建立一个经过身份验证的会话。

Spring Boot Property Mappings

下表概述了 SpringBootOAuthClient 属性到 ClientRegistry 属性的映射。

Spring BootClientRegistration
spring.security.oauth2.client.registration.*[registrationId]*registrationId
spring.security.oauth2.client.registration.*[registrationId]*.client-idclientId
spring.security.oauth2.client.registration.*[registrationId]*.client-secretclientSecret
spring.security.oauth2.client.registration.*[registrationId]*.client-authentication-methodclientAuthenticationMethod
spring.security.oauth2.client.registration.*[registrationId]*.authorization-grant-typeauthorizationGrantType
spring.security.oauth2.client.registration.*[registrationId]*.redirect-uriredirectUri
spring.security.oauth2.client.registration.*[registrationId]*.scopescopes
spring.security.oauth2.client.registration.*[registrationId]*.client-nameclientName
spring.security.oauth2.client.provider.*[providerId]*.authorization-uriproviderDetails.authorizationUri
spring.security.oauth2.client.provider.*[providerId]*.token-uriproviderDetails.tokenUri
spring.security.oauth2.client.provider.*[providerId]*.jwk-set-uriproviderDetails.jwkSetUri
spring.security.oauth2.client.provider.*[providerId]*.issuer-uriproviderDetails.issuerUri
spring.security.oauth2.client.provider.*[providerId]*.user-info-uriproviderDetails.userInfoEndpoint.uri
spring.security.oauth2.client.provider.*[providerId]*.user-info-authentication-methodproviderDetails.userInfoEndpoint.authenticationMethod
spring.security.oauth2.client.provider.*[providerId]*.user-name-attributeproviderDetails.userInfoEndpoint.userNameAttributeName

通过指定 spring.security.oauth2.client.Provider,可以通过发现 OpenID Connect Provider 的 Configuration 端点或 Authorization Server 的 Metadata 端点来最初配置 spring.security.oauth2.client.provider.[providerId].issuer-uri属性。

CommonOAuth2Provider

CommonOAuth2Provider 为许多知名的提供商(Google、 gitHub、 Facebook 和 Okta)预先定义了一组默认客户端属性。

例如,对于提供程序,authority-uri、 token-uri 和 user-info-uri 不会经常更改。因此,提供默认值以减少所需的配置是有意义的。

如前所述,当我们配置 Google 客户机时,只需要client-id 和client-secret属性。

下面的清单显示了一个示例:

spring:security:oauth2:client:registration:google:client-id: google-client-idclient-secret: google-client-secret

在这里,客户端属性的自动默认无缝工作,因为 registrationId (GOOGLE)匹配 CommonOAuth2Provider 中的 GOOGLE 枚举(不区分大小写)。

对于希望指定不同的 registrationId (如 google-login)的情况,您仍然可以通过配置 Provider 属性来利用客户端属性的自动默认。

spring:security:oauth2:client:registration:google-login:	provider: google	client-id: google-client-idclient-secret: google-client-secret
  • RegistrationId 被设置为 google-login。
  • Provider 属性设置为 google,它将利用 CommonOAuth2Provider.GOOGLE.getBuilder ()中设置的客户端属性的自动默认。

Configuring Custom Provider Properties

有一些 OAuth 2.0提供程序支持多租户,这导致每个租户(或子域)有不同的协议端点。

例如,一个在 Okta 注册的 OAuth 客户端被分配到一个特定的子域,并且拥有自己的协议端点。

对于这些情况,Spring Boot 为配置自定义提供程序属性提供了以下基本属性: spring.security.oauth2.client.provider.*[providerId]*.

下面的清单显示了一个示例:

spring:security:oauth2:client:registration:okta:client-id: okta-client-idclient-secret: okta-client-secretprovider:okta:	authorization-uri: https://your-subdomain.oktapreview.com/oauth2/v1/authorizetoken-uri: https://your-subdomain.oktapreview.com/oauth2/v1/tokenuser-info-uri: https://your-subdomain.oktapreview.com/oauth2/v1/userinfouser-name-attribute: subjwk-set-uri: https://your-subdomain.oktapreview.com/oauth2/v1/keys

基本属性(spring.security.oauth2.client.provision. okta)允许对协议端点位置进行自定义配置。

Overriding Spring Boot Auto-configuration

OAuth 客户端支持的 Spring 引导自动配置类是 OAuth2ClientAutoConfiguration。

它执行下列任务:

  • 从配置的 OAuth Client 属性注册一个由 ClientRegistration组成的 ClientRegistrationRepository@Bean。
  • 注册一个 SecurityFilterChain@Bean 并通过 httpSecurity.oauth2Login()启用 OAuth 2.0登录。

如果您需要根据您的具体要求覆盖自动配置,您可以通过以下方式进行:

  • Register a ClientRegistrationRepository @Bean
  • Register a SecurityFilterChain @Bean
  • Completely Override the Auto-configuration

Register a ClientRegistrationRepository @Bean

下面的示例演示如何注册 ClientRegistrationRepository@Bean:

@Configuration
public class OAuth2LoginConfig {@Beanpublic ClientRegistrationRepository clientRegistrationRepository() {return new InMemoryClientRegistrationRepository(this.googleClientRegistration());}private ClientRegistration googleClientRegistration() {return ClientRegistration.withRegistrationId("google").clientId("google-client-id").clientSecret("google-client-secret").clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC).authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).redirectUri("{baseUrl}/login/oauth2/code/{registrationId}").scope("openid", "profile", "email", "address", "phone").authorizationUri("https://accounts.google.com/o/oauth2/v2/auth").tokenUri("https://www.googleapis.com/oauth2/v4/token").userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo").userNameAttributeName(IdTokenClaimNames.SUB).jwkSetUri("https://www.googleapis.com/oauth2/v3/certs").clientName("Google").build();}
}

Register a SecurityFilterChain @Bean

下面的示例演示如何用@EnableWebSecurity 注册 SecurityFilterChain@Bean 并通过 httpSecurity.oauth2Login()启用 OAuth2.0登录:

OAuth2 Login Configuration

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated()).oauth2Login(withDefaults());return http.build();}
}

Completely Override the Auto-configuration

下面的示例显示如何通过注册 ClientRegistrationRepository@Bean 和 SecurityFilterChain@Bean 来完全覆盖自动配置。

Overriding the auto-configuration

@Configuration
public class OAuth2LoginConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated()).oauth2Login(withDefaults());return http.build();}@Beanpublic ClientRegistrationRepository clientRegistrationRepository() {return new InMemoryClientRegistrationRepository(this.googleClientRegistration());}private ClientRegistration googleClientRegistration() {return ClientRegistration.withRegistrationId("google").clientId("google-client-id").clientSecret("google-client-secret").clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC).authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).redirectUri("{baseUrl}/login/oauth2/code/{registrationId}").scope("openid", "profile", "email", "address", "phone").authorizationUri("https://accounts.google.com/o/oauth2/v2/auth").tokenUri("https://www.googleapis.com/oauth2/v4/token").userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo").userNameAttributeName(IdTokenClaimNames.SUB).jwkSetUri("https://www.googleapis.com/oauth2/v3/certs").clientName("Google").build();}
}

Java Configuration without Spring Boot

如果您不能使用 Spring Boot,并且希望在 CommonOAuth2Provider (例如,Google)中配置一个预定义的提供程序,请应用以下配置:

@Configuration
@EnableWebSecurity
public class OAuth2LoginConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated()).oauth2Login(withDefaults());return http.build();}@Beanpublic ClientRegistrationRepository clientRegistrationRepository() {return new InMemoryClientRegistrationRepository(this.googleClientRegistration());}@Beanpublic OAuth2AuthorizedClientService authorizedClientService(ClientRegistrationRepository clientRegistrationRepository) {return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);}@Beanpublic OAuth2AuthorizedClientRepository authorizedClientRepository(OAuth2AuthorizedClientService authorizedClientService) {return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService);}private ClientRegistration googleClientRegistration() {return CommonOAuth2Provider.GOOGLE.getBuilder("google").clientId("google-client-id").clientSecret("google-client-secret").build();}
}

Advanced Configuration

Oauth2Login ()提供了许多用于定制 OAuth 2.0 Login 的配置选项。主要的配置选项被分组到它们的协议端点对应项中。

例如,oauth2Login().authorizationEndpoint()允许配置 AuthorizationEndpoint,而 oauth2Login().tokenEndpoint()允许配置令牌端点。

下面的代码显示了一个示例:

Advanced OAuth2 Login Configuration

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.oauth2Login(oauth2 -> oauth2.authorizationEndpoint(authorization -> authorization...).redirectionEndpoint(redirection -> redirection...).tokenEndpoint(token -> token...).userInfoEndpoint(userInfo -> userInfo...));return http.build();}
}

Oauth2Login() DSL 的主要目标是紧密地与规范中定义的命名保持一致。

OAuth 2.0授权框架将协议端点定义如下:

授权过程使用两个授权服务器端点(HTTP 资源) :

  • 授权端点: 客户端通过用户-代理重定向从资源所有者获得授权。
  • 令牌端点(Token Endpoint) : 客户端用来交换访问令牌的授权许可,通常使用客户端身份验证。

授权过程还使用一个客户端端点:

  • 重定向端点: 授权服务器通过资源所有者用户代理向客户端返回包含授权凭据的响应。

OpenID Connect Core 1.0规范将 UserInfo 端点定义如下:

UserInfo 端点是一个 OAuth 2.0 Protected Resource,它返回关于经过身份验证的最终用户的声明。为了获得关于最终用户的请求声明,客户机使用通过 OpenID Connect Authentication 获得的访问令牌向 UserInfo 端点发出请求。这些声明通常由一个 JSON 对象表示,该对象包含声明的名称-值对的集合。

下面的代码显示了 oauth2Login() DSL 可用的完整配置选项:

OAuth2 Login Configuration Options

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.oauth2Login(oauth2 -> oauth2.clientRegistrationRepository(this.clientRegistrationRepository()).authorizedClientRepository(this.authorizedClientRepository()).authorizedClientService(this.authorizedClientService()).loginPage("/login").authorizationEndpoint(authorization -> authorization.baseUri(this.authorizationRequestBaseUri()).authorizationRequestRepository(this.authorizationRequestRepository()).authorizationRequestResolver(this.authorizationRequestResolver())).redirectionEndpoint(redirection -> redirection.baseUri(this.authorizationResponseBaseUri())).tokenEndpoint(token -> token.accessTokenResponseClient(this.accessTokenResponseClient())).userInfoEndpoint(userInfo -> userInfo.userAuthoritiesMapper(this.userAuthoritiesMapper()).userService(this.oauth2UserService()).oidcUserService(this.oidcUserService())));return http.build();}
}

除了 oauth2Login() DSL 之外,还支持 XML 配置。

下面的代码显示了security namespace中可用的完整配置选项:

OAuth2 Login XML Configuration Options

<http><oauth2-login client-registration-repository-ref="clientRegistrationRepository"authorized-client-repository-ref="authorizedClientRepository"authorized-client-service-ref="authorizedClientService"authorization-request-repository-ref="authorizationRequestRepository"authorization-request-resolver-ref="authorizationRequestResolver"access-token-response-client-ref="accessTokenResponseClient"user-authorities-mapper-ref="userAuthoritiesMapper"user-service-ref="oauth2UserService"oidc-user-service-ref="oidcUserService"login-processing-url="/login/oauth2/code/*"login-page="/login"authentication-success-handler-ref="authenticationSuccessHandler"authentication-failure-handler-ref="authenticationFailureHandler"jwt-decoder-factory-ref="jwtDecoderFactory"/>
</http>

以下各节将详细介绍每个可用配置选项:

  • OAuth 2.0 Login Page
  • Redirection Endpoint
  • UserInfo Endpoint
  • ID Token Signature Verification
  • [oauth2login-advanced-oidc-logout]

OAuth 2.0 Login Page

默认情况下,OAuth 2.0登录页面是由 DefaultLoginPageGeneratingFilter 自动生成的。默认登录页面显示每个配置的 OAuth Client 及其 ClientRegistration.clientName 作为链接,该链接能够初始化授权请求(或 OAuth 2.0 Login)。

为了使 DefaultLoginPageGeneratingFilter 显示已配置 OAuth 客户端的链接,已注册的 ClientRegistrationRepository 还需要实现 Iterable < ClientRegistry > 。有关参考资料,请参见 InmemyClientRegistrationRepository。

每个 OAuth 客户端的链接目标默认如下:

OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/{registrationId}"

下面一行显示了一个示例:

<a href="/oauth2/authorization/google">Google</a>

要覆盖默认登录页面,请配置 oauth2Login().loginPage()和(可选) oauth2Login().authorizationEndpoint().baseUri()。

下面的清单显示了一个示例:

OAuth2 Login Page Configuration

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.oauth2Login(oauth2 -> oauth2.loginPage("/login/oauth2")....authorizationEndpoint(authorization -> authorization.baseUri("/login/oauth2/authorization")...));return http.build();}
}

您需要为@Controller 提供一个@RequestMapping (“/login/oauth2”) ,该映射能够呈现自定义登录页面。

如前所述,配置 oauth2Login().authorizationEndpoint().baseUri()是可选的。但是,如果您选择自定义它,请确保到每个 OAuth Client 的链接与 authorizationEndpoint().baseUri().匹配.

下面一行显示了一个示例:

<a href="/login/oauth2/authorization/google">Google</a>

Redirection Endpoint

授权服务器使用重定向端点通过资源所有者用户代理向客户端返回授权响应(其中包含授权凭据)。

OAuth 2.0 Login 利用了授权代码授权。

默认的 Authorization Response baseUri (重定向端点)是/login/oauth2/code/* ,它在 OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI中定义。

如果希望自定义 Authorization Response baseUri,请按以下方式配置它:

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.oauth2Login(oauth2 -> oauth2.redirectionEndpoint(redirection -> redirection.baseUri("/login/oauth2/callback/*")...));return http.build();}
}

您还需要确保 ClientRegistration.redirectUri 与自定义 Authorization Response baseUri 匹配。

下面的清单显示了一个示例:

return CommonOAuth2Provider.GOOGLE.getBuilder("google").clientId("google-client-id").clientSecret("google-client-secret").redirectUri("{baseUrl}/login/oauth2/callback/{registrationId}").build();

UserInfo Endpoint

UserInfo 端点包括许多配置选项,如下面小节所述:

  • Mapping User Authorities
  • OAuth 2.0 UserService
  • OpenID Connect 1.0 UserService

Mapping User Authorities

在用户成功地通过 OAuth 2.0提供程序进行身份验证之后,OAuth2User.getAuthority()(或 OidcUser.getAuthority())包含一个由 OAuth2UserRequest.getAccessToken()填充的授权列表。GetScope()并以 SCOPE_作为前缀。这些授予的权限可以映射到一组新的 GrantedAuthority 实例,这些实例在完成身份验证时提供给 OAuth2AuthenticationToken。

GetAuthority()用于对请求进行授权,例如 hasRole(‘USER’)或 hasRole (‘ ADMIN’)。

在映射用户权限时,有两个选项可供选择:

  • Using a GrantedAuthoritiesMapper
  • Delegation-based Strategy with OAuth2UserService
Using a GrantedAuthoritiesMapper

授予 GrantedAuthoritiesMapper 一个授权列表,其中包含 OAuth2UserAuthority 类型的特殊授权和授权字符串 OAUTH2_USER (或 OidcUserAuthority 和授权字符串 OIDC_USER)。

提供 GrantedAuthortiesMapper 的实现并配置它,如下所示:

Granted Authorities Mapper Configuration

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.oauth2Login(oauth2 -> oauth2.userInfoEndpoint(userInfo -> userInfo.userAuthoritiesMapper(this.userAuthoritiesMapper())...));return http.build();}private GrantedAuthoritiesMapper userAuthoritiesMapper() {return (authorities) -> {Set<GrantedAuthority> mappedAuthorities = new HashSet<>();authorities.forEach(authority -> {if (OidcUserAuthority.class.isInstance(authority)) {OidcUserAuthority oidcUserAuthority = (OidcUserAuthority)authority;OidcIdToken idToken = oidcUserAuthority.getIdToken();OidcUserInfo userInfo = oidcUserAuthority.getUserInfo();// Map the claims found in idToken and/or userInfo// to one or more GrantedAuthority's and add it to mappedAuthorities} else if (OAuth2UserAuthority.class.isInstance(authority)) {OAuth2UserAuthority oauth2UserAuthority = (OAuth2UserAuthority)authority;Map<String, Object> userAttributes = oauth2UserAuthority.getAttributes();// Map the attributes found in userAttributes// to one or more GrantedAuthority's and add it to mappedAuthorities}});return mappedAuthorities;};}
}

或者,您可以注册一个 GrantedAuthortiesMapper@Bean,让它自动应用到配置中,如下所示:

Granted Authorities Mapper Bean Configuration

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.oauth2Login(withDefaults());return http.build();}@Beanpublic GrantedAuthoritiesMapper userAuthoritiesMapper() {...}
}
Delegation-based Strategy with OAuth2UserService

与使用 GrantedAuthortiesMapper 相比,此策略更先进。但是,它也更加灵活,因为它允许您访问 OAuth2UserRequest 和 OAuth2User (当使用 OAuth 2.0 UserService 时)或 OidcUserRequest 和 OidcUser (当使用 OpenID Connect 1.0 UserService 时)。

OAuth2UserRequest (和 OidcUserRequest)提供了对相关 OAuth2AccessToken 的访问,在委托方需要从受保护的资源获取授权信息才能为用户映射自定义权限的情况下,这非常有用。

下面的示例演示如何使用 OpenID Connect 1.0 UserService 实现和配置基于委托的策略:

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.oauth2Login(oauth2 -> oauth2.userInfoEndpoint(userInfo -> userInfo.oidcUserService(this.oidcUserService())...));return http.build();}private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {final OidcUserService delegate = new OidcUserService();return (userRequest) -> {// Delegate to the default implementation for loading a userOidcUser oidcUser = delegate.loadUser(userRequest);OAuth2AccessToken accessToken = userRequest.getAccessToken();Set<GrantedAuthority> mappedAuthorities = new HashSet<>();// TODO// 1) Fetch the authority information from the protected resource using accessToken// 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities// 3) Create a copy of oidcUser but use the mappedAuthorities insteadProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();String userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName();if (StringUtils.hasText(userNameAttributeName)) {oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo(), userNameAttributeName);} else {oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo());}return oidcUser;};}
}

OAuth 2.0 UserService

DefaultOAuth2UserService 是支持标准 OAuth 2.0提供程序的 OAuth2UserService 的实现。

OAuth2UserService 从 UserInfo 端点(通过在授权流中使用授予客户端的访问令牌)获取终端用户(资源所有者)的用户属性,并以 OAuthenticatedUser 的形式返回 AuthenticatedPrime。

DefaultOAuth2UserService 在 UserInfo 端点请求用户属性时使用 RestOperations 实例。

如果需要自定义 UserInfo 请求的预处理,可以为 DefaultOAuth2UserService.setRequestEntityConverter ()提供自定义 Converter < OAuth2UserRequest,RequestEntity < ?>>.默认实现 OAuth2UserRequestEntityConverter 构建 UserInfo 请求的 RequestEntity 表示形式,该表示形式默认在 Authorization 头中设置 OAuth2AccessToken。

另一方面,如果需要自定义 UserInfo Response 的后处理,则需要为 DefaultOAuth2UserService.setRestOperations ()提供自定义配置的 RestOperations。默认的 RestOperations 配置如下:

RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());

OAuth2ErrorResponseErrorHandler 是一个 ResponseErrorHandler,可以处理 OAuth 2.0错误(400个错误请求)。它使用 OAuth2ErrorHttpMessageConverter 将 OAuth 2.0 Error 参数转换为 OAuth2Error。

无论您是自定义 DefaultOAuth2UserService 还是提供自己的 OAuth2UserService 实现,都需要按以下方式配置它:

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.oauth2Login(oauth2 -> oauth2.userInfoEndpoint(userInfo -> userInfo.userService(this.oauth2UserService())...));return http.build();}private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {...}
}

OpenID Connect 1.0 UserService

OidcUserService 是支持 OpenID Connect 1.0 Provider 的 OAuth2UserService 的实现。

OidcUserService 在 UserInfo 端点请求用户属性时利用 DefaultOAuth2UserService。

如果需要自定义 UserInfo 请求的预处理或 UserInfo 响应的后处理,则需要为 OidcUserService.setOauth2UserService ()提供自定义配置的 DefaultOAuth2UserService。

无论您是自定义 OidcUserService,还是为 OpenID Connect 1.0 Provider 提供自己的 OAuth2UserService 实现,都需要将其配置如下:

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.oauth2Login(oauth2 -> oauth2.userInfoEndpoint(userInfo -> userInfo.oidcUserService(this.oidcUserService())...));return http.build();}private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {...}
}

ID Token Signature Verification

OpenID Connect 1.0 Authentication 引入了 ID 令牌,这是一种安全令牌,包含授权服务器在客户端使用时对最终用户身份验证的声明。

ID 令牌表示为 JSON Web 令牌(JWT) ,必须使用 JSON Web 签名(JWS)进行签名。

OidcIdTokenDecoderFactory 提供用于 OidcIdToken 签名验证的 JwtDecder。默认算法是 RS256,但在客户端注册期间分配时可能会有所不同。对于这些情况,您可以配置解析器来返回为特定客户机分配的预期 JWS 算法。

JWS 算法解析器是一个函数,它接受 ClientRegistry 并为客户机返回预期的 Jws松弛算法,例如 SignatureAlobacm.RS256或 MacAlobacm.HS256

下面的代码显示了如何为所有 ClientRegistry 实例配置 OidcIdTokenDecoderFactory@Bean,使其默认为 MacAlobacm.HS256:

@Bean
public JwtDecoderFactory<ClientRegistration> idTokenDecoderFactory() {OidcIdTokenDecoderFactory idTokenDecoderFactory = new OidcIdTokenDecoderFactory();idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> MacAlgorithm.HS256);return idTokenDecoderFactory;
}

对于基于 MAC 的算法(如 HS256、 HS384或 HS512) ,对应于客户机 ID 的客户机机密被用作签名验证的对称密钥。

如果为 OpenID Connect 1.0 Authentication 配置了多个 ClientRegistry,则 JWS 算法解析器可以评估所提供的 ClientRegistry,以确定返回哪个算法。

然后,您可以继续配置注销

OIDC Logout

一旦终端用户能够登录到您的应用程序,考虑他们将如何登出就很重要了。

一般来说,有三种用例供您考虑:

  • 我只想执行本地注销
  • 我想注销我的应用程序和由我的应用程序启动的 OIDC 提供程序
  • 想注销我的应用程序和由 OIDC 提供程序发起的 OIDC 提供程序

Local Logout

要执行本地注销,不需要特殊的 OIDC 配置。SpringSecurity 会自动启动一个本地注销端点,您可以通过 logout () DSL 对其进行配置。

OpenID Connect 1.0 Client-Initiated Logout

OpenID Connect Session Management 1.0允许使用 Client 在提供者注销最终用户。其中一个可用的策略是 RP 启动注销。

如果 OpenID 提供程序同时支持会话管理和发现,那么客户端可以从 OpenID 提供程序的 Discovery 元数据中获取 end_session_endpoint URL。您可以通过使用issuer-uri配置 ClientRegistry,如下所示:

spring:security:oauth2:client:registration:okta:client-id: okta-client-idclient-secret: okta-client-secret...provider:okta:issuer-uri: https://dev-1234.oktapreview.com

此外,您还应该配置实现 RP-Initiated Logout 的 OidcClientInitiatedLogoutSuccess Handler,如下所示:

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {@Autowiredprivate ClientRegistrationRepository clientRegistrationRepository;@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated()).oauth2Login(withDefaults()).logout(logout -> logout.logoutSuccessHandler(oidcLogoutSuccessHandler()));return http.build();}private LogoutSuccessHandler oidcLogoutSuccessHandler() {OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler =new OidcClientInitiatedLogoutSuccessHandler(this.clientRegistrationRepository);// Sets the location that the End-User's User Agent will be redirected to// after the logout has been performed at the ProvideroidcLogoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}");return oidcLogoutSuccessHandler;}
}

OidcClientInitiatedLogoutSuccess Handler 支持{ baseUrl }占位符。

OpenID Connect 1.0 Back-Channel Logout

OpenID Connect Session Management 1.0允许提供者向客户端发出 API 调用,从而在客户端注销最终用户。这被称为 OIDC 后台注销。

为了实现这一点,您可以像下面这样设置 DSL 中的 Back-Channel Logout 端点:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()).oauth2Login(withDefaults()).oidcLogout((logout) -> logout.backChannel(Customizer.withDefaults()));return http.build();
}

然后,您需要一种方法来监听 Spring Security 发布的事件,以删除旧的 OidcSessionInformation 条目,如下所示:

@Bean
public HttpSessionEventPublisher sessionEventPublisher() {return new HttpSessionEventPublisher();
}

这样,如果调用 HttpSession # 无效,那么会话也将从内存中删除。

这将支持端点/logout/connect/back-channel/{ registrationId } ,OIDC 提供程序可以请求该端点/logout/connect/back-channel/{ registrationId }使应用程序中最终用户的给定会话无效。

Back-Channel Logout Architecture

考虑标识符为 registrationId 的 ClientRegistry。

后台注销的总体流程如下:

  1. 在登录时,Spring Security 在其 OidcSessionStrategy 实现中将 ID 令牌、 CSRF 令牌和提供程序会话 ID (如果有的话)与应用程序的会话 ID 相关联
  2. 然后在注销时,OIDC 提供程序对/Logout/connect/back-channel/registrationId 进行 API 调用,包括一个注销令牌,指示要注销的子(最终用户)或 sid (提供程序会话 ID)。
  3. SpringSecurity 验证令牌的签名和声明。
  4. 如果令牌包含 sid 声明,则只有与该提供程序会话相关联的客户端会话被终止。
  5. 否则,如果令牌包含子声明,则终止该最终用户的所有客户端会话。

Customizing the OIDC Provider Session Strategy

默认情况下,Spring Security 在内存中存储 OIDC 提供程序会话和客户端会话之间的所有链接。

在许多情况下,比如集群应用程序,最好将它存储在一个单独的位置,比如数据库。

您可以通过配置自定义 OidcSessionStrategy 来实现这一点,如下所示:

@Component
public final class MySpringDataOidcSessionStrategy implements OidcSessionStrategy {private final OidcProviderSessionRepository sessions;// ...@Overridepublic void saveSessionInformation(OidcSessionInformation info) {this.sessions.save(info);}@Overridepublic OidcSessionInformation(String clientSessionId) {return this.sessions.removeByClientSessionId(clientSessionId);}@Overridepublic Iterable<OidcSessionInformation> removeSessionInformation(OidcLogoutToken token) {return token.getSessionId() != null ?this.sessions.removeBySessionIdAndIssuerAndAudience(...) :this.sessions.removeBySubjectAndIssuerAndAudience(...);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/870074.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

若依vue集成electron实现打包exe应用程序

一、修改package.json文件,加入相关依赖和配置 {"name": "ruoyi","version": "3.8.6","description": "若依管理系统","author": "若依","license":

分层图最短路,CF 1725M - Moving Both Hands

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1725M - Moving Both Hands 二、解题报告 1、思路分析 题意看似很简单&#xff0c;就是问我们两个人分别处于1, p两个点上&#xff0c;二者同时出发&#xff0c;相遇时二者所用路径之和的最小值 不难想到求…

mysql默认开启索引下推,减少回表的数据

目的&#xff1a;在回表前就进行where多个条件的判断&#xff0c;从而回表到服务器层的数据量足够小 索引下推的工作原理 在没有索引下推优化的情况下&#xff0c;当 MySQL 使用索引进行查询时&#xff0c;它会从索引中获取符合条件的索引条目&#xff0c;然后回表&#xff0…

Ubuntu中如何设置IP地址

在 Ubuntu 中&#xff0c;可以通过几种方式设置 IP 地址&#xff1a;使用网络管理器图形界面、命令行工具&#xff08;如 nmcli 或 nmtui&#xff09;、或直接编辑网络配置文件。以下是这几种方法的详细步骤。 方法一&#xff1a;使用图形界面&#xff08;Network Manager&…

Git协作

文章目录 Git协作冲突冲突的发生情况解决冲突如何处理冲突 1 分支1.1 什么是Git分支1.2 创建分支 2 切换分支2.1 指向分支2.2 暂存分支切换分支与未提交更改的处理使用 Stash 临时保存更改Stash 的工作原理&#xff1a;场景设定使用 Git Stash 3 远程分支3.1 快进合并快进合并的…

13. 求余

问题描述 在 C/C/Java/Python 等语言中, 使用 % 表示求余, 请问 2021 % 20 的值是多少? 答案提交 这是一道结果填空的题, 你只需要算出结果后提交即可。本题的结果为一 个整数, 在提交答案时只填写这个整数, 填写多余的内容将无法得分。 运行限制 最大运行时间&#xff1…

Qt/QML学习-定位器

QML学习 定位器例程视频讲解代码 main.qml import QtQuick 2.15 import QtQuick.Window 2.15Window {width: 640height: 480visible: truetitle: qsTr("positioner")Rectangle {id: rectColumnwidth: parent.width / 2height: parent.height / 2border.width: 1Col…

Qt基础控件总结—多页面切换(QStackWidget类、QTabBar类和QTabWidget类)

QStackedWidget 类 QStackedWidget 类是在 QStackedLayout 之上构造的一个便利的部件,其使用方法与步骤和 QStackedLayout 是一样的。QStackedWidget 类的成员函数与 QStackedLayout 类也基本上是一致的,使用该类就和使用 QStackedLayout 一样。 使用该类可以参考QStackedL…

Perl 语言进阶学习

Perl 语言进阶学习 在掌握 Perl 的基础知识后&#xff0c;进一步学习 Perl 的高级特性和应用&#xff0c;将有助于提升编程效率和解决复杂问题的能力。本文将详细介绍 Perl 语言的高级功能、最佳实践以及实际应用案例。 目录 高级数据结构 多维数组复杂数据结构 引用与匿名数…

计算机如何学习

1. 不要只盯着计算机语言学习&#xff0c;你现在已经学习了C语言和Java&#xff0c;暑假又规划学习Python&#xff0c;最后你掌握的就是计算机语言包而已。 2. 建议你找一门想要深挖的语言&#xff0c;沿着这个方向继续往后学习知识就行。计算机语言是学不完的&#xff0c;而未…

iPhone数据恢复篇:在 iPhone 上恢复找回短信的 5 种方法

方法 1&#xff1a;检查最近删除的文件夹 iOS 允许您在 30 天内恢复已删除的短信。您需要先从“设置”菜单启用“过滤器”。让我们来实际检查一下。 步骤 1&#xff1a;打开“设置” > “信息”。 步骤 2&#xff1a;选择“未知和垃圾邮件”&#xff0c;然后切换到“过滤…

如何将若依vue升级到springboot3.x?

为了确保项目符合要求,Spring Boot 3.x 要求Java版本为17或更高。 1、修改根目录下的pom.xml文件 <!-- java.version版本8更换为17 --> <java.version>17</java.version><!-- 新增节点 --> <mybatis-spring-boot.version>3.0.3<

2019年美赛题目Problem A: Game of Ecology

本题分析&#xff1a; 本题想要要求从实际生物角度出发&#xff0c;对权力游戏中龙这种虚拟生物的生态环境和生物特性进行建模&#xff0c;感觉属于比较开放类型的题目&#xff0c;重点在于参考生物的选择&#xff0c;龙虽然是虚拟的但是龙的生态特性可以参考目前生物圈里存在…

SpringMVC(3)——SpringMVC注解实战

前言 SpringMVC&#xff08;2&#xff09;——controller方法参数与html表单对应&#xff08;请求参数的绑定&#xff09; 上篇博客我们提到了controller方法的参数与html表单之间的对应关系 但是这种对应关系有很多缺点&#xff1a; 传递参数只能放在request的body当中&am…

MySQL数据库字符集utf8mb4的排序规则介绍

在MySQL数据库中&#xff0c;字符集&#xff08;charset&#xff09;和排序规则&#xff08;collation&#xff09;是处理文本数据的重要概念。字符集决定了数据库如何存储字符数据&#xff0c;而排序规则决定了如何比较和排序字符数据。 utf8mb4 字符集 utf8mb4 是 MySQL 中…

极狐Gitlab使用(2)

目录 1. Gitlab命令行修改管理员密码 2. Gitlab服务管理 3. 公司的开发代码提交处理流程 4. Gitlab 备份与恢复 数据备份 测试数据恢复 5. 邮箱配置 1. Gitlab命令行修改管理员密码 [roottty01 ~]# gitlab-rails console -e production # 启动GitLab的Rails控制…

Docker 日志丢失 - 解决方案

Docker 日志默认使用的是 journald 的方式. RateLimitBurst 是 journald 的一个参数&#xff0c;用于限制日志的速率。如果日志的生成速度超过这个限制&#xff0c;journald 可能会丢弃日志。你可以通过调整这个参数来避免日志被丢弃。 调整 RateLimitBurst 和 RateLimitInte…

C++ 入门06:类的进阶(构造函数的重载与拷贝构造函数)

往期回顾&#xff1a; C 入门03&#xff1a;函数与作用域-CSDN博客C 入门04&#xff1a;数组与字符串-CSDN博客C 入门05&#xff1a;类和对象-CSDN博客 一、前言 在前面文章的学习中&#xff0c;我们了解了 C 的基本结构、变量、输入输出、控制结构、循环、函数、作用域、数组…

windows USB 设备驱动开发-USB电源管理(一)

符合通用串行总线 (USB) 规范的 USB 设备的电源管理功能具有一组丰富而复杂的电源管理功能。 请务必了解这些功能如何与 Windows 驱动程序模型 (WDM) 交互&#xff0c;特别是 Microsoft Windows 如何调整标准 USB 功能以支持系统唤醒体系结构。 基于内核模式驱动程序框架的 US…

图像到文本的桥梁:Transformer模型的创新应用

图像到文本的桥梁&#xff1a;Transformer模型的创新应用 在人工智能领域&#xff0c;Transformer模型以其卓越的性能在自然语言处理&#xff08;NLP&#xff09;任务中占据了重要地位。然而&#xff0c;Transformer的潜力并不局限于文本&#xff0c;它在图像到文本转换&#…