spring-boot - Spring Boot + oauth +oidc 添加自定义AuthenticationProvider

标签 spring-boot oauth-2.0 okta openid-connect

我开发了一个基本的 oauth/oidc 示例,使用 SpringBoot 2.1.7 和 Okta 提供身份验证服务。这是我的 Gradle 依赖项设置以供引用:

plugins {
id 'org.springframework.boot' version '2.1.7.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'

sourceCompatibility = '1.8'

configurations {
  developmentOnly
  runtimeClasspath {
    extendsFrom developmentOnly
 }
}

repositories {
    mavenCentral()
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.okta.spring:okta-spring-boot-starter:1.2.1'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
}

Okta 端的所有元素都已正确配置,并且示例按预期工作。这里几乎是一个“hello world”类型的演示。我想添加一个自定义身份验证提供程序,它在 Spring 提供的所有其他 AuthenticationProvider 之后执行。我已经使用调试器逐步完成了代码,并注意到 Spring 自动配置了几个 AuthenticationProviders。他们是:
  • AnonymousAuthenticationProvider
  • OAuth2LoginAuthenticationProvider
  • OidcAuthorizationCodeAuthenticationProvider
  • OAuth2AuthorizationCodeAuthenticationProvider
  • JwtAuthenticationProvider

  • 我想在第 6 个位置运行我的身份验证提供程序。即使 configure(AuthenticationManagerBuilder authBuilder) 方法触发,我尝试了以下 WebSecurityConfig 也不起作用:
    @Configuration
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter
    {
    
        @Autowired
        private CustomAuthenticationProvider customAuthenticationProvider;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception
        {
        http.authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .oauth2Login()
            .successHandler(customOauthLoginSuccessHandler())
            .failureHandler(customOauthLoginFailureHandler())
            .and()
            .oauth2Client();
        http.csrf().disable();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.authenticationProvider(customAuthenticationProvider);
        }
    
        @Bean
        public CustomOauthLoginSuccessHandler customOauthLoginSuccessHandler()
        {
            CustomOauthLoginSuccessHandler handler = new CustomOauthLoginSuccessHandler();
            return handler;
        }
    
        @Bean
        public CustomOauthLoginFailureHandler customOauthLoginFailureHandler()
        {
            CustomOauthLoginFailureHandler handler = new CustomOauthLoginFailureHandler();
            handler.setUseForward(true);
            handler.setDefaultFailureUrl("/oautherror");
            return handler;
        }
    
    }
    

    我的身份验证提供程序永远不会执行。这是我的自定义 AP:
    @Component
    public class CustomAuthenticationProvider implements AuthenticationProvider
    {
        private Logger logger = LoggerFactory.getLogger(CustomAuthenticationProvider.class);
        private CustomOidcUserService customOidcUserService;
    
        public CustomAuthenticationProvider(CustomOidcUserService customOidcUserService)
        {
            this.customOidcUserService = customOidcUserService;
        }
    
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException
        {
            logger.info("CustomAuthenticationProvider executing...");
            Object user = authentication.getPrincipal();
            if (user instanceof DefaultOidcUser)
            {
                logger.info("principal is instanceof DefaultOidcUser");
                DefaultOidcUser authToken = (DefaultOidcUser) user;
                this.customOidcUserService.loadUserByUsername(authToken.getClaims().get("preferred_username").toString());
                // add additional info to the Authentication object
            }
    
            return authentication;
        }
    
        @Override
        public boolean supports(Class<?> authentication)
        {
            return OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication);
        }
    }
    

    我的主要目标是添加有关来自我们旧版 Oracle 数据库的经过身份验证的用户的其他信息。将此附加信息添加到 Okta 端(在云中)不是一个选项。

    请注意,我能够轻松地将成功和失败处理程序添加到身份验证路径中。我觉得 SpringBoot 的自动配置的固有性质可能会妨碍我。只需要知道如何解决这个问题。

    附加信息,yml 文件:
    okta:
      oauth2:
        issuer: https://myhost.okta.com/oauth2/default
        client-id: 123456AAABBBCCC
        client-secret: AAAAAAAAABBBBBBBBBBB
    

    最佳答案

    我正在回答我自己的帖子,因为我已经确定这是错误的方法。在运行了几次测试之后,我能够添加一个自定义的 AuthenticationProvider (AP),但是如果前一个提供者成功并返回结果,则不能保证我的提供者将永远运行。这是 ProviderManager 类在遍历每个 AP 时的默认行为。更好的方法是定义一个 CustomUserDetailsS​​ervice 来扩展 OidcUserService 或 DefaultOAuth2UserService。这是我更新的代码,可以回答我的问题:

    // @Component is removed
    public class CustomAuthenticationProvider implements AuthenticationProvider
    {
        private Logger logger = LoggerFactory.getLogger(CustomAuthenticationProvider.class);
        private CustomOidcUserService customOidcUserService;
    
        public CustomAuthenticationProvider(CustomOidcUserService customOidcUserService)
        {
            this.customOidcUserService = customOidcUserService;
        }
    
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException
    {
        logger.info("CustomAuthenticationProvider executing...");
        Object user = authentication.getPrincipal();
        if (user instanceof DefaultOidcUser)
        {
            logger.info("principal is instanceof DefaultOidcUser");
            DefaultOidcUser authToken = (DefaultOidcUser) user;
            this.customOidcUserService.loadUserByUsername(authToken.getClaims().get("preferred_username").toString());
            // add additional info to the Authentication object
        }
    
        return authentication;
    }
    
    @Override
    public boolean supports(Class<?> authentication)
    {
        return OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication);
    }
    }
    

    以下是我对 WebSecurityConfig 的更改:
        @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        http.authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .oauth2Login()
            .successHandler(customOauthLoginSuccessHandler())
            .failureHandler(customOauthLoginFailureHandler())
            .and()
            .oauth2Client()
            .and().authenticationProvider(new CustomAuthenticationProvider());
    
        http.csrf().disable();
    }
    

    此代码确实将额外的自定义 AP 添加到正确的内部存储的 AP 列表中。在这种情况下,我的自定义 AP 被添加到由 WebSecurityConfigurerAdapter$DefaultPasswordEncoderAuthenticationManagerBuilder 维护的列表中。
    同样,我没有使用这种方法,所以这篇文章被关闭了。

    关于spring-boot - Spring Boot + oauth +oidc 添加自定义AuthenticationProvider,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57633471/

    相关文章:

    php - 动态 Laravel 社会名流配置

    android - GoogleAuthUtil : get access token that will provide an email address?

    spring-security - 将 SAML 断言响应/安全上下文传播到下游服务/应用程序

    Okta IDP 发起的 RelayState

    java - 如何在application.yml中定义bean?

    java - 如何在运行时在 Spring Boot 中重新加载嵌入式 Tomcat?

    php - Yii2 类 yii\authclient\clients\GoogleOAuth 不存在

    amazon-web-services - 如何在 AWS Cognito 用户池中将 Okta 设置为 SAML IDP?

    spring-boot - spring-boot-starter-data-redis无法正常工作

    java - Spring Security OAuth2 - @EnableOauth2Sso 但也接受 token 作为身份验证