spring-authorization-server系列--demo分析

1,012 阅读5分钟

 1、AuthorizationServerConfig分析

(1)HttpSecurity http分析

Oauth2.1的入口AuthorizationServerConfig的authorizationServerSecurityFilterChain(HttpSecurity http),打断点

可以看到 ,启动时候注入的变量HttpSecurity http,http中的configures注入11个,sharedObjects注入3个,有什么用户后面再说。

(2)OAuth2AuthorizationServerConfigurer分析

        继续往下走,到http.getConfigurer(OAuth2AuthorizationServerConfigurer.class),得到http中注入的OAuth2AuthorizationServerConfigurer,进入OAuth2AuthorizationServerConfigurer类可以看一看,这里面都是Oauth2.1支持用户自定义一些方法的入口。例如,

      客户存储的库:

/**
 * Sets the repository of registered clients.
 *
 * @param registeredClientRepository the repository of registered clients
 * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
 */
public OAuth2AuthorizationServerConfigurer registeredClientRepository(RegisteredClientRepository registeredClientRepository) {
   Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
   getBuilder().setSharedObject(RegisteredClientRepository.class, registeredClientRepository);
   return this;
}

        认证服务方法,就是token的增删改查的服务

/**
 * Sets the authorization service.
 *
 * @param authorizationService the authorization service
 * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
 */
public OAuth2AuthorizationServerConfigurer authorizationService(OAuth2AuthorizationService authorizationService) {
   Assert.notNull(authorizationService, "authorizationService cannot be null");
   getBuilder().setSharedObject(OAuth2AuthorizationService.class, authorizationService);
   return this;
}

       自定义一些认证端点

/**
 * Configures the OAuth 2.0 Token Endpoint.
 *
 * @param tokenEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2TokenEndpointConfigurer}
 * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
 */
public OAuth2AuthorizationServerConfigurer tokenEndpoint(Customizer<OAuth2TokenEndpointConfigurer> tokenEndpointCustomizer) {
   tokenEndpointCustomizer.customize(getConfigurer(OAuth2TokenEndpointConfigurer.class));
   return this;
}

       剩下还有很多入口等等。。。

继续往下走,先跳过资源服务oauth2ResourceServer,后面在细说。就是http.build(),意思就是开始初始化

(3)HttpSecurity build()分析

       进入抽象类AbstractSecurityBuilder的build()方法,并进入其实现类doBuild()方法

(4)init()分析

        这里看到,其实就是对每一个configurers的初始化。这个configurers其实就是我们前面谈到的HttpSecurity中的configures(会有所增加),这里我们主要说Oauth2.1相关的,我们直接看OAuth2AuthorizationServerConfigurer的初始化

        看第一行代码,我们进入OAuth2ConfigurerUtils的getAuthorizationServerSettings的方法看看里面的实现

AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);

 

        首先,从httpSecurity的sharedObjects中获取,如果获取到了就返回获取到的,如果没有获取到通过getBean从上下文中获取,并存入sharedObjects。

        从这里看出,初始化OAuth2AuthorizationServerConfigurer一般有两种方式,一种是通过OAuth2AuthorizationServerConfigurer开放的接口设置,一种是通过@Bean注入通过上下文初始化。

       例如我们AuthorizationServerConfig类中的RegisteredClientRepository是通过@Bean注入并通过上下文初始化OAuth2AuthorizationServerConfigurer

@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
        throws Exception {
    //针对 Spring Authorization Server 最佳实践配置
    OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
    http
            .getConfigurer(OAuth2AuthorizationServerConfigurer.class)
            .oidc(Customizer.withDefaults())
    ;  // Enable OpenID Connect 1.0

    http
            .exceptionHandling((exceptions) -> exceptions
                    .defaultAuthenticationEntryPointFor(
                            new LoginUrlAuthenticationEntryPoint("/login"),
                            new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
                    )
            )
            .oauth2ResourceServer((resourceServer) -> resourceServer
                    .jwt(Customizer.withDefaults()));

    return http.build();
}

/**
 * 注册客户信息
 * 注册client信息
 *
*/
@Bean
public RegisteredClientRepository registeredClientRepository() {
    RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
            .clientId("oidc-client")
            .clientSecret("{noop}secret")
            /**
            * client_secret_basic: 将 clientId 和 clientSecret 通过 ‘:’ 号拼接,( clientId 和 clientSecret 都在上面配置中,)并使用 Base64 进行编码得到一串字符,再在前面加个 注意有个 Basic   前缀(Basic后有一个空格)
            * client_secret_post :clientId 和 clientSecret 放到表单去发送请求
            */
            .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
            .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
            .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
            .redirectUri("http://www.baidu.com")
            .redirectUri("http://localhost:9001/login/oauth2/code/oidc-client")
            .redirectUri("http://localhost:9001/api/login/welcome")
            .postLogoutRedirectUri("http://127.0.0.1:8080/")
            .scope(OidcScopes.OPENID)
            .scope(OidcScopes.PROFILE)
            .scope("message.read")
            .scope("message.write")
            .scope("all")
            // 设置 Client 需要页面审核授权
            .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
            .build();

    return new InMemoryRegisteredClientRepository(oidcClient);
}

       如果想换成 OAuth2AuthorizationServerConfigurer开放的接口初始化,只需要设置下就可以了

@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
        throws Exception {
    //针对 Spring Authorization Server 最佳实践配置
    OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
    http
            .getConfigurer(OAuth2AuthorizationServerConfigurer.class)
            .registeredClientRepository(registeredClientRepository())
            .oidc(Customizer.withDefaults())
    ;  // Enable OpenID Connect 1.0

    http
            .exceptionHandling((exceptions) -> exceptions
                    .defaultAuthenticationEntryPointFor(
                            new LoginUrlAuthenticationEntryPoint("/login"),
                            new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
                    )
            )
            .oauth2ResourceServer((resourceServer) -> resourceServer
                    .jwt(Customizer.withDefaults()));

    return http.build();
}

/**
 * 注册客户信息
 * 注册client信息
 *
*/
public RegisteredClientRepository registeredClientRepository() {
    RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
            .clientId("oidc-client")
            .clientSecret("{noop}secret")
            /**
            * client_secret_basic: 将 clientId 和 clientSecret 通过 ‘:’ 号拼接,( clientId 和 clientSecret 都在上面配置中,)并使用 Base64 进行编码得到一串字符,再在前面加个 注意有个 Basic   前缀(Basic后有一个空格)
            * client_secret_post :clientId 和 clientSecret 放到表单去发送请求
            */
            .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
            .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
            .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
            .redirectUri("http://www.baidu.com")
            .redirectUri("http://localhost:9001/login/oauth2/code/oidc-client")
            .redirectUri("http://localhost:9001/api/login/welcome")
            .postLogoutRedirectUri("http://127.0.0.1:8080/")
            .scope(OidcScopes.OPENID)
            .scope(OidcScopes.PROFILE)
            .scope("message.read")
            .scope("message.write")
            .scope("all")
            // 设置 Client 需要页面审核授权
            .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
            .build();

    return new InMemoryRegisteredClientRepository(oidcClient);
}

        基本上,Oauth2.1初始化的方式都是这个样子。 

        继续往下走,走到下图位置

        查看configurers的初始化是通过createConfigurers()方法一个一个new 出来的,这些就是Oauth2.1默认的一些功能,例如OAuth2ClientAuthenticationConfigurer是client的一些默认的功能配置,OAuth2TokenEndpointConfigurer则是默认的几种模式的配置

        进入OAuth2ClientAuthenticationConfigurer的初始化,主要看List authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);

         主要组装Client认证鉴权的Providers

         同理,进入OAuth2TokenEndpointConfigurer,主要封装了默认几种模式的Providers

         并通过httpSecurity.authenticationProvider加入

(3)configure()分析

         同样的道理,configurers也是这样子去看和分析的,AbstractSecurityBuilder的build()方法,看其实现类doBuild()方法中的configure()

        这里先看OAuth2AuthorizationServerConfigurer的configure()初始化,核心就是加了一些fileter去做相应拦截    

   我们再看OAuth2ResourceServerConfigurer,这里很核心的就是加入了 BearerTokenAuthenticationFilter,这个filter就是校验header中的token的过滤器。

        我们再看OAuth2TokenEndpointConfigurer的configure(HttpSecurity httpSecurity),这里就是加入了默认的几种模式数据Converter

(5)总结

           通过HttpSecurity的build(),初始化convert、filter、provider,搭建起来框架的链路。

2、ResourceConfig分析

        该类东西不多,主要是定义需要鉴权或者放行的一些地址。

3、DaoAuthenticationProvider分析

        忽然蹦出DaoAuthenticationProvider,可能有点迷茫,具体是怎么用的呢,后面通过uthorization_code模式实现流程中慢慢串联,先介绍下里面的几个方法。

       additionalAuthenticationChecks方法,看源码,其实就是看下存储的密码和输入的密码是否一致,不一致就是密码出错。

        retrieveUser方法,看源码就是说通过userDetailsService获取用户,在这里就是ResourceConfig中注入的UserDetailsService