I started with a spring boot starter project version 2.3.5, but when I call authenticationManager.authenticate I get a stack overflow error.
java.lang.StackOverflowError: null
at ch.qos.logback.classic.Logger.callTurboFilters(Logger.java:751) ~[logback-classic-1.2.3.jar:na]
at ch.qos.logback.classic.Logger.isDebugEnabled(Logger.java:469) ~[logback-classic-1.2.3.jar:na]
at ch.qos.logback.classic.Logger.isDebugEnabled(Logger.java:465) ~[logback-classic-1.2.3.jar:na]
at org.apache.commons.logging.LogAdapter$Slf4jLog.isDebugEnabled(LogAdapter.java:310) ~[spring-jcl-5.2.10.RELEASE.jar:5.2.10.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:186) ~[spring-security-core-5.3.5.RELEASE.jar:5.3.5.RELEASE]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:524) ~[spring-security-config-5.3.5.RELEASE.jar:5.3.5.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:219) ~[spring-security-core-5.3.5.RELEASE.jar:5.3.5.RELEASE]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:524) ~[spring-security-config-5.3.5.RELEASE.jar:5.3.5.RELEASE]
Initially I did not use the user variable and instead created created the new UsernamePasswordAuthenticationToken inside the authenticationManager.authenticate either way I get the stack overflow error. I also have tried #PostMapping instead of #RequestMapping and Method. All of these cause the same failure.
I get the printline that says I am "in /Authenticate" with the following.
2020-11-09 16:20:32.141 DEBUG 10456 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to io.javabrains.springsecurityjwt.HelloResource#createAuthenticationToken(AuthenticationRequest)
2020-11-09 16:20:32.188 DEBUG 10456 --- [nio-8080-exec-2] m.m.a.RequestResponseBodyMethodProcessor : Read "application/json;charset=UTF-8" to [io.javabrains.springsecurityjwt.models.AuthenticationRequest#758a32c5]
in /Authenticate
2020-11-09 16:20:32.197 DEBUG 10456 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Failed to complete request: org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.StackOverflowError
2020-11-09 16:20:32.201 ERROR 10456 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.StackOverflowError] with root cause
Here is the full code of the Class
Thank you for any info or things I should try!
package io.javabrains.springsecurityjwt;
import io.javabrains.springsecurityjwt.models.AuthenticationRequest;
import io.javabrains.springsecurityjwt.models.AuthenticationResponse;
import io.javabrains.springsecurityjwt.services.MyUserDetailsService;
import io.javabrains.springsecurityjwt.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class HelloResource {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private MyUserDetailsService userDetailsService;
#Autowired
private JwtUtil jwtTokenUtil;
#RequestMapping( "/hello")
public String hello() {
return "Hello World";
}
#RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(#RequestBody AuthenticationRequest authenticationRequest) throws Exception {
UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword());
System.out.println("in /Authenticate");
try {
authenticationManager.authenticate(
user
// new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
);
System.out.println("in Try in /Authenticate");
} catch (BadCredentialsException e) {
throw new Exception("Incorrect Username or Password", e);
}
System.out.println("outside the try catch");
final UserDetails userDetails = userDetailsService
.loadUserByUsername(authenticationRequest.getUsername());
final String jwt = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new AuthenticationResponse(jwt));
}
}
Related
I'm creating an user API and trying to implement an authentication method through Spring Boot security.
Even using the correct password and the default Spring Security user user, my Postman still gives me an authorization error. I can't see where the problem is in this code.
Security config:
package com.api.business_products_management.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
return http.build();
}
}
Controller:
package com.api.business_products_management.controllers;
import com.api.business_products_management.dtos.UserDto;
import com.api.business_products_management.models.UserModel;
import com.api.business_products_management.services.UserService;
import jakarta.validation.Valid;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;
#RestController
#CrossOrigin(origins = "*", maxAge = 3600)
#RequestMapping("/user")
public class UserController {
private BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
#PostMapping
public ResponseEntity<Object> saveUser(#RequestBody #Valid UserDto userDto) {
var userModel = new UserModel();
BeanUtils.copyProperties(userDto, userModel);
userModel.setPassword(passwordEncoder().encode(userModel.getPassword()));
return ResponseEntity.status(HttpStatus.CREATED).body(userService.save(userModel));
}
}
Console:
2023-02-17T18:24:13.152-03:00 INFO 8418 --- [nio-8099-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-02-17T18:24:13.152-03:00 INFO 8418 --- [nio-8099-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-02-17T18:24:13.154-03:00 INFO 8418 --- [nio-8099-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
Without seeing your Postman request, my initial guess would be that it's the lack of CSRF (from your Postman request) which is causing the 401. You can read more about CSRF within Spring here: https://docs.spring.io/spring-security/site/docs/5.0.x/reference/html/csrf.html
From that document:
As of Spring Security 4.0, CSRF protection is enabled by default
You can test this theory by temporarily disabling CSRF as shown in the example configuration here:
https://docs.spring.io/spring-security/site/docs/5.0.x/reference/html/csrf.html#csrf-configure
i.e.:
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
return http.build();
}
}
I'd highly recommend reading the rest of the documentation to familiarize yourself with what CSRF protects against and whether it's an acceptable risk to turn it off (or selectively turn disable for certain paths) prior to disabling it in a production environment though.
I have the following code:
package com.example.helloworld;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.annotation.RequestScope;
#RestController
public class HelloWorldController {
#GetMapping("/")
public ResponseEntity<String> getGet(
#Autowired Foo foo) {
foo.sayHi();
return new ResponseEntity<>("OK", HttpStatus.OK);
}
}
#Component
#RequestScope
class Foo {
private #Autowired HttpServletRequest request;
public void sayHi() {
var name = this.request.getHeader("x-user-name");
System.out.println("Hi " + name);
}
}
When I try to curl with curl -H x-user-name:capybara http://localhost:8080, the following error is produced:
2022-09-09 17:07:28.623 ERROR 26350 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException: Cannot invoke "javax.servlet.http.HttpServletRequest.getHeader(String)" because "this.request" is null] with root cause
java.lang.NullPointerException: Cannot invoke "javax.servlet.http.HttpServletRequest.getHeader(String)" because "this.request" is null
at com.example.helloworld.Foo.sayHi(HelloWorldController.java:32) ~[classes!/:0.0.1-SNAPSHOT]
at com.example.helloworld.HelloWorldController.getGet(HelloWorldController.java:19) ~[classes!/:0.0.1-SNAPSHOT]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:577) ~[na:na]
at
...
Why is this.request null? Giving the popularity of https://stackoverflow.com/a/3324233/2287586, seems like this should work, what am I missing?
It doesn't work becuse it needs to be a Singleton bean and you have #RequestScope.
Maybe try this:
((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
My application is running with an exception. When I try to post to localhost:8080/addUser I get the error that there is no mapping. However I have it in my UserController.java:
2021-10-03 20:28:04.852 INFO 9896 --- [ngodb.net:27017] org.mongodb.driver.cluster : Exception in monitor thread while connecting to server edumeet-shard-00-01.egesp.mongodb.net:27017
com.mongodb.MongoSocketReadException: Prematurely reached end of stream
at com.mongodb.internal.connection.SocketStream.read(SocketStream.java:112) ~[mongodb-driver-core-4.2.3.jar:na]
at com.mongodb.internal.connection.SocketStream.read(SocketStream.java:131) ~[mongodb-driver-core-4.2.3.jar:na]
at com.mongodb.internal.connection.InternalStreamConnection.receiveResponseBuffers(InternalStreamConnection.java:647) ~[mongodb-driver-core-4.2.3.jar:na]
at com.mongodb.internal.connection.InternalStreamConnection.receiveMessageWithAdditionalTimeout(InternalStreamConnection.java:512) ~[mongodb-driver-core-4.2.3.jar:na]
at com.mongodb.internal.connection.InternalStreamConnection.receiveCommandMessageResponse(InternalStreamConnection.java:355) ~[mongodb-driver-core-4.2.3.jar:na]
at com.mongodb.internal.connection.InternalStreamConnection.sendAndReceive(InternalStreamConnection.java:279) ~[mongodb-driver-core-4.2.3.jar:na]
at com.mongodb.internal.connection.CommandHelper.sendAndReceive(CommandHelper.java:83) ~[mongodb-driver-core-4.2.3.jar:na]
at com.mongodb.internal.connection.CommandHelper.executeCommand(CommandHelper.java:33) ~[mongodb-driver-core-4.2.3.jar:na]
at com.mongodb.internal.connection.InternalStreamConnectionInitializer.initializeConnectionDescription(InternalStreamConnectionInitializer.java:107) ~[mongodb-driver-core-4.2.3.jar:na]
at com.mongodb.internal.connection.InternalStreamConnectionInitializer.initialize(InternalStreamConnectionInitializer.java:62) ~[mongodb-driver-core-4.2.3.jar:na]
at com.mongodb.internal.connection.InternalStreamConnection.open(InternalStreamConnection.java:144) ~[mongodb-driver-core-4.2.3.jar:na]
at com.mongodb.internal.connection.DefaultServerMonitor$ServerMonitorRunnable.lookupServerDescription(DefaultServerMonitor.java:188) ~[mongodb-driver-core-4.2.3.jar:na]
at com.mongodb.internal.connection.DefaultServerMonitor$ServerMonitorRunnable.run(DefaultServerMonitor.java:144) ~[mongodb-driver-core-4.2.3.jar:na]
at java.base/java.lang.Thread.run(Thread.java:835) ~[na:na]
2021-10-03 20:28:05.681 INFO 9896 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-10-03 20:28:05.692 INFO 9896 --- [ restartedMain] com.example.login.LoginApplication : Started LoginApplication in 3.95 seconds (JVM running for 4.612)
2021-10-03 20:28:17.960 INFO 9896 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-10-03 20:28:17.960 INFO 9896 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2021-10-03 20:28:17.996 INFO 9896 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 36 ms
2021-10-03 20:28:18.025 WARN 9896 --- [nio-8080-exec-2] o.s.web.servlet.PageNotFound : No mapping for POST /addUser
2021-10-03 20:28:18.032 WARN 9896 --- [nio-8080-exec-2] o.s.web.servlet.PageNotFound : No mapping for POST /error
UserController.java
package com.example.login.User;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#Controller
#RestController
public class UserController {
private final UserRepository userRepository;
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
#RequestMapping(path = "/addUser", method = RequestMethod.POST)
public String addUser(#RequestBody User user) {
userRepository.save(user);
return "added user with id: " + user.getId();
}
#RequestMapping(path = "/getAllUsers", method = RequestMethod.GET)
public List<User> getAllUsers() {
return userRepository.findAll();
}
#RequestMapping(path = "/getAllUsers/{id}", method = RequestMethod.GET)
public Optional<User> getUser(#PathVariable String id) {
return userRepository.findById(id);
}
#RequestMapping(path = "/removeUser", method = RequestMethod.DELETE)
public String deleteUser(#PathVariable String id) {
userRepository.deleteById(id);
return "removed user with id" + id;
}
}
LoginApplication.java
package com.example.login;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
#SpringBootApplication
#EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class,WebMvcAutoConfiguration.class})
public class LoginApplication {
public static void main(String[] args) {
SpringApplication.run(LoginApplication.class, args);
}
}
you should change path to value as shown below:
#RequestMapping(path = "/addUser", method = RequestMethod.POST)
public String addUser(#RequestBody User user) {
userRepository.save(user);
return "added user with id: " + user.getId();
}
change to
#RequestMapping(value = "/addUser", method = RequestMethod.POST)
public String addUser(#RequestBody User user) {
userRepository.save(user);
return "added user with id: " + user.getId();
}
I have been trying for hours now, can't seem to fix this. I have a simple Spring Boot Application.
These are the important files:
DemoApplication.java
package com.whatever.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import com.whatever.config.ConfigurationSettings;
#SpringBootApplication
#ComponentScan(basePackages = {"com.whatever.controllers", "com.whatever.config"})
public class DemoApplication{
public static void main(String[] args) {
Class[] sources = {DemoApplication.class, ConfigurationSettings.class};
SpringApplication.run(sources, args);
System.out.println("Hello WORLD!");
}
}
MainController.java
package com.whatever.controllers;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
#RestController
#RequestMapping("/")
public class MainController{
#RequestMapping("/welcome")
public ModelAndView welcome(){
System.out.println("WELCOME!");
ModelAndView mAndView = new ModelAndView();
mAndView.setViewName("welcome");
return mAndView;
}
}
ConfigurationSettings.java
package com.whatever.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
public class ConfigurationSettings extends WebMvcConfigurationSupport{
#Bean
public InternalResourceViewResolver getResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/");
resolver.setSuffix(".jsp");
return resolver;
}
}
My project structure:
--src
--main
--com.whatever.config
--com.whatever.controllers
--com.whatever.demo
--webapp
--WEB-INF
welcome.jsp
However, it can't load the welcome.jsp page and I keep on getting the /error page.
I can't seem to understand the problem.
Any help is appreciated.
P.S: I get the "Hello world!" and "WELCOME!" messages on the console. So, I know the control reaches there.
The StackTrace says:
2018-07-17 16:40:19.869 DEBUG 11004 --- [-auto-13-exec-9] o.s.w.servlet.view.BeanNameViewResolver : No matching bean found for view name 'welcome'
2018-07-17 16:40:19.869 DEBUG 11004 --- [-auto-13-exec-9] o.s.b.f.s.DefaultListableBeanFactory : Invoking afterPropertiesSet() on bean with name 'welcome'
2018-07-17 16:40:19.869 DEBUG 11004 --- [-auto-13-exec-9] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
2018-07-17 16:40:19.869 DEBUG 11004 --- [-auto-13-exec-9] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
2018-07-17 16:40:19.870 DEBUG 11004 --- [-auto-13-exec-9] o.s.web.servlet.DispatcherServlet : Rendering view [org.springframework.web.servlet.view.JstlView: name 'welcome'; URL [/WEB-INF/welcome.jsp]] in DispatcherServlet with name 'dispatcherServlet'
2018-07-17 16:40:19.871 DEBUG 11004 --- [-auto-13-exec-9] o.s.web.servlet.view.JstlView : Forwarding to resource [/WEB-INF/welcome.jsp] in InternalResourceView 'welcome'
2018-07-17 16:40:19.872 DEBUG 11004 --- [-auto-13-exec-9] o.s.web.servlet.DispatcherServlet : Successfully completed request
2018-07-17 16:40:19.875 DEBUG 11004 --- [-auto-13-exec-9] o.s.web.servlet.DispatcherServlet : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/error]
2018-07-17 16:40:19.875 DEBUG 11004 --- [-auto-13-exec-9] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Looking up handler method for path /error
2018-07-17 16:40:19.876 DEBUG 11004 --- [-auto-13-exec-9] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Did not find handler method for [/error]
You have add #RestController in your controller code,if you want to see the jsp page,you need to remove it and change to #Controller,since #RestController will add #ResponseBody to
you controller method automatically
package com.whatever.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import com.whatever.config.ConfigurationSettings;
#Controller("/")
public class MainController{
#RequestMapping("/welcome")
public ModelAndView welcome(){
System.out.println("WELCOME!");
ModelAndView mAndView = new ModelAndView();
mAndView.setViewName("welcome");
return mAndView;
}
}
You have used #RestController , rather use #Controller , It will solve your problem
Please try as below ,
package com.whatever.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import com.whatever.config.ConfigurationSettings;
#Controller
public class MainController{
#RequestMapping("/welcome")
public String welcome(){
System.out.println("WELCOME!");
return "welcome";
}
}
I have run into a strange problem. I created a project with Spring Boot 2.0.1 with redis, mongodb and elasticsearch . Before adding elasticsearch, everything runs smoothly, but after I add elasticsearch, Spring Boot starts complaining,but the error looks like not related to elasticsearch, it complains that it could not create userRepo 。 Please get noticed I used lombok's #RequiredArgsConstructor to generate constructor to make injection work, so it should not be #autowired issue, Anyone can help me out? thank in adavance
2018-05-02 16:12:58.687 INFO 74244 --- [ restartedMain] o.s.b.w.servlet.ServletRegistrationBean : Servlet dispatcherServlet mapped to [/]
2018-05-02 16:12:59.037 WARN 74244 --- [ restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'securityConfig' defined in file [/Users/wangpeng/workspace/books/gtm/backend/api/out/production/classes/dev/local/gtm/api/config/SecurityConfig.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userDetailsServiceImpl' defined in file [/Users/wangpeng/workspace/books/gtm/backend/api/out/production/classes/dev/local/gtm/api/security/UserDetailsServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepo': Invocation of init method failed; nested exception is java.lang.IllegalStateException: No association found!
2018-05-02 16:12:59.037 DEBUG 74244 --- [ restartedMain] h.i.c.PoolingHttpClientConnectionManager : Connection manager is shutting down
2018-05-02 16:12:59.037 DEBUG 74244 --- [ restartedMain] h.i.c.PoolingHttpClientConnectionManager : Connection manager shut down
Process finished with exit code 1
my subproject's build.gradle is as follows:
apply plugin: 'org.springframework.boot'
configurations {
compile.exclude module: 'spring-boot-starter-tomcat'
}
bootRun {
systemProperties = System.properties as Map<String, ?>
}
test {
systemProperties['spring.profiles.active'] = 'test'
}
dependencies {
implementation("io.springfox:springfox-swagger2:${springFoxVersion}")
implementation("io.springfox:springfox-bean-validators:${springFoxVersion}")
implementation("io.springfox:springfox-swagger-ui:${springFoxVersion}")
implementation("org.springframework.boot:spring-boot-starter-undertow")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("io.jsonwebtoken:jjwt:0.9.0")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-aop")
implementation("org.zalando:problem-spring-web:0.20.1")
implementation("org.redisson:redisson:${redissonVersion}")
implementation("com.fasterxml.jackson.module:jackson-module-afterburner")
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
implementation("org.springframework.boot:spring-boot-starter-data-redis")
implementation("com.github.vanroy:spring-boot-starter-data-jest:3.1.2.RELEASE")
testImplementation("org.springframework.security:spring-security-test")
}
The UserRepo is as follows
package dev.local.gtm.api.repository;
import dev.local.gtm.api.domain.User;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
#Repository
public interface UserRepo extends MongoRepository<User, String> {
String USERS_BY_LOGIN_CACHE = "usersByLogin";
String USERS_BY_MOBILE_CACHE = "usersByMobile";
String USERS_BY_EMAIL_CACHE = "usersByEmail";
#Cacheable(cacheNames = USERS_BY_MOBILE_CACHE)
Optional<User> findOneByMobile(#Param("mobile") String mobile);
#Cacheable(cacheNames = USERS_BY_EMAIL_CACHE)
Optional<User> findOneByEmailIgnoreCase(#Param("email") String email);
#Cacheable(cacheNames = USERS_BY_LOGIN_CACHE)
Optional<User> findOneByLogin(#Param("login") String login);
Page<User> findAllByLoginNot(Pageable pageable, #Param("login") String login);
List<User> findAllByActivatedIsFalseAndCreatedDateBefore(Instant dateTime);
}
The Spring Security Configuration is as follows:
package dev.local.gtm.api.config;
import dev.local.gtm.api.security.AuthoritiesConstants;
import dev.local.gtm.api.security.jwt.JWTConfigurer;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.CorsFilter;
import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport;
import javax.annotation.PostConstruct;
#RequiredArgsConstructor
#Configuration
#ComponentScan(basePackages = "dev.local.gtm.api")
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#Import(SecurityProblemSupport.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final AuthenticationManagerBuilder authenticationManagerBuilder;
private final UserDetailsService userDetailsService;
private final CorsFilter corsFilter;
private final SecurityProblemSupport problemSupport;
private final JWTConfigurer jwtConfigurer;
#PostConstruct
public void init() {
try {
authenticationManagerBuilder
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
} catch (Exception e) {
throw new BeanInitializationException("安全配置失败", e);
}
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/test/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.csrf()
.disable()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/websocket/tracker").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/websocket/**").permitAll()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/v2/api-docs/**").permitAll()
.antMatchers("/swagger-resources/configuration/ui").permitAll()
.antMatchers("/swagger-ui/index.html").permitAll()
.and()
.apply(jwtConfigurer);
}
}
The UserDetailServiceImpl is as follows:
package dev.local.gtm.api.security;
import dev.local.gtm.api.config.Constants;
import dev.local.gtm.api.domain.User;
import dev.local.gtm.api.repository.UserRepo;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import lombok.val;
import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.Locale;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
#Log4j2
#RequiredArgsConstructor
#Component("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepo userRepo;
#Override
public UserDetails loadUserByUsername(final String login) {
log.debug("正在对用户名为 {} 的用户进行鉴权", login);
if (new EmailValidator().isValid(login, null)) {
val userByEmailFromDatabase = userRepo.findOneByEmailIgnoreCase(login);
return userByEmailFromDatabase.map(user -> createSpringSecurityUser(login, user))
.orElseThrow(() -> new UsernameNotFoundException("系统中不存在 email 为 " + login + " 的用户"));
}
if (Pattern.matches(Constants.MOBILE_REGEX, login)) {
val userByMobileFromDatabase = userRepo.findOneByMobile(login);
return userByMobileFromDatabase.map(user -> createSpringSecurityUser(login, user))
.orElseThrow(() -> new UsernameNotFoundException("系统中不存在手机号为 " + login + " 的用户"));
}
String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
val userByLoginFromDatabase = userRepo.findOneByLogin(lowercaseLogin);
return userByLoginFromDatabase.map(user -> createSpringSecurityUser(lowercaseLogin, user))
.orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the database"));
}
private org.springframework.security.core.userdetails.User createSpringSecurityUser(String lowercaseLogin, User user) {
if (!user.isActivated()) {
throw new UserNotActivatedException("用户 " + lowercaseLogin + " 没有激活");
}
val grantedAuthorities = user.getAuthorities().stream()
.map(authority -> new SimpleGrantedAuthority(authority.getName()))
.collect(Collectors.toList());
return new org.springframework.security.core.userdetails.User(user.getLogin(),
user.getPassword(),
grantedAuthorities);
}
}
[update] After I change spring-boot-jest to spring-boot-elasticsearch the error is more specific. it now says the elasticsearchTemplate bean is not defined, but in fact it is.
2018-05-02 17:04:59.776 WARN 76262 --- [ restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'securityConfig' defined in file [/Users/wangpeng/workspace/books/gtm/backend/api/out/production/classes/dev/local/gtm/api/config/SecurityConfig.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userDetailsService' defined in file [/Users/wangpeng/workspace/books/gtm/backend/api/out/production/classes/dev/local/gtm/api/security/UserDetailsServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepo': Cannot resolve reference to bean 'elasticsearchTemplate' while setting bean property 'elasticsearchOperations'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'elasticsearchTemplate' available
2018-05-02 17:04:59.805 INFO 76262 --- [ restartedMain] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2018-05-02 17:04:59.822 INFO 76262 --- [ restartedMain] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-05-02 17:04:59.925 ERROR 76262 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in dev.local.gtm.api.security.UserDetailsServiceImpl required a bean named 'elasticsearchTemplate' that could not be found.
Action:
Consider defining a bean named 'elasticsearchTemplate' in your configuration.
Process finished with exit code 1
The bean is defined in ElasticConfig as follows
package dev.local.gtm.api.config;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.elasticsearch.client.Client;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.EntityMapper;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.io.IOException;
#Configuration
#EnableConfigurationProperties(ElasticsearchProperties.class)
#ConditionalOnProperty("spring.data.elasticsearch.cluster-nodes")
public class ElasticConfig {
#Bean
public ElasticsearchTemplate elasticsearchTemplate(Client client, Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder) {
return new ElasticsearchTemplate(client, new CustomEntityMapper(jackson2ObjectMapperBuilder.createXmlMapper(false).build()));
}
public class CustomEntityMapper implements EntityMapper {
private ObjectMapper objectMapper;
public CustomEntityMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
}
#Override
public String mapToString(Object object) throws IOException {
return objectMapper.writeValueAsString(object);
}
#Override
public <T> T mapToObject(String source, Class<T> clazz) throws IOException {
return objectMapper.readValue(source, clazz);
}
}
}
It turns out I reuse same entities for both Elasticsearch and MongoDB, which raise the exception. So I managed to get it resolved by separating the entities