Я пытаюсь создать сервис аутентификации на весенней загрузке 2.2.5. RELEASE, с реактивным стеком. Я почесал голову с тех пор, как часы.
Функция внутри .map
выполняется дважды.
@Override
public Mono<UserDetails> findByUsername(final String userName) {
System.out.println("findByUsername called");
return Mono.fromSupplier(() -> userRepository.findByUsername(userName)).subscribeOn(SCHEDULER)
.switchIfEmpty(Mono.error(new UsernameNotFoundException("Bad credentials")))
.map(user -> {
System.out.println("inside findByUsername");
new AccountStatusUserDetailsChecker().check(user);
return (UserDetails)user;
}).log();
}
Ниже приведен нереактивный репо postgres (таким образом, поставщик в приведенном выше коде).
@Repository
@Transactional
public interface UserRepository extends JpaRepository<User, Long> {
/**
* Finds a user by username
*
* @param username user's name
* @return user object
*/
User findByUsername(String username);
}
Ниже вызывается функция findByUsername
:
@Override
public Mono<SecurityContext> load(ServerWebExchange swe) {
System.out.println("load called");
final ServerHttpRequest request = swe.getRequest();
final String username = request.getHeaders().getFirst(CustomHTTPHeaders.Keys.USERNAME);
final String password = request.getHeaders().getFirst(CustomHTTPHeaders.Keys.PASSWORD);
final AbstractAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, password);
return userDetailService.findByUsername(username).map(details -> {
System.out.println("isnide flatmap " + Thread.currentThread().getName());
auth.setDetails(details);
SecurityContext securityContext = SecurityContextHolder.getContext();
if(passwordEncoder.matches(auth.getCredentials().toString(), details.getPassword())) {
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken(details.getUsername(), details.getPassword(), details.getAuthorities()));
return securityContext;
}
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken(details.getUsername(), null));
return securityContext;
});
Вывод:
load called
findByUsername called
2020-03-17 20:04:58.281 INFO 44648 --- [oundedElastic-3] reactor.Mono.Map.1 : onSubscribe(FluxMap.MapSubscriber)
2020-03-17 20:04:58.283 INFO 44648 --- [oundedElastic-3] reactor.Mono.Map.1 : request(unbounded)
inside findByUsername
2020-03-17 20:04:58.466 INFO 44648 --- [oundedElastic-4] reactor.Mono.Map.1 : onNext(User(email=william@gmail.com, username=admin, password={bcrypt}$2a$10$EOs8VROb14e7ZnydvXECA.4LoIhPOoFHKvVF/iBZ/ker17Eocz4Vi, enabled=true, accountLocked=false, accountExpired=false, credentialsExpired=false, roles=[Role(name=role_admin, permissions=[Permission(name=CREATE_USER), Permission(name=UPDATE_USER), Permission(name=READ_USER), Permission(name=DELETE_USER), Permission(name=VERIFY_TOKEN)])]))
isnide flatmap boundedElastic-4
2020-03-17 20:04:58.598 INFO 44648 --- [oundedElastic-4] reactor.Mono.Map.1 : cancel()
2020-03-17 20:04:58.604 INFO 44648 --- [oundedElastic-4] reactor.Mono.Map.1 : onSubscribe(FluxMap.MapSubscriber)
2020-03-17 20:04:58.604 INFO 44648 --- [oundedElastic-4] reactor.Mono.Map.1 : request(unbounded)
inside findByUsername
2020-03-17 20:04:58.608 INFO 44648 --- [oundedElastic-3] reactor.Mono.Map.1 : onNext(User(email=william@gmail.com, username=admin, password={bcrypt}$2a$10$EOs8VROb14e7ZnydvXECA.4LoIhPOoFHKvVF/iBZ/ker17Eocz4Vi, enabled=true, accountLocked=false, accountExpired=false, credentialsExpired=false, roles=[Role(name=role_admin, permissions=[Permission(name=CREATE_USER), Permission(name=UPDATE_USER), Permission(name=READ_USER), Permission(name=DELETE_USER), Permission(name=VERIFY_TOKEN)])]))
isnide flatmap boundedElastic-3
2020-03-17 20:04:58.716 INFO 44648 --- [oundedElastic-3] reactor.Mono.Map.1 : onComplete()
WebSecurityConfig:
package com.turtlemint.authservice.config;
import com.turtlemint.authservice.controller.SecurityContextRepository;
import com.turtlemint.authservice.service.impl.AuthenticationManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import reactor.core.publisher.Mono;
/**
* <p>
* This class holds spring security configuration
* </p>
*
* @author praveenkamath
* created on 09/03/20
* @since 1.0.0
*/
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class WebSecurityConfiguration {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
private SecurityContextRepository securityContextRepository;
/**
* Provides a password encoder
*
* @return password encoder instance
*/
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
/**
* Configures end-points for security
*
* @param http http security
*/
@Bean
public SecurityWebFilterChain securityWebFilterChain(final ServerHttpSecurity http) {
return http.cors().disable()
.exceptionHandling()
.authenticationEntryPoint((swe, e) -> Mono.fromRunnable(() -> {
swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
})).accessDeniedHandler((swe, e) -> Mono.fromRunnable(() -> {
swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
})).and()
.csrf().disable()
.authenticationManager(authenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
//.pathMatchers("/api/**").permitAll()
.pathMatchers(HttpMethod.OPTIONS).permitAll()
.anyExchange().authenticated()
.and()
.build();
}
}
Не знаю, является ли это ошибкой в реакторе проекта или в моем коде. Пожалуйста, помогите.