springboot整合jwt和基础用法

项目地址

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.zuke</groupId>
    <artifactId>springboot-jwt</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-jwt</name>
    <description>springboot-jwt</description>

    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <!-- jdk1.8以上需要加入依赖 -->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!-- jwt依赖 -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <!-- web开发包 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- springboot test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

src/main/resources/application.yml

server:
  port: 8802

实体类

src/main/java/com/zuke/springbootjwt/entity/User.java

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author zukedog@163.com
 * @date 2024/8/4 12:54
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String username;
    private String password;
    private String token;
}

jwt工具类(按需修改)

src/main/java/com/zuke/springbootjwt/utils/JwtUtils.java

public class JwtUtil {
    /**
     * 生成JWT
     * @param username 用户名
     * @param role 用户角色
     * @param validTime JWT有效期(毫秒)
     * @param signature 签名密钥
     * @return 生成的JWT字符串
     */
    public static String generateToken(String username, String role, long validTime, String signature) {
        JwtBuilder jwtBuilder = Jwts.builder();
        String token = jwtBuilder
                // Header
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                // Payload
                .claim("username", username)
                .claim("role", role)
                .setExpiration(new Date(System.currentTimeMillis() + validTime))
                .setId(UUID.randomUUID().toString())
                // Signature
                .signWith(SignatureAlgorithm.HS256, signature)
                .compact();
        return token;
    }

    /**
     * 校验并解析JWT
     * @param token JWT字符串
     * @param signature 签名密钥
     * @return 解析后的Claims对象,如果校验失败则返回null
     */
    public static TokenValidationResult checkToken(String token, String signature) {
        if (StringUtils.isEmpty(token) || StringUtils.isEmpty(signature)) {
            return new TokenValidationResult(false, TokenValidationError.INVALID_TOKEN);
        }

        try {
            Claims claims = Jwts.parser()
                    .setSigningKey(signature)
                    .parseClaimsJws(token)
                    .getBody();

            return new TokenValidationResult(true, TokenValidationError.INVALID_TOKEN);
        } catch (ExpiredJwtException e) {
            // 令牌已过期
            return new TokenValidationResult(false, TokenValidationError.EXPIRED_TOKEN);
        } catch (SignatureException e) {
            // 签名验证失败
            return new TokenValidationResult(false, TokenValidationError.INVALID_SIGNATURE);
        } catch (MalformedJwtException e) {
            // 令牌构造不正确
            return new TokenValidationResult(false, TokenValidationError.MALFORMED_TOKEN);
        } catch (JwtException e) {
            // 其他JWT相关异常
            return new TokenValidationResult(false, TokenValidationError.INVALID_TOKEN);
        }
    }

    /**
     * 更新token的过期时间为1小时
     * @param oldToken 原始token
     * @param signature 签名密钥
     * @return 更新后的token,如果原token无效则返回null
     */
    public static String updateTokenExpiration(String oldToken, String signature) {
        if (StringUtils.isEmpty(oldToken) || StringUtils.isEmpty(signature)) {
            return null;
        }

        try {
            // 解析原token
            Claims claims = Jwts.parser()
                    .setSigningKey(signature)
                    .parseClaimsJws(oldToken)
                    .getBody();

            // 获取原token中的信息
            String username = claims.get("username", String.class);
            String role = claims.get("role", String.class);

            // 生成新token,有效期为1小时
            long validTime = 1000 * 60 * 60; // 1小时
            return generateToken(username, role, validTime, signature);

        } catch (JwtException e) {
            // JWT解析失败
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 验证token中的角色类型是否在允许的角色类型列表中
     * @param token JWT字符串
     * @param signature 签名密钥
     * @param roleTypes 允许的角色类型数组
     * @throws JwtException 当token无效或角色类型不匹配时抛出
     * @throws IllegalArgumentException 当参数为空时抛出
     */
    public static void validateTokenRole(String token, String signature, String[] roleTypes) {
        // 参数校验
        if (StringUtils.isEmpty(token) || StringUtils.isEmpty(signature) || roleTypes == null || roleTypes.length == 0) {
            throw new IllegalArgumentException("Token, signature and roleTypes cannot be null or empty");
        }

        try {
            // 解析token
            Claims claims = Jwts.parser()
                    .setSigningKey(signature)
                    .parseClaimsJws(token)
                    .getBody();

            // 获取token中的角色
            String tokenRole = claims.get("role", String.class);
            if (StringUtils.isEmpty(tokenRole)) {
                throw new JwtException("Token does not contain role information");
            }

            // 检查角色是否在允许的列表中
            boolean roleMatched = false;
            for (String allowedRole : roleTypes) {
                if (tokenRole.equals(allowedRole)) {
                    roleMatched = true;
                    break;
                }
            }

            // 如果角色不匹配,抛出异常
            if (!roleMatched) {
                throw new CustomException("此操作不允许使用此用户角色");
            }
        } catch (JwtException e) {
            throw new CustomException(e.getMessage());
        }
    }

    public static Claims parseToken(String token) {
        return Jwts.parser()
                .setSigningKey(Constant.JWT_SIGNATURE)
                .parseClaimsJws(token)
                .getBody();
    }

}

配置允许跨域

src/main/java/com/zuke/springbootjwt/config/CrosConfiguration.java

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author zukedog@163.com
 * @date 2024/8/4 13:45
 */
@Configuration
public class CrosConfiguration implements WebMvcConfigurer {

    //解决跨域问题
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");
    }
}

测试接口

src/main/java/com/zuke/springbootjwt/controller/UserController.java

import com.zuke.springbootjwt.entity.User;
import com.zuke.springbootjwt.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author zukedog@163.com
 * @date 2024/8/4 12:55
 */
@RestController
public class UserController {
    //模拟数据库密码
    private final String USERNAME = "root";
    private final String PASSWORD = "root";
    private final String SIGNATURE = "root";

    @PostMapping("/login")
    public String login(@RequestBody User user) {
        if (USERNAME.equals(user.getUsername()) && PASSWORD.equals(user.getPassword())){
            //添加token
            return JwtUtils.generateToken(user.getUsername(),"user",1000*60*60,SIGNATURE);
        }
        return null;
    }

    @GetMapping("/checkToken")
    public boolean checkToken(HttpServletRequest request){
        String token = request.getHeader("token");
        return JwtUtils.checkToken(token,SIGNATURE);
    }
}
分类: 标签: SpringBoot Jwt 令牌

评论

全部评论