Reputation: 75
To give some context, I am currently migrating from standard Spring Security 5 (with spring-boot-starter-web
) to spring-webflux
- being used with the Spring Cloud Gateway as my API Gateway - which doesn't support spring-boot-starter-web
dependencies.
I have got everything else working with Spring Reactor/Webflux - apart from my OAuth 2.0 User Service (that I had in the previous implementation). So I have copied the user service over - except I am now unable to reference that from the new Security Configuration.
Here's my new Security Configuration:
@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();
}
}
Here's my OAuth 2.0 Custom User Service
@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);
}
}
the only difference between that and my old configuration was that I could use this
http
.userInfoEndpoint()
.userService(customOAuth2UserService)
however, there is no option to use this with the New Security Configuration Options (that I am aware of) - so please answer if you know a way of pointing Spring Security to my Custom User Service.
Many Thanks
Upvotes: 0
Views: 1802
Reputation: 46
Your Service bean is for the regular OAuth flow. The actual one you want is the WebFlux one, which is ReactiveOAuth2UserService
.
You can verify this for yourself by setting a breakpoint on the ReactiveOAuth2UserService::loadUser
method and then authenticating normally.
Upvotes: 2