Spring Boot 安全认证与授权
学习目标
本章将深入讲解 Spring Boot 如何集成 Spring Security,涵盖核心概念、配置方法、认证授权机制以及实际应用场景。重点掌握内存与数据库认证、基于角色和权限的访问控制,以及如何构建安全的登录登出流程。
Spring Security 概述
Spring Security 是 Java 生态中最主流的安全框架之一。它提供了全面的安全功能,包括用户认证(Authentication)和用户授权(Authorization),并支持与其他 Spring 组件无缝整合。相比 Shiro 等其他框架,Spring Security 在可扩展性和与 Spring 生态的整合性上表现更为出色。
快速集成
要在 Spring Boot 项目中启用安全保护,首先需要在 pom.xml 中添加相关依赖:
<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>
添加依赖后,默认情况下 Spring Security 会拦截所有请求并要求 Basic 认证。我们需要自定义配置类来接管安全逻辑:
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();
}
}
配合简单的 Controller 和 Thymeleaf 模板,即可实现基础的页面跳转与权限控制。例如,首页 / 允许匿名访问,而 /admin 路径仅管理员可见。
认证机制
基于内存的认证
上述示例使用的是内存认证,适合开发测试环境。用户信息硬编码在配置中,重启服务即丢失。这种方式简单直接,但无法满足生产环境的数据持久化需求。
基于数据库的认证
生产环境中,通常需要从数据库读取用户信息。这需要引入 JPA 和数据库驱动,并实现 UserDetailsService 接口。
1. 添加依赖
<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>
2. 定义实体类
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;
// Getter, Setter, Constructor 省略
}
3. 实现 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();
}
}
4. 更新 SecurityConfig
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
此时,系统将从数据库加载用户凭据进行校验。如果需要注册新用户,只需提供一个 POST 接口保存数据到数据库即可。
授权策略
基于角色的授权
最常用的方式是 Role-Based Access Control (RBAC)。通过 hasRole("ADMIN") 或 hasAnyRole("ADMIN", "USER") 限制特定 URL 的访问权限。注意,Spring Security 会自动为角色前缀加上 ROLE_。
基于权限的授权
对于更细粒度的控制,可以使用 hasPermission 或自定义注解。但在大多数业务场景中,基于角色的授权已足够覆盖 90% 的需求。
实际应用场景
在实际开发中,我们通常会遇到以下场景:
- 登录与登出:利用
formLogin()和logout()配置自动处理表单提交和会话销毁。 - 角色管理:不同角色访问不同菜单或 API。
- 安全审计:记录关键操作日志。
测试验证
可以通过单元测试验证权限控制是否生效。例如,使用 TestRestTemplate 模拟无认证请求访问受保护资源,应返回 302 重定向;携带正确凭证的请求则应返回 200。
总结
Spring Boot 与 Spring Security 的结合非常紧密,通过简单的配置即可构建健壮的安全体系。核心在于理解 SecurityConfig 的配置流程,以及 UserDetailsService 在认证中的桥梁作用。无论是内存认证还是数据库认证,最终都服务于统一的用户身份验证模型。在实际项目中,建议根据业务复杂度选择合适的授权方式,并始终记得对密码进行加密存储。


