在开发Web应用程序的过程中,安全性是不可忽视的一个重要方面。Spring Security是一个功能强大的框架,为我们提供了用于身份验证、授权、团队权限管理等功能,而JWT (Json Web Token) 是一种轻量级的身份验证和授权机制,通过在客户端和服务器之间传递加密的 JSON 数据进行身份验证和授权。
本篇博客将介绍如何使用Spring Boot集成Spring Security和JWT,实现基于角色的权限管理。
准备工作
在开始之前,我们需要完成以下准备工作:
- 确保你已经安装了Java Development Kit (JDK) 和 Maven。
- 创建一个新的Spring Boot项目,可以使用Spring Initializr快速生成。
添加依赖
在开始集成Spring Security和JWT之前,我们首先需要添加相关的依赖。
在pom.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</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
完成添加依赖后,执行mvn install
命令来更新并安装所需的依赖。
配置Spring Security
在集成Spring Security之前,我们需要进行一些基本的配置。
在application.properties
中添加以下配置:
# Spring Security 路径放行配置
security.ignored = /login
创建一个WebSecurityConfig
类,继承自WebSecurityConfigurerAdapter
,用于配置Spring Security:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
"/swagger-resources/**",
"/v2/api-docs",
"/swagger-ui.html",
"/webjars/**"
);
}
}
在上述配置中,我们禁用了CSRF保护,允许/login
路径的匿名访问,并且要求所有其他路径都需要进行身份验证。
实现用户登录和JWT生成
为了实现用户登录认证和生成JWT,我们需要创建一个UserController
,其中包含login
和generateToken
两个接口。
@RestController
public class UserController {
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
// 身份验证逻辑
// ...
// 生成JWT
String token = generateToken(userDetails);
// 返回JWT
return ResponseEntity.ok(new LoginResponse(token));
}
private String generateToken(UserDetails userDetails) {
// 构建JWT并返回token
// ...
}
}
在generateToken
方法中,我们使用JJWT库来生成JWT。
实现JWT验证和权限管理
创建一个JwtTokenFilter
类,用于验证JWT并实现基于角色的权限管理。
public class JwtTokenFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = extractJwtFromRequest(request);
if (StringUtils.hasText(jwt) && validateToken(jwt)) {
UserDetails userDetails = getUserDetailsFromToken(jwt);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(request, response);
}
// ...
}
在上述代码中,doFilterInternal
方法会从请求中提取JWT,并根据JWT验证用户身份。如果验证通过,将用户详细信息和权限添加到SecurityContextHolder
中。
接下来,我们需要创建一个JwtTokenProvider
类,用于处理JWT的生成和验证逻辑。
@Service
public class JwtTokenProvider {
private static final String SECRET_KEY = "your-secret-key";
private static final long EXPIRATION_TIME = 60 * 60 * 1000; // 1 hour
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put("roles", userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList()));
Date now = new Date();
Date expirationDate = new Date(now.getTime() + EXPIRATION_TIME);
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(now)
.setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
return true;
} catch (Exception ex) {
logger.error("Invalid JWT token", ex);
}
return false;
}
public UserDetails getUserDetailsFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
String username = claims.getSubject();
List<GrantedAuthority> authorities = ((List<?>) claims.get("roles"))
.stream()
.map(authority -> new SimpleGrantedAuthority((String) authority))
.collect(Collectors.toList());
return new User(username, "", authorities);
}
}
在JwtTokenProvider
中,我们使用了一个密钥和过期时间来生成JWT,并提供了验证和从JWT提取用户详细信息的方法。
整合Spring Security和JWT
为了实现Spring Security和JWT的整合,我们需要在WebSecurityConfig
类中添加以下配置:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// ...
@Override
protected void configure(HttpSecurity http) throws Exception {
// ...
// 添加JWT过滤器
http.addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public JwtTokenFilter jwtTokenFilter() {
return new JwtTokenFilter();
}
@Bean
public JwtTokenProvider jwtTokenProvider() {
return new JwtTokenProvider();
}
}
这样,当请求经过JwtTokenFilter
时,将会自动验证JWT并设置用户的认证信息。
至此,我们完成了Spring Boot集成Spring Security和JWT的配置。可以通过/login
接口进行用户登录认证并获取JWT。在其他需要权限认证的接口上,只需要在对应Controller上添加@PreAuthorize
注解,即可实现基于角色的权限管理。
本篇博客介绍了如何使用Spring Boot集成Spring Security和JWT,实现了基于角色的权限管理。通过仔细阅读并实践相关代码,相信您已经掌握了这门技术。希望本篇博客对您有所帮助,谢谢阅读!
参考链接:
本文来自极简博客,作者:风华绝代,转载请注明原文链接:SpringBoot集成SpringSecurity JWT实现权限管理