Configuration of Spring security with Spring boot and a Spring MVC Rest API on Jetty - spring-mvc

I'm currently making a Rest API using spring boot and spring mvc hosted on Jetty. At this point everything works. Now I'd like to add spring security but it throws an exception :
FAILED org.springframework.boot.context.embedded.jetty.ServletContextInitializerConfiguration$InitializerListener#36895c35: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public javax.servlet.Filter org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.springSecurityFilterChain() throws java.lang.Exception] threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration.setGlobalAuthenticationConfigurers(java.util.List) throws java.lang.Exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.security.AuthenticationManagerConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.util.List org.springframework.boot.autoconfigure.security.AuthenticationManagerConfiguration.dependencies; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'securityProperties': Could not bind properties; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'bean' of bean class [org.springframework.boot.autoconfigure.security.SecurityProperties]: Bean property 'bean' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
So here is my main class :
#Configuration
#ComponentScan
#EnableAutoConfiguration
#PropertySource({"classpath:configuration.properties"})
#Import({ApplicationConfig.class, SecurityConfig.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Autowired
private Environment environment;
public Environment getEnvironment() {
return environment;
}
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}
Here is my application configuration
#EnableWebMvc
#Configuration
#EnableTransactionManagement
public class ApplicationConfig {
#Autowired
private Environment environment;
public Environment getEnvironment() {
return environment;
}
public void setEnvironment(Environment environment) {
this.environment = environment;
}
#Bean(name = "dataSource")
public DriverManagerDataSource getDataSource() {
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName(this.getEnvironment().getProperty("database.driver"));
driverManagerDataSource.setUrl(this.getEnvironment().getProperty("database.url"));
driverManagerDataSource.setUsername(this.getEnvironment().getProperty("database.username"));
driverManagerDataSource.setPassword(this.getEnvironment().getProperty("database.password"));
return driverManagerDataSource;
}
#Bean(name = "sessionFactory")
public SessionFactory getSessionFactory() {
LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(this.getDataSource());
builder.scanPackages("apt.model").addProperties(this.getHibernateProperties());
return builder.buildSessionFactory();
}
private Properties getHibernateProperties() {
Properties prop = new Properties();
prop.put("hibernate.format_sql", this.getEnvironment().getProperty("database.verbose"));
prop.put("hibernate.show_sql", this.getEnvironment().getProperty("database.verbose"));
prop.put("hibernate.dialect", this.getEnvironment().getProperty("database.dialect"));
prop.put("hbm2ddl.auto", this.getEnvironment().getProperty("database.hbm2ddl"));
prop.put("c3p0.min_size", "5");
prop.put("c3p0.max_size", "50");
prop.put("c3p0.timeout", "300");
prop.put("c3p0.max_statements", "50");
prop.put("c3p0.idle_test_period", "3000");
return prop;
}
#Bean(name = "txManager")
public HibernateTransactionManager getTransactionManager() {
return new HibernateTransactionManager(this.getSessionFactory());
}
}
and here is the security configuration
#Configuration
#EnableWebSecurity
#EnableAutoConfiguration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AccountService accountService;
#Autowired
private AuthenticationService authenticationService;
public AccountService getAccountService() {
return accountService;
}
public void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
public AuthenticationService getAuthenticationService() {
return authenticationService;
}
public void setAuthenticationService(AuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
#Override
public void setAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) {
super.setAuthenticationConfiguration(authenticationConfiguration);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").permitAll().anyRequest().authenticated();
http.formLogin().loginPage("/authentication/login").permitAll().and().logout().permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.getAuthenticationService()).passwordEncoder(this.getPasswordEncoder());
}
#Bean(name = "passwordEncoder")
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
Do you know where it comes from ?

It sounds as if you have a property defined named security.bean which is causing a binding error on Spring Boot's org.springframework.boot.autoconfigure.security.SecurityProperties.
This happens since SecurityProperties is annotated with #ConfigurationProperties(name = "security", ignoreUnknownFields = false) and does not contain a property named bean.
In short, you should not have any properties that start with security. that are not listed in the reference.

Related

Spring redis unable to autowire repository

I'm using custom crudrespository to persist data in redis. However, I'm unable to autowire custom repository.
All the configuration seems correct and redis is running on my local.
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface CustomRepository extends CrudRepository<String,
Long> {
String get(String key);
void put(String key, String value);
}
//////////
public class StorageServiceImpl implements IStorageService {
#Autowired
private CustomRepository respository;
#Override
public void saveParameter() {
this.respository.put("key1","value1");
}
#Override
public String getParameter() {
return this.respository.get("key1");
}
/////
#Service
public interface IStorageService {
void saveParameter();
String getParameter();
}
///////
#SpringBootApplication(scanBasePackages = {"com.example.cache"})
#EnableRedisRepositories(basePackages = {"com.example.cache.repository"})
public class ApplicationConfiguration {
public static void main(String[] args){
SpringApplication.run(ApplicationConfiguration.class, args);
new StorageServiceImpl().saveParameter();
System.out.println(new StorageServiceImpl().getParameter());
}
}
When I try running this application using gradle bootRun, I get
Exception in thread "main" java.lang.NullPointerException
at com.example.cache.impl.StorageServiceImpl.saveParameter(StorageServiceImpl.java:16)
at com.example.cache.ApplicationConfiguration.main(ApplicationConfiguration.java:17)
Not sure what's wrong?
You can't use new on any bean, you need to #Autowire it. The annotations only work with spring managed beans at every level.
Add a new bean with a a storage service and a method that makes your call after it is created.
Also, I can't remember if the spring-boot creates the bean if there is only one implementation but I believe your StorageServiceImpl needs the #Service annotation, not the interface.
Delete this from your ApplicationConfiguration class.
new StorageServiceImpl().saveParameter();
System.out.println(new StorageServiceImpl().getParameter());
Then add this new class.
#Service
public class Startup {
#Autowired
IStorageService storageService;
#PostConstruct
public void init(){
storageService.saveParameter();
System.out.println(storageService().getParameter());
}
}
And you need a config
#Configuration
#EnableRedisRepositories
public class ApplicationConfig {
#Bean
public RedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory();
}
#Bean
public RedisTemplate<?, ?> redisTemplate() {
RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();
return template;
}
}

Throw custom exception in spring boot with an Autowired variable

A custom exception that requires the use of an Autowired variable
// #Component // should be a component to use autowire variable
#ResponseStatus(value = HttpStatus.UNAUTHORIZED)
public class MyException extends Exception {
// This should be commented in
// #Autowired
// public RequestModel reqModel;
// public MyException(ExceptionType type) {
public MyException(ExceptionType type, RequestModel reqModel) {
super();
switch type {
// ...
}
}
}
And then in the rest controllers and interceptors we throw the error
public class MyInterceptor extends HandlerInterceptorAdapter {
// These should be commented out
#Autowired
public RequestModel reqModel;
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// This should be thrown without adding the reqModel as a param
// Unsure how to throw this without using "new"
throw new MyException.MyException(PARAM_MISSING, reqModel);
}
}
I understand that instantiating a class using new will null the Autowired variables. When I tried dropping the new and converting the MyException class to a #Component, I see issues with calling the MyException class constructor.
What is the best way to implement a custom exception in springboot using an autowired variable?

Test Spring Mvc controller and inject static class

The following code is the standard method to write a JUnit test for a Mvc controller.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = ApplicationTestCassandra.class)
#WebAppConfiguration
public class TestControllerTests {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Before
public void setup() throws Exception {
this.mockMvc = webAppContextSetup(webApplicationContext).build();
}
#Test
public void testupTimeStart() throws Exception {
this.mockMvc.perform(get("/uptime"))
.andExpect(status().isOk());
}
}
This works fine, but I would like to replace an autowired class with a special class for testing. The class CassandraSimpleConnection is injected via #Autowired in my controller.
I have tried several approaches, but no luck.
The following code fails because of an Mvc 404 error, because I guess my application with the REST interface is not running at all.
#RunWith(SpringJUnit4ClassRunner.class)
//ApplicationTestCassandra is SpringBoot application startpoint class with #SpringBootApplication annotation
//#ContextConfiguration(classes = ApplicationTestCassandra.class, loader = AnnotationConfigContextLoader.class)
#ContextConfiguration(loader = AnnotationConfigWebContextLoader.class)//, classes = {ApplicationTestCassandra.class})
#WebAppConfiguration
public class TestControllerTests {
#Service
#EnableWebMvc
#ComponentScan(basePackages={"blabla.functionalTests"})
static class CassandraSimpleConnection {
public Metadata testConnection(TestConfiguration configuration) {
Metadata metadata = null;
// return metadata;
throw new RuntimeException("Could not connect to any server");
}
}
If I use
#ContextConfiguration(loader = AnnotationConfigWebContextLoader.class, classes = {ApplicationTestCassandra.class})
CassandraSimpleConnection is not replaced with my static class.
Could somebody help me please? The documentation about the annotations is quite confusing.
Read the comments and here is the solution:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { MyApplication.class })
public class MyTests {
#MockBean
private MyBeanClass myTestBean;
#Before
public void setup() {
...
when(myTestBean.doSomething()).thenReturn(someResult);
}
#Test
public void test() {
// MyBeanClass bean is replaced with myTestBean in the ApplicationContext here
}
}

RunWith SpringJUnit4ClassRunner gives error fail to load ApplicationContext with InitializingBean

I am using InitializingBean to initialise static properties in a modal class. This object I am auto wiring in a service
When I write a test case of service, I throws error: Failed to load ApplicationContext
Config class
public class AppConfig {
private String prop1;
protected void setProp1(String prop) {
this.prop1 = prop;
}
public String getProp1() {
return prop1;
}
}
PropertyIntilizer class
public class PropertyIntializer implements InitializingBean {
#Autowired
private AppConfig appConfig;
#Override
public void afterPropertiesSet() throws Exception {
appConfig.setProp1("PROP");
}
}
Service Class
#Service
public class Service {
#Autowired
private AppConfig appConfig;
public void doSomething(){
System.out.println(appConfig.getProp1());
}
}
TestClass
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { TestConfig.class })
public class ServiceTest {
#Autowired
private Service service;
#Test
public void testService(){
service.doSomething();
}
}
This gives an error : Failed to load ApplicationContext
But when I remove Autowired AppConfig, it works
Edit:
TestConfig class
#Configuration
#ComponentScan(basePackages = { "base.package" })
public class TestConfig {
}
my main classes are in base.package.main and test classes in base.package.test
similar issue got resolved for me by adding JRE(thats comes with application server in my case websphere) in buid path

spring mvc request method 404 error

#Controller("/UserAction")
#RequestMapping("/greet.json")
public class UserAction extends BaseAction {
#RequestMapping(value = "/hello", method = RequestMethod.GET)
public void hello(HttpServletRequest request,HttpServletResponse response) {
System.out.println("a");
}
#RequestMapping(value = "/word", method = RequestMethod.GET)
public void word(HttpServletRequest request,HttpServletResponse response) {
System.out.println("123123###");
}
}
when I look http://localhost:8080/ProjectName/greet.json/hello
and http://localhost:8080/ProjectName/greet.json/word
can't excute the syso method
Mapped "{[/greet.json],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public void cn.elfsoft.controller.UserAction.hello(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
08:33:38,488 INFO [org.springframework.beans.factory.support.DefaultListableBeanFactory] (org.springframework.beans.factory.support.DefaultSingletonBeanRegistry:433) - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#4eed49c9: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,AnswerAction,FileResourceAction,SendMessageAction,SubjectAction,userAction,videoAction,baseDAO,FileResource,QuestionDao,SubjectDao,videoDao,FileResourceService,QuestionService,SubjectService,UserService,videoService,org.springframework.web.servlet.view.InternalResourceViewResolver#0,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#0,org.springframework.web.servlet.handler.MappedInterceptor#0,org.springframework.aop.config.internalAutoProxyCreator,cn.elfsoft.log.LogAdvice#0,dataSource,org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#0,sessionFactory,jdbcTemplate,txManager,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor,businessService,org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0,txAdvice,org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0]; root of factory hierarchy
08:33:38,490 ERROR [org.springframework.web.servlet.DispatcherServlet] (org.springframework.web.servlet.FrameworkServlet:460) - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0' defined in ServletContext resource [/WEB-INF/web-config.xml]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'userAction' bean method
public void cn.elfsoft.controller.UserAction.word(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
to {[/greet.json],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}: There is already 'userAction' bean method
public void cn.elfsoft.controller.UserAction.hello(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) mapped.
I think you have 2 annotations that confuse your operation, try to use only this:
#Controller
public class UserAction extends BaseAction {
#RequestMapping(value = "/greet.json/hello", method = RequestMethod.GET)
public void hello(HttpServletRequest request,HttpServletResponse response) {
System.out.println("a");
}
#RequestMapping(value = "/greet.json/word", method = RequestMethod.GET)
public void word(HttpServletRequest request,HttpServletResponse response) {
System.out.println("123123###");
}
And if you want to use a common path to your controller add to
#Controller("/commonPath")
And later on each method with the
#RequestMapping("/specificEndPoint")

Resources