Spring Security custom authorization server practice

Event address: CSDN 21-day learning challenge

related articles:

  1. Definition and operation process of OAuth2
  2. Spring Security OAuth implements Gitee quick login
  3. Spring Security OAuth implements GitHub quick login
  4. Spring Security's filter chain mechanism
  5. Spring Security OAuth Client configuration loading source code analysis
  6. Detailed Explanation of Spring Security Built-in Filters
  7. Why are two OAuth2AuthorizationRequestRedirectFilter analysis loaded


We have connected GitHub and Gitee clients before, and using OAuth2 Client can quickly and conveniently integrate third-party logins. On the one hand, integrating third-party logins reduces the cost of customer acquisition for enterprises, and at the same time provides users with a more convenient login experience. But with the development and growth of enterprises, it is more and more necessary to build their own OAuth2 server. OAuth2 includes not only the previous OAuth client, but also the authorization server. Here we need to build our own authorization server with minimal configuration. The authorization server mainly provides functions such as OAuth Client registration, user authentication, token distribution, token verification, and token refresh. In practical applications, the authorization server and the resource server can be implemented in the same application, or can be split into two independent applications. Here, for the convenience of understanding, we split them into two applications.

Authorization Server Changes

The authorization server (Authorization Server) is not currently integrated in the Spring Security project, but exists in the Spring ecosystem as an independent project. Figure 1 shows the position of the Spring Authorization Server in the Spring project list.

figure 1

Why is Spring Authorization Server not integrated in Spring Security?

The reason is that both Spring Security OAuth and Spring Cloud Security in Spring have their own implementation of OAuth. The Spring team initially wanted to separate OAuth into Spring Security, but later the Spring team realized that the OAuth authorization service is not suitable for inclusion in In the Spring Security framework, Spring announced in November 2019 that it will not support the authorization server in Spring Security. The original text is as follows:

original: Since the Spring Security OAuth project was created, the number of authorization server choices has grown significantly. Additionally, we did not feel like creating an authorization server was a common scenario. Nor did we feel like it was appropriate to provide authorization support within a framework with no library support. After careful consideration, the Spring Security team decided that we would not formally support creating authorization servers.

But the community reacted strongly to the fact that Spring Security no longer supports authorization servers. So in April 2020, Spring launched the Spring Authorization Server project. At present, the latest GA version of the project is 0.3 GA, and the preview version is 1.0.0-M1.

Minimal configuration

Install the authorization server

1. Create a new Spring Boot project named spring-security-authorization-server 2. Introduce pom dependencies




Configure Authorization Server

import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
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.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.RequestMatcher;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;

@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfig {

    //Authorization endpoint filter chain
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =
                new OAuth2AuthorizationServerConfigurer<>();
        RequestMatcher endpointsMatcher = authorizationServerConfigurer

                //Without authentication, it will automatically jump to the /login page
                .exceptionHandling((exceptions) -> exceptions
                                new LoginUrlAuthenticationEntryPoint("/login"))
                .authorizeRequests(authorizeRequests ->
                .csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
        return http.build();

    //Filter chain for authentication
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
            throws Exception {
                .authorizeHttpRequests((authorize) -> authorize

        return http.build();

    //Configure principal user
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User.withDefaultPasswordEncoder()

        return new InMemoryUserDetailsManager(userDetails);

    //register client
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
                //client id
                //Client secret key, authorized server needs encrypted storage
                //authorization method
                //Supported authorization types
                //Callback address, supports multiple, local test cannot use localhost
                //authorization scope
                //Whether an authorization page is required, open and jump to the authorization page, manual confirmation is required

        return new InMemoryRegisteredClientRepository(registeredClient);

    //token encryption
    public JWKSource<SecurityContext> jwkSource() {
        KeyPair keyPair = generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAKey rsaKey = new RSAKey.Builder(publicKey)
        JWKSet jwkSet = new JWKSet(rsaKey);
        return new ImmutableJWKSet<>(jwkSet);

    private static KeyPair generateRsaKey() {
        KeyPair keyPair;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPair = keyPairGenerator.generateKeyPair();
        catch (Exception ex) {
            throw new IllegalStateException(ex);
        return keyPair;

    //Configure protocol endpoints, such as /oauth2/authorize, /oauth2/token, etc.
    public ProviderSettings providerSettings() {
        return ProviderSettings.builder().build();


The above is the configuration of the minimal authorization server. Here we store the authorization subject and the client in memory, and of course they can also be persisted in the database, using JdbcUserDetailsManager and JdbcRegisteredClientRepository respectively. ProviderSettings.builder().build() uses the default configuration, and we will use these addresses later:

public static Builder builder() {
    return new Builder()

❗ The official pointed out that @Import(OAuth2AuthorizationServerConfiguration.class) can also be used to minimize configuration, but I personally tested that this method is not very useful, and there are still problems.

configure client

Here we want to use our own to build an authorization server, we need to customize a client, or use the previous example of integrating GitHub, as long as we expand it in the configuration file. The complete configuration is as follows:

            client-id: gitee_clientId
            client-secret: gitee_secret
            authorization-grant-type: authorization_code
            redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
            client-name: Gitee
            client-id: github_clientId
            client-secret: github_secret
          # customize
            client-id: testClientId
            client-secret: testClientSecret
            authorization-grant-type: authorization_code
            redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
            client-name: Customize
              - userinfo
            authorization-uri: https://gitee.com/oauth/authorize
            token-uri: https://gitee.com/oauth/token
            user-info-uri: https://gitee.com/api/v5/user
            user-name-attribute: name
          # customize
            authorization-uri: http://localhost:9000/oauth2/authorize
            token-uri: http://localhost:9000/oauth2/token
            user-info-uri: http://localhost:9000/userinfo
            user-name-attribute: username

❗ When configuring the authorization server uri, do not still use, because it is a local test, the session of the authorization server and the session of the client will overwrite each other, resulting in inexplicable problems. Please distinguish between the callback address and the address of the authorization server endpoint uri.

client session

Authorization server session

to experience

In addition, in order to enable better debugging, you can add @EnableWebSecurity(debug = true) and log logs in the two applications. The logs are as follows, and open the TRACE level log:

    root: INFO
    org.springframework.web: INFO
    org.springframework.security: TRACE
    org.springframework.security.oauth2: TRACE

Now start two applications, visit, and automatically jump to the login page.

Click Customize, it will jump to the authorization server, pay attention to see that the address bar address is localhost:9000/login, enter the username/password to log in, user/user.

After logging in, it will jump to the authorization page. Since we have not customized it, we are using the default page. You can see that the address of the page is http://localhost:9000/oauth2/authorize?response_type=code&client_id=testClientId&scope=userinfo&state=yV1ElAN2855yq3bY5kgj_rmilnCclyvZHkxVB7a1d84%3D&redirect_uri=

We check userinfo and jump back to the client after submission. Let's look at the log received by the client, and the authorization server called back the callback address we filled in with the code. Request received for GET '/login/oauth2/code/customize?code=DPAlx5uyrUpfrZIlBKrpIy_mmcgiyC2qCxPFtUeLA0fBrZd238XM2vN8M1jv9XAgl0KA-D54P_KzVH7RbUw7ApBUc2pbnuSVRZUyHazozmNM4YgQ06CZryfr20qLRhW4&state=_Sgak7GLILLKbwr9JVuwA2xVp95CWPgUMByQcvePkgM%3D'


Request received for GET '/login/oauth2/code/customize?code=DPAlx5uyrUpfrZIlBKrpIy_mmcgiyC2qCxPFtUeLA0fBrZd238XM2vN8M1jv9XAgl0KA-D54P_KzVH7RbUw7ApBUc2pbnuSVRZUyHazozmNM4YgQ06CZryfr20qLRhW4&state=_Sgak7GLILLKbwr9JVuwA2xVp95CWPgUMByQcvePkgM%3D':


connection: keep-alive
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Safari/537.36
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
sec-fetch-site: cross-site
sec-fetch-mode: navigate
sec-fetch-user: ?1
sec-fetch-dest: document
sec-ch-ua: "Chromium";v="104", " Not A;Brand";v="99", "Google Chrome";v="104"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
cookie: JSESSIONID=2527F412F53FA27A30BFBC39161ABB63

Security filter chain: [



The configuration of Spring Security's minimal authorization server is over here. Although the amount of code in this demo is very small, it involves a lot of knowledge and has many pitfalls.

The code description in the Spring Security document is not updated in time. For example, the @Import(OAuth2AuthorizationServerConfiguration.class) document states that it is a minimal configuration, but the quick start of the document provides another minimal configuration method.

In addition, if an exception occurs on the authorization server, it will not print the stack, but put the error information into the response, which is intended to be displayed on the page. However, the default error page of the demo does not display the error details, only the error number 400 , as shown in the figure.

Spring Authorization Server still needs a lot of improvement, and Spring Security is no exception. Not long ago, I also submitted a PR to fix a bug that lasted for several versions Bugs in 😅), I have seen a lot of foreign products, but they are not much better than domestic open source projects, and there are many pitfalls. However, the open source projects of some of our big factories are actually very good, but they are sprayed by netizens.

Tags: Spring http github

Posted by Joshua F on Fri, 02 Dec 2022 16:08:58 +0530