Spring Kafka Template - Connect to Kafka Topic on Spring Boot Startup - java

I have implemented a basic Spring Boot Application which uses Spring Kafka. I want my producer to connect to the Kafka Topic before the first .send() is called but I can't find a way to do so. Is that possible?
Logs to show that KafkaTemplate only connects to the Kafka Topic after I trigger the .send method at 16:12:44:
2021-11-24 16:12:12.602 INFO 63930 --- [ main] c.e.k.KafkaProducerExampleApplication : The following profiles are active: dev
2021-11-24 16:12:13.551 INFO 63930 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2021-11-24 16:12:13.559 INFO 63930 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-11-24 16:12:13.559 INFO 63930 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.53]
2021-11-24 16:12:13.613 INFO 63930 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-11-24 16:12:13.613 INFO 63930 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 974 ms
2021-11-24 16:12:13.989 INFO 63930 --- [ main] pertySourcedRequestMappingHandlerMapping : Mapped URL path [/v2/api-docs] onto method [springfox.documentation.swagger2.web.Swagger2Controller#getDocumentation(String, HttpServletRequest)]
2021-11-24 16:12:14.190 INFO 63930 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-11-24 16:12:14.190 INFO 63930 --- [ main] d.s.w.p.DocumentationPluginsBootstrapper : Context refreshed
2021-11-24 16:12:14.207 INFO 63930 --- [ main] d.s.w.p.DocumentationPluginsBootstrapper : Found 1 custom documentation plugin(s)
2021-11-24 16:12:14.239 INFO 63930 --- [ main] s.d.s.w.s.ApiListingReferenceScanner : Scanning for api listing references
2021-11-24 16:12:14.336 INFO 63930 --- [ main] c.e.k.KafkaProducerExampleApplication : Started KafkaProducerExampleApplication in 7.055 seconds (JVM running for 7.341)
2021-11-24 16:12:44.550 INFO 63930 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-11-24 16:12:44.550 INFO 63930 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2021-11-24 16:12:44.551 INFO 63930 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2021-11-24 16:12:44.649 INFO 63930 --- [nio-8080-exec-1] o.a.k.clients.producer.ProducerConfig : ProducerConfig values:

Regarding Linh Vu's answer, it's best not to create a connection in a bean definition - it's too early in the application context's lifecycle.
Instead, add a bean that implements SmartLifecycle and create the connection in start(); that way, the context will be completely initialized before connecting.
#Bean
SmartLifecycle connector(ProducerFactory<Object ,Object> pf) {
return new SmartLifecycle() {
#Override
public void stop() {
}
#Override
public void start() {
pf.createProducer().close();
}
#Override
public boolean isRunning() {
return false;
}
};
}

With non-transactional producer (transactionIdPrefix is not supplied), when you first call KafkaTemplate.send, it will delegate to ProducerFactory to get a single instance of Producer. At this time, because there's no a single instance of Producer before, ProducerFactory will create this one for you (that's why you saw the log ProducerConfig : ProducerConfig values ...). This producer instance is now used/shared by all clients.
So if you want to create the above producer instance beforehand, you could directly call it on the ProducerFactory, e.g:
#Bean
public KafkaTemplate<?, ?> kafkaTemplate(ProducerFactory<Object, Object> kafkaProducerFactory) {
KafkaTemplate<Object, Object> kafkaTemplate = new KafkaTemplate(kafkaProducerFactory);
kafkaProducerFactory.createProducer();
return kafkaTemplate;
...

SmartLifecycle bean works for us, thanks.
#Component
class KafkaProducer (
private val userChangeLogTemplate: KafkaTemplate<String, UserChangeLog>
private val kafkaProperties: MizenKafkaProperties
) : NotificationProducer{
#Bean
fun connector(pf: ProducerFactory<String, Any>): SmartLifecycle {
return object : SmartLifecycle {
override fun stop() {}
override fun start() {
pf.createProducer().close()
}
override fun isRunning(): Boolean {
return false
}
}
}
override fun sendUserChangeLog(message: UserChangeLog) {
userChangeLogTemplate.send(kafkaProperties.userChangeLogTopic, message)
}
}

Related

Getting anonymous user after authentication with custom filter

I have post this question but with different format and got no answer and I think I didn't provide enough info.
What I am trying to do is parse a login request payload that contain a JSON with username and password. So, I had to extend UsernamePasswordAuthenticationFilter and replace the UsernamePasswordAuthenticationFilter in filter chain with my custom filter.
I've done that and registered my custom filter as you can see in the log from DefaultSecurityFilterChain:
2023-01-30T22:33:13.993+03:00 INFO 20436 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter#1ec09a68, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#7da9b32c, org.springframework.security.web.context.SecurityContextHolderFilter#2b6ff016, org.springframework.security.web.header.HeaderWriterFilter#118c1faa, org.springframework.security.web.authentication.logout.LogoutFilter#4f5df012, ali.yousef.chatty.config.security.JsonUsernamePasswordAuthenticationFilter#7e5c8c80, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#7a491a60, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#4a0f4282, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#46df794e, org.springframework.security.web.access.ExceptionTranslationFilter#470866d1, org.springframework.security.web.access.intercept.AuthorizationFilter#1d0fc0bc]
Until now everything works fine.
Here is the code:
SecurityConfig.java
#Configuration
#EnableWebSecurity
public class SecurityConfig
{
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {
JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter = new JsonUsernamePasswordAuthenticationFilter();
jsonUsernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManager);
http
.csrf().disable()
.formLogin().disable()
.addFilterAfter(jsonUsernamePasswordAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests()
.requestMatchers("/api/**").authenticated()
.anyRequest().permitAll();
return http.build();
}
}
JsonUsernamePasswordAuthenticationFilter.java
public class JsonUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
Logger logger = LoggerFactory.getLogger(JsonUsernamePasswordAuthenticationFilter.class);
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
UsernamePasswordDto usernamePasswordDto;
try {
usernamePasswordDto = new ObjectMapper().readValue(request.getInputStream(), UsernamePasswordDto.class);
logger.info(usernamePasswordDto.toString());
} catch (IOException ioe) {
throw new AuthenticationServiceException(ioe.getMessage(), ioe);
}
UsernamePasswordAuthenticationToken authToken =
UsernamePasswordAuthenticationToken.unauthenticated(usernamePasswordDto.getUsername(), usernamePasswordDto.getPassword());
Authentication result = this.getAuthenticationManager().authenticate(authToken);
for (GrantedAuthority a : result.getAuthorities()) {
logger.info(a.getAuthority());
}
return result;
}
}
TestController.java
#RestController
public class TestController
{
#GetMapping("/")
public String home() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth == null)
return "null";
return auth.getName();
}
}
I send a request to /login and I get anonymousUser response. I thought I missed something in the custom filter so I printed the authorities the user has and got ROLE_USER.
Here is the full output
2023-01-30T22:48:30.329+03:00 INFO 20900 --- [ main] ali.yousef.chatty.ChattyApplication : No active profile set, falling back to 1 default profile: "default"
2023-01-30T22:48:30.851+03:00 INFO 20900 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2023-01-30T22:48:30.903+03:00 INFO 20900 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 37 ms. Found 1 JPA repository interfaces.
2023-01-30T22:48:31.355+03:00 INFO 20900 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2023-01-30T22:48:31.361+03:00 INFO 20900 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-01-30T22:48:31.361+03:00 INFO 20900 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.5]
2023-01-30T22:48:31.431+03:00 INFO 20900 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-01-30T22:48:31.432+03:00 INFO 20900 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1064 ms
2023-01-30T22:48:31.559+03:00 INFO 20900 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2023-01-30T22:48:31.591+03:00 INFO 20900 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.1.6.Final
2023-01-30T22:48:31.696+03:00 WARN 20900 --- [ main] org.hibernate.orm.deprecation : HHH90000021: Encountered deprecated setting [javax.persistence.sharedCache.mode], use [jakarta.persistence.sharedCache.mode] instead
2023-01-30T22:48:31.781+03:00 INFO 20900 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2023-01-30T22:48:31.983+03:00 INFO 20900 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl#77ea806f
2023-01-30T22:48:31.984+03:00 INFO 20900 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2023-01-30T22:48:32.032+03:00 INFO 20900 --- [ main] SQL dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
2023-01-30T22:48:32.847+03:00 INFO 20900 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2023-01-30T22:48:32.853+03:00 INFO 20900 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2023-01-30T22:48:33.070+03:00 WARN 20900 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2023-01-30T22:48:33.220+03:00 INFO 20900 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page: class path resource [static/index.html]
2023-01-30T22:48:33.519+03:00 INFO 20900 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter#5cd6a827, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#6cd65042, org.springframework.security.web.context.SecurityContextHolderFilter#5c30decf, org.springframework.security.web.header.HeaderWriterFilter#5aefdb9e, org.springframework.security.web.authentication.logout.LogoutFilter#50d6af87, ali.yousef.chatty.config.security.JsonUsernamePasswordAuthenticationFilter#7674f9d4, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#4a0f4282, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#356ab368, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#4b5ceb5d, org.springframework.security.web.access.ExceptionTranslationFilter#99f75e4, org.springframework.security.web.access.intercept.AuthorizationFilter#7da9b32c]
2023-01-30T22:48:33.688+03:00 INFO 20900 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2023-01-30T22:48:33.689+03:00 INFO 20900 --- [ main] o.s.m.s.b.SimpleBrokerMessageHandler : Starting...
2023-01-30T22:48:33.690+03:00 INFO 20900 --- [ main] o.s.m.s.b.SimpleBrokerMessageHandler : BrokerAvailabilityEvent[available=true, SimpleBrokerMessageHandler [org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry#5d3a238]]
2023-01-30T22:48:33.690+03:00 INFO 20900 --- [ main] o.s.m.s.b.SimpleBrokerMessageHandler : Started.
2023-01-30T22:48:33.696+03:00 INFO 20900 --- [ main] ali.yousef.chatty.ChattyApplication : Started ChattyApplication in 3.673 seconds (process running for 3.952)
2023-01-30T22:48:52.209+03:00 INFO 20900 --- [nio-8080-exec-3] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-01-30T22:48:52.209+03:00 INFO 20900 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-01-30T22:48:52.209+03:00 INFO 20900 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 ms
2023-01-30T22:48:52.250+03:00 INFO 20900 --- [nio-8080-exec-3] JsonUsernamePasswordAuthenticationFilter : UsernamePasswordDto(username=user01, password=password)
2023-01-30T22:48:52.421+03:00 INFO 20900 --- [nio-8080-exec-3] JsonUsernamePasswordAuthenticationFilter : ROLE_USER
What I think is after finishing the authentication process and sending a redirect to homepage the context is deleted somehow or cleared, but I'm not Spring Security expert.
I've done this in older versions with WebSecurityConfigurerAdapter without any problem but using this way of configuration didn't work.
I really tried everything I could and I would appreciate any help.
The SecurityContext is not saved by default after successful authentication. Moreover, the UsernamePasswordAuthenticationFilter is a form-login-based mechanism, so the Authentication object cannot be detected by the SessionManagementFilter, as the filter is not invoked during the authenticating request. So, you query the wrong Authentication object which in your case defaults to an Anonymous user.
So, first, after a successful authentication you have to save the authenticated Authentication object(returned from the attemptAuthentication() method) in the Security Context. You can do that by overriding the successfulAuthentication() method of your filter.
#Override
protected void successfulAuthentication(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, jakarta.servlet.FilterChain chain, Authentication authResult) throws IOException, jakarta.servlet.ServletException {
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authResult);
//SecurityContextHolder.setContext(context);
HttpSessionSecurityContextRepository secRepo = new HttpSessionSecurityContextRepository();
secRepo.saveContext(context, request, response);
super.successfulAuthentication(request, response, chain, authResult);
}
You have to save it, in a persistent way, i.e. it has to be preserved during the whole session. A way to do so is via using an instance of the HttpSessionSecurityContextRepository class and using its saveContext() method to save it.
After that, in your handler method for the default “/” home(), you can obtain the saved SecurityContextHolder, again by using an instance of the HttpSessionSecurityContextRepository class and via its loadDeferredContext() method.
Note, that for this purpose, first, it is necessary to bring the request object (returned from your filter) in your handler home() method. (For completeness you can do the same for the response object as well).
After that, you can obtain the authenticated user from the extracted security context.
#GetMapping("/")
public String home(jakarta.servlet.http.HttpServletRequest request,
jakarta.servlet.http.HttpServletResponse response) {
//Authentication auth = SecurityContextHolder.getContext().getAuthentication();
HttpSessionSecurityContextRepository secRepo = new HttpSessionSecurityContextRepository();
Authentication auth = secRepo.loadDeferredContext(request).get().getAuthentication();
if (auth == null) return "null";
return (String) auth.getPrincipal();
}
One more thing that should be mentioned, is that if you want you can invalidate the session in your handler. This is a "must" for security reasons, especially if you use a STATELESS application e.g.a REST API application.
Hope this helped you!
I got the solution for my problem from Marcus when I posted an issue on the spring security repository on github because I thought it was a bug. If you want to check the answer yourself you can go here
The issue was that the my custom filter was using RequestAttributeSecurityContextRepository but the default was DelegatingSecurityContextRepository, hence made the authentication not getting saved between requests.
Setting the SecurityContextRepository in jsonUsernamePasswordAuthenticationFilter to DelegatingSecurityContextRepository will solve the issue.
jsonUsernamePasswordAuthenticationFilter.setSecurityContextRepository(new DelegatingSecurityContextRepository(
new RequestAttributeSecurityContextRepository(),
new HttpSessionSecurityContextRepository()
));

Spring boot how to use #PostConstruct correctly

Spring boot 2.5.4 I used #PostConstruct for the very first time in my service class. As following:-
#Slf4j
#Service
#AllArgsConstructor
public class FileMonitorService {
private final AppProperties appProperties;
private final WatchService watchService;
private final RestTemplate restTemplate;
#PostConstruct
#Async
public void startMonitoring() {
FileUtils.setAppProperties(appProperties);
FileUtils.setRestTemplate(restTemplate);
FileUtils.readFilesForDirectory();
log.info("START_MONITORING");
try {
WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent<?> event : key.pollEvents()) {
log.info("Event kind: {}; File affected: {}", event.kind(), event.context());
if((event.kind() == StandardWatchEventKinds.ENTRY_CREATE ||
event.kind() == StandardWatchEventKinds.ENTRY_MODIFY) &&
event.context().toString().contains(".xml")){
try {
restTemplateRequest(event.context().toString()+" processing");
FileUtils.readXml(Path.of(FileUtils.getFileAbsolutePath(appProperties.getDataIn()),
event.context().toString()));
}catch (Exception e){
log.error("startMonitoring Exception: "+e.getMessage());
}
}
}
key.reset();
}
} catch (InterruptedException e) {
log.warn("startMonitoring: interrupted exception for monitoring service: "+e.getMessage());
}
}
}
This method is called as soon as app launched. That is my requirements to process all file as soon as the app starts. I have controller as following:-
#RestController
#RequestMapping("/xml")
public class FileController {
#Autowired
FileMonitorService fileMonitorService;
#SneakyThrows
#GetMapping("/restart")
public String restartFileMonitoring(){
fileMonitorService.startMonitoring();
return "File monitoring restarted started successfully";
}
}
My app starts on port 8080 and no exception at all. But when I get call this end point localhost:8080/xml/restart
It is not reachable. If I comment out the #PostConstruct then I can call the end point. I am confused how to use this annotation properly. What is wrong in my code?
Update info:-
:: Spring Boot :: (v2.5.4)
2021-09-14 18:23:21.521 INFO 71192 --- [ main] c.f.i.task.BatchProcessorApplication : Starting BatchProcessorApplication using Java 14.0.2 on dev with PID 71192 (/home/dev/Desktop/batch-processor/batch-processor/target/classes started by dev in /home/dev/Desktop/batch-processor/batch-processor)
2021-09-14 18:23:21.523 INFO 71192 --- [ main] c.f.i.task.BatchProcessorApplication : No active profile set, falling back to default profiles: default
2021-09-14 18:23:22.485 INFO 71192 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2021-09-14 18:23:22.495 INFO 71192 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-09-14 18:23:22.495 INFO 71192 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.52]
2021-09-14 18:23:22.564 INFO 71192 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-09-14 18:23:22.564 INFO 71192 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 988 ms
File to monitor: /home/dev/Desktop/batch-processor/batch-processor/data/in
2021-09-14 18:23:22.647 INFO 71192 --- [ main] c.f.i.task.config.FileMonitorConfig : MONITORING_DIR: /home/dev/Desktop/batch-processor/batch-processor/data/in/
2021-09-14 18:23:22.667 INFO 71192 --- [ main] c.f.i.task.service.FileMonitorService : START_MONITORING
That is the log when I run the app. After debugging I found that while ((key = watchService.take()) != null) { call never returns until I copy some XML file as this app process xml files. Then I copy any xml file in the monitoring dir. I was expecting that #Async it will run in back ground thread in async mode. How to monitory this dir in background thread? So the caller of this method won't be blocked.
PostContstruct semantics
The PostConstruct annotation is part of JSR 330 (Dependency Injection) and is not a Spring custom annotation.
The annotation specification dictates that the annotated method MUST run before the service being injected into context or translated into a service.
Spring supports the PostConstruct lifecycle hook allowing to perform extra post-initialization actions once a bean has been initialized, i.e., it had all its dependencies injected.
Async semantics
The Async annotation on the other hand is a Spring specific annotation allowing to mark a method or a type as being a candidate for asynchronous execution.
Alternative
In a case where you are interested into starting a background process as long as you application starts, you should better use the application lifecycle events and more specifically the ApplicationReadyEvent to spin your monitoring activity:
#Slf4j
#Service
#AllArgsConstructor
public class FileMonitorService {
private final AppProperties appProperties;
private final WatchService watchService;
private final RestTemplate restTemplate;
#EventListener(ApplicationReadyEvent.class)
#Async
public void startMonitoring() {
// ...
}
}
And don't forget to add the #EnableAsync annotation on your Spring Boot configuration type to activate the asynchronous processing feature.
For your case, you don't need to use #PostConstruct and that is why its working when removing the #PostConstruct
to simplify, #PostConstruct is considered as a class empty constructor but it make sure all the Beans are loaded before being called

Configured a SFTP inbound channel to get a file from a SFTP server but my test case does not bring it alive

I want to get a file from a SFTP server, transform the JSON content into an array of instances of one of my classes.
Therfore I have thought to use Spring Integration within a Spring Boot application and would be happy to have one of the IntegrationFlows fluently programmed to achieve that.
I dived into many articles, questions and answers here in SO so far, but nothing really helped. I am struggling with it since weeks. The Spring Integration documentation is itself hard to understand. It comes with a whole lot of different aspects depending of Spring versions and different programming paradigmas (XML, Java Configuration, and Java DSL), which brings more complexity into the matter, makes it even harder to decide, which of the rare examples to follow, and is eventually no help for a newbee. A newbee wants to find his problem and wants being recommended and guided to the currently best way to solve his problem, being explained the whys and odds and evens. If he is happy enough, he will be able to find his problem in one of the examples (getting a file from SFTP to further process it, is not an exotic task) and the solution is quite in an example, he can copy&paste with minimal adaptations. I was not so lucky until now.
One question here in SO was near to what I probably need: there was an sftpInboundFileSynchonizer configured, together with a MessageSource and a MassageHandler. The asking person wanted to do roughly the same as I. The answer was "That code is doing the reverse (sending to SFTP)." which left me flabbergasted and thinking, did I mixed up basically the understanding of inbound and outbound?
I think I have to use an InboundAdapter based on a SftpSessionFactory. I put the SessionFactory into an extra configuration because I plan to use it with other adapters as well:
#Configuration
#ConfigurationProperties("test.job.common")
public class JobCommonConfiguration {
private static final Logger LOG = LoggerFactory.getLogger(JobCommonConfiguration.class);
private String hostname;
private int port;
private String username;
private String password;
#Bean
public SessionFactory<LsEntry> sftpTest3SessionFactory() {
DefaultSftpSessionFactory sf = new DefaultSftpSessionFactory();
sf.setHost(hostname);
sf.setPort(port);
sf.setUser(username);
sf.setPassword(password);
// factory.setTestSession(true);
return new CachingSessionFactory<LsEntry>(sf);
}
/* getters & setters */
}
The second configuration is to configure an SftpInboundAdapter, assuming my understanding of "inbound" is correct, which comes with a SI Transformer to convert JSON into an array of my Event instances. In the end the instances should be send by request to an HTTP REST service, which I could as well include into the adapter? It is as follows:
#Configuration
#ConfigurationProperties("test.job.transfer3")
#Import({ JobCommonConfiguration.class })
public class Job3Configuration {
private static final Logger LOG = LoggerFactory.getLogger(Job3Configuration.class);
private boolean enabled = false;
private String remoteDir;
private String remoteFile;
private String remoteFilePattern;
private boolean remoteRemove;
private String localDir;
private String localFile;
#Autowired
private SessionFactory<LsEntry> sftpTest3SessionFactory;
#Bean
public FireOnceTrigger fireOnceTest3Trigger() {
return new FireOnceTrigger();
}
#Bean
public IntegrationFlow test3SftpInboundFlow() {
return IntegrationFlows
.from(Sftp.inboundAdapter(sftpTest3SessionFactory)
.preserveTimestamp(true)
.remoteDirectory(remoteDir)
.regexFilter(remoteFilePattern)
.localFilenameExpression(localFile)
.localDirectory(new File(localDir)),
e -> e.id("sftpTest3InboundAdapter")
.autoStartup(true)
.poller(Pollers.trigger(fireOnceTest3Trigger()))
)
.transform(Transformers.fromJson(Event[].class))
.handle(m -> System.out.println(m.getPayload()))
.get();
}
/* getters & setters */
}
My entity Event is quite simple:
public class Event {
private Integer crmId;
private String eventType;
private LocalDateTime dateFrom;
private LocalDateTime dateTo;
/* getters & setter & toString() */
}
My Test is not testing anything yet, because nothing happened so far. It then should assert that I received the correct number of Events. It looks as following:
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(classes=SftpTransferTestApplication.class)
#Import(Job3Configuration.class)
public class GetFileIntegrationTest {
private static final Logger LOG = LoggerFactory.getLogger(GetFileIntegrationTest.class);
#Test
public void testGetFile() throws Exception {
LOG.info("GetIntegrationTest testgetFile");
// assertThat(fileReceived, is(sb.toString()));
}
}
The Test application is straight forward:
#SpringBootApplication
#EnableIntegration
#IntegrationComponentScan
public class SftpTransferTestApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(SftpTransferTestApplication.class).web(
NONE).run(args);
}
}
My project has as parent spring-boot-starter-parent with version 2.3.0.RELEASE and uses spring-integration-sftp with version 5.3.0.RELEASE.
Please help me to bring this test case alive. What do I wrong?
How can I include the Logger features into the IntegrationFlow to see more of what is (or is not) happening?
EDIT 1
I tried to strip my code a bit to avoid configuration proplems:
#Configuration
public class JobConfiguration {
private static final Logger LOG = LoggerFactory.getLogger(JobConfiguration.class);
#Bean
public TransferChannel getTransferChannel() {
TransferChannel channel = new TransferChannel();
channel.setHost("myHost");
channel.setPort(0);
channel.setUser("test");
channel.setPassword("xxx");
return channel;
}
#Bean
public TransferContext getTransferContext() {
TransferContext context = new TransferContext();
context.setEnabled(false);
context.setChannel(getTransferChannel());
context.setRemoteDir("data");
context.setRemoteFilename("GetMessage3.json");
context.setRemoteFilenameFilter("GetMessage\\.json$");
context.setLocalDir("sftp-inbound");
context.setLocalFile("GetMessage3.json");
return context;
}
#Bean
public SessionFactory<LsEntry> getSftpTestSessionFactory(TransferChannel transferChannel) {
DefaultSftpSessionFactory sf = new DefaultSftpSessionFactory();
sf.setHost(transferChannel.getHost());
sf.setPort(transferChannel.getPort());
sf.setUser(transferChannel.getUser());
sf.setPassword(transferChannel.getPassword());
// factory.setTestSession(true);
return new CachingSessionFactory<LsEntry>(sf);
}
#Bean
public FireOnceTrigger fireOnceTestTrigger() {
return new FireOnceTrigger();
}
#Bean
public IntegrationFlow testSftpInboundFlow(TransferContext context) {
return IntegrationFlows
.from(Sftp.inboundAdapter(getSftpTestSessionFactory(context.getChannel()))
.preserveTimestamp(true)
.remoteDirectory(context.getRemoteDir())
.regexFilter(context.getRemoteFilenameFilter())
.localFilenameExpression(context.getLocalFile())
.localDirectory(new File(context.getLocalDir())),
e -> e.id("sftpTestInboundAdapter")
.autoStartup(true)
.poller(Pollers.trigger(fireOnceTestTrigger()))
// .poller(Pollers.fixedDelay(5000))
)
.transform(Transformers.fromJson(Event[].class))
.channel("sftpTestMessageChannel")
// .logAndReply(Level.DEBUG);
// .handle(m -> System.out.println("myHandler: " + m.getPayload().toString()))
.get();
}
#Bean("someService.handler")
#EndpointId("someService")
#ServiceActivator(inputChannel = "sftpTestMessageChannel")
public MessageHandler someHandler() {
return new MessageHandler() {
#Override
public void handleMessage(Message<?> message) throws MessagingException {
System.out.println("myThirdHandler: " + message.getPayload());
System.out.println("myThirdHandler: " + message.getHeaders());
Event ev = ((Event[]) message.getPayload())[1];
System.out.println("myThirdHandler: " + ev);
throw new IllegalStateException("Want to see the next MessageHandler");
}
};
}
}
To let the ftp implementation Jsch speak a bit more, I have a configuration "application-junit.yaml":
logging:
level:
org.springframework.integration: debug
### filter warning
### org.springframework.integration.expression.ExpressionUtils:
### Creating EvaluationContext with no beanFactory
org.springframework.integration.expression.ExpressionUtils: error
### filter info
### because jsch is very talkative
com.jcraft.jsch: debug
com.harry.potter: debug
com.harry.potter.filetransfer: trace
I can't get it to work. Logging output is:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.0.RELEASE)
2021-08-22 23:29:27.958 INFO 9916 --- [ main] c.l.c.f.service.GetFileIntegrationTest : Starting GetFileIntegrationTest on myClient with PID 9916 (started by test in C:\Users\test\git\tis\sftp-client)
2021-08-22 23:29:27.959 DEBUG 9916 --- [ main] c.l.c.f.service.GetFileIntegrationTest : Running with Spring Boot v2.3.0.RELEASE, Spring v5.2.6.RELEASE
2021-08-22 23:29:27.959 INFO 9916 --- [ main] c.l.c.f.service.GetFileIntegrationTest : No active profile set, falling back to default profiles: default
2021-08-22 23:29:28.752 INFO 9916 --- [ main] faultConfiguringBeanFactoryPostProcessor : No bean named 'errorChannel' has been explicitly defined. Therefore, a default PublishSubscribeChannel will be created.
2021-08-22 23:29:28.759 INFO 9916 --- [ main] faultConfiguringBeanFactoryPostProcessor : No bean named 'taskScheduler' has been explicitly defined. Therefore, a default ThreadPoolTaskScheduler will be created.
2021-08-22 23:29:28.763 DEBUG 9916 --- [ main] faultConfiguringBeanFactoryPostProcessor : SpEL function '#xpath' isn't registered: there is no spring-integration-xml.jar on the classpath.
2021-08-22 23:29:28.766 INFO 9916 --- [ main] faultConfiguringBeanFactoryPostProcessor : No bean named 'integrationHeaderChannelRegistry' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created.
2021-08-22 23:29:28.837 INFO 9916 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'integrationChannelResolver' of type [org.springframework.integration.support.channel.BeanFactoryChannelResolver] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2021-08-22 23:29:28.841 INFO 9916 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'integrationDisposableAutoCreatedBeans' of type [org.springframework.integration.config.annotation.Disposables] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2021-08-22 23:29:28.873 INFO 9916 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.integration.config.IntegrationManagementConfiguration' of type [org.springframework.integration.config.IntegrationManagementConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2021-08-22 23:29:30.298 INFO 9916 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2021-08-22 23:29:30.802 INFO 9916 --- [ main] o.s.s.c.ThreadPoolTaskScheduler : Initializing ExecutorService 'taskScheduler'
2021-08-22 23:29:30.894 DEBUG 9916 --- [ main] faultConfiguringBeanFactoryPostProcessor :
Spring Integration global properties:
spring.integration.endpoints.noAutoStartup=
spring.integration.taskScheduler.poolSize=10
spring.integration.channels.maxUnicastSubscribers=0x7fffffff
spring.integration.channels.autoCreate=true
spring.integration.channels.maxBroadcastSubscribers=0x7fffffff
spring.integration.readOnly.headers=
spring.integration.messagingTemplate.throwExceptionOnLateReply=false
2021-08-22 23:29:30.901 DEBUG 9916 --- [ main] .s.i.c.GlobalChannelInterceptorProcessor : No global channel interceptors.
2021-08-22 23:29:30.904 INFO 9916 --- [ main] o.s.i.endpoint.EventDrivenConsumer : Adding {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
2021-08-22 23:29:30.904 INFO 9916 --- [ main] o.s.i.channel.PublishSubscribeChannel : Channel 'application.errorChannel' has 1 subscriber(s).
2021-08-22 23:29:30.904 INFO 9916 --- [ main] o.s.i.endpoint.EventDrivenConsumer : started bean '_org.springframework.integration.errorLogger'
2021-08-22 23:29:30.905 INFO 9916 --- [ main] o.s.i.endpoint.EventDrivenConsumer : Adding {json-to-object-transformer} as a subscriber to the 'testSftpInboundFlow.channel#0' channel
2021-08-22 23:29:30.905 INFO 9916 --- [ main] o.s.integration.channel.DirectChannel : Channel 'application.testSftpInboundFlow.channel#0' has 1 subscriber(s).
2021-08-22 23:29:30.905 INFO 9916 --- [ main] o.s.i.endpoint.EventDrivenConsumer : started bean 'testSftpInboundFlow.org.springframework.integration.config.ConsumerEndpointFactoryBean#0'; defined in: 'class path resource [com/harry/potter/filetransfer/config/JobConfiguration.class]'; from source: 'bean method testSftpInboundFlow'
2021-08-22 23:29:30.905 INFO 9916 --- [ main] o.s.i.endpoint.EventDrivenConsumer : Adding {message-handler:someService} as a subscriber to the 'sftpTestMessageChannel' channel
2021-08-22 23:29:30.905 INFO 9916 --- [ main] o.s.integration.channel.DirectChannel : Channel 'application.sftpTestMessageChannel' has 1 subscriber(s).
2021-08-22 23:29:30.905 INFO 9916 --- [ main] o.s.i.endpoint.EventDrivenConsumer : started bean 'someService'
2021-08-22 23:29:30.910 INFO 9916 --- [ main] o.s.i.e.SourcePollingChannelAdapter : started bean 'sftpTestInboundAdapter'; defined in: 'class path resource [com/harry/potter/filetransfer/config/JobConfiguration.class]'; from source: 'bean method testSftpInboundFlow'
2021-08-22 23:29:30.922 INFO 9916 --- [ main] c.l.c.f.service.GetFileIntegrationTest : Started GetFileIntegrationTest in 3.323 seconds (JVM running for 4.13)
2021-08-22 23:29:30.935 INFO 9916 --- [ask-scheduler-1] com.jcraft.jsch : Connecting to myHost port 22
2021-08-22 23:29:30.968 INFO 9916 --- [ask-scheduler-1] com.jcraft.jsch : Connection established
2021-08-22 23:29:31.041 INFO 9916 --- [ask-scheduler-1] com.jcraft.jsch : Remote version string: SSH-2.0-OpenSSH_for_Windows_8.1
2021-08-22 23:29:31.042 INFO 9916 --- [ask-scheduler-1] com.jcraft.jsch : Local version string: SSH-2.0-JSCH-0.1.54
2021-08-22 23:29:31.042 INFO 9916 --- [ask-scheduler-1] com.jcraft.jsch : CheckCiphers: aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,3des-ctr,arcfour,arcfour128,arcfour256
2021-08-22 23:29:31.216 INFO 9916 --- [ main] c.l.c.f.service.GetFileIntegrationTest : GetIntegrationTest testgetFile
2021-08-22 23:29:31.226 INFO 9916 --- [extShutdownHook] o.s.i.e.SourcePollingChannelAdapter : stopped bean 'sftpTestInboundAdapter'; defined in: 'class path resource [com/harry/potter/filetransfer/config/JobConfiguration.class]'; from source: 'bean method testSftpInboundFlow'
2021-08-22 23:29:31.227 INFO 9916 --- [extShutdownHook] o.s.i.endpoint.EventDrivenConsumer : Removing {json-to-object-transformer} as a subscriber to the 'testSftpInboundFlow.channel#0' channel
2021-08-22 23:29:31.227 INFO 9916 --- [extShutdownHook] o.s.integration.channel.DirectChannel : Channel 'application.testSftpInboundFlow.channel#0' has 0 subscriber(s).
2021-08-22 23:29:31.227 INFO 9916 --- [extShutdownHook] o.s.i.endpoint.EventDrivenConsumer : stopped bean 'testSftpInboundFlow.org.springframework.integration.config.ConsumerEndpointFactoryBean#0'; defined in: 'class path resource [com/harry/potter/filetransfer/config/JobConfiguration.class]'; from source: 'bean method testSftpInboundFlow'
2021-08-22 23:29:31.227 INFO 9916 --- [extShutdownHook] o.s.i.endpoint.EventDrivenConsumer : Removing {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
2021-08-22 23:29:31.227 INFO 9916 --- [extShutdownHook] o.s.i.channel.PublishSubscribeChannel : Channel 'application.errorChannel' has 0 subscriber(s).
2021-08-22 23:29:31.227 INFO 9916 --- [extShutdownHook] o.s.i.endpoint.EventDrivenConsumer : stopped bean '_org.springframework.integration.errorLogger'
2021-08-22 23:29:31.227 INFO 9916 --- [extShutdownHook] o.s.i.endpoint.EventDrivenConsumer : Removing {message-handler:someService} as a subscriber to the 'sftpTestMessageChannel' channel
2021-08-22 23:29:31.227 INFO 9916 --- [extShutdownHook] o.s.integration.channel.DirectChannel : Channel 'application.sftpTestMessageChannel' has 0 subscriber(s).
2021-08-22 23:29:31.227 INFO 9916 --- [extShutdownHook] o.s.i.endpoint.EventDrivenConsumer : stopped bean 'someService'
2021-08-22 23:29:31.227 INFO 9916 --- [extShutdownHook] o.s.s.c.ThreadPoolTaskScheduler : Shutting down ExecutorService 'taskScheduler'
2021-08-22 23:29:31.228 INFO 9916 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
I replaced the FireOnceTestTrigger with Pollers.fixedDelay(5000), nothing.
What do I wrong?
The test is exiting and the JVM shut down before the fetch is completed.
You need to wait for the file to be fetched. One way would be to add a channel interceptor in your test. Something like this:
public class GetFileIntegrationTest {
private static final Logger LOG = LoggerFactory.getLogger(GetFileIntegrationTest.class);
#Autowired
AbstractMessageChannel sftpTestMessageChannel;
#Test
public void testGetFile() throws Exception {
LOG.info("GetIntegrationTest testgetFile");
CountDownLatch latch = new CountDownLatch(1);
this.sftpTestMessageChannel.addInterceptor(new ChannelInterceptor() {
// override preSend and/or postSend, capture the message and
// count down the latch.
});
assertTrue(latch.await(10, TimeUnit.SECONDS));
// assertThat(fileReceived, is(sb.toString()));
}
}

BCrypt: Empty Encoded Password with Spring Security

A CODE SAMPLE TO HELP FIX THE PROBLEM: https://github.com/Suwappertjes/SpringSample
Problem
When trying to implement jwt-security in a Spring Boot application, I run into the following problem:
When I try to login with x-www-form-urlencoded through Postman I get a "Bad client credentials" error, whilst I know the credentials to be correct.
When I look in my log, I see that BCrypt gave a "Empty Encoded Password" warning. This is odd, considering I see correctly encrypted passwords in the database when I look at it through the MySQL interpreter.
Info
I am using Hibernate to build a MySQL database.
compile 'org.springframework.security.oauth:spring-security-oauth2:2.3.6.RELEASE'
compile 'org.springframework.security:spring-security-jwt:1.0.10.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-data-jpa'
Java 1.8.0_212
What I tried
Before starting to implement security, the controllers, repositories, and MySQL database were all functioning correctly.
When I search for this problem online, some people suggest it has to do with the "loadUserByUsername" function. But when I debug that function I notice it is not being called at all.
I also tried allowing every single path in my program AND disabling crsf
in case it had something to do with access rights, but both didn't change anything. (http.requestMatchers.andMatcher("/**").permitAll().and().csrf().disable();)
Update: When putting NO users in the database, I still get the same error.
Some code:
The loadUserByUsername method:
#Override
public UserDetails loadUserByUsername(String userName) {
return userRepository.findByUsername(userName)
.map(user -> new User(
user.getUsername(),
user.getPassword(),
UserRoleAuthority.getAuthorities(user.getRoles())
))
.orElseThrow(() -> new UsernameNotFoundException("Unknown user: " + userName));
}
The authenticationprovider and passwordencoder:
#Bean
public DaoAuthenticationProvider getAuthenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
The signing key:
#Bean
public JwtAccessTokenConverter jwtTokenEnhancer() {
JwtAccessTokenConverter result = new JwtAccessTokenConverter();
result.setSigningKey(signingKey);
return result;
}
Adding a new user:
userRepository.save(new User()
.withUsername("test")
.withPassword(passwordEncoder.encode("password"))
.withFirst("Admin")
.withLast("Nator")
.withEmail("test#notadmin.com")
.withRole(new HashSet<>(Arrays.asList(Role.Admin, Role.NotAdmin)))
);
And the Http configuration:
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/iungo/*/**")
.and()
.authorizeRequests()
.antMatchers("/iungo/system/**")
.permitAll()
.antMatchers("/iungo/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
// We don't use this endpoint but we have to define it anyway in order to not enable web security on the whole application.
http
.userDetailsService(userDetailsService)
.requestMatchers()
.antMatchers("/oauth/authorize");
}
And finally, my console:
2019-06-12 10:29:58.460 INFO 25207 --- [ main] org.hibernate.Version : HHH000412: Hibernate Core {5.3.9.Final}
2019-06-12 10:29:58.461 INFO 25207 --- [ main] org.hibernate.cfg.Environment : HHH000206: hibernate.properties not found
2019-06-12 10:29:58.532 INFO 25207 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.0.4.Final}
2019-06-12 10:29:58.599 INFO 25207 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
2019-06-12 10:29:59.092 INFO 25207 --- [ main] o.h.t.schema.internal.SchemaCreatorImpl : HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl#c30f26d'
2019-06-12 10:29:59.098 INFO 25207 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2019-06-12 10:29:59.382 WARN 25207 --- [ main] o.s.s.o.p.t.s.JwtAccessTokenConverter : Unable to create an RSA verifier from verifierKey (ignoreable if using MAC)
2019-06-12 10:29:59.595 INFO 25207 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: OrRequestMatcher [requestMatchers=[Ant [pattern='/oauth/token'], Ant [pattern='/oauth/token_key'], Ant [pattern='/oauth/check_token']]], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#56a72887, org.springframework.security.web.context.SecurityContextPersistenceFilter#1ddba7a0, org.springframework.security.web.header.HeaderWriterFilter#7adbec34, org.springframework.security.web.authentication.logout.LogoutFilter#296bfddb, org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter#22ab1b8a, org.springframework.security.web.authentication.www.BasicAuthenticationFilter#54033a65, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#7dfec0bc, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#42734b71, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#3c8dea0b, org.springframework.security.web.session.SessionManagementFilter#6fe9c048, org.springframework.security.web.access.ExceptionTranslationFilter#526fc044, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#690e4b00]
2019-06-12 10:29:59.600 INFO 25207 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: OrRequestMatcher [requestMatchers=[Ant [pattern='/iungo/*/**']]], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#1978b0d5, org.springframework.security.web.context.SecurityContextPersistenceFilter#69a3bf40, org.springframework.security.web.header.HeaderWriterFilter#3186f8f5, org.springframework.security.web.authentication.logout.LogoutFilter#a4dcede, org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter#760c777d, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#2c731a16, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#2a341e3d, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#6556471b, org.springframework.security.web.session.SessionManagementFilter#467cd4b9, org.springframework.security.web.access.ExceptionTranslationFilter#3f3f554f, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#d0e4972]
2019-06-12 10:29:59.603 INFO 25207 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: OrRequestMatcher [requestMatchers=[Ant [pattern='/oauth/authorize']]], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#6cf3b3d7, org.springframework.security.web.context.SecurityContextPersistenceFilter#462f8fe9, org.springframework.security.web.header.HeaderWriterFilter#24f2608b, org.springframework.security.web.csrf.CsrfFilter#713497cd, org.springframework.security.web.authentication.logout.LogoutFilter#56193e3a, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#3c6fc4cd, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#b2e1df3, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#2e785b28, org.springframework.security.web.session.SessionManagementFilter#12a9e864, org.springframework.security.web.access.ExceptionTranslationFilter#4b762988]
2019-06-12 10:29:59.731 INFO 25207 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-06-12 10:29:59.902 INFO 25207 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-06-12 10:29:59.903 INFO 25207 --- [ main] nl.highway.iungomain.Application : Started Application in 2.937 seconds (JVM running for 3.345)
2019-06-12 10:29:59.923 INFO 25207 --- [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
2019-06-12 10:30:12.550 INFO 25207 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-06-12 10:30:12.550 INFO 25207 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2019-06-12 10:30:12.554 INFO 25207 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 4 ms
2019-06-12 10:30:12.632 WARN 25207 --- [nio-8080-exec-1] o.s.s.c.bcrypt.BCryptPasswordEncoder : Empty encoded password
I found the problem. I was using Spring boot 2.1.4.RELEASE but this setup only works in 1.5.12.RELEASE. Of course downgrading is not very good practise so I will still try to get it to work with 2.1.4.
you have to add bCryptPasswordEncoder in the configureGlobal method
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder());
}
I was facing the exact same problem. In my case, the cause was my CustomUserDetails class constructor, which didn't set the password to the class, so it was always null.
Credits to this anwser:
why spring security gives empty password to password encoder?
I think #ghazouanbadr's solution is correct. Although, I implemented it differently in my practice project:
#EnableWebSecurity
#Configuration
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Autowired
UserService userService;
#Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserService userService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userService = userService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override // Authorization
protected void configure(HttpSecurity http) throws Exception {
...
}
#Override // Authentication
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userService(userService).passwordEncoder(bCryptPasswordEncoder);
}
}
Be warned, I was only about a month into learning Java when I created this class. Don't remember all to much about security, so it's not my place to tell you that this is the best way to do it. Since I recall dealing with the same issue I figured it might guide you in the right direction.

spring-cloud-config: profile does not work properly

I am learning spring-cloud-config and I would like to use different profiles but I always get back values from the default profile.
I have two property files in the git repository and the file with prod suffix overrides a key from default profile:
image-service.properties
image-service-prod.properties
It seems that my config server works fine:
GET http://localhost:8888/image-service/default:
{"name":"image-service","profiles":["default"],"label":null,"version":"fb78fe4429a33c266d6eb07a9e482b8fd264dd7c","state":null,"propertySources":
[{"name":"https://bitbucket.org/.../...-configuration.git/image-service.properties","source":{"service.image.hello":"image-common"}}]}
GET http://localhost:8888/image-service/prod
{"name":"image-service","profiles":["prod"],"label":null,"version":"fb78fe4429a33c266d6eb07a9e482b8fd264dd7c","state":null,"propertySources":
[{"name":"https://bitbucket.org/.../...-configuration.git/image-service-prod.properties","source":{"service.image.hello":"image-prod"}},
{"name":"https://bitbucket.org/.../...-configuration.git/image-service.properties","source":{"service.image.hello":"image-common"}}]}
I activated prod profile in my REST application but always value from the default profile is shown.
application.properties of the client app:
server.port=8889
spring.application.name=image-service
spring.cloud.config.uri=http://localhost:8888
spring.profiles.active=prod
REST controller of the client app:
#RefreshScope
#RestController
public class EchoController {
#Value("${service.image.hello}")
private String hello;
#RequestMapping("/show")
#ResponseBody
public String showConfig() {
return new StringBuilder()
.append("image-service: ").append(hello)
.toString();
}
}
Result:
image-service: image-common
Log from client app:
c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://localhost:8888
c.c.c.ConfigServicePropertySourceLocator : Located environment: name=image-service, profiles=[default], label=null, version=fb78fe4429a33c266d6eb07a9e482b8fd264dd7c, state=null
b.c.PropertySourceBootstrapConfiguration : Located property source: CompositePropertySource {name='configService', propertySources=[MapPropertySource {name='configClient'}, MapPropertySource {name='https://bitbucket.org/.../...-configuration.git/image-service.properties'}]}
c.r.d.springconfig.client.Application : The following profiles are active: prod
ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext#7ccdc9e7: startup date [Sun Sep 23 22:31:55 CEST 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext#7fd618b5
o.s.cloud.context.scope.GenericScope : BeanFactory id=51790958-c0a2-3d61-91d6-a8dcd5395c7e
trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$bf37cae6] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8889 (http)
It seems I missed something but I can not see what.

Categories

Resources