JWT使用

学习链接

jwt官网:https://jwt.io

JWT详解:讲解的十分详细

参照:JWT 介绍和使用,对称加密,非对称加密,RSA, Tocken?

1.引入jwt的概念

1.1 有状态登录,无状态登录

有状态登录

服务器当中记录每一次的登录信息,从而根据客户端发送的数据来判断登录过来的用户是否合法。

缺点:

  • 服务器当中需要保存大量的session信息,从而增加了服务器的压力。
  • 客户端请求依赖登录服务器,多个请求过来访问同一个服务器。
  • 服务器拓展困难,需要将session存储到其他服务器当中。

无状态登录

服务器当中不记录用户的登录信息,而是将登录成功后的合法用户信息以token方式保存到客户端当中
,用户在每次请求都携带token信息。

好处:

  • 减少服务器存储session信息的压力。
  • 客户端请求不依赖服务器。

1.2 如何实现无状态登录?

流程

  1. 客户端第一次请求服务器,服务器端对登录用户信息进行认证。

  2. 认证通过后,对客户信息进行加密处理形成token登录凭证,然后返回给客户端。

  3. 以后客户端每一次请求都携带这个jwt信息去请求服务器。
    而服务器端请求信息进行解密处理。判断登录用户是否有效。

授权-鉴权流程图

在这里插入图片描述

2.加密技术的引入

2.1对称加密,非对称加密,不可逆加密方式。

对称加密

将明文分成N个组,然后使用密钥对各个组进行加密,形成各自的密文,最后把所有的分组密文进行合并,形成最终的密文。

  • 优势:加密速度快,加密效率高。
  • 缺点:双方都使用同样的密钥,安全性得不到保证。

非对称加密方式

同时生成两把密钥,公钥和私钥私钥服务器自己保存公钥下发到受信任的客户端

  • 优势:安全性高,只要私钥不暴露出去,信息就不会泄露。
  • 缺点:加密效率低

不可逆加密方式:MD5

加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,这种加密后的数据是无法被解密的,无法根据密文推算出明文

严格意义上,这种不属于加密方式。因为加密算法要既能加密也能解密。

3.JWT

概念:全称json wek token 是一种轻量级的身份认证 ,可实现无状态 ,分布式的web应用授权

3.1组成:

JWT由三部分组成分别是:header+payload+签名

header

头部,通常由两部分信息,

  • 声明类型:通常为jwt

  • 签名算法:签名部分使用的算法,需要在header中进行定义。

payload

负载

这里保存着有效的数据部分。

  • jwt当中实际存储数据的部分。

  • 官方规定了7个可以选择的数据。

    • iss:发行人
    • exp:到期时间
    • sub:主题
    • aud:用户
    • nbf:在此之前不可用
    • iat:发布时间
    • jti:JWT ID用于标识该JWT

signature

签名

通常是整个数据的认证信息,一般根据前面两部的数据,再加上服务的密钥,通过加密算法生成,用于验证整个数据的完成性。
在这里插入图片描述
如图所示,这是生成的jwt信息。

3.2jwt系统交互流程

流程图

在这里插入图片描述

交互流程析

  • 通过交互图可以观察到:用户登陆微服务后,还需要拿着jwt到鉴权中心去验证用户的登陆权限,能不能让用户就在服务端就可以完成鉴权的工作,这样就可以减少一次网络请求,加快系统的响应时间。

  • 结论:我们可以使用jwt+rsa的方式,由鉴权中心生成私钥,公钥。在授权中心通过私钥生成jwt信息,然后公钥下发给受信任的服务。再使用公钥再服务器端进行鉴权处理。(如果通过公钥可以获取到jwt当中信息,说明该用户具有对应的权限。可以进行登陆操作。)

3.3 使用jwt+rsa方式的授权+鉴权方式

在这里插入图片描述

4.JWT实例

4.1使用jwt所依赖的maven依赖都有哪些

<!--json web token相关坐标-->
 <dependency>
     <groupId>io.jsonwebtoken</groupId>
     <artifactId>jjwt-api</artifactId>
     <version>0.10.5</version>
 </dependency>
 <dependency>
     <groupId>io.jsonwebtoken</groupId>
     <artifactId>jjwt-impl</artifactId>
     <version>0.10.5</version>
     <scope>runtime</scope>
 </dependency>
 <dependency>
     <groupId>io.jsonwebtoken</groupId>
     <artifactId>jjwt-jackson</artifactId>
     <version>0.10.5</version>
     <scope>runtime</scope>
 </dependency>
 
 <dependency>
     <groupId>joda-time</groupId>
     <artifactId>joda-time</artifactId>
     <version>2.10.10</version>
 </dependency>

4.2RSA工具类:用于生成私钥和公钥

RsaUtils

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * Rsa工具类
 */
public class RsaUtils {

    private static final int DEFAULT_KEY_SIZE = 2048;

    /**
     * 从文件中读取公钥
     *
     * @param filename 公钥保存路径,相对于classpath
     * @return  PublicKey 公钥对象
     * @throws Exception
     */
    public static PublicKey getPublicKey(String filename) throws Exception {
        byte[] bytes = readFile (filename);
        return getPublicKey (bytes);
    }

    /**
     * 从文件中读取密钥
     *
     * @param filename 私钥保存路径,相对于classpath
     * @return  PrivateKey 私钥对象
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String filename) throws Exception {
        byte[] bytes = readFile (filename);
        return getPrivateKey (bytes);
    }

    /**
     * 获取公钥
     * 公钥的字节形式。
     * @param bytes 公钥的字节形式
     * @return
     * @throws Exception
     */
    private static PublicKey getPublicKey(byte[] bytes) throws Exception {
        bytes = Base64.getDecoder ( ).decode (bytes);
        X509EncodedKeySpec spec = new X509EncodedKeySpec (bytes);
        KeyFactory factory = KeyFactory.getInstance ("RSA");
        return factory.generatePublic (spec);
    }

    /**
     * 获取密钥
     *
     * @param bytes 私钥的字节形式
     * @return
     * @throws Exception
     */
    private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        bytes = Base64.getDecoder ( ).decode (bytes);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec (bytes);
        KeyFactory factory = KeyFactory.getInstance ("RSA");
        return factory.generatePrivate (spec);
    }

    /**
     * 根据密文,生存rsa公钥和私钥,并写入指定文件
     *
     * @param publicKeyFilename  公钥文件路径
     * @param privateKeyFilename 私钥文件路径
     * @param secret             生成密钥的密文
     */
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret, int keySize) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance ("RSA");
        SecureRandom secureRandom = new SecureRandom (secret.getBytes ( ));
        keyPairGenerator.initialize (Math.max (keySize, DEFAULT_KEY_SIZE), secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair ( );
        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic ( ).getEncoded ( );
        publicKeyBytes = Base64.getEncoder ( ).encode (publicKeyBytes);
        writeFile (publicKeyFilename, publicKeyBytes);
        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate ( ).getEncoded ( );
        privateKeyBytes = Base64.getEncoder ( ).encode (privateKeyBytes);
        writeFile (privateKeyFilename, privateKeyBytes);
    }

    private static byte[] readFile(String fileName) throws Exception {
        return Files.readAllBytes (new File (fileName).toPath ( ));
    }

    private static void writeFile(String destPath, byte[] bytes) throws IOException {
        File dest = new File (destPath);
        if (!dest.exists ( )) {
            dest.createNewFile ( );
        }
        Files.write (dest.toPath ( ), bytes);
    }

}

RsaUtilsTest

import org.junit.Test;

import java.security.PrivateKey;
import java.security.PublicKey;

public class RsaUtilsTest {
    //生成私钥,公钥地址
    private String privateFilePath = "D:\\Projects\\demo-springboot222\\src\\main\\resources\\id_rsa";
    private String publicFilePath = "D:\\Projects\\demo-springboot222\\src\\main\\resources\\id_rsa_pub";

    @Test
    public void testRSA() throws Exception {
        // 生成密钥对
        RsaUtils.generateKey(publicFilePath, privateFilePath, "hello", 2048);

        // 获取私钥
        PrivateKey privateKey = RsaUtils.getPrivateKey(privateFilePath);
        System.out.println("privateKey = " + privateKey);
        // 获取公钥
        PublicKey publicKey = RsaUtils.getPublicKey(publicFilePath);
        System.out.println("publicKey = " + publicKey);
    }
}

在这里插入图片描述

4.3 jwt当中的载荷信息部分

Payload

import lombok.Data;
import java.util.Date;

@Data
public class Payload<T> {

    // jwt的id
    private String id;
    
    // 用户信息
    private T userInfo;
    
    // 过期时间
    private Date expiration;

}

UserInfo

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

@Data
@NoArgsConstructor
@AllArgsConstructor
/**
 * 载荷 :UserInfo
 */
public class UserInfo {

    private Long id;

    private String username;

    private String role;
}

4.4 jwtutils工具类,用于生成jwt信息,解析jwt信息

JwtUtils

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.joda.time.DateTime;


import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import java.util.UUID;

public class JwtUtils {

    private static final String JWT_PAYLOAD_USER_KEY = "user";

    /**
     *
     * 私钥加密token
     *
     * @param userInfo   载荷中的数据
     * @param privateKey 私钥
     * @param expire     过期时间,单位分钟
     * @return JWT
     */
    public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) {
        return Jwts.builder()
                .claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo))
                .setId(createJTI())
                .setExpiration(DateTime.now().plusMinutes(expire).toDate())
                .signWith(privateKey, SignatureAlgorithm.RS256)
                .compact();
    }

    /**
     * 私钥加密token
     *
     * @param userInfo   载荷中的数据
     * @param privateKey 私钥
     * @param expire     过期时间,单位秒
     * @return JWT
     */
    public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) {
        return Jwts.builder()
                .claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo))
                .setId(createJTI())
                .setExpiration(DateTime.now().plusSeconds(expire).toDate())
                .signWith(privateKey, SignatureAlgorithm.RS256)
                .compact();
    }

    /**
     * 公钥解析token
     *
     * @param token     用户请求中的token
     * @param publicKey 公钥
     * @return Jws<Claims>
     */
    private static Jws<Claims> parserToken(String token, PublicKey publicKey) {
        return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
    }

    private static String createJTI() {
        return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
    }

    /**
     * 获取token中的用户信息
     *
     * @param token     用户请求中的令牌
     * @param publicKey 公钥
     * @return 用户信息
     */
    public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) {
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        Payload<T> claims = new Payload<>();
        claims.setId(body.getId());
        claims.setUserInfo(JsonUtils.toBean(body.get(JWT_PAYLOAD_USER_KEY).toString(), userType));
        claims.setExpiration(body.getExpiration());
        return claims;
    }

    /**
     * 获取token中的载荷信息
     *
     * @param token     用户请求中的令牌
     * @param publicKey 公钥
     * @return 用户信息
     */
    public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey) {
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        Payload<T> claims = new Payload<>();
        claims.setId(body.getId());
        claims.setExpiration(body.getExpiration());
        return claims;
    }
}

测试

public static void main(String[] args) throws Exception {
     String privateFilePath = "D:\\Projects\\demo-springboot222\\src\\main\\resources\\id_rsa";
     String publicFilePath = "D:\\Projects\\demo-springboot222\\src\\main\\resources\\id_rsa_pub";

     PrivateKey privateKey = RsaUtils.getPrivateKey(privateFilePath);
     PublicKey publicKey = RsaUtils.getPublicKey(publicFilePath);

     UserInfo userInfo = new UserInfo();
     userInfo.setId(1L);
     userInfo.setRole("admin");
     userInfo.setUsername("zzhua");

     ObjectMapper mapper = new ObjectMapper();

     String token = Jwts.builder()
             .claim("user", mapper.writeValueAsString((userInfo)))
             .setId(createJTI())
             .setExpiration(DateTime.now().plusMinutes(10).toDate())
             .signWith(privateKey, SignatureAlgorithm.RS256) // 私钥加密 
                                  // 注意这里的算法只能选RS开头的,比如还可以选SignatureAlgorithm.RS384
             .compact();

     Jws<Claims> claimsJws = Jwts.parser()
             .setSigningKey(publicKey) // 公钥解密
             .parseClaimsJws(token);   // token

     Claims body = claimsJws.getBody();
     System.out.println(body.get("user").toString());
 }

解密结果
在这里插入图片描述

5. jwt一些简单demo

jjwt

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.6.0</version>
</dependency>

Base64加密、解密

@Test
public void testGenJwt() {
    JwtBuilder jwtBuilder = Jwts.builder()
                                .setSubject("zzhua")
                                .setId("001")
                                .signWith(SignatureAlgorithm.HS256, "YOUR_secret");
    String token = jwtBuilder.compact();
    System.out.println(token);
    // eyJhbGciOiJIUzI1NiJ9.
    // eyJzdWIiOiJ6emh1YSIsImp0aSI6IjAwMSJ9.
    // yipfa-gsDGg-5KYqeCVo_oiOnUx2D0Sp589gCUXCap8
}

@Test
public void testParseToken() throws IOException {
    BASE64Decoder decoder = new BASE64Decoder();
    // 使用Base64解析jwtToken的负载部分(头部和负载部分都是使用Base64加密的,所以可以解密)
    String data = new String(decoder.decodeBuffer("eyJzdWIiOiJ6emh1YSIsImp0aSI6IjAwMSJ9"));
    System.out.println(data); // 解析结果:{"sub":"zzhua","jti":"001"}

    BASE64Encoder encoder = new BASE64Encoder();
    // 使用Base64加密
    String encode = encoder.encode("{\"sub\":\"zzhua\",\"jti\":\"001\"}".getBytes());
    System.out.println(encode); // 与上面完全符合:eyJzdWIiOiJ6emh1YSIsImp0aSI6IjAwMSJ9
}

用法

//生成token
public class CreateJwtTest {
    public static void main(String[] args) {
        JwtBuilder builder= Jwts.builder().setId("888")
            .setSubject("小白")
            .setIssuedAt(new Date())
            .signWith(SignatureAlgorithm.HS256,"itcast");
        System.out.println( builder.compact() );
    }
}

// 解析token
public class ParseJwtTest {
    public static void main(String[] args) {

        String token ="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiO"+
            "jE1MjM0MTM0NTh9.gq0J-cOM_qCNqU_s-d_IrRytaNenesPmqAIhQpYXHZk";

        Claims claims = Jwts.parser().setSigningKey("itcast").parseClaimsJws(token).getBody();

        System.out.println("id:"+claims.getId());
        System.out.println("subject:"+claims.getSubject());
        System.out.println("IssuedAt:"+claims.getIssuedAt());

    }
}

// 自定义claims数据
public class CreateJwtTest3 {
    public static void main(String[] args) {
        //为了方便测试,我们将过期时间设置为1分钟
        long now = System.currentTimeMillis();//当前时间
        long exp = now + 1000*60;//过期时间为1分钟
        new HashMap<String,Object>()
        JwtBuilder builder= Jwts.builder().setId("888")
            .setSubject("小白")
            .setIssuedAt(new Date())
            .signWith(SignatureAlgorithm.HS256,"itcast")
            .setExpiration(new Date(exp))
            .claim("roles","admin") //自定义claims存储数据
            .claim("logo","logo.png");
        System.out.println( builder.compact() );
    }
}

对称签名

public class JwtUtils {
    // token时效:24小时
    public static final long EXPIRE = 1000 * 60 * 60 * 24;
    // 签名哈希的密钥,对于不同的加密算法来说含义不同
    public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";

    /**
     * 根据用户id和昵称生成token
     * @param id  用户id
     * @param nickname 用户昵称
     * @return JWT规则生成的token
     */
    public static String getJwtToken(String id, String nickname){
        String JwtToken = Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                .setSubject("baobao-user")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
                .claim("id", id)
                .claim("nickname", nickname)
            	// HS256算法实际上就是MD5加盐值,此时APP_SECRET就代表盐值
                .signWith(SignatureAlgorithm.HS256, APP_SECRET)
                .compact();

        return JwtToken;
    }

    /**
     * 判断token是否存在与有效
     * @param jwtToken token字符串
     * @return 如果token有效返回true,否则返回false
     */
    public static boolean checkToken(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) return false;
        try {
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 判断token是否存在与有效
     * @param request Http请求对象
     * @return 如果token有效返回true,否则返回false
     */
    public static boolean checkToken(HttpServletRequest request) {
        try {
            // 从http请求头中获取token字符串
            String jwtToken = request.getHeader("token");
            if(StringUtils.isEmpty(jwtToken)) return false;
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 根据token获取会员id
     * @param request Http请求对象
     * @return 解析token后获得的用户id
     */
    public static String getMemberIdByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        if(StringUtils.isEmpty(jwtToken)) return "";
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("id");
    }
}

非对称签名

生成jwt串的时候需要指定私钥,解析jwt串的时候需要指定公钥

private static final String RSA_PRIVATE_KEY = "...";
private static final String RSA_PUBLIC_KEY = "...";

/**
     * 根据用户id和昵称生成token
     * @param id  用户id
     * @param nickname 用户昵称
     * @return JWT规则生成的token
     */
public static String getJwtTokenRsa(String id, String nickname){
    // 利用hutool创建RSA
    RSA rsa = new RSA(RSA_PRIVATE_KEY, null);
    RSAPrivateKey privateKey = (RSAPrivateKey) rsa.getPrivateKey();
    String JwtToken = Jwts.builder()
        .setSubject("baobao-user")
        .setIssuedAt(new Date())
        .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
        .claim("id", id)
        .claim("nickname", nickname)
        // 签名指定私钥
        .signWith(privateKey, SignatureAlgorithm.RS256)
        .compact();
    return JwtToken;
}

/**
     * 判断token是否存在与有效
     * @param jwtToken token字符串
     * @return 如果token有效返回true,否则返回false
     */
public static Jws<Claims> decodeRsa(String jwtToken) {
    RSA rsa = new RSA(null, RSA_PUBLIC_KEY);
    RSAPublicKey publicKey = (RSAPublicKey) rsa.getPublicKey();
    // 验签指定公钥
    Jws<Claims> claimsJws = Jwts.parserBuilder().setSigningKey(publicKey).build().parseClaimsJws(jwtToken);
    return claimsJws;
}

java-jwt

引入依赖

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.4.0</version>
</dependency>

示例工具类1

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Arrays;
import java.util.Date;
import java.util.List;

public class JWTUtil {

    /**
     * 过期时间3小时
     */
    private static final long EXPIRE_TIME = 3 * 60 * 60 * 1000;

    /**
     * 校验token是否正确
     *
     * @param token  密钥
     * @param secret 用户的密码
     * @return 是否正确
     */
    public static boolean verify(String token, String username, String secret) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withClaim("username", username)
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (Exception exception) {
            return false;
        }
    }

    /**
     * @return token中包含的用户名
     */
    public static String getUsername(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("username").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * 获取当前用户
     *
     * @param token jwt加密信息
     * @return 解析的当前用户信息
     */
    public static Principal getPrincipal(String token) {
        try {
            Principal principal = new Principal();
            DecodedJWT jwt = JWT.decode(token);
            principal.setUserId(jwt.getClaim("userId").asString());
            principal.setUserName(jwt.getClaim("username").asString());
            String[] roleArr = jwt.getClaim("roles").asArray(String.class);
            if (roleArr != null) {
                principal.setRoles(Arrays.asList(roleArr));
            }
            return principal;
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * 获取角色组
     *
     * @param token
     * @return
     */
    public static String[] getRoles(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("roles").asArray(String.class);
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * 生成签名
     *
     * @param username 用户名
     * @param userId   用户id
     * @param secret   用户的密码
     * @return 加密的token
     */
    public static String sign(String username, String userId, List<String> roles, String secret) {
        Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
        Algorithm algorithm = Algorithm.HMAC256(secret);
        String[] roleArr = new String[roles.size()];
        roleArr = roles.toArray(roleArr);
        // 附带username信息
        return JWT.create()
                .withClaim("userId", userId)
                .withClaim("username", username)
                .withArrayClaim("roles", roleArr)
                .withExpiresAt(date)
                .sign(algorithm);
    }
}

对称签名

public class JWTUtils {
    // 签名密钥
    private static final String SECRET = "!DAR$";

    /**
     * 生成token
     * @param payload token携带的信息
     * @return token字符串
     */
    public static String getToken(Map<String,String> payload){
        // 指定token过期时间为7天
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, 7);

        JWTCreator.Builder builder = JWT.create();
        // 构建payload
        payload.forEach((k,v) -> builder.withClaim(k,v));
        // 指定过期时间和签名算法
        String token = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.HMAC256(SECRET));
        return token;
    }


    /**
     * 解析token
     * @param token token字符串
     * @return 解析后的token
     */
    public static DecodedJWT decode(String token){
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
        DecodedJWT decodedJWT = jwtVerifier.verify(token);
        return decodedJWT;
    }
}

非对称签名

生成jwt串的时候需要指定私钥,解析jwt串的时候需要指定公钥。

private static final String RSA_PRIVATE_KEY = "..."; // 需要生成
private static final String RSA_PUBLIC_KEY = "..."; // 需要生成

/**
     * 生成token
     * @param payload token携带的信息
     * @return token字符串
     */
public static String getTokenRsa(Map<String,String> payload){
    // 指定token过期时间为7天
    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.DATE, 7);

    JWTCreator.Builder builder = JWT.create();
    // 构建payload
    payload.forEach((k,v) -> builder.withClaim(k,v));

    // 利用hutool创建RSA
    RSA rsa = new RSA(RSA_PRIVATE_KEY, null);
    // 获取私钥
    RSAPrivateKey privateKey = (RSAPrivateKey) rsa.getPrivateKey();
    // 签名时传入私钥
    String token = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.RSA256(null, privateKey));
    return token;
}

/**
     * 解析token
     * @param token token字符串
     * @return 解析后的token
     */
public static DecodedJWT decodeRsa(String token){
    // 利用hutool创建RSA
    RSA rsa = new RSA(null, RSA_PUBLIC_KEY);
    // 获取RSA公钥
    RSAPublicKey publicKey = (RSAPublicKey) rsa.getPublicKey();
    // 验签时传入公钥
    JWTVerifier jwtVerifier = JWT.require(Algorithm.RSA256(publicKey, null)).build();
    DecodedJWT decodedJWT = jwtVerifier.verify(token);
    return decodedJWT;
}

6. Double Token解决方案

参照:JavaWeb单点登录的实践

流程图

  • 用户登录成功后,后台生成token,但是不直接把此token返回给客户端,而是使用md5对该token加密,并将MD5(token)作为key,token作为value,存入redis,这样避免用户信息被泄露(因为jwt的头部和负载仅仅是Base64加密,很容易被解析出来,因此敏感信息不能放进去,使用md5加密后,这样就解析不出来了),将解密后的结果返回给客户端(可以用cookie的形式写给客户端,让客户端自动携带cookie过来,比较方便)。

  • 客户端访问时携带MD5(token),后台从redis中拿到真正的token,解析出来

在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Hertz JWT 使用通常涉及到在 API 开发中对访问控制、身份验证和授权的管理。JWT (JSON Web Token) 是一种开放标准,用于安全地传输数据作为令牌,并可以在客户端和服务端之间进行验证。当结合 Hertz 框架使用时,可以有效提升 RESTful API 的安全性。 ### Hertz 中 JWT 的应用 1. **身份验证**:通过 JWT 提供了一个自包含的身份认证机制,允许客户端一次性携带身份信息,在请求过程中不需要再次提供凭证。 2. **权限控制**:JWT 可以携带用户角色或其他授权信息,服务端可以根据这些信息做出相应的访问决策。 3. **状态无状态性**:由于 JWT 是自包含的,它消除了对会话存储的需求,使得应用程序更易于分布式部署。 4. **加密与签名**:使用哈希算法如 HMAC 或 RSA 进行签名,保证了数据的完整性和来源的可信度。 ### 实现步骤 #### 客户端生成 JWT 1. 发起一次认证请求给服务器,例如登录请求,返回包含用户名等信息的响应。 ```http POST /login HTTP/1.1 Host: example.com username=example_user&password=example_password ``` 2. 服务器验证凭据并生成 JWT,通常包括 token、过期时间等字段,并将其返回给客户端。 ```json { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", "exp": 1682797600, // 其他信息... } ``` #### 服务端验证 JWT 1. 接收到请求,从请求头(通常是 `Authorization` 字段)提取 JWT。 ```http GET /protected-resource HTTP/1.1 Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ``` 2. 使用相同的密钥解码 JWT,并验证其完整性及有效性。 3. 根据 JWT 中的信息进行授权处理,如检查用户角色、过期时间等。 ### 相关问题: 1. **如何确保 JWT 在传输过程中的安全性?** 使用 HTTPS 协议保护数据传输的安全,防止中间人攻击。同时,可以采用 AES 加密或其他更高级别的加密技术来进一步增强安全性。 2. **如何在 Hertz 框架中集成 JWT 验证机制?** 可以使用第三方库(如 gjwt)简化 JWT 验证的过程,或者自定义实现验证逻辑。这通常涉及拦截 HTTP 请求,读取 JWT使用预设的密钥进行解码和验证。 3. **JWT 的生命周期管理有哪些注意事项?** - 确保设置合理的过期时间,避免长时间有效性的 JWT 被滥用。 - 定期清理无效或已撤销的 JWT,减少资源消耗和潜在的风险。 - 对敏感操作如账户更改密码等,考虑使用短生命周期的刷新令牌而非原始的访问令牌。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值