简介
在现代Web应用中,安全性是至关重要的。Spring Security是一个功能强大且广泛使用的Java安全框架,可以帮助我们轻松地添加身份验证和授权功能。而JWT(JSON Web Token)是一种轻量级且安全的身份验证机制,可以在客户端和服务器之间传递信息。这篇博客将介绍如何使用Spring Boot整合Spring Security和JWT,以提供更安全的Web应用。
环境配置
首先,我们需要创建一个Spring Boot项目。可以通过Spring Initializer进行初始化,也可以手动创建一个空的Maven项目,并将以下依赖添加到pom.xml
中:
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JSON Web Token -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
添加Spring Security配置
我们需要创建一个SecurityConfig
类,用于配置Spring Security组件。该类需要继承自WebSecurityConfigurerAdapter
,并重写configure
方法:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/public/**").permitAll() // 公开访问的API
.anyRequest().authenticated() // 其他API需要身份验证
.and()
.httpBasic(); // 使用基本身份验证
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 在内存中创建一个用户
auth.inMemoryAuthentication()
.withUser("admin")
.password("{noop}password") // 密码加密,使用明文密码
.roles("USER");
}
}
配置中我们定义了两个API,/api/public/**
是公开访问的,其他API需要身份验证。这里我们使用了基本身份验证,并在内存中创建了一个用户名为admin
密码为password
的用户。
添加JWT配置
为了使用JWT,在之前创建的SecurityConfig
类中添加以下方法:
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// ...
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
我们创建了一个名为jwtAuthenticationFilter
的JwtAuthenticationFilter
的Bean,并将其添加到Spring Security过滤器链中。
接下来,我们需要创建JwtUtils
类,用于生成和验证JWT:
public class JwtUtils {
private static final String SECRET_KEY = "secretkey"; // 密钥
private static final long EXPIRATION_TIME = 864_000_000; // 过期时间: 10天
public static String generateToken(String username) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + EXPIRATION_TIME);
return Jwts.builder()
.setSubject(username)
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, SECRET_KEY)
.compact();
}
public static String getUsernameFromToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public static boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
return true;
} catch (SignatureException ex) {
return false;
}
}
}
我们定义了一个密钥和过期时间,并提供了生成、解析和验证JWT的方法。
创建JWT身份验证过滤器
现在,我们创建一个名为JwtAuthenticationFilter
的过滤器类,用于验证请求中的JWT。当用户进行身份验证时,我们将生成一个JWT,并将其添加到响应头中。当用户发送带有JWT的请求时,我们将解析JWT,并使用Spring Security进行身份验证。
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && JwtUtils.validateToken(jwt)) {
String username = JwtUtils.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 (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
该过滤器从请求头中获取JWT,并进行验证。如果JWT有效,则获取用户名,并使用UserDetailsService
加载用户详细信息。然后,我们将身份验证令牌设置到SecurityContextHolder
中,以便后续授权使用。
添加登录API
接下来,我们需要创建一个登录API,用于生成JWT并返回给客户端。
@RestController
@RequestMapping("/api/public")
public class AuthenticationController {
@PostMapping("/login")
public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest request) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.getUsername(),
request.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = JwtUtils.generateToken(request.getUsername());
return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
}
}
在AuthenticationController
中,我们通过AuthenticationManager
对用户进行身份验证。验证成功后,我们将生成的JWT返回给客户端。
结尾
至此,我们已经成功地使用Spring Boot整合了Spring Security和JWT。通过登录API生成的JWT,我们可以在后续的请求中进行身份验证,并授权特定用户访问不同的API。这样,我们就可以为我们的Web应用提供更安全的保障。
希望本文对您有所帮助,感谢阅读!
参考资料:
本文来自极简博客,作者:编程语言译者,转载请注明原文链接:Spring Boot整合Spring Security和JWT