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:

  • AuthorizationServerEndpointsConfigurationEndpoint configuration.
  • AuthorizationServerSecurityConfigurationSecurity 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.

  • UserDetailsServiceThe only abstract method in the interfaceloadUserByUsernameUsed to obtain user information by user name.
  • ClientDetailsServiceThe only abstract method in the interfaceloadClientByClientIdUsed 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:

  1. Instead of the user name and password, it reads client_ID and client_secret from the form to encapsulate authentication information.
  2. Its UserDetailsService implementation class for querying user information isClientDetailsUserDetailsService.

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