java - Spring WebFlux 安全 OAuth 2.0 用户服务
问题描述
为了提供一些上下文,我目前正在从标准 Spring Security 5 (with spring-boot-starter-web
) 迁移到spring-webflux
- 与 Spring Cloud Gateway 一起用作我的 API Gateway - 它不支持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)
但是,没有选项可以将其与新的安全配置选项(我知道)一起使用 - 所以如果您知道将 Spring Security 指向我的自定义用户服务的方法,请回答。
非常感谢
解决方案
您的服务 bean 用于常规 OAuth 流。您想要的实际是 WebFlux 之一,即ReactiveOAuth2UserService
.
您可以通过在ReactiveOAuth2UserService::loadUser
方法上设置断点然后正常进行身份验证来自己验证这一点。
推荐阅读
- ruby-on-rails - 如果使用任何时候 gem 失败,请稍后再次尝试 rake 任务
- python - Discord.py 机器人清单
- rest - Symfony 4 Rest API Token Verification 调用启动方法默认
- javascript - [JS][AJAX] 第二个 ajax 不起作用。为什么?
- javascript - 材质 ui 边框颜色覆盖 texbox
- java - 映射 JavaRDD 时删除空值
- javascript - 如何在没有后端的情况下同步 JS 计时器?
- html - Wordpress CSS 和媒体查询
- python - 用于 ARIMAX 模型的 pymc3
- oracle - 如何以逻辑想要的方式获得触发器?