Spring为什么不使用我的PrincipalExtractor bean?

时间:2019-04-15 15:48:21

标签: java spring spring-boot spring-security spring-security-oauth2

Spring不想使用我的PrincipalExtractor bean。相反,它使用默认的FixedPrincipalExtractor

我试图按照Spring的OAuth2教程进行操作: https://spring.io/guides/tutorials/spring-boot-oauth2/

直到我决定将经过身份验证的用户保存到我的数据库之前,一切都差不多了。该教程只是说:“这太简单了,所以我们将不展示如何做到这一点”。当然,那是我被困几天的时刻。

WebSecurityConfig类。这是一团糟,但用于教育目的。

@Configuration
@EnableWebSecurity
@EnableOAuth2Client
@RestController
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    OAuth2ClientContext oauth2ClientContext;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .antMatcher("/**")
                .authorizeRequests()
                .antMatchers("/", "/login**", "/js/**", "/error**", "/webjars/**").permitAll()
                .anyRequest().authenticated()
                .and().logout().logoutSuccessUrl("/").permitAll()
                .and()
                .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .and()
                .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);

    }

    private Filter ssoFilter() {
        CompositeFilter filter = new CompositeFilter();
        List<Filter> filters = new ArrayList<>();
        filters.add(ssoFilter(google(), "/login/google"));
        filter.setFilters(filters);

        return filter;
    }

    private Filter ssoFilter(ClientResources client, String path) {
        OAuth2ClientAuthenticationProcessingFilter oAuth2ClientAuthenticationFilter = new OAuth2ClientAuthenticationProcessingFilter(path);
        OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
        oAuth2ClientAuthenticationFilter.setRestTemplate(oAuth2RestTemplate);
        UserInfoTokenServices tokenServices = new UserInfoTokenServices(client.getResource().getUserInfoUri(),
                client.getClient().getClientId());
        tokenServices.setRestTemplate(oAuth2RestTemplate);
        oAuth2ClientAuthenticationFilter.setTokenServices(tokenServices);

        return oAuth2ClientAuthenticationFilter;
    }

    @Bean
    @ConfigurationProperties("google")
    public ClientResources google() {
        return new ClientResources();
    }

    @Bean
    public FilterRegistrationBean<OAuth2ClientContextFilter> oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
        FilterRegistrationBean<OAuth2ClientContextFilter> registration = new FilterRegistrationBean<OAuth2ClientContextFilter>();
        registration.setFilter(filter);
        registration.setOrder(-100);

        return registration;
    }

    @Bean
    public PrincipalExtractor principalExtractor(UserDetailsRepo userDetailsRepo) {
        return map -> {
            String id = (String) map.get("sub");
            User user = userDetailsRepo.findById(id).orElseGet(() -> {
                User newUser = new User();

                newUser.setId(id);
                newUser.setEmail((String) map.get("email"));
                // and so on...

                return newUser;
            });

            return userDetailsRepo.save(user);
        };
    }
}

class ClientResources {

    @NestedConfigurationProperty
    private AuthorizationCodeResourceDetails client = new AuthorizationCodeResourceDetails();

    @NestedConfigurationProperty
    private ResourceServerProperties resource = new ResourceServerProperties();

    public AuthorizationCodeResourceDetails getClient() {
        return client;
    }

    public ResourceServerProperties getResource() {
        return resource;
    }
}

还有application.yml

spring:
  datasource:
    url: jdbc:postgresql://localhost/my_db
    username: postgres
    password: password
  jpa:
    generate-ddl: true
    properties:
      hibernate:
        jdbc:
          lob:
            non_contextual_creation: true

google:
  client:
    clientId: 437986124027-7072jmbsba04d11fft0h9megkqcpem2t.apps.googleusercontent.com
    clientSecret: ${clientSecret}
    accessTokenUri: https://www.googleapis.com/oauth2/v4/token
    userAuthorizationUri: https://accounts.google.com/o/oauth2/v2/auth
    clientAuthenticationScheme: form
    scope: openid,email,profile
  resource:
    userInfoUri: https://www.googleapis.com/oauth2/v3/userinfo
    preferTokenInfo: true

如上所述,Spring并不想使用我的PrincipalExtractor bean,而是使用默认的FixedPrincipalExtractor。我花了很多时间尝试解决此问题,但没有任何帮助。除了像这样更改application.yml

security:
  oauth2:
    client:
      clientId: 620652621050-v6a9uqrjq0ejspm5oqbek48sl6od55gt.apps.googleusercontent.com
      clientSecret: ${clientSecret}
  [...]
    resource:
      userInfoUri: https://www.googleapis.com/oauth2/v3/userinfo
      preferTokenInfo: true

google.client.clientId,您将看到它变为security.oauth2.client.clientId

如果您删除所有过滤器方法以及与它们相关的所有内容,那么它将起作用,是的。它确实使用了我的PrincipleExtractor。但是,现在如何添加更多身份验证提供程序(Facebook,GitHub等)和本地身份验证?

最后,我有几个问题:

  1. 如何使Spring使用我的PrincipalExtractor
  2. 我应该完全使用PrincipalExtractor吗?也许还有另一种方法可以做到这一点?
  3. 我的application.yml有问题吗?

我尝试过的事情:

  1. 添加@EnableAuthorizationServer(Why is my spring @bean never instantiated?

什么都没有改变。

  1. 添加ResourceServerTokenServices(PrincipalExtractor and AuthoritiesExtractor doesn't hit

Spring找不到UserInfoRestTemplateFactory。我猜手动添加bean是不对的,而且根本行不通。

  1. 许多不同的解决方案。他们都没有工作。

1 个答案:

答案 0 :(得分:1)

在定义ssoFilter时,请添加以下内容:

tokenServices.setPrincipalExtractor(myCustomPrincipalExtractor());

奖金:AuthorityExtractor也是如此。