Spring MVC4 + Spring Security4 + Hibernate实例
http://www.yiibai.com/spring-security/spring-mvc-4-and-spring-security-4-integration-example.html
注意:
这篇文章展示了一个完整的应用的完整代码。为了缩小本教程文章的篇幅,这里需要省略一些基本的知识点文字描述。如果您有兴趣了解这些细节,这个,这个和这个文章可能会帮助你。
先来做个概括:
- Spring 4.2.5.RELEASE
- Spring Security 4.0.4.RELEASE
- Hibernate Core 4.3.11.Final
- validation-api 1.1.0.Final
- hibernate-validator 5.1.3.Final
- MySQL Server 5.6
- Maven 3
- JDK 1.7
- Tomcat 8.0.21
- Eclipse MARS.1 Release 4.5.1
- logback 1.1.7
现在就让我们开始一步步地学习和实现吧!
现在,让我们解释在上面提到的结构内容每个细节。
第2步: 更新 pom.xml 以包括必需的依懒
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion>
<groupId>com.yiibai.springmvc</groupId>
<artifactId>SpringMVCHibernateManyToManyCRUDExample</artifactId>
<packaging>war</packaging>
<version>1.0.0</version>
<name>SpringMVCHibernateWithSpringSecurityExample</name> <properties>
<springframework.version>4.2.5.RELEASE</springframework.version>
<springsecurity.version>4.0.4.RELEASE</springsecurity.version>
<hibernate.version>4.3.11.Final</hibernate.version>
<mysql.connector.version>5.1.31</mysql.connector.version>
</properties> <dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${springframework.version}</version>
</dependency> <!-- Spring Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${springsecurity.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${springsecurity.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${springsecurity.version}</version>
</dependency> <!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency> <!-- jsr303 validation -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.3.Final</version>
</dependency> <!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connector.version}</version>
</dependency> <!-- SLF4J/Logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency> <!-- Servlet+JSP+JSTL -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency> </dependencies> <build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<warSourceDirectory>src/main/webapp</warSourceDirectory>
<warName>SpringMVCHibernateWithSpringSecurityExample</warName>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<finalName>SpringMVCHibernateWithSpringSecurityExample</finalName>
</build>
</project>
第3步: 配置安全
这是最重要的步骤,在我们的应用程序中添加的Spring Security是创建Spring Security的Java配置。
该配置将创建被称为负责所有安全的springSecurityFilterChain Servlet过滤程序(保护应用程序的URL,验证提交用户名和密码,重定向到日志等等)提供在应用程序内。
package com.yiibai.springmvc.security; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; @Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired
@Qualifier("customUserDetailsService")
UserDetailsService userDetailsService; @Autowired
PersistentTokenRepository tokenRepository; @Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
} @Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/list")
.access("hasRole('USER') or hasRole('ADMIN') or hasRole('DBA')")
.antMatchers("/newuser/**", "/delete-user-*").access("hasRole('ADMIN')").antMatchers("/edit-user-*")
.access("hasRole('ADMIN') or hasRole('DBA')").and().formLogin().loginPage("/login")
.loginProcessingUrl("/login").usernameParameter("ssoId").passwordParameter("password").and()
.rememberMe().rememberMeParameter("remember-me").tokenRepository(tokenRepository)
.tokenValiditySeconds(86400).and().csrf().and().exceptionHandling().accessDeniedPage("/Access_Denied");
} @Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
} @Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
} @Bean
public PersistentTokenBasedRememberMeServices getPersistentTokenBasedRememberMeServices() {
PersistentTokenBasedRememberMeServices tokenBasedservice = new PersistentTokenBasedRememberMeServices(
"remember-me", userDetailsService, tokenRepository);
return tokenBasedservice;
} @Bean
public AuthenticationTrustResolver getAuthenticationTrustResolver() {
return new AuthenticationTrustResolverImpl();
} }
- ‘/’ & ‘/list’ : 供所有用户访问;
- ‘/newuser’ & ‘/delete-user-*’ : 只供管理员(Admin)用户访问;
- ‘/edit-user-*’ : 供 Admin & DBA 用户访问;
由于我们存储凭据在数据库中,所以要在UserDetailsService中配置DaoAuthenticationProvider来处理。此外,为了在数据库加密密码,我们选择BCryptPasswordEncoder。 此外,由于我们也将提供记住我的功能,跟踪令牌数据在数据库中,我们配置PersistentTokenRepository 实现。
Spring Security带有两个PersistentTokenRepository的实现: JdbcTokenRepositoryImpl 和 InMemoryTokenRepositoryImpl. 我们可以选择JdbcTokenRepositoryImpl[此文章演示了rememberMe和JdbcTokenRepositoryImpl]。但在我们的应用程序使用Hibernate,为什么不使用Hibernate来代替JDBC创建一个自定义的实现?下面是相同功能的一个尝试。
package com.yiibai.springmvc.dao; import java.util.Date; import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional; import com.yiibai.springmvc.dao.AbstractDao;
import com.yiibai.springmvc.model.PersistentLogin; @Repository("tokenRepositoryDao")
@Transactional
public class HibernateTokenRepositoryImpl extends AbstractDao<String, PersistentLogin>
implements PersistentTokenRepository { static final Logger logger = LoggerFactory.getLogger(HibernateTokenRepositoryImpl.class); @Override
public void createNewToken(PersistentRememberMeToken token) {
logger.info("Creating Token for user : {}", token.getUsername());
PersistentLogin persistentLogin = new PersistentLogin();
persistentLogin.setUsername(token.getUsername());
persistentLogin.setSeries(token.getSeries());
persistentLogin.setToken(token.getTokenValue());
persistentLogin.setLast_used(token.getDate());
persist(persistentLogin); } @Override
public PersistentRememberMeToken getTokenForSeries(String seriesId) {
logger.info("Fetch Token if any for seriesId : {}", seriesId);
try {
Criteria crit = createEntityCriteria();
crit.add(Restrictions.eq("series", seriesId));
PersistentLogin persistentLogin = (PersistentLogin) crit.uniqueResult(); return new PersistentRememberMeToken(persistentLogin.getUsername(), persistentLogin.getSeries(),
persistentLogin.getToken(), persistentLogin.getLast_used());
} catch (Exception e) {
logger.info("Token not found...");
return null;
}
} @Override
public void removeUserTokens(String username) {
logger.info("Removing Token if any for user : {}", username);
Criteria crit = createEntityCriteria();
crit.add(Restrictions.eq("username", username));
PersistentLogin persistentLogin = (PersistentLogin) crit.uniqueResult();
if (persistentLogin != null) {
logger.info("rememberMe was selected");
delete(persistentLogin);
} } @Override
public void updateToken(String seriesId, String tokenValue, Date lastUsed) {
logger.info("Updating Token for seriesId : {}", seriesId);
PersistentLogin persistentLogin = getByKey(seriesId);
persistentLogin.setToken(tokenValue);
persistentLogin.setLast_used(lastUsed);
update(persistentLogin);
} }
package com.yiibai.springmvc.model; import java.io.Serializable;
import java.util.Date; import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType; @Entity
@Table(name="PERSISTENT_LOGINS")
public class PersistentLogin implements Serializable{ @Id
private String series; @Column(name="USERNAME", unique=true, nullable=false)
private String username; @Column(name="TOKEN", unique=true, nullable=false)
private String token; @Temporal(TemporalType.TIMESTAMP)
private Date last_used; public String getSeries() {
return series;
} public void setSeries(String series) {
this.series = series;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getToken() {
return token;
} public void setToken(String token) {
this.token = token;
} public Date getLast_used() {
return last_used;
} public void setLast_used(Date last_used) {
this.last_used = last_used;
} }
package com.yiibai.springmvc.security; import java.util.ArrayList;
import java.util.List; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
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.Service;
import org.springframework.transaction.annotation.Transactional; import com.yiibai.springmvc.model.User;
import com.yiibai.springmvc.model.UserProfile;
import com.yiibai.springmvc.service.UserService; @Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{ static final Logger logger = LoggerFactory.getLogger(CustomUserDetailsService.class); @Autowired
private UserService userService; @Transactional(readOnly=true)
public UserDetails loadUserByUsername(String ssoId)
throws UsernameNotFoundException {
User user = userService.findBySSO(ssoId);
logger.info("User : {}", user);
if(user==null){
logger.info("User not found");
throw new UsernameNotFoundException("Username not found");
}
return new org.springframework.security.core.userdetails.User(user.getSsoId(), user.getPassword(),
true, true, true, true, getGrantedAuthorities(user));
} private List<GrantedAuthority> getGrantedAuthorities(User user){
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); for(UserProfile userProfile : user.getUserProfiles()){
logger.info("UserProfile : {}", userProfile);
authorities.add(new SimpleGrantedAuthority("ROLE_"+userProfile.getType()));
}
logger.info("authorities : {}", authorities);
return authorities;
} }
package com.yiibai.springmvc.security; import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer { }
第4步: 配置Hibernate
package com.yiibai.springmvc.configuration; import java.util.Properties; import javax.sql.DataSource; import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement; @Configuration
@EnableTransactionManagement
@ComponentScan({ "com.yiibai.springmvc.configuration" })
@PropertySource(value = { "classpath:application.properties" })
public class HibernateConfiguration { @Autowired
private Environment environment; @Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "com.yiibai.springmvc.model" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
} @Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName"));
dataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
dataSource.setUsername(environment.getRequiredProperty("jdbc.username"));
dataSource.setPassword(environment.getRequiredProperty("jdbc.password"));
return dataSource;
} private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
return properties;
} @Bean
@Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}
/src/main/resources/application.properties
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/yiibai
jdbc.username = root
jdbc.password =
hibernate.dialect = org.hibernate.dialect.MySQLDialect
hibernate.show_sql = true
hibernate.format_sql = true
第5步:配置Spring MVC
package com.yiibai.springmvc.configuration; import org.springframework.beans.factory.annotation.Autowired;
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.ResourceBundleMessageSource;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView; import com.yiibai.springmvc.converter.RoleToUserProfileConverter; @Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.yiibai.springmvc")
public class AppConfig extends WebMvcConfigurerAdapter{ @Autowired
RoleToUserProfileConverter roleToUserProfileConverter; /**
* Configure ViewResolvers to deliver preferred views.
*/
@Override
public void configureViewResolvers(ViewResolverRegistry registry) { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
registry.viewResolver(viewResolver);
} /**
* Configure ResourceHandlers to serve static resources like CSS/ Javascript etc...
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
} /**
* Configure Converter to be used.
* In our example, we need a converter to convert string values[Roles] to UserProfiles in newUser.jsp
*/
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(roleToUserProfileConverter);
} /**
* Configure MessageSource to lookup any validation/error message in internationalized property files
*/
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
} /**Optional. It's only required when handling '.' in @PathVariables which otherwise ignore everything after last '.' in @PathVaidables argument.
* It's a known bug in Spring [https://jira.spring.io/browse/SPR-6164], still present in Spring 4.1.7.
* This is a workaround for this issue.
*/
@Override
public void configurePathMatch(PathMatchConfigurer matcher) {
matcher.setUseRegisteredSuffixPatternMatch(true);
}
}
package com.yiibai.springmvc.converter; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component; import com.yiibai.springmvc.model.UserProfile;
import com.yiibai.springmvc.service.UserProfileService; /**
* A converter class used in views to map id's to actual userProfile objects.
*/
@Component
public class RoleToUserProfileConverter implements Converter<Object, UserProfile>{ static final Logger logger = LoggerFactory.getLogger(RoleToUserProfileConverter.class); @Autowired
UserProfileService userProfileService; /**
* Gets UserProfile by Id
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
public UserProfile convert(Object element) {
Integer id = Integer.parseInt((String)element);
UserProfile profile= userProfileService.findById(id);
logger.info("Profile : {}",profile);
return profile;
} }
NotEmpty.user.firstName=First name can not be blank.
NotEmpty.user.lastName=Last name can not be blank.
NotEmpty.user.email=Email can not be blank.
NotEmpty.user.password=Password can not be blank.
NotEmpty.user.ssoId=SSO ID can not be blank.
NotEmpty.user.userProfiles=At least one profile must be selected.
non.unique.ssoId=SSO ID {0} already exist. Please fill in different value.
package com.yiibai.springmvc.configuration; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { AppConfig.class };
} @Override
protected Class<?>[] getServletConfigClasses() {
return null;
} @Override
protected String[] getServletMappings() {
return new String[] { "/" };
} }
第6步: 创建Spring控制器
package com.yiibai.springmvc.controller; import java.util.List;
import java.util.Locale; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes; import com.yiibai.springmvc.model.User;
import com.yiibai.springmvc.model.UserProfile;
import com.yiibai.springmvc.service.UserProfileService;
import com.yiibai.springmvc.service.UserService; @Controller
@RequestMapping("/")
@SessionAttributes("roles")
public class AppController { @Autowired
UserService userService; @Autowired
UserProfileService userProfileService; @Autowired
MessageSource messageSource; @Autowired
PersistentTokenBasedRememberMeServices persistentTokenBasedRememberMeServices; @Autowired
AuthenticationTrustResolver authenticationTrustResolver; /**
* This method will list all existing users.
*/
@RequestMapping(value = { "/", "/list" }, method = RequestMethod.GET)
public String listUsers(ModelMap model) { List<User> users = userService.findAllUsers();
model.addAttribute("users", users);
model.addAttribute("loggedinuser", getPrincipal());
return "userslist";
} /**
* This method will provide the medium to add a new user.
*/
@RequestMapping(value = { "/newuser" }, method = RequestMethod.GET)
public String newUser(ModelMap model) {
User user = new User();
model.addAttribute("user", user);
model.addAttribute("edit", false);
model.addAttribute("loggedinuser", getPrincipal());
return "registration";
} /**
* This method will be called on form submission, handling POST request for
* saving user in database. It also validates the user input
*/
@RequestMapping(value = { "/newuser" }, method = RequestMethod.POST)
public String saveUser(@Valid User user, BindingResult result,
ModelMap model) { if (result.hasErrors()) {
return "registration";
} /*
* Preferred way to achieve uniqueness of field [sso] should be implementing custom @Unique annotation
* and applying it on field [sso] of Model class [User].
*
* Below mentioned peace of code [if block] is to demonstrate that you can fill custom errors outside the validation
* framework as well while still using internationalized messages.
*
*/
if(!userService.isUserSSOUnique(user.getId(), user.getSsoId())){
FieldError ssoError =new FieldError("user","ssoId",messageSource.getMessage("non.unique.ssoId", new String[]{user.getSsoId()}, Locale.getDefault()));
result.addError(ssoError);
return "registration";
} userService.saveUser(user); model.addAttribute("success", "User " + user.getFirstName() + " "+ user.getLastName() + " registered successfully");
model.addAttribute("loggedinuser", getPrincipal());
//return "success";
return "registrationsuccess";
} /**
* This method will provide the medium to update an existing user.
*/
@RequestMapping(value = { "/edit-user-{ssoId}" }, method = RequestMethod.GET)
public String editUser(@PathVariable String ssoId, ModelMap model) {
User user = userService.findBySSO(ssoId);
model.addAttribute("user", user);
model.addAttribute("edit", true);
model.addAttribute("loggedinuser", getPrincipal());
return "registration";
} /**
* This method will be called on form submission, handling POST request for
* updating user in database. It also validates the user input
*/
@RequestMapping(value = { "/edit-user-{ssoId}" }, method = RequestMethod.POST)
public String updateUser(@Valid User user, BindingResult result,
ModelMap model, @PathVariable String ssoId) { if (result.hasErrors()) {
return "registration";
} /*//Uncomment below 'if block' if you WANT TO ALLOW UPDATING SSO_ID in UI which is a unique key to a User.
if(!userService.isUserSSOUnique(user.getId(), user.getSsoId())){
FieldError ssoError =new FieldError("user","ssoId",messageSource.getMessage("non.unique.ssoId", new String[]{user.getSsoId()}, Locale.getDefault()));
result.addError(ssoError);
return "registration";
}*/ userService.updateUser(user); model.addAttribute("success", "User " + user.getFirstName() + " "+ user.getLastName() + " updated successfully");
model.addAttribute("loggedinuser", getPrincipal());
return "registrationsuccess";
} /**
* This method will delete an user by it's SSOID value.
*/
@RequestMapping(value = { "/delete-user-{ssoId}" }, method = RequestMethod.GET)
public String deleteUser(@PathVariable String ssoId) {
userService.deleteUserBySSO(ssoId);
return "redirect:/list";
} /**
* This method will provide UserProfile list to views
*/
@ModelAttribute("roles")
public List<UserProfile> initializeProfiles() {
return userProfileService.findAll();
} /**
* This method handles Access-Denied redirect.
*/
@RequestMapping(value = "/Access_Denied", method = RequestMethod.GET)
public String accessDeniedPage(ModelMap model) {
model.addAttribute("loggedinuser", getPrincipal());
return "accessDenied";
} /**
* This method handles login GET requests.
* If users is already logged-in and tries to goto login page again, will be redirected to list page.
*/
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage() {
if (isCurrentAuthenticationAnonymous()) {
return "login";
} else {
return "redirect:/list";
}
} /**
* This method handles logout requests.
* Toggle the handlers if you are RememberMe functionality is useless in your app.
*/
@RequestMapping(value="/logout", method = RequestMethod.GET)
public String logoutPage (HttpServletRequest request, HttpServletResponse response){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
//new SecurityContextLogoutHandler().logout(request, response, auth);
persistentTokenBasedRememberMeServices.logout(request, response, auth);
SecurityContextHolder.getContext().setAuthentication(null);
}
return "redirect:/login?logout";
} /**
* This method returns the principal[user-name] of logged-in user.
*/
private String getPrincipal(){
String userName = null;
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if (principal instanceof UserDetails) {
userName = ((UserDetails)principal).getUsername();
} else {
userName = principal.toString();
}
return userName;
} /**
* This method returns true if users is already authenticated [logged-in], else false.
*/
private boolean isCurrentAuthenticationAnonymous() {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authenticationTrustResolver.isAnonymous(authentication);
} }
第7步: 创建模型
package com.yiibai.springmvc.model; import java.io.Serializable;
import java.util.HashSet;
import java.util.Set; import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table; import org.hibernate.validator.constraints.NotEmpty; @Entity
@Table(name="APP_USER")
public class User implements Serializable{ @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id; @NotEmpty
@Column(name="SSO_ID", unique=true, nullable=false)
private String ssoId; @NotEmpty
@Column(name="PASSWORD", nullable=false)
private String password; @NotEmpty
@Column(name="FIRST_NAME", nullable=false)
private String firstName; @NotEmpty
@Column(name="LAST_NAME", nullable=false)
private String lastName; @NotEmpty
@Column(name="EMAIL", nullable=false)
private String email; @NotEmpty
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "APP_USER_USER_PROFILE",
joinColumns = { @JoinColumn(name = "USER_ID") },
inverseJoinColumns = { @JoinColumn(name = "USER_PROFILE_ID") })
private Set<UserProfile> userProfiles = new HashSet<UserProfile>(); public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getSsoId() {
return ssoId;
} public void setSsoId(String ssoId) {
this.ssoId = ssoId;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public String getFirstName() {
return firstName;
} public void setFirstName(String firstName) {
this.firstName = firstName;
} public String getLastName() {
return lastName;
} public void setLastName(String lastName) {
this.lastName = lastName;
} public String getEmail() {
return email;
} public void setEmail(String email) {
this.email = email;
} public Set<UserProfile> getUserProfiles() {
return userProfiles;
} public void setUserProfiles(Set<UserProfile> userProfiles) {
this.userProfiles = userProfiles;
} @Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((ssoId == null) ? 0 : ssoId.hashCode());
return result;
} @Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof User))
return false;
User other = (User) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (ssoId == null) {
if (other.ssoId != null)
return false;
} else if (!ssoId.equals(other.ssoId))
return false;
return true;
} /*
* DO-NOT-INCLUDE passwords in toString function.
* It is done here just for convenience purpose.
*/
@Override
public String toString() {
return "User [id=" + id + ", ssoId=" + ssoId + ", password=" + password
+ ", firstName=" + firstName + ", lastName=" + lastName
+ ", email=" + email + "]";
} }
package com.yiibai.springmvc.model; import java.io.Serializable; import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table; @Entity
@Table(name="USER_PROFILE")
public class UserProfile implements Serializable{ @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id; @Column(name="TYPE", length=15, unique=true, nullable=false)
private String type = UserProfileType.USER.getUserProfileType(); public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getType() {
return type;
} public void setType(String type) {
this.type = type;
} @Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
} @Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof UserProfile))
return false;
UserProfile other = (UserProfile) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (type == null) {
if (other.type != null)
return false;
} else if (!type.equals(other.type))
return false;
return true;
} @Override
public String toString() {
return "UserProfile [id=" + id + ", type=" + type + "]";
} }
package com.yiibai.springmvc.model; import java.io.Serializable; public enum UserProfileType implements Serializable{
USER("USER"),
DBA("DBA"),
ADMIN("ADMIN"); String userProfileType; private UserProfileType(String userProfileType){
this.userProfileType = userProfileType;
} public String getUserProfileType(){
return userProfileType;
} }
第7步: 创建DAO
package com.yiibai.springmvc.dao; import java.io.Serializable; import java.lang.reflect.ParameterizedType; import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired; public abstract class AbstractDao<PK extends Serializable, T> { private final Class<T> persistentClass; @SuppressWarnings("unchecked")
public AbstractDao(){
this.persistentClass =(Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
} @Autowired
private SessionFactory sessionFactory; protected Session getSession(){
return sessionFactory.getCurrentSession();
} @SuppressWarnings("unchecked")
public T getByKey(PK key) {
return (T) getSession().get(persistentClass, key);
} public void persist(T entity) {
getSession().persist(entity);
} public void update(T entity) {
getSession().update(entity);
} public void delete(T entity) {
getSession().delete(entity);
} protected Criteria createEntityCriteria(){
return getSession().createCriteria(persistentClass);
} }
package com.yiibai.springmvc.dao; import java.util.List; import com.yiibai.springmvc.model.User; public interface UserDao { User findById(int id); User findBySSO(String sso); void save(User user); void deleteBySSO(String sso); List<User> findAllUsers(); }
package com.yiibai.springmvc.dao; import java.util.List; import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository; import com.yiibai.springmvc.model.User; @Repository("userDao")
public class UserDaoImpl extends AbstractDao<Integer, User> implements UserDao { static final Logger logger = LoggerFactory.getLogger(UserDaoImpl.class); public User findById(int id) {
User user = getByKey(id);
if(user!=null){
Hibernate.initialize(user.getUserProfiles());
}
return user;
} public User findBySSO(String sso) {
logger.info("SSO : {}", sso);
Criteria crit = createEntityCriteria();
crit.add(Restrictions.eq("ssoId", sso));
User user = (User)crit.uniqueResult();
if(user!=null){
Hibernate.initialize(user.getUserProfiles());
}
return user;
} @SuppressWarnings("unchecked")
public List<User> findAllUsers() {
Criteria criteria = createEntityCriteria().addOrder(Order.asc("firstName"));
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);//To avoid duplicates.
List<User> users = (List<User>) criteria.list(); // No need to fetch userProfiles since we are not showing them on list page. Let them lazy load.
// Uncomment below lines for eagerly fetching of userProfiles if you want.
/*
for(User user : users){
Hibernate.initialize(user.getUserProfiles());
}*/
return users;
} public void save(User user) {
persist(user);
} public void deleteBySSO(String sso) {
Criteria crit = createEntityCriteria();
crit.add(Restrictions.eq("ssoId", sso));
User user = (User)crit.uniqueResult();
delete(user);
} }
package com.yiibai.springmvc.dao; import java.util.List; import com.yiibai.springmvc.model.UserProfile; public interface UserProfileDao { List<UserProfile> findAll(); UserProfile findByType(String type); UserProfile findById(int id);
}
package com.yiibai.springmvc.dao; import java.util.List; import org.hibernate.Criteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository; import com.yiibai.springmvc.model.UserProfile; @Repository("userProfileDao")
public class UserProfileDaoImpl extends AbstractDao<Integer, UserProfile>implements UserProfileDao{ public UserProfile findById(int id) {
return getByKey(id);
} public UserProfile findByType(String type) {
Criteria crit = createEntityCriteria();
crit.add(Restrictions.eq("type", type));
return (UserProfile) crit.uniqueResult();
} @SuppressWarnings("unchecked")
public List<UserProfile> findAll(){
Criteria crit = createEntityCriteria();
crit.addOrder(Order.asc("type"));
return (List<UserProfile>)crit.list();
} }
第8步: 创建Services
package com.yiibai.springmvc.service; import java.util.List; import com.yiibai.springmvc.model.User; public interface UserService { User findById(int id); User findBySSO(String sso); void saveUser(User user); void updateUser(User user); void deleteUserBySSO(String sso); List<User> findAllUsers(); boolean isUserSSOUnique(Integer id, String sso); }
package com.yiibai.springmvc.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import com.yiibai.springmvc.dao.UserDao;
import com.yiibai.springmvc.model.User; @Service("userService")
@Transactional
public class UserServiceImpl implements UserService{ @Autowired
private UserDao dao; @Autowired
private PasswordEncoder passwordEncoder; public User findById(int id) {
return dao.findById(id);
} public User findBySSO(String sso) {
User user = dao.findBySSO(sso);
return user;
} public void saveUser(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
dao.save(user);
} /*
* Since the method is running with Transaction, No need to call hibernate update explicitly.
* Just fetch the entity from db and update it with proper values within transaction.
* It will be updated in db once transaction ends.
*/
public void updateUser(User user) {
User entity = dao.findById(user.getId());
if(entity!=null){
entity.setSsoId(user.getSsoId());
if(!user.getPassword().equals(entity.getPassword())){
entity.setPassword(passwordEncoder.encode(user.getPassword()));
}
entity.setFirstName(user.getFirstName());
entity.setLastName(user.getLastName());
entity.setEmail(user.getEmail());
entity.setUserProfiles(user.getUserProfiles());
}
} public void deleteUserBySSO(String sso) {
dao.deleteBySSO(sso);
} public List<User> findAllUsers() {
return dao.findAllUsers();
} public boolean isUserSSOUnique(Integer id, String sso) {
User user = findBySSO(sso);
return ( user == null || ((id != null) && (user.getId() == id)));
} }
package com.yiibai.springmvc.service; import java.util.List; import com.yiibai.springmvc.model.UserProfile; public interface UserProfileService { UserProfile findById(int id); UserProfile findByType(String type); List<UserProfile> findAll(); }
package com.yiibai.springmvc.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import com.yiibai.springmvc.dao.UserProfileDao;
import com.yiibai.springmvc.model.UserProfile; @Service("userProfileService")
@Transactional
public class UserProfileServiceImpl implements UserProfileService{ @Autowired
UserProfileDao dao; public UserProfile findById(int id) {
return dao.findById(id);
} public UserProfile findByType(String type){
return dao.findByType(type);
} public List<UserProfile> findAll() {
return dao.findAll();
}
}
第9步: 创建视图
WEB-INF/views/login.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ page isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Login page</title>
<link href="<c:url value='/static/css/bootstrap.css' />" rel="stylesheet"></link>
<link href="<c:url value='/static/css/app.css' />" rel="stylesheet"></link>
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.css" />
</head> <body>
<div id="mainWrapper">
<div class="login-container">
<div class="login-card">
<div class="login-form">
<c:url var="loginUrl" value="/login" />
<form action="${loginUrl}" method="post" class="form-horizontal">
<c:if test="${param.error != null}">
<div class="alert alert-danger">
<p>Invalid username and password.</p>
</div>
</c:if>
<c:if test="${param.logout != null}">
<div class="alert alert-success">
<p>You have been logged out successfully.</p>
</div>
</c:if>
<div class="input-group input-sm">
<label class="input-group-addon" for="username"><i class="fa fa-user"></i></label>
<input type="text" class="form-control" id="username" name="ssoId" placeholder="Enter Username" required>
</div>
<div class="input-group input-sm">
<label class="input-group-addon" for="password"><i class="fa fa-lock"></i></label>
<input type="password" class="form-control" id="password" name="password" placeholder="Enter Password" required>
</div>
<div class="input-group input-sm">
<div class="checkbox">
<label><input type="checkbox" id="rememberme" name="remember-me"> Remember Me</label>
</div>
</div>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> <div class="form-actions">
<input type="submit"
class="btn btn-block btn-primary btn-default" value="Log in">
</div>
</form>
</div>
</div>
</div>
</div> </body>
</html>
当用户登录成功后,将呈现列表页面并显示现有的所有用户。要特别注意下面 Spring Security 标签的使用。添加,编辑和删除的链接/按钮的显示是基于角色,所以‘User’角色的用户不能看到它们。你可能会问:如果直接在浏览器栏输入网址呢?我们已经在 Spring Security 中配置了URL,因此无后顾之忧。
WEB-INF/views/userslist.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ page isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%> <html> <head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Users List</title>
<link href="<c:url value='/static/css/bootstrap.css' />" rel="stylesheet"></link>
<link href="<c:url value='/static/css/app.css' />" rel="stylesheet"></link>
</head> <body>
<div class="generic-container">
<%@include file="authheader.jsp" %>
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading"><span class="lead">List of Users </span></div>
<table class="table table-hover">
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Email</th>
<th>SSO ID</th>
<sec:authorize access="hasRole('ADMIN') or hasRole('DBA')">
<th width="100"></th>
</sec:authorize>
<sec:authorize access="hasRole('ADMIN')">
<th width="100"></th>
</sec:authorize> </tr>
</thead>
<tbody>
<c:forEach items="${users}" var="user">
<tr>
<td>${user.firstName}</td>
<td>${user.lastName}</td>
<td>${user.email}</td>
<td>${user.ssoId}</td>
<sec:authorize access="hasRole('ADMIN') or hasRole('DBA')">
<td><a href="<c:url value='/edit-user-${user.ssoId}' />" class="btn btn-success custom-width">edit</a></td>
</sec:authorize>
<sec:authorize access="hasRole('ADMIN')">
<td><a href="<c:url value='/delete-user-${user.ssoId}' />" class="btn btn-danger custom-width">delete</a></td>
</sec:authorize>
</tr>
</c:forEach>
</tbody>
</table>
</div>
<sec:authorize access="hasRole('ADMIN')">
<div class="well">
<a href="<c:url value='/newuser' />">Add New User</a>
</div>
</sec:authorize>
</div>
</body>
</html>
WEB-INF/views/authheader.jsp
<div class="authbar">
<span>Dear <strong>${loggedinuser}</strong>, Welcome to CrazyUsers.</span> <span class="floatRight"><a href="<c:url value="/logout" />">Logout</a></span>
</div>
以“Admin”角色的用户可以添加一个新用户。下面显示出的页面它相同于注册页面。
WEB-INF/views/registration.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ page isELIgnored="false" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>User Registration Form</title>
<link href="<c:url value='/static/css/bootstrap.css' />" rel="stylesheet"></link>
<link href="<c:url value='/static/css/app.css' />" rel="stylesheet"></link>
</head> <body>
<div class="generic-container">
<%@include file="authheader.jsp" %> <div class="well lead">User Registration Form</div>
<form:form method="POST" modelAttribute="user" class="form-horizontal">
<form:input type="hidden" path="id" id="id"/> <div class="row">
<div class="form-group col-md-12">
<label class="col-md-3 control-lable" for="firstName">First Name</label>
<div class="col-md-7">
<form:input type="text" path="firstName" id="firstName" class="form-control input-sm"/>
<div class="has-error">
<form:errors path="firstName" class="help-inline"/>
</div>
</div>
</div>
</div> <div class="row">
<div class="form-group col-md-12">
<label class="col-md-3 control-lable" for="lastName">Last Name</label>
<div class="col-md-7">
<form:input type="text" path="lastName" id="lastName" class="form-control input-sm" />
<div class="has-error">
<form:errors path="lastName" class="help-inline"/>
</div>
</div>
</div>
</div> <div class="row">
<div class="form-group col-md-12">
<label class="col-md-3 control-lable" for="ssoId">SSO ID</label>
<div class="col-md-7">
<c:choose>
<c:when test="${edit}">
<form:input type="text" path="ssoId" id="ssoId" class="form-control input-sm" disabled="true"/>
</c:when>
<c:otherwise>
<form:input type="text" path="ssoId" id="ssoId" class="form-control input-sm" />
<div class="has-error">
<form:errors path="ssoId" class="help-inline"/>
</div>
</c:otherwise>
</c:choose>
</div>
</div>
</div> <div class="row">
<div class="form-group col-md-12">
<label class="col-md-3 control-lable" for="password">Password</label>
<div class="col-md-7">
<form:input type="password" path="password" id="password" class="form-control input-sm" />
<div class="has-error">
<form:errors path="password" class="help-inline"/>
</div>
</div>
</div>
</div> <div class="row">
<div class="form-group col-md-12">
<label class="col-md-3 control-lable" for="email">Email</label>
<div class="col-md-7">
<form:input type="text" path="email" id="email" class="form-control input-sm" />
<div class="has-error">
<form:errors path="email" class="help-inline"/>
</div>
</div>
</div>
</div> <div class="row">
<div class="form-group col-md-12">
<label class="col-md-3 control-lable" for="userProfiles">Roles</label>
<div class="col-md-7">
<form:select path="userProfiles" items="${roles}" multiple="true" itemValue="id" itemLabel="type" class="form-control input-sm" />
<div class="has-error">
<form:errors path="userProfiles" class="help-inline"/>
</div>
</div>
</div>
</div> <div class="row">
<div class="form-actions floatRight">
<c:choose>
<c:when test="${edit}">
<input type="submit" value="Update" class="btn btn-primary btn-sm"/> or <a href="<c:url value='/list' />">Cancel</a>
</c:when>
<c:otherwise>
<input type="submit" value="Register" class="btn btn-primary btn-sm"/> or <a href="<c:url value='/list' />">Cancel</a>
</c:otherwise>
</c:choose>
</div>
</div>
</form:form>
</div>
</body>
</html>
WEB-INF/views/registrationsuccess.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ page isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Registration Confirmation Page</title>
<link href="<c:url value='/static/css/bootstrap.css' />" rel="stylesheet"></link>
<link href="<c:url value='/static/css/app.css' />" rel="stylesheet"></link>
</head>
<body>
<div class="generic-container">
<%@include file="authheader.jsp" %> <div class="alert alert-success lead">
${success}
</div> <span class="well floatRight">
Go to <a href="<c:url value='/list' />">Users List</a>
</span>
</div>
</body> </html>
WEB-INF/views/accessDenied.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ page isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>AccessDenied page</title>
</head>
<body>
<div class="generic-container">
<div class="authbar">
<span>Dear <strong>${loggedinuser}</strong>, You are not authorized to access this page.</span> <span class="floatRight"><a href="<c:url value="/logout" />">Logout</a></span>
</div>
</div>
</body>
</html>
/*All User's gets stored in APP_USER table*/
create table APP_USER (
id BIGINT NOT NULL AUTO_INCREMENT,
sso_id VARCHAR(30) NOT NULL,
password VARCHAR(100) NOT NULL,
first_name VARCHAR(30) NOT NULL,
last_name VARCHAR(30) NOT NULL,
email VARCHAR(30) NOT NULL,
PRIMARY KEY (id),
UNIQUE (sso_id)
); /* USER_PROFILE table contains all possible roles */
create table USER_PROFILE(
id BIGINT NOT NULL AUTO_INCREMENT,
type VARCHAR(30) NOT NULL,
PRIMARY KEY (id),
UNIQUE (type)
); /* JOIN TABLE for MANY-TO-MANY relationship*/
CREATE TABLE APP_USER_USER_PROFILE (
user_id BIGINT NOT NULL,
user_profile_id BIGINT NOT NULL,
PRIMARY KEY (user_id, user_profile_id),
CONSTRAINT FK_APP_USER FOREIGN KEY (user_id) REFERENCES APP_USER (id),
CONSTRAINT FK_USER_PROFILE FOREIGN KEY (user_profile_id) REFERENCES USER_PROFILE (id)
); /* Populate USER_PROFILE Table */
INSERT INTO USER_PROFILE(type)
VALUES ('USER'); INSERT INTO USER_PROFILE(type)
VALUES ('ADMIN'); INSERT INTO USER_PROFILE(type)
VALUES ('DBA'); /* Populate one Admin User which will further create other users for the application using GUI */
INSERT INTO APP_USER(sso_id, password, first_name, last_name, email)
VALUES ('sam','$2a$10$WnZOXD/FO9qZo7aMkzmr.utH/dDH19jTsqJOs2loSnkojh7dRs9cC', 'Sam','Smith','samy@yiibai.com'); /* Populate JOIN Table */
INSERT INTO APP_USER_USER_PROFILE (user_id, user_profile_id)
SELECT user.id, profile.id FROM app_user user, user_profile profile
where user.sso_id='sam' and profile.type='ADMIN'; /* Create persistent_logins Table used to store rememberme related stuff*/
CREATE TABLE persistent_logins (
username VARCHAR(64) NOT NULL,
series VARCHAR(64) NOT NULL,
token VARCHAR(64) NOT NULL,
last_used TIMESTAMP NOT NULL,
PRIMARY KEY (series)
);
请注意,我们要手动插入一个用户(我们需要一个管理员用户来登录应用程序并创建更多的用户)。T这是一个真实世界的场景。请注意,这里是加密密码:“123456” 的结果。它用下述实用类[它甚至可以是一个脚本],它仅仅是用来生成一个初始生成管理员用户的密码。它完全可以从应用程序中删除。
package com.yiibai.springsecurity.util; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; public class QuickPasswordEncodingGenerator { /**
* @param args
*/
public static void main(String[] args) {
String password = "123456";
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
System.out.println(passwordEncoder.encode(password));
} }
第11步:构建,部署和运行应用程序
现在构造 war(通过 eclipse/m2eclipse)或通过Maven的命令行(mvn clean install)。部署WAR文件到Servlet3.0容器。由于这里我使用的是在 eclipse 中配置 Tomcat,可以直接发布到 Tomcat 服务容器中。如果不知道怎么使用,可以参考:http://www.yiibai.com/maven/create-a-maven-web-project-with-eclipse.html
打开浏览器并访问 - http://localhost:8080/SpringMVCHibernateWithSpringSecurity/
使用用户名:Sam 和密码:123456 来登录,同时选择 “记住我” 。
但是对于 APP_USER 表,它没有任何数据上的变化 -
现在,单击 ‘Add new user’ 链接。添加一个 ‘USER’ 角色的用户。如下图中所示 -
点击注册(Register),用户应该就被添加了。
点击 ‘Users List’ 链接。您应该看到刚才新添加的用户信息了。
添加另外一个拥有 DBA & USER 角色的用户,如下图中所示 -
点击“Register",现在我们再来看一下用户列表 -
查看验证 APP_USER 表中的数据,如下所示 -
现在注销登录,如下图中所示 -
<
下载源代码 - 13-SpringMVCHibernateWithSpringSecurity.zip
参考
Spring MVC4 + Spring Security4 + Hibernate实例的更多相关文章
- SSH---整合Struts2&Spring&Hibernate(实例)
一.SSH回顾 Struts2:核心为过滤器+拦截器.过程:Filter--->FilterDispatcher-->ActionMapper-->ActionProxy--> ...
- Spring + SpringMVC + Druid + JPA(Hibernate impl) 给你一个稳妥的后端解决方案
最近手头的工作不太繁重,自己试着倒腾了一套用开源框架组建的 JavaWeb 后端解决方案. 感觉还不错的样子,但实践和项目实战还是有很大的落差,这里只做抛砖引玉之用. 项目 git 地址:https: ...
- java框架之Spring(4)-Spring整合Hibernate和Struts2
准备 导包 Struts2 导入 Struts2 zip 包解压目录下 'apps/struts-blank.war' 中所有 jar 包,如下: asm-3.3.jar asm-commons-3. ...
- 8 -- 深入使用Spring -- 8...2 管理Hibernate的SessionFactory
8.8.2 管理Hibernate的SessionFactory 当通过Hibernate进行持久层访问时,必须先获得SessionFactory对象,它是单个数据库映射关系编译后的内存镜像.在大部分 ...
- Spring入门1. IoC入门实例
Spring入门1. IoC入门实例 Reference:Java EE轻量级解决方案——S2SH 前言: 之前学习过关于Spring的一点知识,曾经因为配置出现问题,而总是被迫放弃学习这些框架技术, ...
- 08点睛Spring MVC4.1-Spring MVC的配置(含自定义HttpMessageConverter)
8.1 配置 Spring MVC的配置是通过继承WebMvcConfigurerAdapter类并重载其方法实现的; 前几个教程已做了得配置包括 01点睛Spring MVC 4.1-搭建环境 配置 ...
- Struts2、Spring MVC4 框架下的ajax统一异常处理
本文算是struts2 异常处理3板斧.spring mvc4:异常处理 后续篇章,普通页面出错后可以跳到统一的错误处理页面,但是ajax就不行了,ajax的本意就是不让当前页面发生跳转,仅局部刷新, ...
- spring mvc4:异常处理
前面学习过struts2的异常处理,今天来看下spring mvc4的异常处理: 一.Servlet配置文件修改 <bean id="exceptionResolver" c ...
- VS2010+MVC4+Spring.NET2+NHibernate4-传统三层架构-前篇
VS2010+MVC4+Spring.NET2+NHibernate4 - 传统三层架构 - 前篇 一直追求使用开源项目,就因一个字:懒! 一直想整理一下的,却一直懒到现在!从当初用的MVC3到现在的 ...
随机推荐
- 树递归写法ref实现
using System; using System.Collections.Generic; using System.Linq; namespace ConsoleAppTest { class ...
- [转] React风格的企业前端技术
亲爱的各位朋友们,大家下午好! 首先祝大家国庆节快乐! 很高兴可以在国庆前夕,可以为大家分享一下React风格的企业前端技术. 谈到前端,可能以前大家的第一感觉就是,前端嘛,无非就是做做页面切图,顶多 ...
- Linux系统监控命令及定位Java线程
1.PID.TID的区分 uid是user id,即用户id,root用户的uid是0,0为最高权限,gid是group id,用户组id,使用 id 命令可以很简单的通过用户名查看UID.GID:~ ...
- Python Web开发问题收集(二)
- spark操作kudu之DML操作
Kudu支持许多DML类型的操作,其中一些操作包含在Spark on Kudu集成 包括: INSERT - 将DataFrame的行插入Kudu表.请注意,虽然API完全支持INSERT,但不鼓励在 ...
- TopCoder SRM502 Div1 1000 动态规划
原文链接https://www.cnblogs.com/zhouzhendong/p/SRM502-1000.html SRM502 Div1 1000 题意 从 [0,n-1] 中选择 k 个不同的 ...
- ASP.NET MVC 路由篇二
轉載 http://www.cnblogs.com/yaozhenfa/p/asp_net_mvc_route_2.html 7.解决与物理路径的冲突 当发送一个请求至ASP.NET MVC时,其实会 ...
- fastadmin默认的controller已实现的方法
fastadmin控制器 <?php namespace app\admin\controller\peizi; use app\common\controller\Backend; /** * ...
- Harmonic Number(调和级数+欧拉常数)
In mathematics, the nth harmonic number is the sum of the reciprocals of the first n natural numbers ...
- JavaEE 之 RESTful
1.RESTful a.定义:一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件.它主要用于客户端和服务器交互类的软件.基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等 ...