Spring Authorization Server 密码模式

3,635 阅读3分钟

一、背景

由于Spring-Security-Oauth2已停止维护,官方推荐采用新的Spring Authorization Server来替代。Spring Authorization Server 目前最新版本1.0.0,但需要Spring Boot 3以上的版本来支持,所以,本文基于0.4.0版本来讲述。

Srping Authorization Server 移除来密码模式(Password),但实际业务中,我们还是需要使用密码模式来登录,所以本文将通过自定义来实现密码模式。

二、核心代码编写

1、引入授权服务依赖

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-authorization-server</artifactId>
    <version>0.4.0</version>
</dependency>

2、核心代码

package com.example.demo.config.pwd;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.util.Assert;

/**
 * 自定义Oauth2 密码模式认证Token
 * @author zhongyu
 */
public class OAuth2ResourceOwnerPasswordAuthenticationToken extends AbstractAuthenticationToken {

   private static final long serialVersionUID = -6067207202119450764L;
   
   private final AuthorizationGrantType authorizationGrantType;
   private final Authentication clientPrincipal;
   private final Set<String> scopes;
   private final Map<String, Object> additionalParameters;

   /**
    * Constructs an {@code OAuth2ClientCredentialsAuthenticationToken} using the provided parameters.
    *
    * @param clientPrincipal the authenticated client principal
    */
   
   public OAuth2ResourceOwnerPasswordAuthenticationToken(AuthorizationGrantType authorizationGrantType,
         Authentication clientPrincipal,  @Nullable Set<String> scopes, @Nullable Map<String, Object> additionalParameters) {
      super(Collections.emptyList());
      Assert.notNull(authorizationGrantType, "authorizationGrantType cannot be null");
      Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
      this.authorizationGrantType = authorizationGrantType;
      this.clientPrincipal = clientPrincipal;
      this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
      this.additionalParameters = Collections.unmodifiableMap(additionalParameters != null ? new HashMap<>(additionalParameters) : Collections.emptyMap());
   }
   
   /**
    * Returns the authorization grant type.
    *
    * @return the authorization grant type
    */
   public AuthorizationGrantType getGrantType() {
      return this.authorizationGrantType;
   }

   @Override
   public Object getPrincipal() {
      return this.clientPrincipal;
   }

   @Override
   public Object getCredentials() {
      return "";
   }

   /**
    * Returns the requested scope(s).
    *
    * @return the requested scope(s), or an empty {@code Set} if not available
    */
   public Set<String> getScopes() {
      return this.scopes;
   }
   
   /**
    * Returns the additional parameters.
    *
    * @return the additional parameters
    */
   public Map<String, Object> getAdditionalParameters() {
      return this.additionalParameters;
   }
   
}
package com.example.demo.config.pwd;

import java.security.Principal;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClaimAccessor;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;
import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

public class OAuth2ResourceOwnerPasswordAuthenticationProvider implements AuthenticationProvider {

   private static final Logger LOGGER = LogManager.getLogger();
   
   private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
   private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN);
   private final AuthenticationManager authenticationManager;
   private final OAuth2AuthorizationService authorizationService;
   private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
   
   /**
    * Constructs an {@code OAuth2ResourceOwnerPasswordAuthenticationProviderNew} using the provided parameters.
    *
    * @param authenticationManager the authentication manager
    * @param authorizationService the authorization service
    * @param tokenGenerator the token generator
    * @since 0.2.3
    */
   public OAuth2ResourceOwnerPasswordAuthenticationProvider(AuthenticationManager authenticationManager, 
         OAuth2AuthorizationService authorizationService, OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
      Assert.notNull(authorizationService, "authorizationService cannot be null");
      Assert.notNull(tokenGenerator, "tokenGenerator cannot be null");
      this.authenticationManager = authenticationManager;
      this.authorizationService = authorizationService;
      this.tokenGenerator = tokenGenerator;
   }
   
   @Override
   public Authentication authenticate(Authentication authentication) throws AuthenticationException {
      
      OAuth2ResourceOwnerPasswordAuthenticationToken resouceOwnerPasswordAuthentication = (OAuth2ResourceOwnerPasswordAuthenticationToken) authentication;

      OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(resouceOwnerPasswordAuthentication);
      
      RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
      
      if (LOGGER.isTraceEnabled()) {
         LOGGER.trace("Retrieved registered client");
      }

      if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.PASSWORD)) {
         throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
      }

      Authentication usernamePasswordAuthentication = getUsernamePasswordAuthentication(resouceOwnerPasswordAuthentication);
      
      Set<String> authorizedScopes = registeredClient.getScopes();      // Default to configured scopes
      Set<String> requestedScopes = resouceOwnerPasswordAuthentication.getScopes();
      if (!CollectionUtils.isEmpty(requestedScopes)) {
         Set<String> unauthorizedScopes = requestedScopes.stream()
               .filter(requestedScope -> !registeredClient.getScopes().contains(requestedScope))
               .collect(Collectors.toSet());
         if (!CollectionUtils.isEmpty(unauthorizedScopes)) {
            throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE);
         }
         
         authorizedScopes = new LinkedHashSet<>(requestedScopes);
      }
      
      if (LOGGER.isTraceEnabled()) {
         LOGGER.trace("Validated token request parameters");
      }

      // @formatter:off
      DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
            .registeredClient(registeredClient)
            .principal(usernamePasswordAuthentication)
            .authorizationServerContext(AuthorizationServerContextHolder.getContext())
            .authorizedScopes(authorizedScopes)
            .authorizationGrantType(AuthorizationGrantType.PASSWORD)
            .authorizationGrant(resouceOwnerPasswordAuthentication);
      // @formatter:on

      // ----- Access token -----
      OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();
      OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);
      if (generatedAccessToken == null) {
         OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
               "The token generator failed to generate the access token.", ERROR_URI);
         throw new OAuth2AuthenticationException(error);
      }
      
      if (LOGGER.isTraceEnabled()) {
         LOGGER.trace("Generated access token");
      }
      
      OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
            generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
            generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
      
      // @formatter:off
      OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)
            .principalName(usernamePasswordAuthentication.getName())
            .authorizationGrantType(AuthorizationGrantType.PASSWORD)
            .authorizedScopes(authorizedScopes)
            .attribute(Principal.class.getName(), usernamePasswordAuthentication);
      // @formatter:on
      if (generatedAccessToken instanceof ClaimAccessor) {
         authorizationBuilder.token(accessToken, (metadata) ->
               metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) generatedAccessToken).getClaims()));
      } else {
         authorizationBuilder.accessToken(accessToken);
      }
      
      // ----- Refresh token -----
      OAuth2RefreshToken refreshToken = null;
      if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN) &&
            // Do not issue refresh token to public client
            !clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) {
         
         tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
         OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
         if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {
            OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
                  "The token generator failed to generate the refresh token.", ERROR_URI);
            throw new OAuth2AuthenticationException(error);
         }
         
         if (LOGGER.isTraceEnabled()) {
            LOGGER.trace("Generated refresh token");
         }
         
         refreshToken = (OAuth2RefreshToken) generatedRefreshToken;
         authorizationBuilder.refreshToken(refreshToken);
      }
      
      // ----- ID token -----
      OidcIdToken idToken;
      if (requestedScopes.contains(OidcScopes.OPENID)) {
         // @formatter:off
         tokenContext = tokenContextBuilder
               .tokenType(ID_TOKEN_TOKEN_TYPE)
               .authorization(authorizationBuilder.build())   // ID token customizer may need access to the access token and/or refresh token
               .build();
         // @formatter:on
         OAuth2Token generatedIdToken = this.tokenGenerator.generate(tokenContext);
         if (!(generatedIdToken instanceof Jwt)) {
            OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
                  "The token generator failed to generate the ID token.", ERROR_URI);
            throw new OAuth2AuthenticationException(error);
         }

         if (LOGGER.isTraceEnabled()) {
            LOGGER.trace("Generated id token");
         }

         idToken = new OidcIdToken(generatedIdToken.getTokenValue(), generatedIdToken.getIssuedAt(),
               generatedIdToken.getExpiresAt(), ((Jwt) generatedIdToken).getClaims());
         authorizationBuilder.token(idToken, (metadata) ->
               metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()));
      } else {
         idToken = null;
      }

      OAuth2Authorization authorization = authorizationBuilder.build();

      this.authorizationService.save(authorization);
      
      if (LOGGER.isTraceEnabled()) {
         LOGGER.trace("Saved authorization");
      }
      
      Map<String, Object> additionalParameters = Collections.emptyMap();
      if (idToken != null) {
         additionalParameters = new HashMap<>();
         additionalParameters.put(OidcParameterNames.ID_TOKEN, idToken.getTokenValue());
      }

      if (LOGGER.isTraceEnabled()) {
         LOGGER.trace("Authenticated token request");
      }
      
      return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken, additionalParameters);
   }
   
   @Override
   public boolean supports(Class<?> authentication) {
      boolean supports = OAuth2ResourceOwnerPasswordAuthenticationToken.class.isAssignableFrom(authentication);
      LOGGER.debug("supports authentication=" + authentication + " returning " + supports);
      return supports;
   }
   
   private Authentication getUsernamePasswordAuthentication(OAuth2ResourceOwnerPasswordAuthenticationToken resouceOwnerPasswordAuthentication) {
      
      Map<String, Object> additionalParameters = resouceOwnerPasswordAuthentication.getAdditionalParameters();
      
      String username = (String) additionalParameters.get(OAuth2ParameterNames.USERNAME);
      String password = (String) additionalParameters.get(OAuth2ParameterNames.PASSWORD);
      
      UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, password);
      LOGGER.debug("got usernamePasswordAuthenticationToken=" + usernamePasswordAuthenticationToken);
      
      Authentication usernamePasswordAuthentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
      return usernamePasswordAuthentication;
   }
   
   private OAuth2ClientAuthenticationToken getAuthenticatedClientElseThrowInvalidClient(Authentication authentication) {
      
      OAuth2ClientAuthenticationToken clientPrincipal = null;
      
      if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication.getPrincipal().getClass())) {
         clientPrincipal = (OAuth2ClientAuthenticationToken) authentication.getPrincipal();
      }
      
      if (clientPrincipal != null && clientPrincipal.isAuthenticated()) {
         return clientPrincipal;
      }
      
      throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
   }
   
}
package com.example.demo.config.pwd;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;

public class OAuth2ResourceOwnerPasswordAuthenticationConverter implements AuthenticationConverter {

   @Override
   public Authentication convert(HttpServletRequest request) {
      
      // grant_type (REQUIRED)
      String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
      if (!AuthorizationGrantType.PASSWORD.getValue().equals(grantType)) {
         return null;
      }

      MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getParameters(request);

      // scope (OPTIONAL)
      String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);
      if (StringUtils.hasText(scope) &&
            parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {
         OAuth2EndpointUtils.throwError(
               OAuth2ErrorCodes.INVALID_REQUEST,
               OAuth2ParameterNames.SCOPE,
               OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
      }
      
      Set<String> requestedScopes = null;
      if (StringUtils.hasText(scope)) {
         requestedScopes = new HashSet<>(
               Arrays.asList(StringUtils.delimitedListToStringArray(scope, " ")));
      }
      
      // username (REQUIRED)
      String username = parameters.getFirst(OAuth2ParameterNames.USERNAME);
      if (!StringUtils.hasText(username) || parameters.get(OAuth2ParameterNames.USERNAME).size() != 1) {
         OAuth2EndpointUtils.throwError(
            OAuth2ErrorCodes.INVALID_REQUEST,
            OAuth2ParameterNames.USERNAME,
            OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
      }
      
      // password (REQUIRED)
      String password = parameters.getFirst(OAuth2ParameterNames.PASSWORD);
      if (!StringUtils.hasText(password) || parameters.get(OAuth2ParameterNames.PASSWORD).size() != 1) {
         OAuth2EndpointUtils.throwError(
            OAuth2ErrorCodes.INVALID_REQUEST,
            OAuth2ParameterNames.PASSWORD,
            OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
      }
      
      Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
      if (clientPrincipal == null) {
         OAuth2EndpointUtils.throwError(
            OAuth2ErrorCodes.INVALID_REQUEST,
            OAuth2ErrorCodes.INVALID_CLIENT,
            OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
      }
      
      Map<String, Object> additionalParameters = parameters
            .entrySet()
            .stream()
            .filter(e -> !e.getKey().equals(OAuth2ParameterNames.GRANT_TYPE) &&
                  !e.getKey().equals(OAuth2ParameterNames.SCOPE))
            .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0)));
      
      return new OAuth2ResourceOwnerPasswordAuthenticationToken(AuthorizationGrantType.PASSWORD, clientPrincipal, requestedScopes, additionalParameters);

   }
   
}
package com.example.demo.config.pwd;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

/**
 * @author zhongyu
 */
public class OAuth2EndpointUtils {

   public static final String ACCESS_TOKEN_REQUEST_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";

   private OAuth2EndpointUtils() {
   }

   public static MultiValueMap<String, String> getParameters(HttpServletRequest request) {
      Map<String, String[]> parameterMap = request.getParameterMap();
      MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(parameterMap.size());
      parameterMap.forEach((key, values) -> {
         if (values.length > 0) {
            for (String value : values) {
               parameters.add(key, value);
            }
         }
      });
      return parameters;
   }

   public static boolean matchesPkceTokenRequest(HttpServletRequest request) {
      return AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(
            request.getParameter(OAuth2ParameterNames.GRANT_TYPE)) &&
            request.getParameter(OAuth2ParameterNames.CODE) != null &&
            request.getParameter(PkceParameterNames.CODE_VERIFIER) != null;
   }

   public static void throwError(String errorCode, String parameterName, String errorUri) {
      OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Parameter: " + parameterName, errorUri);
      throw new OAuth2AuthenticationException(error);
   }
   
}

3、配置

将自定义的密码模式,配置到认证服务器,使其支持密码模式认证

package com.example.demo.config;


import java.util.Arrays;

import com.example.demo.config.pwd.OAuth2ResourceOwnerPasswordAuthenticationConverter;
import com.example.demo.config.pwd.OAuth2ResourceOwnerPasswordAuthenticationProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientCredentialsAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2RefreshTokenAuthenticationConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.RequestMatcher;

@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfiguration {


   @Bean
   @Order(Ordered.HIGHEST_PRECEDENCE)
   public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
      
      OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();

      http.apply(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> tokenEndpoint.accessTokenRequestConverter(
         new DelegatingAuthenticationConverter(Arrays.asList(
            new OAuth2AuthorizationCodeAuthenticationConverter(),
            new OAuth2RefreshTokenAuthenticationConverter(),
            new OAuth2ClientCredentialsAuthenticationConverter(),
            new OAuth2ResourceOwnerPasswordAuthenticationConverter()))
      )));
      RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
      http
         .requestMatcher(endpointsMatcher)
         .authorizeRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated())
         .csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
         .apply(authorizationServerConfigurer);
      
      SecurityFilterChain securityFilterChain = http.formLogin(Customizer.withDefaults()).build();
      
      /**
       * Custom configuration for Resource Owner Password grant type. Current implementation has no support for Resource Owner 
       * Password grant type
       */
      addCustomOAuth2ResourceOwnerPasswordAuthenticationProvider(http);

      return securityFilterChain;
   }

   @SuppressWarnings("unchecked")
   private void addCustomOAuth2ResourceOwnerPasswordAuthenticationProvider(HttpSecurity http) {
      
      AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
      OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class);
      OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator = http.getSharedObject(OAuth2TokenGenerator.class);
      
      OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider =
            new OAuth2ResourceOwnerPasswordAuthenticationProvider(authenticationManager, authorizationService, tokenGenerator);
      // This will add new authentication provider in the list of existing authentication providers.
      http.authenticationProvider(resourceOwnerPasswordAuthenticationProvider);
   }   
}

三、测试

通过Password获取Token

curl -i -X POST \ 
    -H "Authorization:Basic Y3Nkbjpjc2RuMTIz" \
    'http://127.0.0.1:8080/oauth2/token?grantType=password&username=&password='

能够获取到token,则表示集成成功

仅以此文记录工作中,集成Spring Authorization Server 开启密码模式;