This is the seventh day of my participation in the First Challenge 2022
At present, almost all Spring projects are built with Spring Boot as the basic framework. Through a variety of starter, we can easily use various Spring and third-party components in Spring Boot projects. For example, We can introduce Spring Security request of the starter, you can easily use @ EnableAuthorizationServer annotations, Automatically enable and configure the Authorization service component of Spring Security OAuth in your application.
In this article, we’ll start with this annotation and see how it accomplishes these configurations.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AuthorizationServerEndpointsConfiguration.class, AuthorizationServerSecurityConfiguration.class})
public @interface EnableAuthorizationServer {
}
Copy the code
Can see from the source, EnableAuthorizationServer annotations by @ Import, Import the two configuration files:
AuthorizationServerEndpointsConfiguration
Endpoint configuration.AuthorizationServerSecurityConfiguration
Security configuration.
Next, analyze one by one.
AuthorizationServerEndpointsConfiguration endpoint configuration
In the official Spring Security OAuth documentation, the two most important endpoints are described:
-
AuthorizationEndpoint
is used to service requests for authorization. Default URL:/oauth/authorize
.
-
TokenEndpoint
is used to service requests for access tokens. Default URL:/oauth/token
.
Translation:
- AuthorizationEndpoint is used to service authorization requests. The default URL is:
/oauth/authorize
. - TokenEndpoint is used to service access token requests. The default URL is:
/oauth/token
.
The two endpoints, are among the AuthorizationServerEndpointsConfiguration configuration, the following is the part of the configuration of the source code:
@Bean
public AuthorizationEndpoint authorizationEndpoint(a) throws Exception {
AuthorizationEndpoint authorizationEndpoint = new AuthorizationEndpoint();
FrameworkEndpointHandlerMapping mapping = getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));
authorizationEndpoint.setProviderExceptionHandler(exceptionTranslator());
authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error"));
authorizationEndpoint.setTokenGranter(tokenGranter());
authorizationEndpoint.setClientDetailsService(clientDetailsService);
authorizationEndpoint.setAuthorizationCodeServices(authorizationCodeServices());
authorizationEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
authorizationEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());
authorizationEndpoint.setUserApprovalHandler(userApprovalHandler());
authorizationEndpoint.setRedirectResolver(redirectResolver());
return authorizationEndpoint;
}
@Bean
public TokenEndpoint tokenEndpoint(a) throws Exception {
TokenEndpoint tokenEndpoint = new TokenEndpoint();
tokenEndpoint.setClientDetailsService(clientDetailsService);
tokenEndpoint.setProviderExceptionHandler(exceptionTranslator());
tokenEndpoint.setTokenGranter(tokenGranter());
tokenEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
tokenEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());
tokenEndpoint.setAllowedRequestMethods(allowedTokenEndpointRequestMethods());
return tokenEndpoint;
}
Copy the code
With the configuration of these two endpoints, the client can request authorization and access token to the authorization server, AuthorizationEndpoint and TokenEndpoint two endpoint source, which contains OAuth 2.0 each authorization mode processing logic, specific source analysis, Refer to the previous article. I put the link at the end.
AuthorizationServerSecurityConfiguration
In AuthorizationServerSecurityConfiguration, we look at the major configuration code:
@Override
protected void configure(HttpSecurity http) throws Exception {
AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer();
FrameworkEndpointHandlerMapping handlerMapping = endpoints.oauth2EndpointHandlerMapping();
http.setSharedObject(FrameworkEndpointHandlerMapping.class, handlerMapping);
configure(configurer);
http.apply(configurer);
String tokenEndpointPath = handlerMapping.getServletPath("/oauth/token");
String tokenKeyPath = handlerMapping.getServletPath("/oauth/token_key");
String checkTokenPath = handlerMapping.getServletPath("/oauth/check_token");
if(! endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) { UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class); endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService); }// @formatter:off
http
.authorizeRequests()
.antMatchers(tokenEndpointPath).fullyAuthenticated()
.antMatchers(tokenKeyPath).access(configurer.getTokenKeyAccess())
.antMatchers(checkTokenPath).access(configurer.getCheckTokenAccess())
.and()
.requestMatchers()
.antMatchers(tokenEndpointPath, tokenKeyPath, checkTokenPath)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
// @formatter:on
http.setSharedObject(ClientDetailsService.class, clientDetailsService);
}
Copy the code
The configuration of UserDetailsService and ClientDetailsService can be seen in the source code.
UserDetailsService
The only abstract method in the interfaceloadUserByUsername
Used to obtain user information by user name.ClientDetailsService
The only abstract method in the interfaceloadClientByClientId
Used to obtain client information by client ID.
The two components are similar in that they serve the same purpose, one for the user (resource owner) and one for the client. They are used during authentication and authorization to verify user and client information. You can also see where they are used in the last two articles.
At the start of the configuration method, we can also see loaded AuthorizationServerSecurityConfigurer class configuration, we look at the detail.
AuthorizationServerSecurityConfigurer
Let’s focus on the configuration here.
ClientDetailsUserDetailsService
By looking at the source code, can configure a ClientDetailsUserDetailsService found here.
@Override
public void init(HttpSecurity http) throws Exception {
registerDefaultAuthenticationEntryPoint(http);
if(passwordEncoder ! =null) {
ClientDetailsUserDetailsService clientDetailsUserDetailsService = new ClientDetailsUserDetailsService(clientDetailsService());
clientDetailsUserDetailsService.setPasswordEncoder(passwordEncoder());
http.getSharedObject(AuthenticationManagerBuilder.class)
.userDetailsService(clientDetailsUserDetailsService)
.passwordEncoder(passwordEncoder());
}
else {
http.userDetailsService(new ClientDetailsUserDetailsService(clientDetailsService()));
}
http.securityContext().securityContextRepository(new NullSecurityContextRepository()).and().csrf().disable()
.httpBasic().realmName(realm);
if(sslOnly) { http.requiresChannel().anyRequest().requiresSecure(); }}Copy the code
ClientDetailsUserDetailsService the name of this class got up easily confusing, I am here to explain: This is a C ‘ ‘lientDetailsService masquerading as U’ ‘serDetailsService that implements U’ ‘serDetailsService and contains a parameter of type C’ ‘lientDetailsService, It implements the loadUserByUsername method by calling loadClientByClientId of C ‘ ‘lientDetailsService in it.
What is it for, you might wonder? When configuring Spring Security OAuth, we can allow clientId and clientSecret to be submitted via forms. In this case, Spring Security needs to authenticate client information as well as user information. At this time will use ClientDetailsUserDetailsService.
ClientCredentialsTokenEndpointFilter
At this point, have to say about the AuthorizationServerSecurityConfigurer in another configuration.
@Override
public void configure(HttpSecurity http) throws Exception {
// ensure this is initialized
frameworkEndpointHandlerMapping();
if (allowFormAuthenticationForClients) {
clientCredentialsTokenEndpointFilter(http);
}
for (Filter filter : tokenEndpointAuthenticationFilters) {
http.addFilterBefore(filter, BasicAuthenticationFilter.class);
}
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}
private ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter(HttpSecurity http) {
ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter = new ClientCredentialsTokenEndpointFilter(
frameworkEndpointHandlerMapping().getServletPath("/oauth/token"));
clientCredentialsTokenEndpointFilter
.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
OAuth2AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
authenticationEntryPoint.setTypeName("Form");
authenticationEntryPoint.setRealmName(realm);
clientCredentialsTokenEndpointFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
clientCredentialsTokenEndpointFilter = postProcess(clientCredentialsTokenEndpointFilter);
http.addFilterBefore(clientCredentialsTokenEndpointFilter, BasicAuthenticationFilter.class);
return clientCredentialsTokenEndpointFilter;
}
Copy the code
Here by clientCredentialsTokenEndpointFilter configured with a clientCredentialsTokenEndpointFilter filter. Its source code, you can check it on its own, it is very similar and UsernamePasswordAuthenticationFilter, the main difference is:
- Instead of the user name and password, it reads client_ID and client_secret from the form to encapsulate authentication information.
- Its UserDetailsService implementation class for querying user information is
ClientDetailsUserDetailsService
.
That is, it authenticates the client’s information by authenticating the user name and password.
The last
Related articles:
- Spring Security authentication process
- Spring Security OAuth Authentication Process: Password mode
- Spring Security OAuth Authentication Process: Authorization Code mode