JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用环境间安全地将信息作为JSON对象传输。JWT令牌自动刷新是一种机制,用于在令牌过期前自动获取新的令牌,以避免用户频繁登录。
JWT通常由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。头部和载荷分别是Base64Url编码的JSON字符串,它们之间用一个点(.
)分隔。签名是使用头部、载荷和一个密钥通过指定的算法生成的。
自动刷新JWT令牌通常涉及以下几个步骤:
自动刷新JWT令牌适用于需要长时间保持用户登录状态的Web应用和移动应用,例如社交网络、在线购物平台等。
以下是一个简单的Spring Boot示例,展示如何实现JWT令牌的自动刷新:
在pom.xml
中添加Spring Security和JWT相关的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtTokenUtil {
@Value("${jwt.secret}")
private String secret;
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10小时过期
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public String refreshToken(String token) {
String username = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject();
return generateToken(username);
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
String token = header.substring(7);
if (jwtTokenUtil.validateToken(token)) {
Authentication authentication = jwtTokenUtil.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private UserDetailsService jwtUserDetailsService;
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.authorizeRequests().antMatchers("/authenticate").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
客户端可以使用JavaScript定时器来定期检查令牌的过期时间,并在需要时刷新令牌。
function refreshToken() {
const token = localStorage.getItem('token');
const expirationTime = new Date(localStorage.getItem('expirationTime'));
if (expirationTime && new Date() >= expirationTime - 60000) { // 提前1分钟刷新
fetch('/refreshToken', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(response => response.json())
.then(data => {
localStorage.setItem('token', data.token);
localStorage.setItem('expirationTime', new Date().getTime() + 1000 * 60 * 60 * 10); // 10小时过期
});
}
}
setInterval(refreshToken, 60000); // 每分钟检查一次
通过上述步骤,你可以实现JWT令牌的自动刷新机制,确保用户在应用中的持续登录状态。
领取专属 10元无门槛券
手把手带您无忧上云