- 6. Java配置
- 6.1 Hello Web Security Java配置
- 6.2 HttpSecurity
- 6.3 Java配置和表单登录
- 6.4授权请求
- 6.5处理注销
- 6.6 OAuth 2.0客户端
- 6.6.1 ClientRegistration
- 6.6.2 ClientRegistrationRepository
- 6.6.3 OAuth2AuthorizedClient
- 6.6.4 OAuth2AuthorizedClientRepository / OAuth2AuthorizedClientService
- 6.6.5 RegisteredOAuth2AuthorizedClient
- 6.6.6 AuthorizationRequestRepository
- 6.6.7 OAuth2AuthorizationRequestResolver
- 6.6.8 OAuth2AccessTokenResponseClient
- 6.7 OAuth 2.0登录
6. Java配置
Spring 3.1在Spring Framework中添加了对Java Configuration的一般支持。自Spring Security 3.2以来,Spring Security Java Configuration支持使用户无需使用任何XML即可轻松配置Spring Security。
如果您熟悉第7章安全命名空间配置,那么您应该发现它与安全Java配置支持之间有很多相似之处。
| Spring Security提供了许多示例应用程序,用于演示Spring Security Java Configuration的使用。
6.1 Hello Web Security Java配置
第一步是创建Spring Security Java配置。该配置创建一个Servlet过滤器,称为springSecurityFilterChain
负责应用程序内的所有安全性(保护应用程序URL,验证提交的用户名和密码,重定向到登录表单等)。您可以在下面找到Spring Security Java配置的最基本示例:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.security.config.annotation.authentication.builders.*;
import org.springframework.security.config.annotation.web.configuration.*;
@EnableWebSecurity
public class WebSecurityConfig implements WebMvcConfigurer {
@Bean
public UserDetailsService userDetailsService() throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build());
return manager;
}
}
这种配置确实没什么用,但它做了很多。您可以在下面找到以下功能的摘要:
要求对应用程序中的每个URL进行身份验证
为您生成登录表单
允许具有Username 用户和密码 密码的用户使用基于表单的身份验证进行身份验证
允许用户注销
CSRF攻击预防
会话固定保护
安全标头集成
- 用于安全请求的 HTTP严格传输安全性
- X-Content-Type-Options.aspx)集成
- 缓存控制(稍后可由应用程序覆盖以允许缓存静态资源)
- X-XSS-Protection.aspx)集成
- X-Frame-Options集成有助于防止Clickjacking
与以下Servlet API方法集成
6.1.1 AbstractSecurityWebApplicationInitializer
下一步是注册springSecurityFilterChain
战争。这可以在Java配置中使用Spring的WebApplicationInitializer支持在Servlet 3.0+环境中完成。并不令人惊讶的是,Spring Security提供了一个基类AbstractSecurityWebApplicationInitializer
,可以确保springSecurityFilterChain
为您注册。我们使用的方式AbstractSecurityWebApplicationInitializer
取决于我们是否已经使用Spring,或者Spring Security是否是我们应用程序中唯一的Spring组件。
- 第6.1.2节“没有现有Spring的AbstractSecurityWebApplicationInitializer” - 如果您还没有使用Spring,请使用这些说明
- 第6.1.3节“使用Spring MVC的AbstractSecurityWebApplicationInitializer” - 如果您已经使用Spring,请使用这些说明
6.1.2没有现有Spring的AbstractSecurityWebApplicationInitializer
如果您不使用Spring或Spring MVC,则需要将其WebSecurityConfig
传入超类以确保获取配置。你可以在下面找到一个例子:
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
}
这SecurityWebApplicationInitializer
将做以下事情:
- 自动为应用程序中的每个URL注册springSecurityFilterChain过滤器
添加一个加载WebSecurityConfig的ContextLoaderListener。
6.1.3使用Spring MVC的AbstractSecurityWebApplicationInitializer
如果我们在应用程序的其他地方使用Spring,我们可能已经有了
WebApplicationInitializer
加载Spring配置的东西。如果我们使用以前的配置,我们会收到错误。相反,我们应该使用现有的注册Spring SecurityApplicationContext
。例如,如果我们使用Spring MVC,我们SecurityWebApplicationInitializer
将看起来如下所示:
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
}
这只是为应用程序中的每个URL注册springSecurityFilterChain过滤器。之后,我们将确保WebSecurityConfig
在现有的ApplicationInitializer中加载。例如,如果我们使用Spring MVC,它将被添加到getRootConfigClasses()
public class MvcWebApplicationInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebSecurityConfig.class };
}
// ... other overrides ...
}
6.2 HttpSecurity
到目前为止,我们的WebSecurityConfig仅包含有关如何验证用户身份的信息。Spring Security如何知道我们要求所有用户都经过身份验证?Spring Security如何知道我们想要支持基于表单的身份验证?这样做的原因是WebSecurityConfigurerAdapter
在configure(HttpSecurity http)
方法中提供了一个默认配置,如下所示:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
上面的默认配置:
- 确保对我们的应用程序的任何请求都要求用户进行身份验证
- 允许用户使用基于表单的登录进行身份验证
- 允许用户使用HTTP基本身份验证进行身份验证
您会注意到此配置与XML命名空间配置非常相似:
<http>
<intercept-url pattern="/**" access="authenticated"/>
<form-login />
<http-basic />
</http>
使用and()
允许我们继续配置父标记的方法表示关闭XML标记的Java配置等效项。如果您阅读代码,它也是有道理的。我想配置授权请求并配置表单登录并配置HTTP基本身份验证。
6.3 Java配置和表单登录
当您被提示登录时,您可能想知道登录表单的来源,因为我们没有提及任何HTML文件或JSP。由于Spring Security的默认配置未明确设置登录页面的URL,因此Spring Security会根据启用的功能自动生成一个URL,并使用处理提交的登录的URL的标准值,用户将使用的默认目标URL登录后发送给等等。
虽然自动生成的登录页面便于快速启动和运行,但大多数应用程序都希望提供自己的登录页面。为此,我们可以更新我们的配置,如下所示:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login") //1
.permitAll(); //2
}
//1
更新的配置指定登录页面的位置。//2
我们必须授予所有用户(即未经身份验证的用户)访问我们的登录页面的权限。该formLogin().permitAll()
方法允许为与基于表单的登录相关联的所有URL授予对所有用户的访问权限
使用JSP实现当前配置的示例登录页面如下所示:
| 下面的登录页面代表我们当前的配置。如果某些默认设置不符合我们的需求,我们可以轻松更新配置。
<c:url value="/login" var="loginUrl"/>
<form action="${loginUrl}" method="post"> //1
<c:if test="${param.error != null}"> //2
<p>
Invalid username and password.
</p>
</c:if>
<c:if test="${param.logout != null}"> //3
<p>
You have been logged out.
</p>
</c:if>
<p>
<label for="username">Username</label>
<input type="text" id="username" name="username"/> //4
</p>
<p>
<label for="password">Password</label>
<input type="password" id="password" name="password"/> //5
</p>
<input type="hidden" //6
name="${_csrf.parameterName}"
value="${_csrf.token}"/>
<button type="submit" class="btn">Log in</button>
</form>
//1
对/login
URL 的POST 将尝试对用户进行身份验证//2
如果查询参数error
存在,则尝试进行身份验证并失败//3
如果查询参数logout
存在,则用户已成功注销
//4
用户名必须作为名为username的HTTP参数出现//5
密码必须作为名为password的HTTP参数出现
//6
我们必须在“包含CSRF令牌”一节中了解更多信息,请参阅第10.6节“跨站点请求伪造(CSRF)”部分的参考
6.4授权请求
我们的示例仅要求用户进行身份验证,并且已针对应用程序中的每个URL进行了身份验证。我们可以通过向http.authorizeRequests()
方法添加多个子项来指定URL的自定义要求。例如:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() // 1
.antMatchers("/resources/**", "/signup", "/about").permitAll() //2
.antMatchers("/admin/**").hasRole("ADMIN") //3
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") //4
.anyRequest().authenticated() //5
.and()
// ...
.formLogin();
}
1 | http.authorizeRequests() 方法有多个子节点,每个匹配器按其声明的顺序进行考虑。 |
---|---|
2 | 我们指定了任何用户都可以访问的多种URL模式。具体来说,如果URL以“/ resources /”开头,等于“/ signup”或等于“/ about”,则任何用户都可以访问请求。 |
3 | 任何以“/ admin /”开头的URL都将仅限于具有“ROLEADMIN”角色的用户。您会注意到,由于我们正在调用该hasRole 方法,因此我们不需要指定“ROLE”前缀。 |
4 | 任何以“/ db /”开头的URL都要求用户同时拥有“ROLEADMIN”和“ROLE_DBA”。您会注意到,由于我们使用的是hasRole 表达式,因此我们不需要指定“ROLE”前缀。 |
5 | 任何尚未匹配的URL只需要对用户进行身份验证 |
6.5处理注销
使用时WebSecurityConfigurerAdapter
,会自动应用注销功能。默认情况下,访问URL /logout
将通过以下方式记录用户:
- 使HTTP会话无效
- 清理已配置的任何RememberMe身份验证
- 清除
SecurityContextHolder
- 重定向到
/login?logout
但是,与配置登录功能类似,您还可以使用各种选项来进一步自定义注销要求:
protected void configure(HttpSecurity http) throws Exception {
http
.logout() // 1
.logoutUrl("/my/logout") // 2
.logoutSuccessUrl("/my/index") // 3
.logoutSuccessHandler(logoutSuccessHandler) // 4
.invalidateHttpSession(true) // 5
.addLogoutHandler(logoutHandler) // 6
.deleteCookies(cookieNamesToClear) // 7
.and()
...
}
1 | 提供注销支持。使用时会自动应用此选项WebSecurityConfigurerAdapter 。 |
---|---|
2 | 触发注销的URL(默认为/logout )。如果启用了CSRF保护(默认),则该请求也必须是POST。有关更多信息,请参阅JavaDoc。 |
3 | 注销后重定向到的URL。默认是/login?logout 。有关更多信息,请参阅JavaDoc。 |
4 | 我们指定一个自定义LogoutSuccessHandler 。如果指定了,logoutSuccessUrl() 则忽略。有关更多信息,请参阅JavaDoc。 |
5 | 指定HttpSession 在注销时是否使其无效。默认情况下这是真的。配置SecurityContextLogoutHandler 封面。有关更多信息,请参阅JavaDoc。 |
6 | 添加一个LogoutHandler 。 默认情况下SecurityContextLogoutHandler 添加为最后一个LogoutHandler 。 |
7 | 允许指定在注销成功时删除的cookie的名称。这是CookieClearingLogoutHandler 显式添加的快捷方式。 |
| ===当然也可以使用XML Namespace表示法配置注销。有关更多详细信息,请参阅Spring Security XML Namespace部分中logout元素的文档。===
通常,为了自定义注销功能,您可以添加 LogoutHandler
和/或 LogoutSuccessHandler
实现。对于许多常见场景,使用流畅的API时,这些处理程序将在幕后应用。
6.5.1 LogoutHandler
通常,LogoutHandler
实现指示能够参与注销处理的类。预计将调用它们以进行必要的清理。因此,他们不应该抛出异常。提供了各种实现:
- 对PersistentTokenBasedRememberMeServices
- TokenBasedRememberMeServices
- CookieClearingLogoutHandler
- CsrfLogoutHandler
- SecurityContextLogoutHandler
- HeaderWriterLogoutHandler
有关详细信息,请参见第10.5.4节“记住我的接口和实现”。
而不是LogoutHandler
直接提供实现,流畅的API还提供了快捷方式,提供了各自的LogoutHandler
实现。例如,deleteCookies()
允许指定在注销成功时要删除的一个或多个cookie的名称。与添加a相比,这是一个捷径CookieClearingLogoutHandler
。
6.5.2 LogoutSuccessHandler
该LogoutSuccessHandler
被成功注销后调用LogoutFilter
,来处理如重定向或转发到相应的目的地。请注意,界面几乎与该界面相同,LogoutHandler
但可能引发异常。
提供以下实现:
- SimpleUrlLogoutSuccessHandler
- HttpStatusReturningLogoutSuccessHandler
如上所述,您无需SimpleUrlLogoutSuccessHandler
直接指定。相反,流畅的API通过设置提供快捷方式logoutSuccessUrl()
。这将设置SimpleUrlLogoutSuccessHandler
封底。发生注销后,提供的URL将重定向到。默认是/login?logout
。
本HttpStatusReturningLogoutSuccessHandler
可以在REST API类型场景有趣。成功注销后,LogoutSuccessHandler
不允许重定向到URL,而是允许您提供要返回的纯HTTP状态代码。如果未配置,则默认情况下将返回状态代码200。
6.5.3进一步注销相关参考
- 注销处理
- 测试注销
- HttpServletRequest.logout()
- 第10.5.4节“记住我的接口和实现”
- 登录 CSRF警告部分
- 部分单点注销(CAS协议)
- Spring Security XML Namespace部分中 logout元素的 文档
6.6 OAuth 2.0客户端
OAuth 2.0客户端功能为OAuth 2.0授权框架中定义的客户端角色提供支持。
可以使用以下主要功能:
- 授权代码授予
- 客户凭证授权
WebClient
Servlet环境的扩展(用于创建受保护的资源请求)
HttpSecurity.oauth2Client()
提供了许多用于自定义OAuth 2.0 Client的配置选项。以下代码显示了可用于oauth2Client()
DSL 的完整配置选项:
@EnableWebSecurity
public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Client()
.clientRegistrationRepository(this.clientRegistrationRepository())
.authorizedClientRepository(this.authorizedClientRepository())
.authorizedClientService(this.authorizedClientService())
.authorizationCodeGrant()
.authorizationRequestRepository(this.authorizationRequestRepository())
.authorizationRequestResolver(this.authorizationRequestResolver())
.accessTokenResponseClient(this.accessTokenResponseClient());
}
}
以下部分详细介绍了每个可用的配置选项:
- 第6.6.1节“ClientRegistration”
- 第6.6.2节“ClientRegistrationRepository”
- 第6.6.3节“OAuth2AuthorizedClient”
- 第6.6.4节“OAuth2AuthorizedClientRepository / OAuth2AuthorizedClientService”
- 第6.6.5节“RegisteredOAuth2AuthorizedClient”
- 第6.6.6节“AuthorizationRequestRepository”
- 第6.6.7节“OAuth2AuthorizationRequestResolver”
- 第6.6.8节“OAuth2AccessTokenResponseClient”
6.6.1 ClientRegistration
ClientRegistration
表示在OAuth 2.0或OpenID Connect 1.0提供程序中注册的客户端。
客户端注册保存信息,例如客户端ID,客户端密钥,授权授权类型,重定向URI,范围,授权URI,令牌URI和其他详细信息。
ClientRegistration
其属性定义如下:
public final class ClientRegistration {
private String registrationId; //1
private String clientId; //2
private String clientSecret; // 3
private ClientAuthenticationMethod clientAuthenticationMethod; //4
private AuthorizationGrantType authorizationGrantType; // 5
private String redirectUriTemplate;// 6
private Set<String> scopes; //7
private ProviderDetails providerDetails;
private String clientName; // 8
public class ProviderDetails {
private String authorizationUri; // 9
private String tokenUri; // 10
private UserInfoEndpoint userInfoEndpoint;
private String jwkSetUri; // 11
private Map<String, Object> configurationMetadata; // 12
public class UserInfoEndpoint {
private String uri;// 13
private AuthenticationMethod authenticationMethod;// 14
private String userNameAttributeName; // 15
}
}
}
1 | registrationId :唯一标识的ID ClientRegistration 。 |
---|---|
2 | clientId :客户端标识符。 |
3 | clientSecret :客户的秘密。 |
4 | clientAuthenticationMethod :用于使用Provider对客户端进行身份验证的方法。支持的值是基本和后期。 |
5 | authorizationGrantType :OAuth 2.0授权框架定义了四种授权授权类型。支持的值是authorization_code,implicit和client_credentials。 |
6 | redirectUriTemplate :客户端的注册重定向URI,授权服务器将最终用户的用户代理重定向到最终用户对客户端进行身份验证和授权访问之后。 |
7 | scopes :客户端在授权请求流程中请求的范围,例如openid,电子邮件或配置文件。 |
8 | clientName :用于客户端的描述性名称。该名称可能在某些情况下使用,例如在自动生成的登录页面中显示客户端的名称时。 |
9 | authorizationUri :授权服务器的授权端点URI。 |
10 | tokenUri :授权服务器的令牌端点URI。 |
11 | jwkSetUri :用于从授权服务器检索JSON Web密钥(JWK)集的URI ,其包含用于验证ID令牌的JSON Web签名(JWS)以及可选的UserInfo响应的加密密钥。 |
12 | configurationMetadata :OpenID提供程序配置信息。仅当spring.security.oauth2.client.provider.[providerId].issuerUri 配置了Spring Boot 2.x属性时,才能使用此信息。 |
13 | (userInfoEndpoint)uri :UserInfo端点URI,用于访问经过身份验证的最终用户的声明/属性。 |
14 | (userInfoEndpoint)authenticationMethod :将访问令牌发送到UserInfo端点时使用的身份验证方法。支持的值是标题,表单和查询。 |
15 | userNameAttributeName :UserInfo响应中返回的属性的名称,该属性引用最终用户的名称或标识符。 |
6.6.2 ClientRegistrationRepository
它ClientRegistrationRepository
充当OAuth 2.0 / OpenID Connect 1.0的存储库ClientRegistration
。
| 客户端注册信息最终由关联的授权服务器存储和拥有。此存储库提供检索主客户端注册信息的子集的功能,该子集与授权服务器一起存储。
Spring Boot 2.x自动配置将每个属性绑定到一个实例,然后组成一个实例中的每个实例。spring.security.oauth2.client.registration.*[registrationId]*``ClientRegistration``ClientRegistration``ClientRegistrationRepository
| 默认实现
ClientRegistrationRepository
是InMemoryClientRegistrationRepository
。
如果应用程序需要,自动配置还会将其注册ClientRegistrationRepository
为a @Bean
,ApplicationContext
以便可用于依赖注入。
以下清单显示了一个示例:
@Controller
public class OAuth2ClientController {
@Autowired
private ClientRegistrationRepository clientRegistrationRepository;
@RequestMapping("/")
public String index() {
ClientRegistration googleRegistration =
this.clientRegistrationRepository.findByRegistrationId("google");
...
return "index";
}
}
6.6.3 OAuth2AuthorizedClient
OAuth2AuthorizedClient
是授权客户的代表。当最终用户(资源所有者)已授权客户端访问其受保护资源时,将认为客户端已获得授权。
OAuth2AuthorizedClient
用于将OAuth2AccessToken
(和可选的OAuth2RefreshToken
)关联到ClientRegistration
(客户端)和资源所有者,该Principal
用户是授予授权的最终用户。
6.6.4 OAuth2AuthorizedClientRepository / OAuth2AuthorizedClientService
OAuth2AuthorizedClientRepository
负责OAuth2AuthorizedClient
在Web请求之间保持持久性。然而,主要作用OAuth2AuthorizedClientService
是OAuth2AuthorizedClient
在应用程序级别进行管理。
从开发人员的角度来看,OAuth2AuthorizedClientRepository
或OAuth2AuthorizedClientService
提供查找OAuth2AccessToken
与客户端关联的功能,以便可以使用它来启动受保护的资源请求。
| Spring Boot2.X自动配置寄存器的
OAuth2AuthorizedClientRepository
和/或OAuth2AuthorizedClientService
@Bean
在ApplicationContext
。
开发者还可注册一个OAuth2AuthorizedClientRepository
或OAuth2AuthorizedClientService
@Bean
在ApplicationContext
(覆盖弹簧引导2.x的自动配置)以便具有查找一个的能力OAuth2AccessToken
与特定关联ClientRegistration
(客户端)。
以下清单显示了一个示例:
@Controller
public class OAuth2LoginController {
@Autowired
private OAuth2AuthorizedClientService authorizedClientService;
@RequestMapping("/userinfo")
public String userinfo(OAuth2AuthenticationToken authentication) {
// authentication.getAuthorizedClientRegistrationId() returns the
// registrationId of the Client that was authorized during the oauth2Login() flow
OAuth2AuthorizedClient authorizedClient =
this.authorizedClientService.loadAuthorizedClient(
authentication.getAuthorizedClientRegistrationId(),
authentication.getName());
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
...
return "userinfo";
}
}
6.6.5 RegisteredOAuth2AuthorizedClient
所述@RegisteredOAuth2AuthorizedClient
注释提供解决方法参数,以类型的参数值的能力OAuth2AuthorizedClient
。与通过查找OAuth2AuthorizedClient
通道相比,这是一种方便的替代方案OAuth2AuthorizedClientService
。
@Controller
public class OAuth2LoginController {
@RequestMapping("/userinfo")
public String userinfo(@RegisteredOAuth2AuthorizedClient("google") OAuth2AuthorizedClient authorizedClient) {
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
...
return "userinfo";
}
}
该@RegisteredOAuth2AuthorizedClient
注释被处理OAuth2AuthorizedClientArgumentResolver
,并提供以下功能:
- 一个
OAuth2AccessToken
如果客户尚未授权将自动请求。- 因为
authorization_code
,这涉及触发授权请求重定向以启动流程 - 因为
client_credentials
,使用令牌端点直接获取访问令牌DefaultClientCredentialsTokenResponseClient
- 因为
6.6.6 AuthorizationRequestRepository
AuthorizationRequestRepository
负责OAuth2AuthorizationRequest
从启动授权请求到收到授权响应(回调)的持续时间。
| 将
OAuth2AuthorizationRequest
被用来关联和验证授权响应。
默认实现AuthorizationRequestRepository
是HttpSessionOAuth2AuthorizationRequestRepository
,它存储OAuth2AuthorizationRequest
在HttpSession
。
如果你想提供一个自定义实现AuthorizationRequestRepository
存储的属性OAuth2AuthorizationRequest
中Cookie
,你可以配置它,如下面的例子:
@EnableWebSecurity
public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Client()
.authorizationCodeGrant()
.authorizationRequestRepository(this.cookieAuthorizationRequestRepository())
...
}
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> cookieAuthorizationRequestRepository() {
return new HttpCookieOAuth2AuthorizationRequestRepository();
}
}
6.6.7 OAuth2AuthorizationRequestResolver
该主要角色OAuth2AuthorizationRequestResolver
是OAuth2AuthorizationRequest
从提供的Web请求中解析一个。默认实现DefaultOAuth2AuthorizationRequestResolver
匹配(默认)路径/oauth2/authorization/{registrationId}
提取registrationId
并使用它来构建OAuth2AuthorizationRequest
关联的路径ClientRegistration
。
OAuth2AuthorizationRequestResolver
可以实现的主要用例之一是能够使用超出OAuth 2.0授权框架中定义的标准参数的附加参数来定制授权请求。
例如,OpenID Connect为授权代码流定义了额外的OAuth 2.0请求参数,这些参数扩展自OAuth 2.0授权框架中定义的标准参数。其中一个扩展参数是prompt
参数。
| 可选的。空格分隔,区分大小写的ASCII字符串值列表,指定授权服务器是否提示最终用户进行重新认证和同意。定义的值为:none,login,consent,select_account
以下示例显示如何通过包含请求参数来实现OAuth2AuthorizationRequestResolver
自定义授权请求的方法。oauth2Login()``prompt=consent
@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private ClientRegistrationRepository clientRegistrationRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.authorizationEndpoint()
.authorizationRequestResolver(
new CustomAuthorizationRequestResolver(
this.clientRegistrationRepository)); //1
}
}
public class CustomAuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver {
private final OAuth2AuthorizationRequestResolver defaultAuthorizationRequestResolver;
public CustomAuthorizationRequestResolver(
ClientRegistrationRepository clientRegistrationRepository) {
this.defaultAuthorizationRequestResolver =
new DefaultOAuth2AuthorizationRequestResolver(
clientRegistrationRepository, "/oauth2/authorization");
}
@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
OAuth2AuthorizationRequest authorizationRequest =
this.defaultAuthorizationRequestResolver.resolve(request); // 2
return authorizationRequest != null ? // 3
customAuthorizationRequest(authorizationRequest) :
null;
}
@Override
public OAuth2AuthorizationRequest resolve(
HttpServletRequest request, String clientRegistrationId) {
OAuth2AuthorizationRequest authorizationRequest =
this.defaultAuthorizationRequestResolver.resolve(
request, clientRegistrationId); // 4
return authorizationRequest != null ? // 5
customAuthorizationRequest(authorizationRequest) :
null;
}
private OAuth2AuthorizationRequest customAuthorizationRequest(
OAuth2AuthorizationRequest authorizationRequest) {
Map<String, Object> additionalParameters =
new LinkedHashMap<>(authorizationRequest.getAdditionalParameters());
additionalParameters.put("prompt", "consent"); // 6
return OAuth2AuthorizationRequest.from(authorizationRequest) // 7
.additionalParameters(additionalParameters)// 8
.build();
}
}
|
OAuth2AuthorizationRequest.Builder.build()
构造OAuth2AuthorizationRequest.authorizationRequestUri
,表示完整的授权请求URI,包括使用该application/x-www-form-urlencoded
格式的所有查询参数。
上面的示例显示了在标准参数之上添加自定义参数的常见用例。但是,如果您需要删除或更改标准参数或者您的要求更高级,则可以通过简单地覆盖OAuth2AuthorizationRequest.authorizationRequestUri
属性来完全控制构建授权请求URI 。
以下示例显示了customAuthorizationRequest()
前一示例中方法的变体,而是覆盖了该OAuth2AuthorizationRequest.authorizationRequestUri
属性。
private OAuth2AuthorizationRequest customAuthorizationRequest(
OAuth2AuthorizationRequest authorizationRequest) {
String customAuthorizationRequestUri = UriComponentsBuilder
.fromUriString(authorizationRequest.getAuthorizationRequestUri())
.queryParam("prompt", "consent")
.build(true)
.toUriString();
return OAuth2AuthorizationRequest.from(authorizationRequest)
.authorizationRequestUri(customAuthorizationRequestUri)
.build();
}
6.6.8 OAuth2AccessTokenResponseClient
主要角色OAuth2AccessTokenResponseClient
是在授权服务器的令牌端点处为访问令牌凭证交换授权授予凭证。
默认实现OAuth2AccessTokenResponseClient
的authorization_code
补助DefaultAuthorizationCodeTokenResponseClient
,它采用RestOperations
了在令牌端点访问令牌交换一个授权码。
的DefaultAuthorizationCodeTokenResponseClient
,因为它允许您自定义的令牌响应的令牌请求和/或装卸后的前处理非常灵活。
如果您需要自定义令牌请求的预处理,则可以提供DefaultAuthorizationCodeTokenResponseClient.setRequestEntityConverter()
自定义Converter<OAuth2AuthorizationCodeGrantRequest, RequestEntity<?>>
。默认实现OAuth2AuthorizationCodeGrantRequestEntityConverter
构建RequestEntity
标准OAuth 2.0访问令牌请求的表示。但是,提供自定义Converter
将允许您扩展标准令牌请求并添加自定义参数。
| 重要 | 自定义
Converter
必须返回RequestEntity
预期OAuth 2.0提供程序可以理解的OAuth 2.0访问令牌请求的有效表示。
另一方面,如果您需要自定义令牌响应的后处理,则需要提供DefaultAuthorizationCodeTokenResponseClient.setRestOperations()
自定义配置RestOperations
。默认RestOperations
配置如下:
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
new FormHttpMessageConverter(),
new OAuth2AccessTokenResponseHttpMessageConverter()));
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
|
FormHttpMessageConverter
在发送OAuth 2.0访问令牌请求时使用Spring MVC 是必需的。
OAuth2AccessTokenResponseHttpMessageConverter`是一个`HttpMessageConverter`OAuth 2.0访问令牌响应。您可以提供用于将OAuth 2.0访问令牌响应参数转换为`OAuth2AccessTokenResponseHttpMessageConverter.setTokenResponseConverter()`的自定义。`Converter<Map<String, String>, OAuth2AccessTokenResponse>``OAuth2AccessTokenResponse
OAuth2ErrorResponseErrorHandler
是一个ResponseErrorHandler
可以处理OAuth 2.0错误(400错误请求)。它OAuth2ErrorHttpMessageConverter
用于将OAuth 2.0 Error参数转换为OAuth2Error
。
无论您是自定义DefaultAuthorizationCodeTokenResponseClient
还是提供自己的实现OAuth2AccessTokenResponseClient
,都需要对其进行配置,如以下示例所示:
@EnableWebSecurity
public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Client()
.authorizationCodeGrant()
.accessTokenResponseClient(this.customAccessTokenResponseClient())
...
}
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> customAccessTokenResponseClient() {
...
}
}
6.7 OAuth 2.0登录
OAuth 2.0登录功能为应用程序提供了使用OAuth 2.0提供程序(例如GitHub)或OpenID Connect 1.0提供程序(例如Google)上的现有帐户登录应用程序的功能。OAuth 2.0 Login实现了用例:“使用Google登录”或“使用GitHub登录”。
| OAuth 2.0登录是使用授权代码授予实现的,如OAuth 2.0授权框架和OpenID Connect Core 1.0中所指定。
6.7.1 Spring Boot 2.x示例
Spring Boot 2.x为OAuth 2.0登录带来了完整的自动配置功能。
本部分介绍如何使用Google作为身份验证提供程序配置OAuth 2.0登录示例,并介绍以下主题:
初始设置
要使用Google的OAuth 2.0身份验证系统进行登录,您必须在Google API控制台中设置项目以获取OAuth 2.0凭据。
| Google的OAuth 2.0身份验证实施符合OpenID Connect 1.0规范,并通过OpenID认证。
按照OpenID Connect页面上的说明操作,从“设置OAuth 2.0”部分开始。
完成“获取OAuth 2.0凭据”说明后,您应该拥有一个新的OAuth客户端,其凭据包含客户端ID和客户端密钥。
设置重定向URI
重定向URI是应用程序中的路径,最终用户的用户代理在通过Google进行身份验证并在“同意”页面上授予了对OAuth客户端(在上一步中创建)的访问权限后重定向回的路径。
在“设置重定向URI”子部分中,确保将“ 授权重定向URI”字段设置为http://localhost:8080/login/oauth2/code/google
。
| 默认的重定向URI模板是
{baseUrl}/login/oauth2/code/{registrationId}
。该registrationId是用于唯一标识符ClientRegistration。
配置application.yml
既然您有一个新的OAuth客户端与Google,您需要配置应用程序以使用OAuth客户端进行身份验证流程。为此:
- 转到
application.yml
并设置以下配置:
spring:
security:
oauth2:
client:
registration: // 1
google: // 2
client-id: google-client-id
client-secret: google-client-secret
例6.1。OAuth客户端属性
![]() |
spring.security.oauth2.client.registration 是OAuth客户端属性的基本属性前缀。 |
---|---|
![]() |
基本属性前缀后面是ClientRegistration的ID ,例如google。 |
- 使用您之前创建的OAuth 2.0凭据替换
client-id
andclient-secret
属性中的值。
启动应用程序
启动Spring Boot 2.x示例并转到http://localhost:8080
。然后,您将被重定向到默认的自动生成的登录页面,该页面显示Google的链接。
点击Google链接,然后您将重定向到Google进行身份验证。
使用您的Google帐户凭据进行身份验证后,显示给您的下一页是“同意”屏幕。“同意”屏幕会要求您允许或拒绝访问您之前创建的OAuth客户端。单击“ 允许”以授权OAuth客户端访问您的电子邮件地址和基本配置文件信息。
此时,OAuth客户端从UserInfo端点检索您的电子邮件地址和基本配置文件信息,并建立经过身份验证的会话。
6.7.2 Spring Boot 2.x属性映射
下表概述了Spring Boot 2.x OAuth客户端属性到ClientRegistration属性的映射。
Spring Boot 2.x | ClientRegistration |
---|---|
spring.security.oauth2.client.registration.*[registrationId]* |
registrationId |
spring.security.oauth2.client.registration.*[registrationId]*.client-id |
clientId |
spring.security.oauth2.client.registration.*[registrationId]*.client-secret |
clientSecret |
spring.security.oauth2.client.registration.*[registrationId]*.client-authentication-method |
clientAuthenticationMethod |
spring.security.oauth2.client.registration.*[registrationId]*.authorization-grant-type |
authorizationGrantType |
spring.security.oauth2.client.registration.*[registrationId]*.redirect-uri |
redirectUriTemplate |
spring.security.oauth2.client.registration.*[registrationId]*.scope |
scopes |
spring.security.oauth2.client.registration.*[registrationId]*.client-name |
clientName |
spring.security.oauth2.client.provider.*[providerId]*.authorization-uri |
providerDetails.authorizationUri |
spring.security.oauth2.client.provider.*[providerId]*.token-uri |
providerDetails.tokenUri |
spring.security.oauth2.client.provider.*[providerId]*.jwk-set-uri |
providerDetails.jwkSetUri |
spring.security.oauth2.client.provider.*[providerId]*.user-info-uri |
providerDetails.userInfoEndpoint.uri |
spring.security.oauth2.client.provider.*[providerId]*.user-info-authentication-method |
providerDetails.userInfoEndpoint.authenticationMethod |
spring.security.oauth2.client.provider.*[providerId]*.userNameAttribute |
providerDetails.userInfoEndpoint.userNameAttributeName |
6.7.3 CommonOAuth2Provider
CommonOAuth2Provider
为许多知名提供商预定义一组默认客户端属性:Google,GitHub,Facebook和Okta。
例如,authorization-uri
,token-uri
,和user-info-uri
不经常对供应商变更。因此,提供默认值以减少所需配置是有意义的。
如前所述,当我们配置Google客户端时,只需要client-id
和client-secret
属性。
以下清单显示了一个示例:
spring:
security:
oauth2:
client:
registration:
google:
client-id:google-client-id
client-secret:google-client-secret
| 客户端属性的自动默认无缝地在这里工作,因为
registrationId
(google
)匹配GOOGLE
enum
(不区分大小写)in CommonOAuth2Provider
。
对于您可能希望指定其他内容的情况registrationId
,例如google-login
,您仍然可以通过配置属性来利用客户端属性的自动默认provider
。
以下清单显示了一个示例:
spring:
security:
oauth2:
client:
registration:
google-login: //1
provider: google // 2
client-id: google-client-id
client-secret: google-client-secret
![]() |
该registrationId 设置为google-login 。 |
---|---|
![]() |
该provider 属性设置为google ,将利用设置的客户端属性的自动默认值CommonOAuth2Provider.GOOGLE.getBuilder() 。 |