本文档旨在指导开发者使用Spring Boot框架实现用户注册功能,并自动为新注册用户分配默认角色。我们将详细介绍如何配置数据模型、Repository、Service以及Controller,并提供完整的代码示例,帮助你快速构建安全可靠的用户认证系统。本文档将解决用户注册时数据无法保存到数据库的问题,重点在于RoleRepository的正确配置。
首先,确保你已经安装了以下环境:
创建一个新的 Spring Boot 项目,并在 pom.xml 文件中添加以下依赖:
org.springframework.boot spring-boot-starter-data-jpaorg.springframework.boot spring-boot-starter-securityorg.springframework.boot spring-boot-starter-validationorg.springframework.boot spring-boot-starter-webmysql mysql-connector-javaruntime org.springframework.boot spring-boot-starter-testtest org.springframework.security spring-security-testtest org.apache.tomcat.embed tomcat-embed-jasperjavax.servlet jstljavax.validation validation-api
在 application.properties 文件中配置数据库连接信息:
spring.datasource.url=jdbc:mysql://localhost:3306/auth?useSSL=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.jpa.hibernate.ddl-auto=update spring.mvc.view.prefix=/WEB-INF/ spring.mvc.view.suffix=.jsp
请根据你的实际数据库配置进行修改。
创建 User 和 Role 实体类:
User.java:
package developer.andy.auth.models;
import java.util.Date;
import java.util.List;
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.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.persistence.Transient;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
@Transient
private String confirm;
@Column(updatable=false)
private Date createdAt;
private Date updatedAt;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "users_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private List roles;
public User() {}
// Getters and Setters
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;
}
public String getConfirm() {
return confirm;
}
public void setConfirm(String confirm) {
this.confirm = confirm;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public List getRoles() {
return roles;
}
public void setRoles(List roles) {
this.roles = roles;
}
@PrePersist
protected void onCreated() {
this.createdAt = new Date();
}
@PreUpdate
protected void onUpdate() {
this.updatedAt = new Date();
}
} Role.java:
package developer.andy.auth.models;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import java.util.List;
@Entity
@Table(name = "roles")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "roles")
private List users;
public Role() {}
// Getters and Setters
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;
}
public List getUsers() {
return users;
}
public void setUsers(List users) {
this.users = users;
}
} 创建 UserRepository 和 RoleRepository 接口:
UserRepository.java:
package developer.andy.auth.repositories; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; import developer.andy.auth.models.User; @Repository public interface UserRepository extends CrudRepository{ User findByUsername(String username); }
RoleRepository.java:
package developer.andy.auth.repositories; import java.util.List; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; import developer.andy.auth.models.Role; @Repository public interface RoleRepository extends CrudRepository{ // Corrected data type to Long List findAll(); List findByName(String name); }
注意: RoleRepository 的泛型参数需要修改为
创建 UserService 类:
package developer.andy.auth.services;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import developer.andy.auth.models.User;
import developer.andy.auth.repositories.RoleRepository;
import developer.andy.auth.repositories.UserRepository;
import java.util.List;
@Service
public class UserService {
private UserRepository userRepo;
private RoleRepository roleRepo;
private BCryptPasswordEncoder pwEncoder;
public UserService(UserRepository userRepo, RoleRepository roleRepo, BCryptPasswordEncoder pwEncoder) {
this.userRepo = userRepo;
this.roleRepo = roleRepo;
this.pwEncoder = pwEncoder;
}
// save with user role
public void saveWithUserRole(User user) {
user.setPassword(pwEncoder.encode(user.getPassword()));
List roles = roleRepo.findByName("ROLE_USER");
user.setRoles(roles);
userRepo.save(user);
}
// save with admin role
public void saveWithAdminRole(User user) {
user.setPassword(pwEncoder.encode(user.getPassword()));
List roles = roleRepo.findByName("ROLE_ADMIN");
user.setRoles(roles);
userRepo.save(user);
}
// find user by username
public User findByUsername(String username) {
return userRepo.findByUsername(username);
}
} 创建 UserController 类:
package developer.andy.auth.controllers;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
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.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import developer.andy.auth.models.User;
import developer.andy.auth.services.UserService;
@Controller
public class UserController {
private UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@RequestMapping("/register")
public String register(@Valid @ModelAttribute("user") User user) {
return "register.jsp";
}
@PostMapping("/process")
public String process(@Valid @ModelAttribute("user") User user, BindingResult result, Model model, HttpSession session) {
if(result.hasErrors()) {
return "register.jsp";
}
userService.saveWithUserRole(user);
return "redirect:/login";
}
@RequestMapping("/login")
public String login() {
return "login.jsp";
}
}创建 WebSecurityConfiguration 类:
package developer.andy.auth.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class WebSecurityConfiguration {
@Bean
public BCryptPasswordEncoder pwEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/css/**", "/js/**", "/register").permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().permitAll();
return http.build();
}
}创建 register.jsp 和 login.jsp 页面。
register.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
Registration Page
Register!
Username:
Password:
Confirm:
login.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
Login Page
Login
为了保证程序正常运行,需要在数据库中预先创建角色数据。你可以使用 SQL 脚本或者在 Spring Boot 启动时初始化数据。
以下是一个使用 SQL 脚本初始化角色数据的例子:
INSERT INTO roles (name) VALUES ('ROLE_USER');
INSERT INTO roles (name) VALUES ('ROLE_ADMIN');通过以上步骤,你就可以实现用户注册并自动分配默认角色的功能了。 关键在于确保 RoleRepository 的泛型参数类型与 Role 实体的主键类型一致。
注意事项:
修改 RoleRepository 接口的定义,将 CrudRepository