具有自定义身份验证提供程序的 OAUTH2 用户服务

Posted

技术标签:

【中文标题】具有自定义身份验证提供程序的 OAUTH2 用户服务【英文标题】:OAUTH2 user service with Custom Authentication Providers 【发布时间】:2022-01-08 16:17:38 【问题描述】:

我是 Spring Security 和 Oauth2 的新手。在我的 Spring Boot 应用程序中,我使用 Oauth2 实现了身份验证,并进行了以下更改:

自定义Ouath2用户服务如下:

  @Component
  public class CustomOAuth2UserService extends DefaultOAuth2UserService 

     private UserRepository userRepository;

     @Autowired
     public void setUserRepository(UserRepository userRepository) 
        this.userRepository = userRepository;
     

    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException 
        ...
    
 

安全配置如下:

@EnableWebSecurity
@Import(SecurityProblemSupport.class)
@ConditionalOnProperty(
        value = "myapp.authentication.type",
        havingValue = "oauth",
        matchIfMissing = true
)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter 
  private final CustomOAuth2UserService customOAuth2UserService;
    
  public SecurityConfiguration(CustomOAuth2UserService customOAuth2UserService) 
    this.customOAuth2UserService = customOAuth2UserService;
  

  @Override
  public void configure(WebSecurity web) 
    web.ignoring()
        .antMatchers(HttpMethod.OPTIONS, "/**")
        .antMatchers("/app/**/*.js,html")
        .antMatchers("/bundle.js")
        .antMatchers("/slds-icons/**")
        .antMatchers("/assets/**")
        .antMatchers("/i18n/**")
        .antMatchers("/content/**")
        .antMatchers("/swagger-ui/**")
        .antMatchers("/swagger-resources")
        .antMatchers("/v2/api-docs")
        .antMatchers("/api/redirectToHome")
        .antMatchers("/test/**");
  

  public void configure(HttpSecurity http) throws Exception 
    RequestMatcher csrfRequestMatcher = new RequestMatcher() 
      private RegexRequestMatcher requestMatcher =
          new RegexRequestMatcher("/api/", null);

      @Override
      public boolean matches(HttpServletRequest request) 
        return requestMatcher.matches(request);
      
    ;

    http.csrf()
        .requireCsrfProtectionMatcher(csrfRequestMatcher)
        .and()
        .authorizeRequests()
        .antMatchers("/login**").permitAll()
        .antMatchers("/manage/**").permitAll()
        .antMatchers("/api/auth-info").permitAll()
        .antMatchers("/api/**").authenticated()
        .antMatchers("/management/health").permitAll()
        .antMatchers("/management/info").permitAll()
        .antMatchers("/management/prometheus").permitAll()
        .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .anyRequest().authenticated()//.and().oauth2ResourceServer().jwt()
        .and()
        .oauth2Login()
        .redirectionEndpoint()
        .baseUri("/oauth2**")
        .and()
        .failureUrl("/api/redirectToHome")
        .userInfoEndpoint().userService(oauth2UserService())
    ;
    http.cors().disable();
  


  private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() 
    return customOAuth2UserService;
  

application.properties 内容如下:

spring.security.oauth2.client.registration.keycloak.client-id=abcd
spring.security.oauth2.client.registration.keycloak.client-name=Auth Server
spring.security.oauth2.client.registration.keycloak.scope=api
spring.security.oauth2.client.registration.keycloak.provider=keycloak
spring.security.oauth2.client.registration.keycloak.client-authentication-method=basic
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
myapp.oauth2.path=https://internal.authprovider.com/oauth2/
spring.security.oauth2.client.provider.keycloak.token-uri=$myapp.oauth2.pathtoken
spring.security.oauth2.client.provider.keycloak.authorization-uri=$myapp.oauth2.pathauthorize
spring.security.oauth2.client.provider.keycloak.user-info-uri=$myapp.oauth2.pathuserinfo
spring.security.oauth2.client.provider.keycloak.user-name-attribute=name
myapp.authentication.type=oauth

现在,使用现有的身份验证机制,我想添加对多个身份验证提供程序的支持:LDAP、Form-Login 等。

在这方面,我浏览了几篇文章:

    https://www.baeldung.com/spring-security-multiple-auth-providers Custom Authentication provider with Spring Security and Java Config

但是,对于我应该在现有代码库中进行哪些更改以实现此目标,我没有任何具体的想法。

有人可以帮忙吗?谢谢。

【问题讨论】:

【参考方案1】:

我从您的代码开始创建了一个简化的设置,支持 OAuth2 和基本身份验证。

/tenant2/** 将启动基本身份验证。 /**(其他所有内容)触发 OAuth2 授权码身份验证。

实现这一点的关键是每个身份验证类型都有一个@Configuration 类。

让我们从控制器开始:

Tenant1HomeController

@Controller
public class Tenant1HomeController 

    @GetMapping("/tenant1/home")
    public String home() 
        return "tenant1Home";
    


Tenant2HomeController

@Controller
public class Tenant2HomeController 

    @GetMapping("/tenant2/home")
    public String home() 
        return "tenant2Home";
    


现在,配置类:

Tenant1SecurityConfiguration

@Configuration
public class Tenant1SecurityConfiguration extends WebSecurityConfigurerAdapter 

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/login**").permitAll()
                .antMatchers("/manage/**").permitAll()
                .antMatchers("/api/auth-info").permitAll()
                .antMatchers("/api/**").authenticated()
                .antMatchers("/management/health").permitAll()
                .antMatchers("/management/info").permitAll()
                .antMatchers("/management/prometheus").permitAll()
                .antMatchers("/management/**").hasAuthority("ADMIN")
                .antMatchers("/tenant1/**").authenticated()
                .and()
                .oauth2Login()
                .and()
                .cors()
                .disable();
    

Tenant2SecurityConfiguration(注意@Order(90),这很重要

@Order(90)
@Configuration
public class Tenant2SecurityConfiguration extends WebSecurityConfigurerAdapter 

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.requestMatcher(new AntPathRequestMatcher("/tenant2/**"))
                .csrf()
                .disable()
                .authorizeRequests()
                .antMatchers("/tenant2/**").hasAuthority("BASIC_USER")
                .and()
                .httpBasic();
        http.cors().disable();
    

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        auth.inMemoryAuthentication()
                .withUser("user")
                .password("nooppassword")
                .roles("BASIC_USER");
    

终于配置好了:

spring:
  security:
    oauth2:
      client:
        registration:
          keycloak:
            client-id: myclient
            client-secret: c6dce03e-ea13-4b76-8aab-c876f5c2c1d9
        provider:
          keycloak:
            issuer-uri: http://localhost:8180/auth/realms/myrealm

有了这个,如果我们点击http://localhost:8080/tenant2/home,将会弹出基本的身份验证提示:

尝试使用http://localhost:8080/tenant1/home 会将您发送到 Keycloak 的登录表单:

更新:

使用上述配置配置多租户应用程序是完全可行的。

关键是每个身份验证提供程序都与一组不同的用户(租户)合作,例如:

租户 1(OAuth2 身份验证):

@Configuration
public class Tenant1SecurityConfiguration extends WebSecurityConfigurerAdapter 

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.
            ...
            .and()
            .oauth2Login()
            .and()
            ...
            

第一个用户子集由 OAuth2 提供者联合,在本例中为 Keycloak。

TENANT 2(基本/表单/xxx身份验证):

@Order(90)
@Configuration
public class Tenant2SecurityConfiguration extends WebSecurityConfigurerAdapter 

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        ...
    

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        auth.userDetailsService(XXX)

对于第二个租户,您可以使用指向不同用户存储库(LDAP、数据库...)的 userDetailsS​​ervice。

【讨论】:

感谢您的详细回答。我实际上正在努力使我的应用程序成为多租户,其中每个租户可能有自己的身份验证提供程序,比如租户 A 使用 Oauth2,帐篷 B 使用表单登录等。我的问题是:我应该如何通过你的回答来实现这一点? 我已经用更多信息更新了答案 感谢您的回答。实际上,我无法理解以下内容:假设一个用户 A 登录应用程序,这里将如何确定关联用户的租户 ID,然后如何根据租户 ID 调用适当的身份验证提供程序? 一种方法是为每个租户使用不同的 URL(我已经用一个例子更新了答案)。 谢谢。您能否也展示一下这种方法,其中应用程序只有一个入口点并登录到应用程序后,根据用户的身份验证令牌,确定租户并调用相关的身份验证提供程序?

以上是关于具有自定义身份验证提供程序的 OAUTH2 用户服务的主要内容,如果未能解决你的问题,请参考以下文章

现有表单登录应用程序中具有 OAuth2 的自定义主体

具有自定义后端的 Next-auth 自定义身份验证提供程序

具有基本身份验证和自定义 UserDetailsS​​ervice 的 Spring Boot OAuth2

WSO2 APIM 的自定义身份验证

jhipster spring boot 自定义身份验证提供程序

具有 Spring Security 和 Java Config 的自定义身份验证提供程序