使用Spring Boot和Spring Security构建安全的RESTful API
引言
在现代Web应用中,安全性是至关重要的。Spring Boot和Spring Security为开发者提供了一套强大的工具,用于构建安全的RESTful API。本文将详细介绍如何结合Spring Boot和Spring Security,并使用JWT(JSON Web Token)实现身份验证与授权。
技术栈
- 核心框架: Spring Boot, Spring Security
- 身份验证: JWT
- 数据库: H2(示例)
- 构建工具: Maven
项目初始化
首先,使用Spring Initializr创建一个新的Spring Boot项目,添加以下依赖:
- Spring Web
- Spring Security
- H2 Database
- Lombok(可选)
配置Spring Security
1. 安全配置类
创建一个配置类SecurityConfig
,继承WebSecurityConfigurerAdapter
,并覆盖configure
方法:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
2. JWT工具类
创建一个工具类JwtTokenProvider
,用于生成和验证JWT令牌:
@Component
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String jwtSecret;
@Value("${jwt.expiration}")
private int jwtExpirationInMs;
public String generateToken(Authentication authentication) {
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpirationInMs);
return Jwts.builder()
.setSubject(Long.toString(userPrincipal.getId()))
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public Long getUserIdFromJWT(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return Long.parseLong(claims.getSubject());
}
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (Exception ex) {
// Handle exception
}
return false;
}
}
实现身份验证
1. 用户认证服务
创建一个服务类CustomUserDetailsService
,实现UserDetailsService
接口:
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username));
return UserPrincipal.create(user);
}
}
2. 认证控制器
创建一个控制器AuthController
,处理用户登录和注册请求:
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenProvider tokenProvider;
@PostMapping("/login")
public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = tokenProvider.generateToken(authentication);
return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
}
}
保护API端点
在需要保护的API端点中添加@PreAuthorize
注解:
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/me")
@PreAuthorize("hasRole('USER')")
public ResponseEntity<UserProfile> getCurrentUser(@CurrentUser UserPrincipal userPrincipal) {
UserProfile userProfile = userService.getUserProfile(userPrincipal.getId());
return ResponseEntity.ok(userProfile);
}
}
总结
通过本文,我们学习了如何使用Spring Boot和Spring Security构建安全的RESTful API,并整合JWT实现身份验证与授权。这种方法不仅简单高效,而且可以轻松扩展到更复杂的场景中。