Spring Security入门:构建安全应用
在Java开发领域,Spring Security是实现应用安全的首选框架。深入了解这个强大的安全框架,掌握认证与授权的核心技术。
一、什么是Spring Security?
Spring Security是Spring生态系统中最强大的安全框架,它为Java应用提供全面的安全服务。无论是传统Web应用,还是RESTful API,甚至是微服务架构,Spring Security都能提供可靠的安全保障。

为什么选择Spring Security?
- 功能全面:涵盖认证、授权、防护等所有安全需求
- 高度可定制:灵活扩展,满足各种业务场景
- 社区活跃:Spring官方维护,文档完善
- 与Spring深度集成:无缝衔接Spring Boot
二、核心概念
2.1 认证 vs 授权
- 认证:确认"你是谁",如登录验证
- 授权:确认"你能做什么",如权限控制
2.2 核心组件
| 组件 | 说明 |
|---|---|
| SecurityContextHolder | 存储安全上下文信息 |
| Authentication | 认证信息对象 |
| UserDetailsService | 加载用户信息 |
| PasswordEncoder | 密码加密 |
| SecurityFilterChain | 过滤器链 |
三、项目搭建
3.1 添加依赖
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
</parent>
<dependencies>
<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>
</dependencies>
3.2 基础配置类
java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/home")
);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
四、认证流程详解

Spring Security的认证流程是一个精心设计的链式处理过程:
- 用户提交认证信息:用户名和密码
- 创建Authentication对象:封装认证信息
- AuthenticationManager验证:委托给Provider
- 加载用户详情:通过UserDetailsService
- 验证成功:存入SecurityContext
4.1 自定义用户详情服务
java
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
return User.builder()
.username(user.getUsername())
.password(user.getPassword())
.roles(user.getRoles().toArray(new String[0]))
.build();
}
}
4.2 自置认证提供者
java
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication) {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (passwordEncoder.matches(password, userDetails.getPassword())) {
return new UsernamePasswordAuthenticationToken(
username,
password,
userDetails.getAuthorities()
);
}
throw new BadCredentialsException("密码错误");
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
五、授权机制

授权是指确定用户是否有权访问特定资源的过程。Spring Security支持多种授权方式。
5.1 URL级别授权
java
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
// 公开接口
.requestMatchers("/api/public/**").permitAll()
// 管理员专属
.requestMatchers("/api/admin/**").hasRole("ADMIN")
// 需要特定权限
.requestMatchers("/api/write/**").hasAuthority("WRITE")
// 已认证用户
.anyRequest().authenticated()
);
return http.build();
}
5.2 方法级授权
java
@RestController
@RequestMapping("/api")
public class ApiController {
// 需要USER角色
@GetMapping("/user")
@PreAuthorize("hasRole('USER')")
public String userEndpoint() {
return "用户专属内容";
}
// 需要ADMIN角色
@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')")
public String adminEndpoint() {
return "管理员专属内容";
}
// 复杂表达式
@GetMapping("/secure")
@PreAuthorize("hasRole('ADMIN') and #username == authentication.name")
public String secureEndpoint(String username) {
return "只能访问自己的数据";
}
// 返回后过滤
@GetMapping("/data/{id}")
@PostAuthorize("returnObject.owner == authentication.name")
public Data getData(@PathVariable Long id) {
return dataService.findById(id);
}
}
5.3 自定义权限注解
java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasRole('ADMIN') or hasRole('SUPERVISOR')")
public @interface IsAdminOrSupervisor {
}
六、JWT令牌认证

JWT(JSON Web Token)是目前最流行的跨域认证解决方案。
6.1 JWT服务类
java
@Service
public class JwtService {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
public String generateToken(String username, List<String> roles) {
Map<String, Object> claims = new HashMap<>();
claims.put("roles", roles);
return Jwts.builder()
.claims(claims)
.subject(username)
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + expiration))
.signWith(getSigningKey())
.compact();
}
public String extractUsername(String token) {
return extractClaims(token).getSubject();
}
public boolean validateToken(String token, String username) {
String extractedUsername = extractUsername(token);
return extractedUsername.equals(username) && !isTokenExpired(token);
}
private Claims extractClaims(String token) {
return Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload();
}
}
6.2 JWT认证过滤器
java
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtService jwtService;
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String token = extractTokenFromRequest(request);
if (token != null && SecurityContextHolder.getContext().getAuthentication() == null) {
String username = jwtService.extractUsername(token);
if (username != null) {
List<String> roles = jwtService.extractRoles(token);
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(
username,
null,
roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList())
);
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
filterChain.doFilter(request, response);
}
private String extractTokenFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
6.3 登录控制器
java
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtService jwtService;
@PostMapping("/login")
public LoginResponse login(@RequestBody LoginRequest request) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.getUsername(),
request.getPassword()
)
);
User user = (User) authentication.getPrincipal();
String token = jwtService.generateToken(
user.getUsername(),
user.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList())
);
return new LoginResponse(token, user.getUsername());
}
}
七、过滤器链机制

Spring Security通过过滤器链处理所有HTTP请求,理解过滤器链的工作原理对定制安全功能至关重要。
7.1 常用过滤器
| 过滤器 | 功能 |
|---|---|
| UsernamePasswordAuthenticationFilter | 处理表单登录 |
| JwtAuthenticationFilter | 处理JWT令牌 |
| LogoutFilter | 处理登出 |
| FilterSecurityInterceptor | 最终访问决策 |
| ExceptionTranslationFilter | 处理安全异常 |
7.2 自定义过滤器
java
public class CustomFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// 前置处理
String requestId = UUID.randomUUID().toString();
request.setAttribute("requestId", requestId);
// 继续过滤器链
filterChain.doFilter(request, response);
// 后置处理
logger.info("Request {} completed", requestId);
}
}
// 注册自定义过滤器
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) {
http.addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
八、异常处理
java
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json;charset=UTF-8");
ApiResponse<?> apiResponse = ApiResponse.unauthorized("未登录或登录已过期");
response.getWriter().write(new ObjectMapper().writeValueAsString(apiResponse));
}
}
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(
HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setContentType("application/json;charset=UTF-8");
ApiResponse<?> apiResponse = ApiResponse.error("权限不足");
response.getWriter().write(new ObjectMapper().writeValueAsString(apiResponse));
}
}
九、应用场景

9.1 企业后台系统
典型的RBAC(基于角色的访问控制)场景:
- 用户表、角色表、权限表
- 用户-角色、角色-权限多对多关系
- 支持动态权限配置
9.2 API网关
统一认证授权中心:
- JWT令牌签发与验证
- 单点登录支持
- 第三方登录集成
十、总结
Spring Security是企业级Java应用的安全基石:
- 核心功能:认证与授权
- 扩展能力:过滤器链、自定义提供者
- 现代方案:JWT、OAuth2