springmvc jpa
在本文中,我们将看到如何使用JavaConfig配置和集成SpringMVC4,带有Hibernate的Spring Data JPA和SpringSecurity。
1.首先让我们在pom.xml中配置所有必需的依赖项
<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.在application.properties中配置数据库连接属性和电子邮件设置
################### DataSource Configuration ##########################jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=admininit-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.在com.sivalabs.springapp.config.AppConfig.java中配置公共服务层bean,例如PropertySourcesPlaceholderConfigurer和JavaMailSender等。
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
{@Autowiredprivate Environment env;@Beanpublic static PropertySourcesPlaceholderConfigurer placeHolderConfigurer(){return new PropertySourcesPlaceholderConfigurer();}@Beanpublic 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;}@Beanpublic CacheManager cacheManager(){return new ConcurrentMapCacheManager();}
}
观察到我们已经使用新的REGEX excludeFilter类型从组件扫描中排除了软件包“ com.sivalabs.springapp.web。* ”。
如果我们不排除与Web相关的软件包,并尝试对服务层bean运行JUnit测试,我们将遇到以下异常:
java.lang.IllegalArgumentException:需要ServletContext来配置默认的Servlet处理
还要注意,我们已经使用@EnableCaching启用了缓存,因此我们应该声明CacheManager bean。
4.在com.sivalabs.springapp.config.PersistenceConfig.java中配置持久层bean,如下所示:
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
{@Autowiredprivate Environment env;@Value("${init-db:false}")private String initDatabase;@Beanpublic PlatformTransactionManager transactionManager(){EntityManagerFactory factory = entityManagerFactory().getObject();return new JpaTransactionManager(factory);}@Beanpublic 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;}@Beanpublic HibernateExceptionTranslator hibernateExceptionTranslator(){return new HibernateExceptionTranslator();}@Beanpublic 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;}@Beanpublic 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;}
}
在这里,我们使用Hibernate实现配置了DataSource和JPA EntityManagerFactory bean。
此外,我们还配置了DataSourceInitializer bean来初始化并使用种子数据填充表。 我们可以通过更改application.properties中的init-db属性值来启用/禁用执行此db.sql脚本。
最后,我们使用@EnableJpaRepositories启用了Spring Data JPA仓库扫描,以扫描JPA仓库接口的“ com.sivalabs.springapp.repositories ”包。
5.现在,让我们在com.sivalabs.springapp.web.config.WebMvcConfig.java中配置与Web相关的bean。
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
{@Overridepublic void addViewControllers(ViewControllerRegistry registry){super.addViewControllers(registry);registry.addViewController("login/form").setViewName("login"); registry.addViewController("welcome").setViewName("welcome");registry.addViewController("admin").setViewName("admin");}@Beanpublic ViewResolver resolver(){InternalResourceViewResolver url = new InternalResourceViewResolver();url.setPrefix("/WEB-INF/jsp/");url.setSuffix(".jsp");return url;}@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry){registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");}@Overridepublic 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;}@Beanpublic SimpleMappingExceptionResolver simpleMappingExceptionResolver(){SimpleMappingExceptionResolver b = new SimpleMappingExceptionResolver();Properties mappings = new Properties();mappings.put("org.springframework.dao.DataAccessException", "error");b.setExceptionMappings(mappings);return b;}
}
6.使用AbstractAnnotationConfigDispatcherServletInitializer便利类配置DispatcherService。
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
{@Overrideprotected Class<?>[] getRootConfigClasses(){return new Class<?>[] { AppConfig.class};}@Overrideprotected Class<?>[] getServletConfigClasses(){return new Class<?>[] { WebMvcConfig.class };}@Overrideprotected String[] getServletMappings(){return new String[] { "/" };}@Overrideprotected Filter[] getServletFilters() {return new Filter[]{ new OpenEntityManagerInViewFilter()};}}
这里需要注意的几件事是,我们将AppConfig.class配置为RootConfig类,将WebMvcConfig.class配置为ServletConfigClasses,这与我们使用ContextLoaderListener和DispatcherServlet的contextConfigLocation在web.xml中进行配置的方式类似。
另外,我们已经注册了OpenEntityManagerInViewFilter,以允许在视图呈现阶段延迟加载JPA实体图。
7.让我们配置SpringSecurity。
首先让我们创建一个SecurityUser类,该类扩展了我们特定于应用程序的User类并实现了org.springframework.security.core.userdetails.UserDetails 。
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());} }@Overridepublic 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;}@Overridepublic String getPassword() {return super.getPassword();}@Overridepublic String getUsername() {return super.getEmail();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}
我们将实现一个自定义的UserDetailsService并使用Spring Data JPA存储库加载用户详细信息。
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
{@Autowiredprivate UserService userService;@Overridepublic 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);}
}
现在创建com.sivalabs.springapp.config.SecurityConfig.java ,其中包含与SpeingSecurity相关的bean定义。
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
{@Autowiredprivate DataSource dataSource;@Autowiredprivate CustomUserDetailsService customUserDetailsService;@Overrideprotected 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);}@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/resources/**");}@Overrideprotected 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();}
}
根据我们的SpringSecurity自定义表单登录配置,我们将在login.jsp中使用以下登录表单。
<!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>
成功登录后,我们可以使用身份验证来获取经过身份验证的使用详细信息,并通过以下方式保护视图的安全部分:
<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>
- 您可以在github https://github.com/sivaprasadreddy/sivalabs-blog-samples-code/tree/master/springmvc-datajpa-security-demo上找到源代码
翻译自: https://www.javacodegeeks.com/2014/03/springmvc4-spring-data-jpa-springsecurity-configuration-using-javaconfig.html
springmvc jpa