1. First let's configure all the necessary dependencies in pom.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
<modelVersion>4.0.0</modelVersion> | |
<groupId>com.sivalabs</groupId> | |
<artifactId>spring-javaconfig</artifactId> | |
<version>1.0</version> | |
<packaging>war</packaging> | |
<name>SpringApp JavaConfig Demo</name> | |
<properties> | |
<java.version>1.7</java.version> | |
<junit.version>4.11</junit.version> | |
<slf4j.version>1.7.5</slf4j.version> | |
<logback.version>1.0.13</logback.version> | |
<spring.version>4.0.0.RELEASE</spring.version> | |
<spring-data-jpa.version>1.4.1.RELEASE</spring-data-jpa.version> | |
<spring-security.version>3.2.0.RELEASE</spring-security.version> | |
<hibernate.version>4.2.6.Final</hibernate.version> | |
<aspectj.version>1.7.2</aspectj.version> | |
<mysql.version>5.1.26</mysql.version> | |
<jackson-json.version>2.3.1</jackson-json.version> | |
<commons-dbcp.version>1.2.2</commons-dbcp.version> | |
<commons-lang3.version>3.1</commons-lang3.version> | |
</properties> | |
<build> | |
<finalName>${project.artifactId}</finalName> | |
<plugins> | |
<plugin> | |
<groupId>org.apache.maven.plugins</groupId> | |
<artifactId>maven-compiler-plugin</artifactId> | |
<version>3.1</version> | |
<configuration> | |
<source>${java.version}</source> | |
<target>${java.version}</target> | |
</configuration> | |
</plugin> | |
</plugins> | |
</build> | |
<dependencies> | |
<!-- Logging dependencies --> | |
<dependency> | |
<groupId>org.slf4j</groupId> | |
<artifactId>jcl-over-slf4j</artifactId> | |
<version>${slf4j.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.slf4j</groupId> | |
<artifactId>slf4j-api</artifactId> | |
<version>${slf4j.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.slf4j</groupId> | |
<artifactId>slf4j-log4j12</artifactId> | |
<version>${slf4j.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>ch.qos.logback</groupId> | |
<artifactId>logback-classic</artifactId> | |
<version>${logback.version}</version> | |
</dependency> | |
<!-- Spring dependencies --> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-context-support</artifactId> | |
<exclusions> | |
<exclusion> | |
<groupId>commons-logging</groupId> | |
<artifactId>commons-logging</artifactId> | |
</exclusion> | |
</exclusions> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-webmvc</artifactId> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-test</artifactId> | |
</dependency> | |
<!-- Spring Data JPA dependencies --> | |
<dependency> | |
<groupId>org.springframework.data</groupId> | |
<artifactId>spring-data-jpa</artifactId> | |
<version>${spring-data-jpa.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.hibernate</groupId> | |
<artifactId>hibernate-entitymanager</artifactId> | |
<version>${hibernate.version}</version> | |
</dependency> | |
<!-- SpringSecurity dependencies --> | |
<dependency> | |
<groupId>org.springframework.security</groupId> | |
<artifactId>spring-security-core</artifactId> | |
<version>${spring-security.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.security</groupId> | |
<artifactId>spring-security-web</artifactId> | |
<version>${spring-security.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.security</groupId> | |
<artifactId>spring-security-config</artifactId> | |
<version>${spring-security.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.security</groupId> | |
<artifactId>spring-security-taglibs</artifactId> | |
<version>${spring-security.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.aspectj</groupId> | |
<artifactId>aspectjweaver</artifactId> | |
<version>${aspectj.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.aspectj</groupId> | |
<artifactId>aspectjrt</artifactId> | |
<version>${aspectj.version}</version> | |
</dependency> | |
<!-- Testing dependencies --> | |
<dependency> | |
<groupId>junit</groupId> | |
<artifactId>junit</artifactId> | |
<version>${junit.version}</version> | |
<scope>test</scope> | |
</dependency> | |
<!-- DB dependencies --> | |
<dependency> | |
<groupId>mysql</groupId> | |
<artifactId>mysql-connector-java</artifactId> | |
<version>${mysql.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>commons-dbcp</groupId> | |
<artifactId>commons-dbcp</artifactId> | |
<version>${commons-dbcp.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>com.fasterxml.jackson.core</groupId> | |
<artifactId>jackson-databind</artifactId> | |
<version>${jackson-json.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>javax.mail</groupId> | |
<artifactId>mail</artifactId> | |
<version>1.4.3</version> | |
</dependency> | |
<!-- Web dependencies --> | |
<dependency> | |
<groupId>javax.servlet</groupId> | |
<artifactId>javax.servlet-api</artifactId> | |
<version>3.0.1</version> | |
<scope>provided</scope> | |
</dependency> | |
<dependency> | |
<groupId>taglibs</groupId> | |
<artifactId>standard</artifactId> | |
<version>1.1.2</version> | |
<scope>compile</scope> | |
</dependency> | |
<dependency> | |
<groupId>jstl</groupId> | |
<artifactId>jstl</artifactId> | |
<version>1.2</version> | |
<scope>compile</scope> | |
</dependency> | |
</dependencies> | |
<dependencyManagement> | |
<dependencies> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-framework-bom</artifactId> | |
<version>${spring.version}</version> | |
<type>pom</type> | |
<scope>import</scope> | |
</dependency> | |
</dependencies> | |
</dependencyManagement> | |
</project> |
2. Configure database connection properties and email settings in application.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
################### DataSource Configuration ########################## | |
jdbc.driverClassName=com.mysql.jdbc.Driver | |
jdbc.url=jdbc:mysql://localhost:3306/test | |
jdbc.username=root | |
jdbc.password=admin | |
init-db=false | |
################### Hibernate Configuration ########################## | |
hibernate.dialect=org.hibernate.dialect.MySQLDialect | |
hibernate.show_sql=true | |
hibernate.hbm2ddl.auto=update | |
################### JavaMail Configuration ########################## | |
smtp.host=smtp.gmail.com | |
smtp.port=465 | |
smtp.protocol=smtps | |
smtp.username=sivaprasadreddy.k@gmail.com | |
smtp.password= | |
support.email=sivaprasadreddy.k@gmail.com |
3. Configure common Service Layer beans such as PropertySourcesPlaceholderConfigurer and JavaMailSender etc in com.sivalabs.springapp.config.AppConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.sivalabs.springapp.config; | |
import java.util.Properties; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.cache.CacheManager; | |
import org.springframework.cache.annotation.EnableCaching; | |
import org.springframework.cache.concurrent.ConcurrentMapCacheManager; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.ComponentScan; | |
import org.springframework.context.annotation.Configuration; | |
import org.springframework.context.annotation.EnableAspectJAutoProxy; | |
import org.springframework.context.annotation.FilterType; | |
import org.springframework.context.annotation.PropertySource; | |
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; | |
import org.springframework.core.env.Environment; | |
import org.springframework.mail.javamail.JavaMailSenderImpl; | |
import org.springframework.scheduling.annotation.EnableScheduling; | |
@Configuration | |
@ComponentScan(basePackages={"com.sivalabs.springapp"}, | |
excludeFilters=@ComponentScan.Filter(type=FilterType.REGEX, pattern={"com.sivalabs.springapp.web.*"})) | |
@PropertySource(value = { "classpath:application.properties" }) | |
@EnableScheduling | |
@EnableAspectJAutoProxy | |
@EnableCaching | |
public class AppConfig | |
{ | |
@Autowired | |
private Environment env; | |
@Bean | |
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() | |
{ | |
return new PropertySourcesPlaceholderConfigurer(); | |
} | |
@Bean | |
public JavaMailSenderImpl javaMailSenderImpl() { | |
JavaMailSenderImpl mailSenderImpl = new JavaMailSenderImpl(); | |
mailSenderImpl.setHost(env.getProperty("smtp.host")); | |
mailSenderImpl.setPort(env.getProperty("smtp.port", Integer.class)); | |
mailSenderImpl.setProtocol(env.getProperty("smtp.protocol")); | |
mailSenderImpl.setUsername(env.getProperty("smtp.username")); | |
mailSenderImpl.setPassword(env.getProperty("smtp.password")); | |
Properties javaMailProps = new Properties(); | |
javaMailProps.put("mail.smtp.auth", true); | |
javaMailProps.put("mail.smtp.starttls.enable", true); | |
mailSenderImpl.setJavaMailProperties(javaMailProps); | |
return mailSenderImpl; | |
} | |
@Bean | |
public CacheManager cacheManager() | |
{ | |
return new ConcurrentMapCacheManager(); | |
} | |
} |
Observe that we have excluded the package "com.sivalabs.springapp.web.*" from component scanning using new REGEX excludeFilter type.
If we don't exclude web related packages and tries to run JUnit test for service layer beans we will encounter the following Exception:
java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
Also note that we have enabled Caching using @EnableCaching, so we should declare CacheManager bean.
4. Configure Persistence Layer beans in com.sivalabs.springapp.config.PersistenceConfig.java as follows:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.sivalabs.springapp.config; | |
import java.util.Properties; | |
import javax.persistence.EntityManagerFactory; | |
import javax.sql.DataSource; | |
import org.apache.commons.dbcp.BasicDataSource; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.beans.factory.annotation.Value; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.Configuration; | |
import org.springframework.core.env.Environment; | |
import org.springframework.core.io.ClassPathResource; | |
import org.springframework.data.jpa.repository.config.EnableJpaRepositories; | |
import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver; | |
import org.springframework.jdbc.datasource.init.DataSourceInitializer; | |
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; | |
import org.springframework.orm.hibernate4.HibernateExceptionTranslator; | |
import org.springframework.orm.jpa.JpaTransactionManager; | |
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; | |
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; | |
import org.springframework.transaction.PlatformTransactionManager; | |
import org.springframework.transaction.annotation.EnableTransactionManagement; | |
@Configuration | |
@EnableTransactionManagement | |
@EnableJpaRepositories(basePackages="com.sivalabs.springapp.repositories") | |
public class PersistenceConfig | |
{ | |
@Autowired | |
private Environment env; | |
@Value("${init-db:false}") | |
private String initDatabase; | |
@Bean | |
public PlatformTransactionManager transactionManager() | |
{ | |
EntityManagerFactory factory = entityManagerFactory().getObject(); | |
return new JpaTransactionManager(factory); | |
} | |
@Bean | |
public LocalContainerEntityManagerFactoryBean entityManagerFactory() | |
{ | |
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); | |
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); | |
vendorAdapter.setGenerateDdl(Boolean.TRUE); | |
vendorAdapter.setShowSql(Boolean.TRUE); | |
factory.setDataSource(dataSource()); | |
factory.setJpaVendorAdapter(vendorAdapter); | |
factory.setPackagesToScan("com.sivalabs.springapp.entities"); | |
Properties jpaProperties = new Properties(); | |
jpaProperties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); | |
factory.setJpaProperties(jpaProperties); | |
factory.afterPropertiesSet(); | |
factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver()); | |
return factory; | |
} | |
@Bean | |
public HibernateExceptionTranslator hibernateExceptionTranslator() | |
{ | |
return new HibernateExceptionTranslator(); | |
} | |
@Bean | |
public DataSource dataSource() | |
{ | |
BasicDataSource dataSource = new BasicDataSource(); | |
dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName")); | |
dataSource.setUrl(env.getProperty("jdbc.url")); | |
dataSource.setUsername(env.getProperty("jdbc.username")); | |
dataSource.setPassword(env.getProperty("jdbc.password")); | |
return dataSource; | |
} | |
@Bean | |
public DataSourceInitializer dataSourceInitializer(DataSource dataSource) | |
{ | |
DataSourceInitializer dataSourceInitializer = new DataSourceInitializer(); | |
dataSourceInitializer.setDataSource(dataSource); | |
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(); | |
databasePopulator.addScript(new ClassPathResource("db.sql")); | |
dataSourceInitializer.setDatabasePopulator(databasePopulator); | |
dataSourceInitializer.setEnabled(Boolean.parseBoolean(initDatabase)); | |
return dataSourceInitializer; | |
} | |
} |
Here we have configured DataSource and JPA EntityManagerFactory bean using Hibernate implementation.
Also we have configured DataSourceInitializer bean to initialize and populate our tables with seed data. We can enable/disable executing this db.sql script by changing init-db property value in application.properties.
And finally we have enabled Spring Data JPA repositories scanning using @EnableJpaRepositories to scan "com.sivalabs.springapp.repositories" package for JPA repository interfaces.
5. Now let us configure Web related beans in com.sivalabs.springapp.web.config.WebMvcConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.sivalabs.springapp.web.config; | |
import java.util.Properties; | |
import org.springframework.context.MessageSource; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.ComponentScan; | |
import org.springframework.context.annotation.Configuration; | |
import org.springframework.context.support.ReloadableResourceBundleMessageSource; | |
import org.springframework.web.servlet.ViewResolver; | |
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; | |
import org.springframework.web.servlet.config.annotation.EnableWebMvc; | |
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; | |
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; | |
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; | |
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; | |
import org.springframework.web.servlet.view.InternalResourceViewResolver; | |
@Configuration | |
@ComponentScan(basePackages = { "com.sivalabs.springapp.web"}) | |
@EnableWebMvc | |
public class WebMvcConfig extends WebMvcConfigurerAdapter | |
{ | |
@Override | |
public void addViewControllers(ViewControllerRegistry registry) | |
{ | |
super.addViewControllers(registry); | |
registry.addViewController("login/form").setViewName("login"); | |
registry.addViewController("welcome").setViewName("welcome"); | |
registry.addViewController("admin").setViewName("admin"); | |
} | |
@Bean | |
public ViewResolver resolver() | |
{ | |
InternalResourceViewResolver url = new InternalResourceViewResolver(); | |
url.setPrefix("/WEB-INF/jsp/"); | |
url.setSuffix(".jsp"); | |
return url; | |
} | |
@Override | |
public void addResourceHandlers(ResourceHandlerRegistry registry) | |
{ | |
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/"); | |
} | |
@Override | |
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) | |
{ | |
configurer.enable(); | |
} | |
@Bean(name = "messageSource") | |
public MessageSource configureMessageSource() | |
{ | |
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); | |
messageSource.setBasename("classpath:messages"); | |
messageSource.setCacheSeconds(5); | |
messageSource.setDefaultEncoding("UTF-8"); | |
return messageSource; | |
} | |
@Bean | |
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() | |
{ | |
SimpleMappingExceptionResolver b = new SimpleMappingExceptionResolver(); | |
Properties mappings = new Properties(); | |
mappings.put("org.springframework.dao.DataAccessException", "error"); | |
b.setExceptionMappings(mappings); | |
return b; | |
} | |
} |
6. Configure DispatcherService using AbstractAnnotationConfigDispatcherServletInitializer convinient class.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.sivalabs.springapp.web.config; | |
import javax.servlet.Filter; | |
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter; | |
import org.springframework.web.filter.DelegatingFilterProxy; | |
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; | |
import com.sivalabs.springapp.config.AppConfig; | |
public class SpringWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer | |
{ | |
@Override | |
protected Class<?>[] getRootConfigClasses() | |
{ | |
return new Class<?>[] { AppConfig.class}; | |
} | |
@Override | |
protected Class<?>[] getServletConfigClasses() | |
{ | |
return new Class<?>[] { WebMvcConfig.class }; | |
} | |
@Override | |
protected String[] getServletMappings() | |
{ | |
return new String[] { "/" }; | |
} | |
@Override | |
protected Filter[] getServletFilters() { | |
return new Filter[]{ | |
new OpenEntityManagerInViewFilter() | |
}; | |
} | |
} |
Here few things to note are we configured AppConfig.class as RootConfig classes and WebMvcConfig.class as ServletConfigClasses which is similar to how we configure in web.xml using ContextLoaderListener and DispatcherServlet's contextConfigLocation
Also we have registered OpenEntityManagerInViewFilter to enable lazy loading of JPA entity graphs in view rendering phase.
7. Let us configure SpringSecurity.
First let us create a SecurityUser class which extends our application specific User class and implements org.springframework.security.core.userdetails.UserDetails.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.sivalabs.springapp.web.config; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.Set; | |
import org.springframework.security.core.GrantedAuthority; | |
import org.springframework.security.core.authority.SimpleGrantedAuthority; | |
import org.springframework.security.core.userdetails.UserDetails; | |
import com.sivalabs.springapp.entities.Role; | |
import com.sivalabs.springapp.entities.User; | |
public class SecurityUser extends User implements UserDetails | |
{ | |
private static final long serialVersionUID = 1L; | |
public SecurityUser(User user) { | |
if(user != null) | |
{ | |
this.setId(user.getId()); | |
this.setName(user.getName()); | |
this.setEmail(user.getEmail()); | |
this.setPassword(user.getPassword()); | |
this.setDob(user.getDob()); | |
this.setRoles(user.getRoles()); | |
} | |
} | |
@Override | |
public Collection<? extends GrantedAuthority> getAuthorities() { | |
Collection<GrantedAuthority> authorities = new ArrayList<>(); | |
Set<Role> userRoles = this.getRoles(); | |
if(userRoles != null) | |
{ | |
for (Role role : userRoles) { | |
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.getRoleName()); | |
authorities.add(authority); | |
} | |
} | |
return authorities; | |
} | |
@Override | |
public String getPassword() { | |
return super.getPassword(); | |
} | |
@Override | |
public String getUsername() { | |
return super.getEmail(); | |
} | |
@Override | |
public boolean isAccountNonExpired() { | |
return true; | |
} | |
@Override | |
public boolean isAccountNonLocked() { | |
return true; | |
} | |
@Override | |
public boolean isCredentialsNonExpired() { | |
return true; | |
} | |
@Override | |
public boolean isEnabled() { | |
return true; | |
} | |
} |
We will implement a custom UserDetailsService and use Spring Data JPA repositories to load User details.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.sivalabs.springapp.config; | |
import org.springframework.beans.factory.annotation.Autowired; | |
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 com.sivalabs.springapp.entities.User; | |
import com.sivalabs.springapp.services.UserService; | |
import com.sivalabs.springapp.web.config.SecurityUser; | |
@Component | |
public class CustomUserDetailsService implements UserDetailsService | |
{ | |
@Autowired | |
private UserService userService; | |
@Override | |
public UserDetails loadUserByUsername(String userName) | |
throws UsernameNotFoundException { | |
User user = userService.findUserByEmail(userName); | |
if(user == null){ | |
throw new UsernameNotFoundException("UserName "+userName+" not found"); | |
} | |
return new SecurityUser(user); | |
} | |
} |
Now create com.sivalabs.springapp.config.SecurityConfig.java which contains SpeingSecurity related bean definitions.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.sivalabs.springapp.config; | |
import javax.sql.DataSource; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.context.annotation.Configuration; | |
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.core.userdetails.UserDetailsService; | |
@Configuration | |
@EnableWebSecurity | |
public class SecurityConfig extends WebSecurityConfigurerAdapter | |
{ | |
@Autowired | |
private DataSource dataSource; | |
@Autowired | |
private CustomUserDetailsService customUserDetailsService; | |
@Override | |
protected void configure(AuthenticationManagerBuilder registry) throws Exception { | |
/* | |
registry | |
.inMemoryAuthentication() | |
.withUser("siva") | |
.password("siva") | |
.roles("USER") | |
.and() | |
.withUser("admin") | |
.password("admin") | |
.roles("ADMIN","USER"); | |
*/ | |
//registry.jdbcAuthentication().dataSource(dataSource); | |
registry.userDetailsService(customUserDetailsService); | |
} | |
@Override | |
public void configure(WebSecurity web) throws Exception { | |
web | |
.ignoring() | |
.antMatchers("/resources/**"); | |
} | |
@Override | |
protected void configure(HttpSecurity http) throws Exception { | |
http | |
.csrf().disable() | |
.authorizeRequests() | |
.antMatchers("/login","/login/form**","/register","/logout").permitAll() | |
.antMatchers("/admin","/admin/**").hasRole("ADMIN") | |
.anyRequest().authenticated() | |
.and() | |
.formLogin() | |
.loginPage("/login/form") | |
.loginProcessingUrl("/login") | |
.failureUrl("/login/form?error") | |
.permitAll(); | |
} | |
} |
Update SpringWebAppInitializer which we created eariler to add SecurityConfig configuration class.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class SpringWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer | |
{ | |
@Override | |
protected Class<?>[] getRootConfigClasses() | |
{ | |
return new Class<?>[] { AppConfig.class}; | |
//As we have SecurityConfig.java in same package as AppConfig.java and enabled ComponentScan to scan "com.sivalabs.springapp.config" we don't need to explicitely configure it. | |
//otherwise we should add SecurityConfig.class to getRootConfigClasses() | |
//return new Class<?>[] { AppConfig.class, SecurityConfig.class}; | |
} | |
... | |
... | |
@Override | |
protected Filter[] getServletFilters() { | |
return new Filter[]{ | |
new DelegatingFilterProxy("springSecurityFilterChain"), | |
new OpenEntityManagerInViewFilter()}; | |
} | |
} |
As per our SpringSecurity custom Form Login configuration, we will use the following login form in login.jsp.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%> | |
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form" %> | |
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> | |
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %> | |
<c:url var="rootURL" value="/"/> | |
<html> | |
<head> | |
<title>Login</title> | |
<link href="${rootURL}resources/bootstrap/css/bootstrap.css" media="screen" rel="stylesheet" type="text/css" /> | |
<script type="text/javascript" src="${rootURL}resources/jquery/jquery-1.10.2.js"></script> | |
<script type="text/javascript" src="${rootURL}resources/bootstrap/js/bootstrap.js"></script> | |
<script type="text/javascript" src="${rootURL}resources/js/app.js"></script> | |
</head> | |
<body> | |
<div class="col-md-6 col-md-offset-2"> | |
<c:if test="${param.error != null}"> | |
<div class="alert alert-danger"> | |
Invalid UserName and Password. | |
</div> | |
</c:if> | |
<c:if test="${param.logout != null}"> | |
<div class="alert alert-success"> | |
You have been logged out. | |
</div> | |
</c:if> | |
</div> | |
<div class="row"> | |
<div class="col-md-6 col-md-offset-2"> | |
<h2>User Login Form</h2> | |
<form:form id="loginForm" method="post" action="${rootURL}login" modelAttribute="user" | |
class="form-horizontal" role="form" cssStyle="width: 800px; margin: 0 auto;"> | |
<div class="form-group"> | |
<label for="username" class="col-sm-2 control-label">UserName*</label> | |
<div class="col-sm-4"> | |
<input type="text" id="username" name="username" class="form-control" placeholder="UserName" /> | |
</div> | |
</div> | |
<div class="form-group"> | |
<label for="password" class="col-sm-2 control-label">Password*</label> | |
<div class="col-sm-4"> | |
<input type="password" id="password" name="password" class="form-control" placeholder="Password" /> | |
</div> | |
</div> | |
<div class="form-group"> | |
<div class="col-sm-offset-2 col-sm-4"> | |
<input type="submit" class="btn btn-primary" value="Login"> | |
</div> | |
</div> | |
</form:form> | |
</div> | |
</div> | |
</body> | |
</html> |
Once we successfully login we can obtain the authenticated use details using
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<h3>Email: <sec:authentication property="name"/></h3> | |
<h3> | |
<sec:authorize access="hasRole('ROLE_ADMIN')"> | |
<a href="admin">Administration</a> | |
</sec:authorize> | |
</h3> | |
<p> <a href="logout">Logout</a></p> | |
</body> |
You can find the source code at github https://github.com/sivaprasadreddy/sivalabs-blog-samples-code/tree/master/springmvc-datajpa-security-demo
There are few issues while running the same application on JBoss AS 7.1. I have made few changes to run on JBossAS7.1 and published code at https://github.com/sivaprasadreddy/sivalabs-blog-samples-code/tree/master/springmvc-datajpa-security-demo-jboss7
Hi siva;
ReplyDeletethis is a greate work, thinks a lot.
just one thing : when accessing admin pages without ADMIN_ROLE we need to add Access Denied Page (i.e handling org.springframework.security.access.AccessDeniedException)
Hi siva ;
ReplyDeletevery helpful tuto, greate work, thinks a lot.
just one question : how to implement AccessDeniedHandler using JavaConfig ?
Please see http://www.mkyong.com/spring-security/customize-http-403-access-denied-page-in-spring-security/
DeleteHi siva:
ReplyDeleteThanks a lot, I've question about java config, Please clarify me.
In my organization we are using JDK 6/JROCKIT and JBOSS 7.1.1 AS for develops the application,
We are developing Spring MVC 3 application using old style xml configuration .
Now i planned to migrate my Spring MVC 3 + Hibernate xml configuration application to pure java config application and i followed your tutorial series.
Unfortunately My Spring MVC 3 + Hibernate pure java config application does not works on jboss 7.1 AS, But it works on both tomcat and jetty server
With xml configuration working fine in jboss 7.1 AS. And I'm not tested the application with new WILDFLY 8 AS.Because we are using JDK 6/JROCKIT only.
is there any problem in JBOSS 7 AS with SPRING MVC java config ?
and
should i upgrade JAVA version 6 to 7 and JBOSS 7 to WILDFLY 8 ?
Please answer me.
Thanks & Regards
Kudpudeen
What issues you are facing?
DeleteI'm getting 404 error page,
Deleteplease help me
Hi Siva
DeleteI compiled this springmvc4-spring-data-jpa sample application in JDK 6 with small modification in its sources.
Unfortunately i got 404 error while deploying this springmvc4-spring-data-jpa sample application on JBOSS 7.1.1 AS. no mapping found in JBOSS log.
But it successfully deployed and executed in tomcat 7 server
is there problem in JBOSS 7 AS with SPRING MVC java config ?
Please help me.
Hi,
DeleteI tried deploying on JBossAS71.1 and i am also facing issues.
Still I couldn't figure out the root cause of the issue, but after few experiments I could deploy the app with few tweaks.
Give me your email id, i will email you the code.
This comment has been removed by the author.
DeleteSent the updated code.
DeleteThanks It works for me
DeleteHi Siva,
ReplyDeleteThank you for your solution.
I would like to use it with Jboss and I have some error (as Kudpudeen .M) . if you can send me your jboss version I will be very happy.
Thank you
Hi,
DeleteI have published code for JBoss AS 7.1 changes at https://github.com/sivaprasadreddy/sivalabs-blog-samples-code/tree/master/springmvc-datajpa-security-demo-jboss7.
Hi Siva,
DeleteIt's working well !!!!
Sorry for the delay to answer ^^
What did you change just to know what have you done ?
Thank you again mate !
Hi there
ReplyDeleteI'm currently facing an issue with Spring Data JPA and Spring Security. When i call the getOne method from my UserDetailsService implementation, it returns a null object, i've already setup the database connection using a jndi server resource.
Is there an additional config for Spring Data JPA and Spring Security??
Hi,
DeletePlease try to check whether the Spring Data JPA methods are working fine or not first, just to figure out where is the root cause.
Sometime back I also faced this kind of issue, spending a lot of time looking into Security configuration whereas the actual issue is with my Spring Data JPA Repository query.
Hello Sival,
ReplyDeleteI want to understand why did you disable csrf() ?
thanks
Hi Siva,
ReplyDeleteI wanted to ask, once you have a userdetailsservice and wish to add a "Sign up new user" functionality, how do you add a new user to the repo?
Good work mate. Need your help here, I'm not able to login. I have created User table and did required entries. but it's not working. On debug I found that you don't have UserRepositary implementation class. How does that work.
ReplyDeleteHi,
DeleteWe don't need to implement Spring Data JPA interfaces, spring will do at runtime.
What error you are getting?
Got that error and it was my mistake (in fact a very stupid one :), named USERS table as USER). This is sorted. But after I got over this, I got another issue where in it was looking for some authorities table. What was that? What is that used for. I just created a table with this name and did required entries but I'm not able to get the context here. Please guide me and yes many thanks for that prompt response.
ReplyDeleteHi,
DeletePlease take a look at the Spring Security database shcema for managing users and roles. http://docs.spring.io/spring-security/site/docs/3.0.x/reference/appendix-schema.html
Thanks Siva
DeleteHi,
ReplyDeleteHow to enable @EnableGlobalMethodSecurity. I imported your code and un commented @EnableGlobalMethodSecurity but I get error
Caused by: java.lang.IllegalArgumentException: Expecting to only find a single bean for type interface org.springframework.security.authentication.AuthenticationManager, but found
[]