基于Spring Security的JWT认证授权系统设计与实现

AliveWarrior
AliveWarrior 2026-02-14T05:06:10+08:00
0 0 0

引言

在现代Web应用开发中,安全认证和授权是至关重要的组成部分。随着微服务架构的普及和前后端分离的流行,传统的Session认证机制已经无法满足现代应用的需求。JWT(JSON Web Token)作为一种开放标准(RFC 7519),为构建安全可靠的认证授权体系提供了理想的解决方案。

Spring Security作为Spring生态系统中最重要的安全框架,提供了强大的认证和授权功能。结合JWT技术,我们可以构建一个既安全又灵活的认证授权系统。本文将详细介绍如何使用Spring Security结合JWT技术构建企业级的认证授权体系,涵盖用户认证、权限控制、Token刷新机制、跨域处理等核心功能。

JWT技术原理

什么是JWT

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息。JWT由三部分组成,用点(.)分隔:

  1. Header(头部):包含令牌类型和签名算法信息
  2. Payload(载荷):包含声明信息,如用户身份、权限等
  3. Signature(签名):用于验证令牌的完整性

JWT的优势

  • 无状态:服务器不需要存储会话信息
  • 跨域支持:可以在不同域名间共享
  • 移动友好:适合移动端应用
  • 可扩展性:可以包含自定义声明
  • 性能优越:减少数据库查询次数

系统架构设计

整体架构

基于Spring Security的JWT认证授权系统采用分层架构设计,主要包括以下几个核心组件:

  1. 认证服务层:处理用户登录、Token生成等
  2. 授权服务层:处理权限验证、角色控制等
  3. 安全过滤器层:拦截请求,验证Token
  4. 业务逻辑层:具体的业务实现
  5. 数据访问层:用户信息存储和查询

核心流程

用户登录 → 验证凭据 → 生成JWT → 返回Token → 后续请求携带Token → 验证Token → 执行业务逻辑

技术栈选型

核心依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

配置文件

server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/security_db?useSSL=false&serverTimezone=UTC
    username: root
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL8Dialect

jwt:
  secret: mySecretKeyForSecurity
  expiration: 86400000  # 24小时
  refresh-expiration: 604800000  # 7天

用户实体设计

用户实体类

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(unique = true, nullable = false)
    private String username;
    
    @Column(nullable = false)
    private String password;
    
    @Column(unique = true, nullable = false)
    private String email;
    
    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"))
    @Column(name = "role")
    private List<String> roles;
    
    private Boolean enabled = true;
    
    // 构造函数、getter、setter
    public User() {}
    
    public User(String username, String password, String email, List<String> roles) {
        this.username = username;
        this.password = password;
        this.email = email;
        this.roles = roles;
    }
    
    // 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 getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    
    public List<String> getRoles() { return roles; }
    public void setRoles(List<String> roles) { this.roles = roles; }
    
    public Boolean getEnabled() { return enabled; }
    public void setEnabled(Boolean enabled) { this.enabled = enabled; }
}

角色枚举

public enum Role {
    ROLE_USER,
    ROLE_ADMIN,
    ROLE_MANAGER
}

JWT工具类实现

JWT工具类

@Component
public class JwtTokenUtil {
    
    private String secret;
    private int jwtExpiration;
    private int refreshExpiration;
    
    @Value("${jwt.secret}")
    public void setSecret(String secret) {
        this.secret = secret;
    }
    
    @Value("${jwt.expiration}")
    public void setJwtExpiration(int jwtExpiration) {
        this.jwtExpiration = jwtExpiration;
    }
    
    @Value("${jwt.refresh-expiration}")
    public void setRefreshExpiration(int refreshExpiration) {
        this.refreshExpiration = refreshExpiration;
    }
    
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, userDetails.getUsername());
    }
    
    public String generateRefreshToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return createRefreshToken(claims, userDetails.getUsername());
    }
    
    private String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + jwtExpiration))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
    
    private String createRefreshToken(Map<String, Object> claims, String subject) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + refreshExpiration))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
    
    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
    
    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.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }
    
    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }
    
    public String refreshToken(String token) {
        final Date createdDate = new Date();
        final Date expirationDate = new Date(createdDate.getTime() + jwtExpiration);
        
        final String username = getUsernameFromToken(token);
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(createdDate)
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
}

Spring Security配置

安全配置类

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
    
    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    
    @Autowired
    private JwtRequestFilter jwtRequestFilter;
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) 
            throws Exception {
        return authConfig.getAuthenticationManager();
    }
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeHttpRequests(authz -> authz
                        .requestMatchers("/api/auth/**").permitAll()
                        .requestMatchers("/api/public/**").permitAll()
                        .requestMatchers("/api/admin/**").hasRole("ADMIN")
                        .requestMatchers("/api/manager/**").hasAnyRole("ADMIN", "MANAGER")
                        .requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN", "MANAGER")
                        .anyRequest().authenticated()
                )
                .exceptionHandling(exceptions -> exceptions
                        .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                )
                .sessionManagement(session -> session
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                );
        
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
        
        return http.build();
    }
}

认证入口点

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, 
                        AuthenticationException authException) throws IOException {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }
}

JWT过滤器实现

JWT请求过滤器

@Component
public class JwtRequestFilter extends OncePerRequestFilter {
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, 
                                  FilterChain chain) throws ServletException, IOException {
        
        final String requestTokenHeader = request.getHeader("Authorization");
        
        String username = null;
        String jwtToken = null;
        
        if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
            jwtToken = requestTokenHeader.substring(7);
            try {
                username = jwtTokenUtil.getUsernameFromToken(jwtToken);
            } catch (IllegalArgumentException e) {
                System.out.println("Unable to get JWT Token");
            } catch (Exception e) {
                System.out.println("JWT Token has expired");
            }
        } else {
            logger.warn("JWT Token does not begin with Bearer String");
        }
        
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
            
            if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = 
                    new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken.setDetails(
                    new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            }
        }
        chain.doFilter(request, response);
    }
}

认证服务实现

用户服务接口

public interface UserService {
    User findByUsername(String username);
    User findByEmail(String email);
    User save(User user);
    List<User> findAll();
    void delete(Long id);
}

用户服务实现

@Service
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    @Override
    public User findByUsername(String username) {
        return userRepository.findByUsername(username);
    }
    
    @Override
    public User findByEmail(String email) {
        return userRepository.findByEmail(email);
    }
    
    @Override
    public User save(User user) {
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        return userRepository.save(user);
    }
    
    @Override
    public List<User> findAll() {
        return userRepository.findAll();
    }
    
    @Override
    public void delete(Long id) {
        userRepository.deleteById(id);
    }
}

认证控制器

@RestController
@RequestMapping("/api/auth")
public class AuthController {
    
    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
        try {
            Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                    loginRequest.getUsername(),
                    loginRequest.getPassword()
                )
            );
            
            UserDetails userDetails = (UserDetails) authentication.getPrincipal();
            String token = jwtTokenUtil.generateToken(userDetails);
            String refreshToken = jwtTokenUtil.generateRefreshToken(userDetails);
            
            return ResponseEntity.ok(new JwtResponse(token, refreshToken, userDetails.getUsername(), 
                userDetails.getAuthorities()));
        } catch (AuthenticationException e) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                .body("Invalid credentials");
        }
    }
    
    @PostMapping("/refresh")
    public ResponseEntity<?> refresh(@RequestBody RefreshTokenRequest refreshTokenRequest) {
        try {
            String refreshToken = refreshTokenRequest.getRefreshToken();
            String username = jwtTokenUtil.getUsernameFromToken(refreshToken);
            UserDetails userDetails = userService.findByUsername(username);
            
            if (jwtTokenUtil.validateToken(refreshToken, userDetails)) {
                String newToken = jwtTokenUtil.refreshToken(refreshToken);
                return ResponseEntity.ok(new JwtResponse(newToken, refreshToken, username, 
                    userDetails.getAuthorities()));
            } else {
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                    .body("Invalid refresh token");
            }
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                .body("Invalid refresh token");
        }
    }
    
    @PostMapping("/register")
    public ResponseEntity<?> register(@RequestBody RegisterRequest registerRequest) {
        if (userService.findByUsername(registerRequest.getUsername()) != null) {
            return ResponseEntity.badRequest().body("Username already exists");
        }
        
        if (userService.findByEmail(registerRequest.getEmail()) != null) {
            return ResponseEntity.badRequest().body("Email already exists");
        }
        
        User user = new User(
            registerRequest.getUsername(),
            registerRequest.getPassword(),
            registerRequest.getEmail(),
            Arrays.asList("ROLE_USER")
        );
        
        userService.save(user);
        return ResponseEntity.ok("User registered successfully");
    }
}

请求响应对象

public class LoginRequest {
    private String username;
    private String password;
    
    // 构造函数、getter、setter
    public LoginRequest() {}
    
    public LoginRequest(String username, String password) {
        this.username = username;
        this.password = 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; }
}

public class RefreshTokenRequest {
    private String refreshToken;
    
    // 构造函数、getter、setter
    public RefreshTokenRequest() {}
    
    public RefreshTokenRequest(String refreshToken) {
        this.refreshToken = refreshToken;
    }
    
    public String getRefreshToken() { return refreshToken; }
    public void setRefreshToken(String refreshToken) { this.refreshToken = refreshToken; }
}

public class JwtResponse {
    private String token;
    private String refreshToken;
    private String username;
    private Collection<? extends GrantedAuthority> authorities;
    
    public JwtResponse(String token, String refreshToken, String username, 
                      Collection<? extends GrantedAuthority> authorities) {
        this.token = token;
        this.refreshToken = refreshToken;
        this.username = username;
        this.authorities = authorities;
    }
    
    // getter和setter方法
    public String getToken() { return token; }
    public void setToken(String token) { this.token = token; }
    
    public String getRefreshToken() { return refreshToken; }
    public void setRefreshToken(String refreshToken) { this.refreshToken = refreshToken; }
    
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    
    public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; }
    public void setAuthorities(Collection<? extends GrantedAuthority> authorities) { 
        this.authorities = authorities; 
    }
}

权限控制实现

基于注解的权限控制

@RestController
@RequestMapping("/api/admin")
@PreAuthorize("hasRole('ADMIN')")
public class AdminController {
    
    @GetMapping("/users")
    public ResponseEntity<List<User>> getAllUsers() {
        // 只有ADMIN角色可以访问
        return ResponseEntity.ok(userService.findAll());
    }
    
    @PostMapping("/users")
    public ResponseEntity<User> createUser(@RequestBody User user) {
        // 只有ADMIN角色可以创建用户
        return ResponseEntity.ok(userService.save(user));
    }
}

基于URL的权限控制

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeHttpRequests(authz -> authz
                        .requestMatchers("/api/auth/**").permitAll()
                        .requestMatchers("/api/public/**").permitAll()
                        .requestMatchers("/api/admin/**").hasRole("ADMIN")
                        .requestMatchers("/api/manager/**").hasAnyRole("ADMIN", "MANAGER")
                        .requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN", "MANAGER")
                        .anyRequest().authenticated()
                )
                .exceptionHandling(exceptions -> exceptions
                        .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                )
                .sessionManagement(session -> session
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                );
        
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
        
        return http.build();
    }
}

Token刷新机制

刷新Token实现

@PostMapping("/refresh")
public ResponseEntity<?> refresh(@RequestBody RefreshTokenRequest refreshTokenRequest) {
    try {
        String refreshToken = refreshTokenRequest.getRefreshToken();
        String username = jwtTokenUtil.getUsernameFromToken(refreshToken);
        UserDetails userDetails = userService.findByUsername(username);
        
        if (jwtTokenUtil.validateToken(refreshToken, userDetails)) {
            String newToken = jwtTokenUtil.refreshToken(refreshToken);
            return ResponseEntity.ok(new JwtResponse(newToken, refreshToken, username, 
                userDetails.getAuthorities()));
        } else {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                .body("Invalid refresh token");
        }
    } catch (Exception e) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
            .body("Invalid refresh token");
    }
}

刷新Token的安全考虑

@Component
public class RefreshTokenUtil {
    
    private static final int MAX_REFRESH_TOKENS = 10;
    
    public boolean isRefreshTokenValid(String refreshToken, String username) {
        // 检查刷新Token是否过期
        // 检查是否被撤销
        // 检查是否超过最大使用次数
        return true;
    }
    
    public void revokeRefreshToken(String refreshToken) {
        // 将刷新Token标记为已撤销
        // 可以使用Redis存储撤销列表
    }
}

跨域处理

跨域配置

@Configuration
public class CorsConfig {
    
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowCredentials(true);
        configuration.setExposedHeaders(Arrays.asList("Authorization", "Content-Type"));
        
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

前端调用示例

// 前端JavaScript调用示例
const login = async (username, password) => {
    try {
        const response = await fetch('/api/auth/login', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ username, password })
        });
        
        const data = await response.json();
        if (response.ok) {
            localStorage.setItem('token', data.token);
            localStorage.setItem('refreshToken', data.refreshToken);
            return data;
        } else {
            throw new Error(data);
        }
    } catch (error) {
        console.error('Login failed:', error);
        throw error;
    }
};

const apiCall = async (url, method = 'GET', data = null) => {
    const token = localStorage.getItem('token');
    
    const config = {
        method,
        headers: {
            'Authorization': `Bearer ${token}`,
            'Content-Type': 'application/json',
        }
    };
    
    if (data) {
        config.body = JSON.stringify(data);
    }
    
    const response = await fetch(url, config);
    
    if (response.status === 401) {
        // Token过期,尝试刷新
        const refreshToken = localStorage.getItem('refreshToken');
        const refreshResponse = await fetch('/api/auth/refresh', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ refreshToken })
        });
        
        if (refreshResponse.ok) {
            const refreshData = await refreshResponse.json();
            localStorage.setItem('token', refreshData.token);
            // 重新执行原始请求
            return apiCall(url, method, data);
        } else {
            // 刷新失败,跳转到登录页
            window.location.href = '/login';
        }
    }
    
    return response.json();
};

安全最佳实践

输入验证和过滤

@PostMapping("/register")
public ResponseEntity<?> register(@Valid @RequestBody RegisterRequest registerRequest, 
                                 BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        return ResponseEntity.badRequest().body("Invalid input data");
    }
    
    // 验证用户名和邮箱的唯一性
    if (userService.findByUsername(registerRequest.getUsername()) != null) {
        return ResponseEntity.badRequest().body("Username already exists");
    }
    
    if (userService.findByEmail(registerRequest.getEmail()) != null) {
        return ResponseEntity.badRequest().body("Email already exists");
    }
    
    // 密码强度验证
    if (!isValidPassword(registerRequest.getPassword())) {
        return ResponseEntity.badRequest().body("Password does not meet requirements");
    }
    
    User user = new User(
        registerRequest.getUsername(),
        registerRequest.getPassword(),
        registerRequest.getEmail(),
        Arrays.asList("ROLE_USER")
    );
    
    userService.save(user);
    return ResponseEntity.ok("User registered successfully");
}

private boolean isValidPassword(String password) {
    // 密码至少8位,包含大小写字母、数字和特殊字符
    return password != null && 
           password.length() >= 8 && 
           password.matches(".*[a-z].*") && 
           password.matches(".*[A-Z].*") && 
           password.matches(".*\\d.*") && 
           password.matches(".*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?].*");
}

安全头配置

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.csrf().disable()
            .authorizeHttpRequests(authz -> authz
                    .anyRequest().authenticated()
            )
            .headers(headers -> headers
                    .frameOptions().deny()
                    .contentTypeOptions().and()
                    .httpStrictTransportSecurity().maxAgeInSeconds(31536000).includeSubdomains(true).preload(true)
            )
            .sessionManagement(session -> session
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            );
    
    return http.build();
}

性能优化

缓存机制

@Service
public class CachedUserService {
    
    @Autowired
    private UserService userService;
    
    @Cacheable(value = "users", key = "#username")
    public User findByUsername(String username) {
        return userService.findByUsername(username);
    }
    
    @CacheEvict(value = "users", key = "#user.username")
    public User save(User user) {
        return userService.save(user);
    }
    
    @CacheEvict(value = "users", key = "#id")
    public void delete(Long id) {
        userService.delete(id);
    }
}

Redis集成

@Configuration
@EnableCaching
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        return template;
    }
    
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        
        return RedisCacheManager.builder(connectionFactory)
                .withCacheConfiguration("users", config)
                .build();
    }
}

测试策略

单元测试

@SpringBootTest
class JwtTokenUtilTest {
    
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    @Test
    void testGenerateToken() {
        UserDetails userDetails = User.builder()
                .username("testuser")
                .password("password")
                .authorities("ROLE_USER")
                .build();
        
        String token = jwtTokenUtil.generateToken(userDetails);
        assertNotNull(token);
        assertFalse(token.isEmpty());
    }
    
    @Test
    void testValidateToken() {
        UserDetails userDetails = User.builder()
                .username("testuser")
                .password("password")
                .authorities("ROLE_USER")
                .build();
        
        String token = jwtTokenUtil.generateToken(userDetails);
        boolean isValid = jwtTokenUtil.validateToken(token, userDetails);
        assertTrue(isValid);
    }
}

集成测试

@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class AuthIntegrationTest {
    
    @Autowired
    private TestRestTemplate restTemplate;
    
    @Test
    void testUserLogin() {
        LoginRequest loginRequest = new LoginRequest("testuser", "password");
        
        ResponseEntity<JwtResponse> response = restTemplate.postForEntity(
            "/api/auth/login", loginRequest, Jwt
相关推荐
广告位招租

相似文章

    评论 (0)

    0/2000