SpringBoot JWT实现token登录刷新功能

下面就为你详细讲解“SpringBoot JWT实现token登录刷新功能”的完整攻略。

什么是JWT

JWT即Json Web Token,是基于JSON格式的令牌,包含有用户的一些身份信息和一些验证信息。在用户登录后,服务器会生成一个JWT给前端返回,在之后的请求中,前端只需在HTTP头中携带该令牌即可实现状态保持。

实现流程

首先,我们需要在项目中引入JWT的依赖,以Maven为例,添加以下依赖:

<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>

其中,jjwt-api是API接口,jjwt-impl是实现,jjwt-jackson是用于JSON序列化和反序列化的库。

在编写后端登录接口时,我们需要先对用户进行密码验证,验证通过后,我们需要生成一个JWT并将其返回给前端。代码示例:

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtTokenUtil {

    //定义过期时间为1小时
    @Value("${jwt.expiration}")
    private long expiration;

    @Value("${jwt.secret}")
    private String secret;

    //生成JWT
    public String generateToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("sub", username);
        claims.put("created", new Date());
        return Jwts.builder()
            .setClaims(claims)
            .setExpiration(new Date(System.currentTimeMillis() + expiration))
            .signWith(SignatureAlgorithm.HS512, secret)
            .compact();
    }
}

其中,@Value注解从配置文件中获取属性值,用于生成JWT时的过期时间和加密密钥。生成JWT时,我们需要用到Jwts工具类,将用户信息放入JWT的claims中,并设置JWT过期时间和签名。

在后续的请求中,前端需要在HTTP头中携带该JWT,后端需要验证该JWT是否正确以及其是否过期。代码示例:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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;
import java.util.List;
import java.util.stream.Collectors;

public class JwtTokenAuthenticationFilter extends OncePerRequestFilter {

    private final String HEADER = "Authorization";
    private final String PREFIX = "Bearer ";
    @Value("${jwt.secret}")
    private String secret;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            if (checkJWTToken(request)) {
                Claims claims = validateToken(request);
                if (claims.get("authorities") != null) {
                    setUpSpringAuthentication(claims);
                } else {
                    SecurityContextHolder.clearContext();
                }
            }
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
            return;
        }
    }

    private Claims validateToken(HttpServletRequest request) {
        String jwtToken = request.getHeader(HEADER).replace(PREFIX, "");
        return Jwts.parser().setSigningKey(secret.getBytes())
                .parseClaimsJws(jwtToken).getBody();
    }

    private boolean checkJWTToken(HttpServletRequest request) {
        String authenticationHeader = request.getHeader(HEADER);
        if (authenticationHeader == null || !authenticationHeader.startsWith(PREFIX))
            return false;
        return true;
    }

    private void setUpSpringAuthentication(Claims claims) {
        List<String> authorities = (List) claims.get("authorities");
        UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
                claims.getSubject(), null, authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()));
        SecurityContextHolder.getContext().setAuthentication(auth);
    }
}

在该过滤器中,我们通过验证JWT是否存在、是否以正确的格式携带、验证JWT的签名和过期时间来确认JWT的合法性。如果合法,我们将通过JWT中的用户信息设置SpringSecurity的authentication信息,以保证请求的安全性。

实现刷新功能

JWT有一个明显的缺点,即一旦JWT过期了,就必须重新向服务器请求新的JWT。但这样做的话会影响用户体验。为了提高用户体验,我们可以实现JWT的刷新功能。

刷新功能的实现非常简单,当JWT快要过期时,我们生成一个新的JWT,新JWT的过期时间为原JWT的两倍。这样,在旧JWT即将过期之前,前端可以使用新JWT请求服务器,避免重新登录。代码示例:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

//省略代码

public class JwtTokenAuthenticationFilter extends OncePerRequestFilter {

    private final String HEADER = "Authorization";
    private final String PREFIX = "Bearer ";
    @Value("${jwt.secret}")
    private String secret;
    @Value("${jwt.expiration}")
    private long expiration;
    @Value("${jwt.refreshTime}")
    private long refreshTime;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            if (checkJWTToken(request)) {
                Claims claims = validateToken(request);
                if (claims.get("authorities") != null) {
                    setUpSpringAuthentication(claims);
                } else {
                    SecurityContextHolder.clearContext();
                }
                if (checkRefreshToken(claims)) {
                    response.setHeader("Authorization", refreshJWTToken(request));
                }
            }
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
            return;
        }
    }

    private boolean checkRefreshToken(Claims claims) {
        Date expirationDate = claims.getExpiration();
        Date now = new Date(System.currentTimeMillis());
        long timeDiff = expirationDate.getTime() - now.getTime();
        if (timeDiff <= refreshTime * 1000 && timeDiff > 0) {
            return true;
        }
        return false;
    }

    private String refreshJWTToken(HttpServletRequest request) {
        String authToken = request.getHeader(HEADER).replace(PREFIX, "");
        Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(authToken).getBody();

        if (claims.getSubject() == null || claims.get("authorities") == null) {
            throw new RuntimeException("JWT claims is empty.");
        }

        String subject = Jwts
                .builder()
                .setClaims(claims)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 2))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
        return PREFIX + subject;
    }
}

在该过滤器中,我们添加了两个配置属性,一个是refreshTime,指定了延长JWT的时间,如果当前JWT的过期时间小于refreshTime设定值,我们就会在原JWT的基础生成一个 token,该 token 的过期时间是原 token 的两倍,接下来将新的 token 返回给用户。稍微读懂代码就会知道,JWT token 是由原 token的claims重新组装成新的 token的。

以上就是“SpringBoot JWT实现token登录刷新功能”的实现攻略。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot JWT实现token登录刷新功能 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Java将文件夹保留目录打包为 ZIP 压缩包并下载的教程详解

    下面是关于“Java将文件夹保留目录打包为 ZIP 压缩包并下载的教程详解”的完整攻略。 前言 在Java程序中,我们有时会需要将一个文件夹以及其中的文件打包成ZIP格式的压缩文件并下载。本文将介绍如何实现这个功能。 代码实现 Java提供了ZipOutputStream类和ZipEntry类,可以轻松地打包一个文件夹中的所有文件并生成ZIP文件。我们可以使…

    Java 2023年5月19日
    00
  • JSP实现客户信息管理系统

    下面是“JSP实现客户信息管理系统”的完整攻略: 1. 设计数据库 首先需要设计数据库,数据库中应包含客户信息的各种属性,例如客户编号(id)、姓名(name)、性别(gender)、年龄(age)、联系方式(phone)等等。 2. 搭建环境 安装JDK、IDE和Tomcat服务器。在IDE中创建一个Web项目,使用Maven来管理项目依赖。在项目中依次创…

    Java 2023年6月15日
    00
  • Android自定义view制作绚丽的验证码

    感谢您对Android自定义View制作绚丽验证码的关注,下面是我对此的完整攻略。 1. 前言 自定义View是Android很重要的一部分,因为它可以帮助我们创建最适合我们业务逻辑的用户界面。这个教程将向您展示如何制作一个绚丽的验证码。首先,我们将介绍带有随机数字和字母的简单验证码,然后我们将介绍如何使用自定义View类创建更复杂的验证码。 2. 制作带有…

    Java 2023年5月26日
    00
  • 在java中ArrayList集合底层的扩容原理

    在Java中,ArrayList是一个可以动态扩容的数组,其底层实现是基于数组而设计的。当ArrayList的容量不足以存储新的元素时,就需要进行扩容操作。本文将详细讲解在Java中ArrayList集合底层的扩容原理。 ArrayList内部数组实现 首先,我们需要了解ArrayList内部数组的实现方式。在ArrayList中,用于存储元素的是一个Obj…

    Java 2023年5月26日
    00
  • java 获取当前路径下的所有xml文档的方法

    让我们来详细讲解如何用java代码获取指定目录下的所有以xml结尾的文件。 1. 获取当前路径 首先,我们需要获取当前路径,即指定目录所在的路径。可以使用System.getProperty()方法获取系统属性中的当前路径。 String currentPath = System.getProperty("user.dir"); Syst…

    Java 2023年5月19日
    00
  • 全面解析Spring Security 过滤器链的机制和特性

    全面解析Spring Security 过滤器链的机制和特性 什么是Spring Security过滤器链? Spring Security过滤器链是Spring Security处理HTTP请求的核心组件之一。在Spring Security框架中,每一个安全的URL请求都需要通过一系列的过滤器组成的过滤器链来进行权限的校验和身份认证,该过滤器链是有顺序的…

    Java 2023年5月20日
    00
  • 详解Spring MVC CORS 跨域

    详解Spring MVC CORS 跨域 CORS(Cross-Origin Resource Sharing)是一种Web浏览器的安全机制,用于限制跨域请求。在Spring MVC中,我们可以使用@CrossOrigin注解来处理CORS跨域请求。 @CrossOrigin注解 @CrossOrigin注解是Spring MVC提供的一个注解,它可以用来处…

    Java 2023年5月18日
    00
  • Java解码H264格式视频流中的图片

    针对“Java解码H264格式视频流中的图片”的需求,我整理了以下完整攻略: 确定准备工作 了解H.264编解码标准及相关概念(可以参考视频编解码入门知识); 熟悉Java开发语言以及常用的流处理库,如Netty、I/O等; 确定H.264格式视频流的来源和传输方式。 解码H.264视频流 接收H.264视频流; 使用H.264解码器库,例如h264lib、…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部