【问题标题】:Spring WebFlux Security OAuth 2.0 User ServiceSpring WebFlux 安全 OAuth 2.0 用户服务
【发布时间】:2021-04-06 17:40:11
【问题描述】:

为了提供一些上下文,我目前正在从标准 Spring Security 5(使用 spring-boot-starter-web)迁移到 spring-webflux - 与 Spring Cloud Gateway 一起用作我的 API 网关 - 它不支持 spring-boot-starter-web 依赖项。

除了我的 OAuth 2.0 用户服务(我在之前的实现中拥有)之外,我还可以使用 Spring Reactor/Webflux 进行其他所有工作。所以我已经复制了用户服务——除了我现在无法从新的安全配置中引用它。

这是我的新安全配置:

@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfig {

  private final OAuth2SuccessHandler oAuth2SuccessHandler;
  private final OAuth2FailureHandler oAuth2FailureHandler;
  private final OAuth2UserService customOAuth2UserService;

  public SecurityConfig(OAuth2SuccessHandler oAuth2SuccessHandler, OAuth2FailureHandler oAuth2FailureHandler, OAuth2UserService customOAuth2UserService) {
    this.oAuth2SuccessHandler = oAuth2SuccessHandler;
    this.oAuth2FailureHandler = oAuth2FailureHandler;
    this.customOAuth2UserService = customOAuth2UserService;
  }

  @Bean
  public TokenAuthenticationFilter tokenAuthenticationFilter() {
    return new TokenAuthenticationFilter();
  }

  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }

  @Bean
  public ReactiveAuthenticationManager reactiveAuthenticationManager(
          UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
    UserDetailsRepositoryReactiveAuthenticationManager authenticationManager =
        new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService);
    authenticationManager.setPasswordEncoder(passwordEncoder);

    return authenticationManager;
  }

  @Bean
  public SecurityWebFilterChain springWebFilterChain(
      ServerHttpSecurity http) {
    http
            .requestCache()
            .requestCache(NoOpServerRequestCache.getInstance())
            .and()
            .securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
            .authorizeExchange()
            .pathMatchers(
                    "/",
                    "/error",
                    "/favicon.ico",
                    "/*/*.png",
                    "/*/*.gif",
                    "/*/*.svg",
                    "/*/*.jpg",
                    "/*/*.html",
                    "/*/*.css",
                    "/*/*.js")
            .permitAll()
            .pathMatchers("/login/*", "/auth/*", "/oauth2/*")
            .permitAll()
            .anyExchange()
            .authenticated()
            .and()
            .oauth2Login()
            .authenticationSuccessHandler(oAuth2SuccessHandler)
            .authenticationFailureHandler(oAuth2FailureHandler)
            .and()
            .formLogin()
            .disable()
            .exceptionHandling()
            .authenticationEntryPoint(new AuthenticationEntryPoint())
            .and()
            .oauth2Client();

    http.addFilterBefore(tokenAuthenticationFilter(), SecurityWebFiltersOrder.AUTHENTICATION);

    return http.build();
  }
}

这是我的 OAuth 2.0 自定义用户服务

@Service
public class OAuth2UserService extends DefaultOAuth2UserService {

  private final UserDao userDao;

  public OAuth2UserService(UserDao userDao) {
    this.userDao = userDao;
  }

  @Override
  public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
    OAuth2User oAuth2User = super.loadUser(userRequest);

    OAuth2UserInfo oAuth2UserInfo =
            OAuth2UserInfoFactory.getOAuth2UserInfo(
                    userRequest.getClientRegistration().getRegistrationId(), oAuth2User.getAttributes());

    if (StringUtils.isEmpty(oAuth2UserInfo.getEmail())) {
      throw new OAuth2AuthenticationProcessingException("Email not found from any providers");
    }

    /* Find the user by email */
    User user = userDao.findUserByEmail(oAuth2UserInfo.getEmail());

    try {
      if (user == null) throw new OAuth2AuthenticationProcessingException("You must sign up!");

      if (!user.getProvider().equals(AuthProvider.valueOf(userRequest.getClientRegistration().getRegistrationId()))) {
        throw new OAuth2AuthenticationProcessingException(
            "Woah! Looks like you're already signed up with your "
                + user.getProvider().getValue()
                + ". Please use your "
                + user.getProvider().getValue()
                + " account to login.");
      }

      user = registerNewUser(userRequest, oAuth2UserInfo);
    } catch (Exception e) {

    }

    return UserPrincipal.create(user, oAuth2User.getAttributes());
  }

  private User registerNewUser(OAuth2UserRequest oAuth2UserRequest, OAuth2UserInfo oAuth2UserInfo) {
    User user = new User();

    user.setProvider(
            AuthProvider.valueOf(oAuth2UserRequest.getClientRegistration().getRegistrationId()));
    user.setProviderId(oAuth2UserInfo.getId());
    user.setUsername(oAuth2UserInfo.getName());
    user.setEmail(oAuth2UserInfo.getEmail());
    user.setProfilePicture(oAuth2UserInfo.getImageUrl());

    return userDao.save(user);
  }
}

这与我的旧配置之间的唯一区别是我可以使用它

http
    .userInfoEndpoint()
    .userService(customOAuth2UserService)

但是,没有选项可以将其与 New Security Configuration 选项(我知道)一起使用 - 如果您知道将 Spring Security 指向我的自定义用户服务的方法,请回答.

非常感谢

【问题讨论】:

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


    【解决方案1】:

    您的服务 bean 用于常规 OAuth 流。您想要的实际是 WebFlux 之一,即ReactiveOAuth2UserService

    您可以通过在ReactiveOAuth2UserService::loadUser 方法上设置断点然后正常进行身份验证来自己验证这一点。

    【讨论】:

    • 非常感谢您的评论。实际上,从那时起,我想出了另一种“hacky”方式来做这件事,你可以在我已经拥有的 OAuth2SuccessHandlers 实现中使用 userService 做同样的事情。稍后将尝试它,看看它是否有效! ??
    • 我确实相信这是可行的,而且当我考虑它时肯定会更有意义,一旦我实际实施它就会将答案标记为正确。
    猜你喜欢
    • 2020-08-17
    • 2020-08-31
    • 2019-04-29
    • 2016-11-13
    • 2014-05-26
    • 2021-05-11
    • 2020-04-26
    • 2020-11-18
    • 2020-10-09
    相关资源
    最近更新 更多