Spring Boot 安全认证与授权实战
在构建企业级应用时,安全是绕不开的话题。Spring Security 作为 Spring 生态中最主流的安全框架,提供了强大的认证和授权能力。本文将带你从零开始,掌握如何在 Spring Boot 中集成 Spring Security,实现基于内存和数据库的认证,以及基于角色的权限控制。
一、Spring Security 概述
Spring Security 是一个功能全面的安全框架,专注于实现用户认证(Authentication)和用户授权(Authorization)。相比 Shiro 等其他框架,它与 Spring 生态的整合度更高,支持可扩展的编程模型,能够轻松应对复杂的业务场景。
它的主要特点包括:
- 全面性:覆盖从 HTTP 请求拦截到方法调用的全方位保护。
- 可扩展性:支持自定义认证逻辑和权限判断。
- 易用性:提供简洁的配置接口,配合 Spring Boot 自动配置使用体验更佳。
二、项目集成与基础配置
要在 Spring Boot 项目中启用安全功能,首先需要引入依赖。除了常规的 Web 依赖外,必须添加 spring-boot-starter-security。
1. Maven 依赖配置
<dependencies>
<!-- 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>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2. 安全配置类
默认情况下,Spring Security 会自动配置一个登录页面并强制所有请求都需要认证。我们需要自定义配置来满足业务需求,比如指定登录页、登出路径以及不同 URL 的访问权限。
import org.springframework.context.annotation.Configuration;
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.crypto.password.NoOpPasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 配置内存中的用户信息
auth.inMemoryAuthentication()
.passwordEncoder(NoOpPasswordEncoder.getInstance())
.withUser("admin")
.password("admin123")
.roles("ADMIN")
.and()
.withUser("user")
.password("user123")
.roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/").permitAll()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
}
这里我们定义了两个用户:admin 拥有管理员角色,user 拥有普通用户角色。同时,根路径允许匿名访问,而 /admin 和 /user 路径则分别限制了角色。
三、认证模式详解
1. 基于内存的认证
上述示例使用的就是基于内存的认证方式。这种方式适合开发测试环境,用户数据直接硬编码在配置类中。虽然方便,但生产环境中显然需要持久化存储。
2. 基于数据库的认证
在生产环境中,用户信息通常存储在数据库中。我们需要结合 JPA 来查询用户详情,并实现 UserDetailsService 接口。
依赖补充
<!-- Data JPA 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- H2 数据库依赖 (仅用于演示) -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
实体类设计
import javax.persistence.*;
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private String role;
public User() {}
public User(String username, String password, String role) {
this.username = username;
this.password = password;
this.role = role;
}
// Getter 和 Setter 方法省略...
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 getRole() { return role; }
public void setRole(String role) { this.role = role; }
}
用户服务实现
核心在于实现 UserDetailsService,告诉 Spring Security 如何从数据库加载用户及其权限。
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 java.util.ArrayList;
import java.util.List;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用户不存在:" + username);
}
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_" + user.getRole()));
return org.springframework.security.core.userdetails.User.builder()
.username(user.getUsername())
.password(user.getPassword())
.authorities(authorities)
.build();
}
}
更新安全配置
现在需要将配置类指向这个服务,而不是内存用户。
// 在 SecurityConfig 中注入 UserDetailsServiceImpl
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
此外,还需要提供一个注册接口,以便新用户能够存入数据库。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class SecurityController {
@Autowired
private UserRepository userRepository;
@GetMapping("/")
public String index() { return "index"; }
@GetMapping("/login")
public String login() { return "login"; }
@PostMapping("/register")
public String registerUser(String username, String password, String role) {
User user = new User(username, password, role);
userRepository.save(user);
return "redirect:/login";
}
// ... 其他 Controller 方法
}
四、授权策略
Spring Security 提供了灵活的授权机制,最常见的是基于角色的授权(Role-Based Access Control)。
在配置中,我们通过 hasRole("ADMIN") 或 hasRole("USER") 来限制特定路径的访问。如果用户没有对应角色,请求会被重定向到登录页或直接返回 403 错误。
对于更细粒度的控制,还可以结合 @PreAuthorize 注解在 Service 层进行方法级别的权限校验,或者使用 hasAnyRole 等组合条件。
五、实际应用场景总结
在实际开发中,Spring Boot 与 Spring Security 的组合可以解决多种安全问题:
- 登录与登出管理:通过表单登录或 Token 认证,配合登出清除会话。
- 角色管理:区分普通用户、管理员、运营等不同身份。
- 权限控制:精确控制哪些用户可以访问哪些 API 或页面。
- 安全审计:记录关键操作日志,便于追溯。
通过上述步骤,我们已经搭建了一个具备基本认证和授权能力的 Spring Boot 应用。你可以在此基础上扩展更多功能,如密码加密(BCrypt)、JWT 无状态认证等,以适应更复杂的生产环境需求。


