
✨道路是曲折的,前途是光明的!
📝 专注C/C++、Linux编程与人工智能领域,分享学习笔记!
🌟 感谢各位小伙伴的长期陪伴与支持,欢迎文末添加好友一起交流!

-
- 引言
- 核心概念
-
- [认证 vs 授权](#认证 vs 授权)
- [Spring Security架构](#Spring Security架构)
- 项目依赖配置
- 核心实现代码
-
- [1. 用户实体类](#1. 用户实体类)
- [2. JWT工具类](#2. JWT工具类)
- [3. JWT认证过滤器](#3. JWT认证过滤器)
- [4. UserDetailsService实现](#4. UserDetailsService实现)
- [5. Security配置类](#5. Security配置类)
- [6. 认证控制器](#6. 认证控制器)
- [7. 认证服务](#7. 认证服务)
- [8. 自定义异常处理](#8. 自定义异常处理)
- 认证流程图
- 基于角色的访问控制
- 配置文件
- 测试API
- 安全最佳实践
- 总结
引言
Spring Security是Spring生态系统中功能强大、高度可定制的认证和访问控制框架。它为Java企业应用提供全面的安全解决方案,保护应用免受常见安全威胁。

核心概念
认证 vs 授权
你是谁?
你能做什么?
用户
认证
验证身份
授权
访问资源
允许/拒绝
| 概念 | 作用 | 常见实现 |
|---|---|---|
| 认证 (Authentication) | 验证用户身份 | 用户名密码、JWT、OAuth2 |
| 授权 (Authorization) | 控制资源访问 | 角色权限、RBAC、ABAC |
Spring Security架构
HTTP请求
FilterChainProxy
Security Filter Chain
UsernamePasswordAuthenticationFilter
JwtAuthenticationFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
AuthenticationManager
UserDetailsService
Database
AccessDecisionManager
SecurityContext
项目依赖配置
Maven依赖
xml
<dependencies>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JWT支持 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
核心实现代码
1. 用户实体类
java
package com.example.security.entity;
import jakarta.persistence.*;
import lombok.Data;
import java.time.LocalDateTime;
@Entity
@Table(name = "users")
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false)
private String password;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
private String role;
private LocalDateTime createdAt;
private boolean enabled = true;
@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
}
}
2. JWT工具类
java
package com.example.security.util;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
@Component
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String jwtSecret;
@Value("${jwt.expiration}")
private long jwtExpiration;
// 生成JWT令牌
public String generateToken(Authentication authentication) {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpiration);
return Jwts.builder()
.subject(userDetails.getUsername())
.issuedAt(now)
.expiration(expiryDate)
.signWith(getSigningKey())
.compact();
}
// 从令牌获取用户名
public String getUsernameFromToken(String token) {
Claims claims = Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload();
return claims.getSubject();
}
// 验证令牌
public boolean validateToken(String token) {
try {
Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
private SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(jwtSecret.getBytes());
}
}
3. JWT认证过滤器
java
package com.example.security.filter;
import com.example.security.util.JwtTokenProvider;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider tokenProvider;
private final UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// 从请求头获取JWT
String jwt = getJwtFromRequest(request);
// 验证并设置认证
if (jwt != null && tokenProvider.validateToken(jwt)) {
String username = tokenProvider.getUsernameFromToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
4. UserDetailsService实现
java
package com.example.security.service;
import com.example.security.entity.User;
import com.example.security.repository.UserRepository;
import lombok.RequiredArgsConstructor;
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.Collections;
@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
return org.springframework.security.core.userdetails.User.builder()
.username(user.getUsername())
.password(user.getPassword())
.authorities(Collections.singletonList(
new SimpleGrantedAuthority("ROLE_" + user.getRole())))
.disabled(!user.isEnabled())
.build();
}
}
5. Security配置类
java
package com.example.security.config;
import com.example.security.filter.JwtAuthenticationFilter;
import com.example.security.handler.CustomAccessDeniedHandler;
import com.example.security.handler.CustomAuthenticationEntryPoint;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthFilter;
private final UserDetailsService userDetailsService;
private final CustomAuthenticationEntryPoint authenticationEntryPoint;
private final CustomAccessDeniedHandler accessDeniedHandler;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// 禁用CSRF(使用JWT时不需要)
.csrf(AbstractHttpConfigurer::disable)
// 配置授权规则
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
)
// 配置会话管理(无状态)
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
// 配置异常处理
.exceptionHandling(exception -> exception
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler)
)
// 添加JWT过滤器
.authenticationProvider(authenticationProvider())
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
6. 认证控制器
java
package com.example.security.controller;
import com.example.security.dto.LoginRequest;
import com.example.security.dto.LoginResponse;
import com.example.security.dto.RegisterRequest;
import com.example.security.service.AuthService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {
private final AuthService authService;
@PostMapping("/login")
public ResponseEntity<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
return ResponseEntity.ok(authService.login(request));
}
@PostMapping("/register")
public ResponseEntity<String> register(@Valid @RequestBody RegisterRequest request) {
authService.register(request);
return ResponseEntity.ok("注册成功");
}
@PostMapping("/refresh")
public ResponseEntity<LoginResponse> refreshToken(@RequestHeader("Authorization") String token) {
return ResponseEntity.ok(authService.refreshToken(token));
}
}
7. 认证服务
java
package com.example.security.service;
import com.example.security.dto.LoginRequest;
import com.example.security.dto.LoginResponse;
import com.example.security.dto.RegisterRequest;
import com.example.security.entity.User;
import com.example.security.repository.UserRepository;
import com.example.security.util.JwtTokenProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class AuthService {
private final AuthenticationManager authenticationManager;
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final JwtTokenProvider tokenProvider;
public LoginResponse login(LoginRequest request) {
// 认证用户
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.getUsername(),
request.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
// 生成令牌
String token = tokenProvider.generateToken(authentication);
return LoginResponse.builder()
.token(token)
.type("Bearer")
.username(request.getUsername())
.build();
}
public void register(RegisterRequest request) {
// 检查用户名是否存在
if (userRepository.existsByUsername(request.getUsername())) {
throw new RuntimeException("用户名已存在");
}
// 创建新用户
User user = new User();
user.setUsername(request.getUsername());
user.setPassword(passwordEncoder.encode(request.getPassword()));
user.setEmail(request.getEmail());
user.setRole("USER"); // 默认角色
userRepository.save(user);
}
}
8. 自定义异常处理
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");
response.getWriter().write("{\"error\":\"未授权,请先登录\"}");
}
}
// 访问拒绝处理器(权限不足)
@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");
response.getWriter().write("{\"error\":\"权限不足\"}");
}
}
认证流程图
DB UserDetailsService AuthManager Filter Client DB UserDetailsService AuthManager Filter Client POST /api/auth/login authenticate(username, password) loadUserByUsername() 查询用户 返回用户信息 UserDetails 验证密码 Authentication对象 生成JWT 返回Token
基于角色的访问控制
java
@RestController
@RequestMapping("/api")
public class ResourceController {
// 方法级别权限控制
@GetMapping("/admin/dashboard")
@PreAuthorize("hasRole('ADMIN')")
public String adminDashboard() {
return "管理员面板";
}
@GetMapping("/user/profile")
@PreAuthorize("hasAnyRole('USER', 'ADMIN')")
public String userProfile() {
return "用户资料";
}
@GetMapping("/public/info")
@PermitAll
public String publicInfo() {
return "公开信息";
}
// 自定义权限表达式
@GetMapping("/user/{id}")
@PreAuthorize("#id == authentication.principal.id or hasRole('ADMIN')")
public String getUserById(@PathVariable Long id) {
return "用户信息";
}
}
配置文件
yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/security_db
username: root
password: your_password
jpa:
hibernate:
ddl-auto: update
# JWT配置
jwt:
secret: your-super-secret-key-at-least-256-bits-long-for-hs256
expiration: 86400000 # 24小时,单位毫秒
# 应用配置
app:
security:
cors:
allowed-origins: http://localhost:3000
allowed-methods: GET,POST,PUT,DELETE,OPTIONS
测试API
bash
# 1. 注册新用户
curl -X POST http://localhost:8080/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"username": "testuser",
"password": "password123",
"email": "test@example.com"
}'
# 2. 登录获取Token
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "testuser",
"password": "password123"
}'
# 3. 使用Token访问受保护资源
curl http://localhost:8080/api/user/profile \
-H "Authorization: Bearer YOUR_TOKEN_HERE"
# 4. 访问管理员资源(需要ADMIN角色)
curl http://localhost:8080/api/admin/dashboard \
-H "Authorization: Bearer YOUR_TOKEN_HERE"
安全最佳实践
| 实践 | 说明 |
|---|---|
| 密码加密 | 使用BCrypt等强哈希算法 |
| HTTPS | 生产环境必须使用HTTPS |
| 令牌刷新 | 实现令牌刷新机制 |
| CSRF防护 | 有状态的会话需要启用 |
| CORS配置 | 严格限制跨域来源 |
| 输入验证 | 使用@Valid验证所有输入 |
| 日志审计 | 记录安全相关事件 |
| 定期更新 | 及时更新安全补丁 |
总结
通过本文,我们学习了:
- Spring Security的核心概念和架构
- JWT认证的完整实现
- 基于角色的访问控制
- 自定义异常处理
- 安全配置的最佳实践
Spring Security提供了强大而灵活的安全框架,掌握它将为你的企业应用提供可靠的安全保障。
参考资料:
- Spring Security官方文档
- JWT.io
- OWASP安全指南
✍️ 坚持用 清晰易懂的图解 + 可落地的代码,让每个知识点都 简单直观!
💡 座右铭 :"道路是曲折的,前途是光明的!"
