学习时间: 4-5小时
学习目标: 掌握Spring Security安全框架,实现JWT认证授权,保护API接口安全
详细学习清单
✅ 第一部分:Spring Security基础概念(60分钟)
1. Spring Security与前端安全对比
Vue.js (你熟悉的前端安全)
javascript
// 前端路由守卫
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token')
if (to.meta.requiresAuth && !token) {
next('/login')
} else if (to.path === '/login' && token) {
next('/dashboard')
} else {
next()
}
})
// 请求拦截器
axios.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
// 响应拦截器
axios.interceptors.response.use(
response => response,
error => {
if (error.response.status === 401) {
localStorage.removeItem('token')
router.push('/login')
}
return Promise.reject(error)
}
)
Spring Security (今天学习的后端安全)
java
// 安全配置类
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/user/**").hasRole("USER")
.anyRequest().authenticated()
)
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
2. 安全概念理解
认证(Authentication)vs 授权(Authorization)
diff
认证:你是谁? → 验证用户身份
授权:你能做什么? → 控制用户权限
例如:
- 登录系统 → 认证(验证用户名密码)
- 访问管理页面 → 授权(检查是否有ADMIN角色)
✅ 第二部分:JWT令牌机制(60分钟)
1. JWT结构理解
JWT组成
makefile
Header.Payload.Signature
Header: 算法类型和令牌类型
Payload: 用户信息、权限、过期时间
Signature: 签名验证,防止篡改
JWT工具类
java
// JwtTokenUtil.java
package com.example.demo.security;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
@Component
public class JwtTokenUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
// 生成JWT令牌
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return createToken(claims, userDetails.getUsername());
}
// 生成JWT令牌(带额外信息)
public String generateToken(UserDetails userDetails, Map<String, Object> claims) {
return createToken(claims, userDetails.getUsername());
}
private String createToken(Map<String, Object> claims, String subject) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + expiration * 1000);
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(getSigningKey(), SignatureAlgorithm.HS512)
.compact();
}
// 从令牌中获取用户名
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
// 从令牌中获取过期时间
public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
// 从令牌中获取指定声明
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
// 从令牌中获取所有声明
private Claims getAllClaimsFromToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
}
// 检查令牌是否过期
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
// 验证令牌
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
// 获取签名密钥
private SecretKey getSigningKey() {
byte[] keyBytes = secret.getBytes();
return Keys.hmacShaKeyFor(keyBytes);
}
// 从请求头中提取令牌
public String extractTokenFromHeader(String header) {
if (header != null && header.startsWith("Bearer ")) {
return header.substring(7);
}
return null;
}
}
2. JWT配置属性
application.yml配置
yaml
# JWT配置
jwt:
secret: your-secret-key-must-be-at-least-256-bits-long
expiration: 86400 # 24小时,单位:秒
refresh-expiration: 604800 # 7天,单位:秒
# 安全配置
security:
cors:
allowed-origins: "http://localhost:3000,http://localhost:8080"
allowed-methods: "GET,POST,PUT,DELETE,OPTIONS"
allowed-headers: "*"
allow-credentials: true
✅ 第三部分:用户认证与授权(90分钟)
1. 用户实体与角色
User实体类
java
// User.java - 增强版用户实体
package com.example.demo.model;
import java.time.LocalDateTime;
import java.util.Set;
public class User {
private Long id;
private String username;
private String email;
private String password;
private String firstName;
private String lastName;
private String phone;
private String avatar;
private boolean enabled;
private boolean accountNonExpired;
private boolean credentialsNonExpired;
private boolean accountNonLocked;
private LocalDateTime lastLoginTime;
private LocalDateTime createTime;
private LocalDateTime updateTime;
private Set<Role> roles;
// 构造函数
public User() {}
public User(String username, String email, String password) {
this.username = username;
this.email = email;
this.password = password;
this.enabled = true;
this.accountNonExpired = true;
this.credentialsNonExpired = true;
this.accountNonLocked = true;
this.createTime = LocalDateTime.now();
this.updateTime = LocalDateTime.now();
}
// 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 getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public String getAvatar() { return avatar; }
public void setAvatar(String avatar) { this.avatar = avatar; }
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public boolean isAccountNonExpired() { return accountNonExpired; }
public void setAccountNonExpired(boolean accountNonExpired) { this.accountNonExpired = accountNonExpired; }
public boolean isCredentialsNonExpired() { return credentialsNonExpired; }
public void setCredentialsNonExpired(boolean credentialsNonExpired) { this.credentialsNonExpired = credentialsNonExpired; }
public boolean isAccountNonLocked() { return accountNonLocked; }
public void setAccountNonLocked(boolean accountNonLocked) { this.accountNonLocked = accountNonLocked; }
public LocalDateTime getLastLoginTime() { return lastLoginTime; }
public void setLastLoginTime(LocalDateTime lastLoginTime) { this.lastLoginTime = lastLoginTime; }
public LocalDateTime getCreateTime() { return createTime; }
public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
public LocalDateTime getUpdateTime() { return updateTime; }
public void setUpdateTime(LocalDateTime updateTime) { this.updateTime = updateTime; }
public Set<Role> getRoles() { return roles; }
public void setRoles(Set<Role> roles) { this.roles = roles; }
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", email='" + email + '\'' +
", enabled=" + enabled +
", roles=" + roles +
'}';
}
}
Role角色类
java
// Role.java - 角色实体
package com.example.demo.model;
public class Role {
private Long id;
private String name;
private String description;
private String code; // ROLE_USER, ROLE_ADMIN
public Role() {}
public Role(String name, String description, String code) {
this.name = name;
this.description = description;
this.code = code;
}
// Getter和Setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getCode() { return code; }
public void setCode(String code) { this.code = code; }
}
2. 用户详情服务
CustomUserDetailsService.java
java
// CustomUserDetailsService.java
package com.example.demo.security;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
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.stream.Collectors;
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
try {
User user = userService.getUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用户不存在: " + username);
}
// 转换角色为Spring Security的权限格式
var authorities = user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority(role.getCode()))
.collect(Collectors.toList());
return org.springframework.security.core.userdetails.User.builder()
.username(user.getUsername())
.password(user.getPassword())
.authorities(authorities)
.accountExpired(!user.isAccountNonExpired())
.accountLocked(!user.isAccountNonLocked())
.credentialsExpired(!user.isCredentialsNonExpired())
.disabled(!user.isEnabled())
.build();
} catch (Exception e) {
throw new UsernameNotFoundException("加载用户信息失败: " + username, e);
}
}
}
3. JWT认证过滤器
JwtAuthenticationFilter.java
java
// JwtAuthenticationFilter.java
package com.example.demo.security;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
// 提取JWT令牌
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtTokenUtil.getUsernameFromToken(jwt);
}
// 验证令牌并设置认证信息
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
} catch (Exception e) {
logger.error("JWT认证过滤器处理失败", e);
}
filterChain.doFilter(request, response);
}
}
✅ 第四部分:Spring Security配置(60分钟)
1. 主安全配置类
SecurityConfig.java
java
// SecurityConfig.java
package com.example.demo.config;
import com.example.demo.security.CustomUserDetailsService;
import com.example.demo.security.JwtAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.http.SessionCreationPolicy;
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;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 禁用CSRF(因为使用JWT)
.csrf(csrf -> csrf.disable())
// 配置CORS
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
// 配置会话管理(无状态)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
// 配置授权规则
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/actuator/**").permitAll()
.requestMatchers("/h2-console/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/user/**").hasRole("USER")
.anyRequest().authenticated()
)
// 添加JWT过滤器
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOriginPatterns(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
2. 认证控制器
AuthController.java
java
// AuthController.java
package com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.security.JwtTokenUtil;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserService userService;
// 用户登录
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
try {
// 认证用户
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword()
)
);
// 生成JWT令牌
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
String token = jwtTokenUtil.generateToken(userDetails);
// 获取用户信息
User user = userService.getUserByUsername(userDetails.getUsername());
// 更新最后登录时间
userService.updateLastLoginTime(user.getId());
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "登录成功");
response.put("token", token);
response.put("user", Map.of(
"id", user.getId(),
"username", user.getUsername(),
"email", user.getEmail(),
"roles", user.getRoles()
));
return ResponseEntity.ok(response);
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", "用户名或密码错误"
));
}
}
// 用户注册
@PostMapping("/register")
public ResponseEntity<?> register(@RequestBody RegisterRequest registerRequest) {
try {
// 检查用户名是否已存在
if (userService.getUserByUsername(registerRequest.getUsername()) != null) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", "用户名已存在"
));
}
// 检查邮箱是否已存在
if (userService.getUserByEmail(registerRequest.getEmail()) != null) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", "邮箱已被注册"
));
}
// 创建用户
User user = new User(
registerRequest.getUsername(),
registerRequest.getEmail(),
registerRequest.getPassword()
);
User createdUser = userService.createUser(user);
return ResponseEntity.ok(Map.of(
"success", true,
"message", "注册成功",
"data", createdUser
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", e.getMessage()
));
}
}
// 刷新令牌
@PostMapping("/refresh")
public ResponseEntity<?> refreshToken(@RequestHeader("Authorization") String authHeader) {
try {
String token = jwtTokenUtil.extractTokenFromHeader(authHeader);
if (token == null) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", "无效的令牌"
));
}
String username = jwtTokenUtil.getUsernameFromToken(token);
UserDetails userDetails = userService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(token, userDetails)) {
String newToken = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(Map.of(
"success", true,
"message", "令牌刷新成功",
"token", newToken
));
} else {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", "令牌已过期"
));
}
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", "令牌刷新失败"
));
}
}
// 登出
@PostMapping("/logout")
public ResponseEntity<?> logout() {
// 由于使用JWT,服务端无法真正"登出"
// 客户端需要删除本地存储的令牌
return ResponseEntity.ok(Map.of(
"success", true,
"message", "登出成功"
));
}
// 获取当前用户信息
@GetMapping("/me")
public ResponseEntity<?> getCurrentUser(@RequestHeader("Authorization") String authHeader) {
try {
String token = jwtTokenUtil.extractTokenFromHeader(authHeader);
if (token == null) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", "无效的令牌"
));
}
String username = jwtTokenUtil.getUsernameFromToken(token);
User user = userService.getUserByUsername(username);
return ResponseEntity.ok(Map.of(
"success", true,
"data", user
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", "获取用户信息失败"
));
}
}
}
// 登录请求DTO
class LoginRequest {
private String username;
private String password;
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; }
}
// 注册请求DTO
class RegisterRequest {
private String username;
private String email;
private String password;
private String firstName;
private String lastName;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
}
✅ 第五部分:受保护的API接口(60分钟)
1. 用户管理接口(需要认证)
SecureUserController.java
java
// SecureUserController.java
package com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/user")
public class SecureUserController {
@Autowired
private UserService userService;
// 获取当前用户信息
@GetMapping("/profile")
public ResponseEntity<?> getProfile() {
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authentication.getName();
User user = userService.getUserByUsername(username);
return ResponseEntity.ok(Map.of(
"success", true,
"data", user
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", e.getMessage()
));
}
}
// 更新用户信息
@PutMapping("/profile")
public ResponseEntity<?> updateProfile(@RequestBody User updateUser) {
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authentication.getName();
User currentUser = userService.getUserByUsername(username);
User updatedUser = userService.updateUser(currentUser.getId(), updateUser);
return ResponseEntity.ok(Map.of(
"success", true,
"message", "个人信息更新成功",
"data", updatedUser
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", e.getMessage()
));
}
}
// 修改密码
@PutMapping("/password")
public ResponseEntity<?> changePassword(@RequestBody ChangePasswordRequest request) {
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authentication.getName();
User user = userService.getUserByUsername(username);
userService.changePassword(user.getId(), request.getOldPassword(), request.getNewPassword());
return ResponseEntity.ok(Map.of(
"success", true,
"message", "密码修改成功"
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", e.getMessage()
));
}
}
// 获取用户列表(需要USER角色)
@PreAuthorize("hasRole('USER')")
@GetMapping("/list")
public ResponseEntity<?> getUserList() {
try {
List<User> users = userService.getAllUsers();
return ResponseEntity.ok(Map.of(
"success", true,
"data", users,
"total", users.size()
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", e.getMessage()
));
}
}
}
// 修改密码请求DTO
class ChangePasswordRequest {
private String oldPassword;
private String newPassword;
public String getOldPassword() { return oldPassword; }
public void setOldPassword(String oldPassword) { this.oldPassword = oldPassword; }
public String getNewPassword() { return newPassword; }
public void setNewPassword(String newPassword) { this.newPassword = newPassword; }
}
2. 管理员接口(需要ADMIN角色)
AdminController.java
java
// AdminController.java
package com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/admin")
@PreAuthorize("hasRole('ADMIN')") // 类级别权限控制
public class AdminController {
@Autowired
private UserService userService;
// 获取所有用户
@GetMapping("/users")
public ResponseEntity<?> getAllUsers() {
try {
List<User> users = userService.getAllUsers();
return ResponseEntity.ok(Map.of(
"success", true,
"data", users,
"total", users.size()
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", e.getMessage()
));
}
}
// 根据ID获取用户
@GetMapping("/users/{id}")
public ResponseEntity<?> getUserById(@PathVariable Long id) {
try {
User user = userService.getUserById(id);
return ResponseEntity.ok(Map.of(
"success", true,
"data", user
));
} catch (Exception e) {
return ResponseEntity.notFound().build();
}
}
// 创建用户
@PostMapping("/users")
public ResponseEntity<?> createUser(@RequestBody User user) {
try {
User createdUser = userService.createUser(user);
return ResponseEntity.ok(Map.of(
"success", true,
"message", "用户创建成功",
"data", createdUser
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", e.getMessage()
));
}
}
// 更新用户
@PutMapping("/users/{id}")
public ResponseEntity<?> updateUser(@PathVariable Long id, @RequestBody User user) {
try {
User updatedUser = userService.updateUser(id, user);
return ResponseEntity.ok(Map.of(
"success", true,
"message", "用户更新成功",
"data", updatedUser
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", e.getMessage()
));
}
}
// 删除用户
@DeleteMapping("/users/{id}")
public ResponseEntity<?> deleteUser(@PathVariable Long id) {
try {
userService.deleteUser(id);
return ResponseEntity.ok(Map.of(
"success", true,
"message", "用户删除成功"
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", e.getMessage()
));
}
}
// 启用/禁用用户
@PatchMapping("/users/{id}/status")
public ResponseEntity<?> toggleUserStatus(@PathVariable Long id, @RequestParam boolean enabled) {
try {
userService.updateUserStatus(id, enabled);
return ResponseEntity.ok(Map.of(
"success", true,
"message", enabled ? "用户已启用" : "用户已禁用"
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", e.getMessage()
));
}
}
// 重置用户密码
@PatchMapping("/users/{id}/reset-password")
public ResponseEntity<?> resetUserPassword(@PathVariable Long id) {
try {
String newPassword = userService.resetUserPassword(id);
return ResponseEntity.ok(Map.of(
"success", true,
"message", "密码重置成功",
"newPassword", newPassword
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", e.getMessage()
));
}
}
// 获取系统统计信息
@GetMapping("/stats")
public ResponseEntity<?> getSystemStats() {
try {
Map<String, Object> stats = userService.getSystemStats();
return ResponseEntity.ok(Map.of(
"success", true,
"data", stats
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"success", false,
"message", e.getMessage()
));
}
}
}
🎯 今日学习总结
1. 掌握的核心技能
- ✅ Spring Security安全框架基础
- ✅ JWT令牌生成与验证
- ✅ 用户认证与授权机制
- ✅ 基于角色的访问控制
- ✅ 安全配置与过滤器
2. 安全体系特点
- JWT认证:无状态、可扩展的认证方式
- 角色授权:基于角色的细粒度权限控制
- 安全配置:灵活的安全规则配置
- CORS支持:跨域请求安全处理
- 密码加密:BCrypt安全哈希算法
3. API安全层次
bash
公开接口:/api/auth/**, /api/public/**
用户接口:/api/user/** (需要USER角色)
管理接口:/api/admin/** (需要ADMIN角色)
4. 下一步学习方向
- 数据库事务管理
- 微服务安全架构
- OAuth2认证授权
- API网关安全
- 安全审计与日志
学习建议
- 安全测试:使用Postman测试不同角色的接口访问
- 令牌管理:理解JWT的生命周期管理
- 权限设计:学会设计合理的角色权限体系
- 安全配置:掌握Spring Security的配置选项
- 问题排查:学会使用日志排查安全相关问题