Registration and Login using Spring Boot, Spring Security, Spring Data JPA, Hibernate, H2, JSP and Bootstraphttps://www.javaguides.net/2019/09/user-account-registration-and-login.html
在本教程中,我们将逐步学习如何 使用 Spring Boot、Spring Security、Spring Data JPA、Hibernate、MySQL、JSP 和 Bootstrap创建用户帐户注册和登录模块。
您可以从我的 GitHub 存储库(本教程末尾提供的链接)下载本教程的源代码。├─src │ ├─main │ │ ├─java │ │ │ └─net │ │ │ └─guides │ │ │ └─springboot │ │ │ └─loginregistrationspringbootauthjsp │ │ │ │ Application.java │ │ │ │ │ │ │ ├─config │ │ │ │ WebSecurityConfig.java │ │ │ │ │ │ │ ├─model │ │ │ │ Role.java │ │ │ │ User.java │ │ │ │ │ │ │ ├─repository │ │ │ │ RoleRepository.java │ │ │ │ UserRepository.java │ │ │ │ │ │ │ ├─service │ │ │ │ UserDetailsServiceImpl.java │ │ │ │ UserService.java │ │ │ │ UserServiceImpl.java │ │ │ │ │ │ │ ├─validator │ │ │ │ UserValidator.java │ │ │ │ │ │ │ └─web │ │ │ UserController.java │ │ │ │ │ ├─resources │ │ │ application.properties │ │ │ validation.properties │ │ │ │ │ └─webapp │ │ │ index.jsp │ │ │ │ │ ├─resources │ │ │ ├─css │ │ │ │ bootstrap.min.css │ │ │ │ common.css │ │ │ │ │ │ │ └─js │ │ │ bootstrap.min.js │ │ │ │ │ └─WEB-INF │ │ │ web.xml │ │ │ │ │ └─jsp │ │ login.jsp │ │ registration.jsp │ │ welcome.jsp
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>springboot.javaguides</groupId> <artifactId>login-registration-springboot-hibernate-jsp-auth</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>login-registration-springboot-hibernate-jsp-auth Maven Webapp</name> <url>http://maven.apache.org</url> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>3.3.6</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap-datepicker</artifactId> <version>1.0.1</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>1.9.1</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
package net.guides.springboot.loginregistrationspringbootauthjsp.model; import javax.persistence.*; import java.util.Set; @Entity @Table(name = "user") public class User { private Long id; private String username; private String password; private String passwordConfirm; private Set<Role> roles; @Id @GeneratedValue(strategy = GenerationType.AUTO) public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Transient public String getPasswordConfirm() { return passwordConfirm; } public void setPasswordConfirm(String passwordConfirm) { this.passwordConfirm = passwordConfirm; } @ManyToMany @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) public Set<Role> getRoles() { return roles; } public void setRoles(Set<Role> roles) { this.roles = roles; } }
package net.guides.springboot.loginregistrationspringbootauthjsp.model; import javax.persistence.*; import java.util.Set; @Entity @Table(name = "role") public class Role { private Long id; private String name; private Set<User> users; @Id @GeneratedValue(strategy = GenerationType.AUTO) public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @ManyToMany(mappedBy = "roles") public Set<User> getUsers() { return users; } public void setUsers(Set<User> users) { this.users = users; } }
package net.guides.springboot.loginregistrationspringbootauthjsp.repository; import org.springframework.data.jpa.repository.JpaRepository; import net.guides.springboot.loginregistrationspringbootauthjsp.model.User; public interface UserRepository extends JpaRepository<User, Long> { User findByUsername(String username); }
package net.guides.springboot.loginregistrationspringbootauthjsp.repository; import org.springframework.data.jpa.repository.JpaRepository; import net.guides.springboot.loginregistrationspringbootauthjsp.model.Role; public interface RoleRepository extends JpaRepository<Role, Long>{ }
package net.guides.springboot.loginregistrationspringbootauthjsp.service; import java.util.HashSet; import java.util.Set; 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 net.guides.springboot.loginregistrationspringbootauthjsp.model.Role; import net.guides.springboot.loginregistrationspringbootauthjsp.model.User; import net.guides.springboot.loginregistrationspringbootauthjsp.repository.UserRepository; @Service public class UserDetailsServiceImpl implements UserDetailsService{ @Autowired private UserRepository userRepository; @Override @Transactional(readOnly = true) public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username); Set<GrantedAuthority> grantedAuthorities = new HashSet<>(); for (Role role : user.getRoles()){ grantedAuthorities.add(new SimpleGrantedAuthority(role.getName())); } return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities); } }
package net.guides.springboot.loginregistrationspringbootauthjsp.service; import net.guides.springboot.loginregistrationspringbootauthjsp.model.User; public interface UserService { void save(User user); User findByUsername(String username); }
package net.guides.springboot.loginregistrationspringbootauthjsp.service; import java.util.HashSet; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import net.guides.springboot.loginregistrationspringbootauthjsp.model.User; import net.guides.springboot.loginregistrationspringbootauthjsp.repository.RoleRepository; import net.guides.springboot.loginregistrationspringbootauthjsp.repository.UserRepository; @Service public class UserServiceImpl implements UserService { @Autowired private UserRepository userRepository; @Autowired private RoleRepository roleRepository; @Autowired private BCryptPasswordEncoder bCryptPasswordEncoder; @Override public void save(User user) { user.setPassword(bCryptPasswordEncoder.encode(user.getPassword())); user.setRoles(new HashSet<>(roleRepository.findAll())); userRepository.save(user); } @Override public User findByUsername(String username) { return userRepository.findByUsername(username); } }
package net.guides.springboot.loginregistrationspringbootauthjsp.validator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; import net.guides.springboot.loginregistrationspringbootauthjsp.model.User; import net.guides.springboot.loginregistrationspringbootauthjsp.service.UserService; @Component public class UserValidator implements Validator { @Autowired private UserService userService; @Override public boolean supports(Class<?> aClass) { return User.class.equals(aClass); } @Override public void validate(Object o, Errors errors) { User user = (User) o; ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username", "NotEmpty"); if (user.getUsername().length() < 6 || user.getUsername().length() > 32) { errors.rejectValue("username", "Size.userForm.username"); } if (userService.findByUsername(user.getUsername()) != null) { errors.rejectValue("username", "Duplicate.userForm.username"); } ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "NotEmpty"); if (user.getPassword().length() < 8 || user.getPassword().length() > 32) { errors.rejectValue("password", "Size.userForm.password"); } if (!user.getPasswordConfirm().equals(user.getPassword())) { errors.rejectValue("passwordConfirm", "Diff.userForm.passwordConfirm"); } } }
package net.guides.springboot.loginregistrationspringbootauthjsp.web; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import net.guides.springboot.loginregistrationspringbootauthjsp.model.User; import net.guides.springboot.loginregistrationspringbootauthjsp.service.UserService; import net.guides.springboot.loginregistrationspringbootauthjsp.validator.UserValidator; @Controller public class UserController { @Autowired private UserService userService; @Autowired private UserValidator userValidator; @RequestMapping(value = "/registration", method = RequestMethod.GET) public String registration(Model model) { model.addAttribute("userForm", new User()); return "registration"; } @RequestMapping(value = "/registration", method = RequestMethod.POST) public String registration(@ModelAttribute("userForm") User userForm, BindingResult bindingResult, Model model) { userValidator.validate(userForm, bindingResult); if (bindingResult.hasErrors()) { return "registration"; } userService.save(userForm); return "redirect:/welcome"; } @RequestMapping(value = "/login", method = RequestMethod.GET) public String login(Model model, String error, String logout) { if (error != null) model.addAttribute("error", "Your username and password is invalid."); if (logout != null) model.addAttribute("message", "You have been logged out successfully."); return "login"; } @RequestMapping(value = {"/", "/welcome"}, method = RequestMethod.GET) public String welcome(Model model) { return "welcome"; } }
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <c:set var="contextPath" value="${pageContext.request.contextPath}"/> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags --> <meta name="description" content=""> <meta name="author" content=""> <title>Create an account</title> <link href="${contextPath}/resources/css/bootstrap.min.css" rel="stylesheet"> <link href="${contextPath}/resources/css/common.css" rel="stylesheet"> <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <![endif]--> </head> <body> <div class="container"> <form:form method="POST" modelAttribute="userForm" class="form-signin"> <h2 class="form-signin-heading">Create your account</h2> <spring:bind path="username"> <div class="form-group ${status.error ? 'has-error' : ''}"> <form:input type="text" path="username" class="form-control" placeholder="Username" autofocus="true"></form:input> <form:errors path="username"></form:errors> </div> </spring:bind> <spring:bind path="password"> <div class="form-group ${status.error ? 'has-error' : ''}"> <form:input type="password" path="password" class="form-control" placeholder="Password"></form:input> <form:errors path="password"></form:errors> </div> </spring:bind> <spring:bind path="passwordConfirm"> <div class="form-group ${status.error ? 'has-error' : ''}"> <form:input type="password" path="passwordConfirm" class="form-control" placeholder="Confirm your password"></form:input> <form:errors path="passwordConfirm"></form:errors> </div> </spring:bind> <button class="btn btn-lg btn-primary btn-block" type="submit">Submit</button> </form:form> </div> <!-- /container --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <script src="${contextPath}/resources/js/bootstrap.min.js"></script> </body> </html>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <c:set var="contextPath" value="${pageContext.request.contextPath}"/> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags --> <meta name="description" content=""> <meta name="author" content=""> <title>Log in with your account</title> <link href="${contextPath}/resources/css/bootstrap.min.css" rel="stylesheet"> <link href="${contextPath}/resources/css/common.css" rel="stylesheet"> <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <![endif]--> </head> <body> <div class="container"> <form method="POST" action="${contextPath}/login" class="form-signin"> <h2 class="form-heading">Log in</h2> <div class="form-group ${error != null ? 'has-error' : ''}"> <span>${message}</span> <input name="username" type="text" class="form-control" placeholder="Username" autofocus="true"/> <input name="password" type="password" class="form-control" placeholder="Password"/> <span>${error}</span> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> <button class="btn btn-lg btn-primary btn-block" type="submit">Log In</button> <h4 class="text-center"><a href="${contextPath}/registration">Create an account</a></h4> </div> </form> </div> <!-- /container --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <script src="${contextPath}/resources/js/bootstrap.min.js"></script> </body> </html>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <c:set var="contextPath" value="${pageContext.request.contextPath}"/> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags --> <meta name="description" content=""> <meta name="author" content=""> <title>Create an account</title> <link href="${contextPath}/resources/css/bootstrap.min.css" rel="stylesheet"> <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <![endif]--> </head> <body> <div class="container"> <c:if test="${pageContext.request.userPrincipal.name != null}"> <form id="logoutForm" method="POST" action="${contextPath}/logout"> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> </form> <h2>Welcome ${pageContext.request.userPrincipal.name} | <a onclick="document.forms['logoutForm'].submit()">Logout</a></h2> </c:if> </div> <!-- /container --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <script src="${contextPath}/resources/js/bootstrap.min.js"></script> </body> </html>
## Spring view resolver set up spring.mvc.view.prefix=/WEB-INF/jsp/ spring.mvc.view.suffix=.jsp
spring.datasource.url=jdbc:mysql://localhost:3306/loginregistration?autoReconnect=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=root spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect spring.jpa.hibernate.ddl-auto = update不要忘记 根据您的 MySQL 安装更改spring.datasource.username 和 spring.datasource.password 。 此外,在继续下一节之前,在 MySQL 中创建一个名为 demo的数据库。 您不需要创建任何表。这些表将由 Hibernate 从 我们将在下一步中定义的Employee 实体自动创建。这可以通过属性 spring.jpa.hibernate.ddl-auto = update 实现。
NotEmpty=This field is required. Size.userForm.username=Please use between 6 and 32 characters. Duplicate.userForm.username=Someone already has that username. Size.userForm.password=Try one with at least 8 characters. Diff.userForm.passwordConfirm=These passwords don't match.
package net.guides.springboot.loginregistrationspringbootauthjsp.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.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; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true, proxyTargetClass = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/resources/**", "/registration").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); } }
package net.guides.springboot.loginregistrationspringbootauthjsp; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) throws Exception { SpringApplication.run(Application.class, args); } }
本教程的源代码位于我的 GitHub 存储库 login-registration-spring-boot-hibernate-jsp-auth上。