Spring Boot 集成 Spring Security 完整示例
下面是一个完整的 Spring Boot 应用集成 Spring Security 的示例,包含用户认证、授权、自定义登录页面和密码加密等功能。
1. 添加依赖
首先在 pom.xml
中添加必要依赖:
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Thymeleaf (用于视图) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 数据库相关 (使用JPA和H2作为示例) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
2. 安全配置类
创建安全配置类 SecurityConfig.java
:
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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/", "/home", "/register", "/css/**", "/js/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.permitAll()
)
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.permitAll()
)
.exceptionHandling(exception -> exception
.accessDeniedPage("/access-denied")
);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
3. 用户实体和仓库
创建用户实体 User.java
:
import javax.persistence.*;
import java.util.Collection;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String username;
@Column(nullable = false)
private String password;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "users_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Collection<Role> roles;
// 构造方法、getter和setter
}
创建角色实体 Role.java
:
import javax.persistence.*;
import java.util.Collection;
@Entity
@Table(name = "roles")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "roles")
private Collection<User> users;
// 构造方法、getter和setter
}
创建用户仓库 UserRepository.java
:
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
4. 用户详情服务
创建自定义用户详情服务 CustomUserDetailsService.java
:
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;
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found");
}
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
user.getAuthorities()
);
}
}
5. 控制器
创建控制器 MainController.java
:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class MainController {
@GetMapping("/")
public String home() {
return "home";
}
@GetMapping("/login")
public String login() {
return "login";
}
@GetMapping("/dashboard")
public String dashboard() {
return "dashboard";
}
@GetMapping("/admin")
public String admin() {
return "admin";
}
@GetMapping("/access-denied")
public String accessDenied() {
return "access-denied";
}
}
6. 视图模板
创建 Thymeleaf 模板文件(位于 src/main/resources/templates/
):
login.html
:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login</title>
<link rel="stylesheet" th:href="@{/css/style.css}">
</head>
<body>
<div class="login-container">
<h2>Login</h2>
<form th:action="@{/login}" method="post">
<div class="form-group">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
</div>
<div th:if="${param.error}" class="error">
Invalid username or password.
</div>
<button type="submit">Login</button>
</form>
<p>Don't have an account? <a th:href="@{/register}">Register</a></p>
</div>
</body>
</html>
dashboard.html
:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Dashboard</title>
</head>
<body>
<h1>Welcome to Dashboard</h1>
<p>Hello, <span th:text="${#authentication.name}">User</span>!</p>
<a th:href="@{/logout}">Logout</a>
<div sec:authorize="hasRole('ADMIN')">
<p><a th:href="@{/admin}">Admin Panel</a></p>
</div>
</body>
</html>
7. 初始化数据
创建数据初始化类 DataInitializer.java
:
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class DataInitializer implements CommandLineRunner {
private final UserRepository userRepository;
private final RoleRepository roleRepository;
private final PasswordEncoder passwordEncoder;
public DataInitializer(UserRepository userRepository, RoleRepository roleRepository,
PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
this.passwordEncoder = passwordEncoder;
}
@Override
public void run(String... args) throws Exception {
// 初始化角色
Role adminRole = new Role();
adminRole.setName("ROLE_ADMIN");
roleRepository.save(adminRole);
Role userRole = new Role();
userRole.setName("ROLE_USER");
roleRepository.save(userRole);
// 初始化管理员用户
User admin = new User();
admin.setUsername("admin");
admin.setPassword(passwordEncoder.encode("admin123"));
admin.setRoles(Set.of(adminRole, userRole));
userRepository.save(admin);
// 初始化普通用户
User user = new User();
user.setUsername("user");
user.setPassword(passwordEncoder.encode("user123"));
user.setRoles(Set.of(userRole));
userRepository.save(user);
}
}
8. 应用属性配置
application.properties
:
# H2 Database
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# H2 Console
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
# JPA
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
# Thymeleaf
spring.thymeleaf.cache=false
9. 运行应用
创建主应用类 SecurityApplication.java
:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityApplication.class, args);
}
}
10. 测试应用
- 访问
http://localhost:8080
查看主页 - 访问
http://localhost:8080/login
登录- 管理员: admin/admin123
- 普通用户: user/user123
- 访问
http://localhost:8080/dashboard
查看仪表盘 - 访问
http://localhost:8080/admin
测试权限控制 - 访问
http://localhost:8080/h2-console
查看H2数据库
补充功能
注册功能
添加注册控制器方法:
@GetMapping("/register")
public String showRegistrationForm(Model model) {
model.addAttribute("user", new User());
return "register";
}
@PostMapping("/register")
public String registerUser(@ModelAttribute("user") User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
Role userRole = roleRepository.findByName("ROLE_USER");
user.setRoles(Set.of(userRole));
userRepository.save(user);
return "redirect:/login?registered";
}
记住我功能
在安全配置中添加:
.rememberMe(remember -> remember
.key("uniqueAndSecret")
.tokenValiditySeconds(86400) // 24小时
)
在登录表单中添加复选框:
<div class="form-group">
<input type="checkbox" id="remember-me" name="remember-me">
<label for="remember-me">Remember me</label>
</div>
这个完整示例涵盖了 Spring Boot 中使用 Spring Security 的主要功能,包括认证、授权、自定义登录页面、数据库用户存储和密码加密等。