Spring Boot 整合 JWT 实现一个安全的Restful 接口?
JWT 是一种用于身份验证和授权的开放标准(RFC 7519),它允许使用签名验证和信息加密来传输数据。集成 JSON Web Token(JWT)到 Spring Boot 以实现安全的 RESTful 接口是一种常见的做法。下面是一个简单的步骤,展示如何在 Spring Boot 中整合 JWT。
第一步、添加POM依赖
首先,需要添加JWT相关依赖到POM文件中。这里需要根据自己项目的版本对JWT版本做对应的适配,否则可能会出现版本差异导致相关功能不能用的情况出现。
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
创建JWT工具类
需要创建一个JWT的通用工具类,完成JWT的创建与解析操作,同时需要完成认证的签发、验证令牌等操作。如下所示。
@Component
public class JwtUtils {
private static final String SECRET_KEY = "yourSecretKey";
private static final long EXPIRATION_TIME = 86400000; // 24 hours
public 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 String getUsernameFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
return true;
} catch (Exception ex) {
return false;
}
}
}
注意这里需要指定对应的秘钥以及过期时间等信息。
创建接口安全配置类
引入SpringSecurity的POM依赖
<!-- spring security 安全认证 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
创建配置类
创建一个 Spring Security 配置类,配置认证和授权规则,并且集成 JWT。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// 配置用户认证信息
auth.inMemoryAuthentication()
.withUser("admin").password(passwordEncoder().encode("password")).roles("ADMIN")
.and()
.withUser("user").password(passwordEncoder().encode("password")).roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/authenticate").permitAll()
.antMatchers(HttpMethod.GET, "/api/**").hasAnyRole("USER", "ADMIN")
.antMatchers(HttpMethod.POST, "/api/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
需要注意,这里提供了一个
UsernamePasswordAuthenticationFilter的拦截器,这个拦截器就是用来进行请求拦截进行判断处理的。
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
{
@Autowired
private JwtUtils jwtUtils;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws ServletException, IOException{
// 具体使用
// 1、通过Request请求获取到请求头中的Token信息
// 2、将Token信息传入到JwtUtils进行认证,如果通过则继续调用
// 如果拦截返回未授权信息
chain.doFilter(request, response);
}
}
创建认证请求实体对象
创建一个通用的认证请求以及相应对象来进行测试调用。
public class AuthRequest {
private String username;
private String password;
// getters and setters
}
public class AuthResponse {
private final String token;
public AuthResponse(String token) {
this.token = token;
}
// getter
}
创建 REST 控制器
创建 REST 控制器来处理认证请求,并且使用 JWT 工具类来生成令牌。
@RestController
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtUtils jwtUtils;
@Autowired
private CustomUserDetailsService userDetailsService;
@PostMapping("/authenticate")
public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthRequest authenticationRequest) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword()));
} catch (AuthenticationException e) {
throw new Exception("Incorrect username or password", e);
}
final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtUtils.generateToken(userDetails.getUsername());
return ResponseEntity.ok(new AuthResponse(token));
}
}
创建完成之后,就可以通过接口调用认证接口,然后获取到认证的Token令牌,然后根据访问令牌来进行身份验证,这样就可以保证接口调用的安全性。