• 【OAuth2】十一、认识SpringAuthorization Server


    一、Spring Authorization Server

    Spring Authorization Server 起初是一个社区驱动的项目,在 Spring 的实验项目中启动,由 Spring Security 团队领导,其目的主要是为 Spring 社区提供 OAuth 2.0 授权服务器支持,并最终取代 Spring Security OAuth 。该项目使用 ZenHub 来确定功能路线图的优先次序,并帮助组织项目计划。

    自 2020 年 4 月发布 Spring Authorization Server 以来,其已实现的功能已经足够为大部分 OAuth 2.1 授权框架提供支持。

    Spring Authorization Server 是一个框架,提供了OAuth 2.1和OpenID Connect 1.0规范以及其他相关规范的实现。它建立在Spring Security之上,为构建 OpenID Connect 1.0 Identity Providers 和 OAuth2 Authorization Server 产品提供安全、轻量级和可定制的基础。
    此外,在已经发布到 Spring Authorization Server 0.3.1 版本,这也是由 Spring 的新政策支持的正式生产版本。
    官网地址

    1、 功能列表在这里插入图片描述

    2、 依赖坐标

    -我现在练习的是一下版本,最新的是0.3.1

        <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-oauth2-authorization-server</artifactId>
                <version>0.2.2</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3、环境要求

    虽然目前官方并没有明确说明,但是从源代码很容易分析出来Spring Authorization Server的环境要求。

    • Java 8及以上。
    • Spring Boot 2.5.9及以上。
    • Spring Security 5.5.4及以上。
    • Servlet Web环境,将来不排除对Reactive Web的支持。

    二、Spring Authorization Server初体验

    分支: springauthserver

    1、环境依赖:

    像OAuth2 Client、Resource Server一样,Spring Authorization Server也是以插件的形式接入Spring Security的体系中。下面列举了目前必备的环境依赖:

        <dependencies>
            <!--  actuator 指标监控  非必须 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <!--  spring security starter 必须  -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-oauth2-authorization-server</artifactId>
                <version>0.2.2</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-oauth2-resource-server</artifactId>
            </dependency>
            <!--      orm  -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            <dependency>
                <groupId>com.h2database</groupId>
                <artifactId>h2</artifactId>
            </dependency>
    
            <!-- spring mvc  servlet web  必须  -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!--   lombok 插件 非必须       -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <!-- 测试   -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.mockito</groupId>
                <artifactId>mockito-core</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    2、授权服务器过滤器链

    OAuth2授权服务器专门处理OAuth2客户端的授权请求流程,授权端点、Token端点、用户信息端点等等都需要对应的过滤器支持,这些过滤器由Spring Authorization Server中的OAuth2AuthorizationServerConfigurer负责初始化和配置。我们只需要定义一个优先级最高的过滤器链,把授权服务器配置类初始化并激活即可。

     @Bean
        @Order(Ordered.HIGHEST_PRECEDENCE)
        public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
            OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =
                    new OAuth2AuthorizationServerConfigurer<>();
            // TODO 你可以根据需求对authorizationServerConfigurer进行一些个性化配置
            RequestMatcher authorizationServerEndpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
    
            // ①
            http.requestMatcher(authorizationServerEndpointsMatcher)
                    .authorizeRequests().anyRequest().authenticated()
                    .and()
                    // ②忽略掉相关端点的csrf
                    .csrf(csrf -> csrf
                            .ignoringRequestMatchers(authorizationServerEndpointsMatcher))
                    // 开启form登录
                    .formLogin()
                    .and()
                    // ③应用 授权服务器的配置
                    .apply(authorizationServerConfigurer);
            return http.build();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    上面是一个基本的配置,关键的步骤为:

    • 配置拦截授权服务器相关的请求端点。
    • 由于是接口调用,同时关闭相关端点的CSRF功能。
    • 将配置类加入HttpSecurity激活配置。

    3、客户端的注册和持久化管理

    按照OAuth2协议,所有的OAuth2客户端都应该在授权服务器中进行信息注册。你去申请接入第三方开放平台,都要提交一些信息,第三方平台审核通过后会把一些OAuth2客户端信息发给你,这些信息你不会陌生,大部分都包含在OAuth2客户端类库的OAuth2ClientProperties.Registration中,对应Spring Authorization Server授权服务器的实体为RegisteredClient
    在这里插入图片描述

    这些属性多数在前面的章节中已经介绍了,redirect_uri变成了复数以适应多个OAuth2客户端,另外redirect_uri还有一些隐含规则和操作
    ,相关源码:
    在这里插入图片描述
    这里简单总结一个要点:

    • redirect_uri不能有锚点(fragment),比如微信DEMO中携带了锚点#wechat_redirect,这种事实上是不符合OAuth2规范的。
    • redirect_uri的host不能为null或者localhost,这一点非常重要。
    • 如果redirect_uri的host不是环回地址,必须注册到授权服务器,精确匹配到URI字符串。
    • 如果redirect_uri的host是环回地址,可以在调用时切换端口port。
    • 如果OAuth2授权服务器是Spring Authorization Server,目前必须严格按照这个规则配置redirect_uri。

    4、 ClientSettings

    该OAuth2客户端的一些规则配置,包括:

    • REQUIRE_PROOF_KEY 授权码授权流程中是否需要对密钥进行质询和验证,默认false。当为true时,开启授权码PKCE支持 RFC7636。
    • REQUIRE_AUTHORIZATION_CONSENT 客户端请求授权时是否添加同意授权选项。
    • JWK_SET_URL 这个参见Spring Security中的JOSE类库中相关的描述。
    • TOKEN_ENDPOINT_AUTHENTICATION_SIGNING_ALGORITHM 为private_key_jwt和client_secret_jwt声明JWS签名算法。只能用于令牌端点对客户端进行身份验证环节。
    private_key_jwt和client_secret_jwt参见ClientAuthenticationMethod。
    
    • 1

    5、TokenSettings

    注册OAuth2客户端时对该客户端令牌的通用规则配置,包含了:

    • ACCESS_TOKEN_TIME_TO_LIVE 访问令牌生存时间,默认5分钟。
    • REUSE_REFRESH_TOKENS 是否可以复用刷新令牌,默认true。
    • ID_TOKEN_SIGNATURE_ALGORITHM OIDC ID Token使用的签名算法,默认RS256
    你可以通过TokenSettings.withSettings添加额外的自定义属性或者覆盖已有的属性。
    
    • 1

    我们来初始化一个OAuth2客户端,这里我们使用的客户端授权方法ClientAuthenticationMethod是client_secret_basic,因为之前对应的basic已经不建议使用了:

      private RegisteredClient createRegisteredClient(final String id) {
            return RegisteredClient.withId(UUID.randomUUID().toString())
    //               客户端ID和密码
                    .clientId("felord")
    //               此处为了避免频繁启动重复写入仓库
                    .id(id)
    //                client_secret_basic    客户端需要存明文   服务器存密文
                    .clientSecret(PasswordEncoderFactories.createDelegatingPasswordEncoder()
                            .encode("secret"))
    //                名称 可不定义
                    .clientName("felord")
    //                授权方法
                    .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
    //                授权类型
                    .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                    .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                    .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
    //                回调地址名单,不在此列将被拒绝 而且只能使用IP或者域名  不能使用 localhost
                    .redirectUri("http://127.0.0.1:8082/login/oauth2/code/test-client-oidc")
                    .redirectUri("http://127.0.0.1:8082/authorized")
                    .redirectUri("http://127.0.0.1:8082/login/oauth2/code/test")
                    .redirectUri("http://127.0.0.1:8082/test/bar")
                    .redirectUri("https://baidu.com")
    //                OIDC支持
                    .scope(OidcScopes.OPENID)
    //                其它Scope
                    .scope("message.read")
                    .scope("userinfo")
                    .scope("message.write")
    //                JWT的配置项 包括TTL  是否复用refreshToken等等
                    .tokenSettings(TokenSettings.builder().build())
    //                配置客户端相关的配置项,包括验证密钥或者 是否需要授权页面
                    .clientSettings(ClientSettings.builder()
                            .requireAuthorizationConsent(true).build())
                    .build();
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    上面注册的OAuth2客户端信息需要持久化到数据库,RegisteredClientRepository接口抽象了对RegisteredClient的持久化操作,这里我们直接启用内置的JDBC实现以代替默认的内存实现:

       @SneakyThrows
        @Bean
        public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
            //         每次都会初始化  生产的话 只初始化JdbcRegisteredClientRepository
            JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
            // TODO 生产上 注册客户端需要使用接口 不应该采用下面的方式
            // only@test begin
            final String id = "10000";
            RegisteredClient registeredClient = registeredClientRepository.findById(id);
            if (registeredClient == null) {
                registeredClient = this.createRegisteredClient(id);
                //这里为了测试,我们在初始化JdbcRegisteredClientRepository的时候保存了一个OAuth2客户端信息。
                registeredClientRepository.save(registeredClient);
            }
            // only@test end
            return registeredClientRepository;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    6、授权状态信息持久化

    资源拥有者的OAuth2授权状态信息OAuth2Authorization也需要持久化管理,Spring Authorization Server提供了OAuth2AuthorizationService来负责这个工作,我们同样需要启用内置的JDBC实现以代替默认的内存实现:

        @Bean
        public OAuth2AuthorizationService authorizationService(
                JdbcTemplate jdbcTemplate, 
                RegisteredClientRepository registeredClientRepository) {
            return new JdbcOAuth2AuthorizationService(jdbcTemplate,
                    registeredClientRepository);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    授权确认状态持久化
    如果该客户端配置ClientSettings开启了授权确认REQUIRE_AUTHORIZATION_CONSENT ,授权确认的信息也要持久化管理,需要启用内置的JDBC实现以代替默认的内存实现:

        @Bean
        public OAuth2AuthorizationConsentService authorizationConsentService(
                JdbcTemplate jdbcTemplate,
                RegisteredClientRepository registeredClientRepository) {
            return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, 
                    registeredClientRepository);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    OAuth2客户端注册到授权服务器的注册信息中配置了授权确认功能才有用。
    
    • 1

    7、JWK源配置

    授权服务器公私钥都需要,参考Spring Security中的JOSE类库中的方法,结合Spring Authorization Server提供的方案,我们只需要定义一个JWKSource类型的Spring Bean即可:

    /**
         * 加载JWK资源
         *
         * @return the jwk source
         */
        @SneakyThrows
        @Bean
        public JWKSource<SecurityContext> jwkSource() {
            //TODO 这里优化到配置
            // jks classpath路径
            String path = "jose.jks";
            // key alias
            String alias = "jose";
            // password
            String pass = "test.cn";
    
            ClassPathResource resource = new ClassPathResource(path);
            KeyStore jks = KeyStore.getInstance("jks");
            char[] pin = pass.toCharArray();
            jks.load(resource.getInputStream(), pin);
            RSAKey rsaKey = RSAKey.load(jks, alias, pin);
    
            JWKSet jwkSet = new JWKSet(rsaKey);
            return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    到这里就配置完了。启动项目,访问下面的issue端点:

    http://localhost:9000/.well-known/oauth-authorization-server
    
    • 1

    将返回授权服务器的元信息:

    {
        "issuer": "http://localhost:9000",
        "authorization_endpoint": "http://localhost:9000/oauth2/authorize",
        "token_endpoint": "http://localhost:9000/oauth2/token",
        "token_endpoint_auth_methods_supported": [
            "client_secret_basic",
            "client_secret_post",
            "client_secret_jwt",
            "private_key_jwt"
        ],
        "jwks_uri": "http://localhost:9000/oauth2/jwks",
        "response_types_supported": [
            "code"
        ],
        "grant_types_supported": [
            "authorization_code",
            "client_credentials",
            "refresh_token"
        ],
        "revocation_endpoint": "http://localhost:9000/oauth2/revoke",
        "revocation_endpoint_auth_methods_supported": [
            "client_secret_basic",
            "client_secret_post",
            "client_secret_jwt",
            "private_key_jwt"
        ],
        "introspection_endpoint": "http://localhost:9000/oauth2/introspect",
        "introspection_endpoint_auth_methods_supported": [
            "client_secret_basic",
            "client_secret_post",
            "client_secret_jwt",
            "private_key_jwt"
        ],
        "code_challenge_methods_supported": [
            "plain",
            "S256"
        ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    这些配置是提供给OAuth2客户端的,里面也有不少的端点,比如jwks_uri你可以访问一下,看看能否获取公钥JWK。

    注册一个用户

    OAuth2客户端请求授权跳转到授权服务器,需要一个授权服务器用户登录认证并同意授权。我们在Spring Authorization Server授权服务器中临时指定一个测试用户test,密码为123456:

    @Bean
        UserDetailsService users() {
            UserDetails user = User.builder()
                    .username("test")
                    .password("123456")
                    .passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()::encode)
                    .roles("USER")
                    .build();
            return new InMemoryUserDetailsManager(user);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这个用户就是OAuth2中的资源拥有者(Resource Owner)。

    附数据库DDL脚本

    Spring Authorization Server的类库内置了数据库DDL脚本,在org/springframework/security/oauth2/server/authorization下,分别是

    • oauth2-authorization-schema.sql
    • oauth2-authorization-consent-schema.sql
    • oauth2-registered-client-schema.sql
      你可以手动或者借助于spring.sql.init系列命令进行初始化。

    三、Spring Authorization Server客户端

    Spring Authorization Server的服务器已经在上面中搭建好了,并注册了一个OAuth2客户端,本篇将利用这个注册的客户端实现HttpSecurity.oauth2Client功能。

    1、OAuth2客户端配置

    1.1、配置文件

    先配置OAuth2客户端的配置文件,这里要对照着Spring Authorization Server中注册的那个OAuth2客户端。这里抄过来对照:

    private RegisteredClient createRegisteredClient(final String id) {
            return RegisteredClient.withId(UUID.randomUUID().toString())
    //               客户端ID
                    .clientId("felord")
    //               此处为了避免频繁启动重复写入仓库
                    .id(id)
    //                client_secret_basic 模式下的密码  在客户端需要存明文 在授权服务器存密文
                    .clientSecret(PasswordEncoderFactories.createDelegatingPasswordEncoder()
                                  .encode("secret"))
    //                名称可不定义
                    .clientName("felord")
    //                授权方法
                    .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
    //                支持的授权类型
                    .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                    .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                    .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
    //                回调地址名单,不在此列将被拒绝 而且只能使用IP或者域名  不能使用 localhost
                    .redirectUri("http://127.0.0.1:8082/login/oauth2/code/test-client-oidc")
                    .redirectUri("http://127.0.0.1:8082/authorized")
                    .redirectUri("http://127.0.0.1:8082/login/oauth2/code/felord")
                    .redirectUri("http://127.0.0.1:8082/test/bar")
                    .redirectUri("https://baidu.com")
    //                OIDC支持
                    .scope(OidcScopes.OPENID)
    //                其它Scope
                    .scope("message.read")
                    .scope("userinfo")
                    .scope("message.write")
    //                JWT的配置项 包括TTL  是否复用refreshToken等等
                    .tokenSettings(TokenSettings.builder().build())
    //                配置客户端相关的配置项,包括验证密钥或者 是否需要授权页面
                    .clientSettings(ClientSettings.builder()
                                    .requireAuthorizationConsent(true).build())
                    .build();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    1.2、对应的yaml配置:

    spring:
      security:
        oauth2:
          client:
            registration:
              test:
                client-id: felord
                client-secret: secret
                redirect-uri:  'http://127.0.0.1:8082/test/bar'
                authorization-grant-type: authorization_code
                client-authentication-method: client_secret_basic
                scope: message.read,message.write
            provider:
              test:
                #todo       provider 尽量用域名  不要用localhost或者IP  而且要和well-known接口中保持一致
                issuer-uri: http://localhost:9000
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    /.well-known/oauth-authorization-server端点能替代很多provider的配置项,如果同时存在issuer-uri和其它端点,issuer-uri的优先级要低一些,相关的解析逻辑请参考ClientRegistrations类。
    
    • 1

    1.3、 redirect-uri

    /test/bar是一个测试接口:

        /**
         * 测试Spring Authorization Server
         *
         * @see HttpSecurity#oauth2Client()
         * @param client the client
         * @return the map
         */
        @GetMapping("/test/bar")
        public Map<String,Object> bar(@RegisteredOAuth2AuthorizedClient("test") OAuth2AuthorizedClient client){
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            Map<String, Object> map = new HashMap<>();
            map.put("authentication",authentication);
            // OAuth2AuthorizedClient 为敏感信息不应该返回前端
            map.put("oAuth2AuthorizedClient",client);
            return map;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    根据OAuth2ClientConfigurer一文中的讲解,/test/bar需要配置匿名访问,伪代码:

    
            http.authorizeRequests((requests) -> requests
                            .antMatchers("/test/bar")
                            .hasAnyAuthority("ROLE_ANONYMOUS","SCOPE_userinfo")
                            .anyRequest().authenticated())
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其它的复用前面几个客户端HttpSecurity.oauth2Client()的配置即可。

    1.4、测试

    这里要先启动OAuth2授权服务器,后启动OAuth2客户端,因为客户端需要调用issuer-uri初始化OAuth2授权服务器的信息。

    1. 浏览器打开http://127.0.0.1:8082/test/bar,注意不能用localhost。
    2. 浏览器会跳转授权服务器的登录页面http://localhost:9000/login,依次输入用户名test和密码123456
    3. 然后会重定向到http://localhost:9000/oauth2/authorize,这里增加了一个让用户二次确认的选项,这个功能是我们在授权服务器中特地开启的,你可以勾选同意授权或者直接拒绝授权
    4. 当勾选scope同意授权后,又重定向到/test/bar完成请求拿到了JSON信息,可以看出来确实是一个匿名用户。

    在这里插入图片描述

    三、授权服务器处理客户端授权请求流程

    客户端通过/oauth2/authorize向授权服务器发起了授权请求,这期间发生了什么?通过日志我们来看一个究竟。这里分为两个阶段:

    1、用户登录之前

    下面是授权服务器收到授权请求处理并跳转到登录/login前的日志:
    在这里插入图片描述
    日志中标记了四个关键点:

    在这里插入图片描述

    1. 授权服务器接收到了授权请求并经过过滤器链处理。
    2. OAuth2AuthorizationEndpointFilter拦截到了授权请求,然后由授权服务器接收到了授权请求并经过过滤器链处理。
    3. HttpSession中对授权请求进行了缓存,然后发现本次授权请求是匿名访问。
    4. 匿名访问被投票拒绝,跳转到登录页。

    2、用户登录之后

    跳转到登录页后,用户输入用户名和密码登录成功后,会跳转到授权确认页。这里从日志发现重复的还是上面步骤②,说明授权确认页的逻辑还是在OAuth2AuthorizationEndpointFilter中。

    3、授权确认后

    经过点选确认授权后授权服务器执行了下列逻辑:

    • POST 请求/oauth2/authorize,再次被OAuth2AuthorizationEndpointFilter拦截处理,

    • 由于用户是认证的,通知OAuth2客户端重定向到最开始的redirect_uri地址http://127.0.0.1:8082/test/bar?code=CODE&state=STATE

    OAuth2客户端OAuth2AuthorizationCodeGrantFilter拦截到携带了code和state的redirect_uri后向授权服务器发起/oauth2/token请求获取token,具体的流程参考前面的相关文章。

    这里需要提及一个重要的知识点,这里由于我们采用的客户端认证方式(ClientAuthenticationMethod)是client_secret_basic,所以获取token的请求是这样的:

    POST /oauth2/token HTTP/1.1
    Accept: application/json;charset=UTF-8
    Content-Type: application/x-www-form-urlencoded;charset=UTF-8
    Authorization: Basic ZmVsb3JkOnNlY3JldA==
    Host: localhost:9000
    
    • 1
    • 2
    • 3
    • 4
    • 5

    该请求通过HTTP BASIC的方式来认证客户端。 它使用客户端的client-id和client-secret作为凭证信息,并且使用 BASE64算法进行编码。 而且授权服务器保存得客户端密码是经过摘要的密文。这和前面gitee和wechat采用的机制完全不同,更加安全。那授权服务器如何处理/oauth2/token呢?

    其它的ClientAuthenticationMethod方式后面后面也会做一些专门的测试样例。
    
    • 1

    4、授权服务器发放Token

    经过日志分析发现/oauth2/token是被授权服务器的OAuth2TokenEndpointFilter拦截处理的。具体交给了OAuth2AuthorizationCodeAuthenticationProvider来处理,最终返回包含Access TokenOAuth2AccessTokenAuthenticationToken

    上面对Spring Authorization Server的案例流程进行了日志分析,发现了几个关键的过滤器。可以预见到Spring Authorization Server的配置就是围绕这几个过滤器展开的。

  • 相关阅读:
    身份证OCR识别:黑科技助力证件信息快速提取
    爬虫学习日记第七篇(爬取github搜索仓库接口,其实不算爬虫)
    Charles乱码和SSL 代理问题解决
    The normalized eigenfunction may not be uniformly bounded
    虹科分享 | 简单实用的CANopen介绍,看完你就明白了(1)
    红队专题-从零开始VC++C/S远程控制软件RAT-MFC-远程控制软件总结
    MySQL主从搭建--保姆级教学
    Golang 实现接口和继承
    网络安全(黑客)自学
    Android随笔-线程池
  • 原文地址:https://blog.csdn.net/weixin_43333483/article/details/126086634