注意:
这篇文章展示了一个完整的应用的完整代码。为了缩小本教程文章的篇幅,这里需要省略一些基本的知识点文字描述。如果您有兴趣了解这些细节,这个,这个和这个文章可能会帮助你。
先来做个概括:
- 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
现在就让我们开始一步步地学习和实现吧!
第1步:创建目录结构
现在,让我们解释在上面提到的结构内容每个细节。
第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>
第10步:创建和填充数据库模式
/*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
参考