JWT基本使用

114 阅读4分钟

学习jwt

JWT的全称是json web token,jwt本身基于http的,而http是无状态的,所以jwt是无状态的。

jwt的构成

JWT 是一种以紧凑、可验证的形式在双方之间传输信息的手段。在 JWT 正文中编码的信息位称为 。JWT 的扩展形式采用 JSON 格式,因此每个都是 JSON 对象中的一个键。

jwt字符由三部分构成,每个部分都由.隔开

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.ipevRNuRP6HflG8cFKnmUPtypruRC4fb1DWtoLL62SY

每个部分都采用Base64URL编码

针对上面的解析

第一部分是标题header

{
   "alg":"HSC256"
}

第二部分是body,用于存放具体的验证信息,过期时间等

{
   "sub":"Joe"
}

第三部分是签证,即签证需要的算法和密钥

签证加密算法如下:

  • HS256:使用SHA-256的HMAC
  • HS384:使用SHA-384的HMAC
  • HS512:使用SHA-512的HMAC
  • ES256: 使用 P-256 和 SHA-256 的 ECDSA
  • ES384: 使用 P-384 和 SHA-384 的 ECDSA
  • ES512: 使用 P-521 和 SHA-512 的 ECDSA
  • RS256: RSASSA-PKCS-v1_5 使用 SHA-256
  • RS384: RSASSA-PKCS-v1_5 使用 SHA-384
  • RS512: RSASSA-PKCS-v1_5 使用 SHA-512
  • PS256:使用 SHA-256 的 RSASSA-PSS 和带有 SHA-1 的 MGF256
  • PS384:使用 SHA-384 的 RSASSA-PSS 和带有 SHA-1 的 MGF384
  • PS512:使用 SHA-512 的 RSASSA-PSS 和带有 SHA-1 的 MGF512

jdk项目使用jwt

maven引入依赖

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
		<!--    工具类用到的security依赖    -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
		<!--    解决configurationProperties注解报红问题    -->
		<dependency>
    	<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-configuration-processor</artifactId>
    		<optional>true</optional>
		</dependency>
		<!--    解决项目启动时,报javax.xml.bind.DatatypeConverter找不到错误    -->
		<dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>

jwt测试类

@SpringBootTest(classes = test.class)
public class test {
    //    设置过期时间
    private long expiration = 1000 * 60 * 60;
    //设置密钥对
    private String secret = "jwtTest";

    @Test//生成token
    public void createToken() {
        JwtBuilder builder = Jwts.builder();//获取token生成构建类
        builder.setHeaderParam("date", new Date());//设置header
        HashMap<String, Object> claim = new HashMap<>();//创建map,存放键值对,最后通过setClaims方法生成Claims
        claim.put("username", "tzf");
        claim.put("password", 20030317);
        //也可以创建Claims
        Claims claims = new DefaultClaims();
        claims.put("username", "tzf");
        claims.put("password", 20030317);
        builder.setClaims(claims);//设置body,
        //设置主题
        builder.setSubject("test");
        //设置userid
        builder.setId("30248392");
        //设置过期时间
        builder.setExpiration(new Date(System.currentTimeMillis() + expiration));
        //设置签证,同时需要密钥对
        builder.signWith(SignatureAlgorithm.HS256, secret);
        //最后通过compact方法将上面三部分合起来组成jwt
        String compact = builder.compact();
        System.out.println(compact);
    }
    
     @Test
    public void parseToken() {
        JwtParser parser = Jwts.parser();//获取token解析构建类
        Jws<Claims> claimsJws = parser.setSigningKey(secret).parseClaimsJws("eyJkYXRlIjoxNjg0MTQ3NDg3MDMwLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InR6ZiIsInBhc3N3b3JkIjoyMDAzMDMxNywic3ViIjoidGVzdCIsImp0aSI6IjMwMjQ4MzkyIiwiZXhwIjoxNjg0MTUxMDg3fQ.l2sTkI_csKsaRKJPpAakzXDxhJrowfLSpGjFAEe4568");
        Claims body = claimsJws.getBody();//返回Claim对象,里面封装了该token的第一二部分的信息
        System.out.println(body.get("username"));//获取username
        System.out.println(body.get("password"));//获取password
        System.out.println(body.getExpiration());//获取过期时间
        String subject = body.getSubject();//获取主题
        System.out.println(subject);
    }
}

工具类


import io.jsonwebtoken.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

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

//需要springSecurity依赖以及lombok以及jwt依赖
//注册组件
@Component
//@Data和@ConfigurationProperties结合使用用于在yaml中对其常量进行注入
@Data
@ConfigurationProperties("jwt.data")
@Slf4j
public class JWTUtil {

    //创建对象主题
    private static final String CLAIM_KEY_SUBJECT = "subject";
    //创建创建时间
    private static final String CLAIM_KEY_CREATED = "created";
	//创建用户名
    private static final String CLAIM_KEY_USERNAME = "username";

    //@Value这个注解一定要引入spring-boot-starter-validation才能使用
    //@Value注解可以代替@Data和@ConfigurationProperties结合
    //这两个二者选一即可
    //我建议使用@Data和@ConfigurationProperties结合
    //@Value("${jwt.data.SECRET}")
    private String SECRET;//创建加密盐

    //过期时间
    private Long expiration;

    //根据用户名生成token
    //传入的是使用SpringSecurity里的UserDetails
    public String createToken(UserDetails userDetails) {
        HashMap<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
        claims.put(CLAIM_KEY_CREATED, new Date());
        return createToken(claims);//装入map
    }

    //根据token获取用户名
    public String getUsernameFromToken(String token) {
        String username = "";
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.get(CLAIM_KEY_USERNAME,String.class);
        } catch (Exception e) {
            username = null;
            log.info("error:{}", "用户名未能获取 from token");
        }
        return username;
    }

    //从token中获取荷载
    private Claims getClaimsFromToken(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (ExpiredJwtException e) {
            e.printStackTrace();
        } catch (UnsupportedJwtException e) {
            e.printStackTrace();
        } catch (MalformedJwtException e) {
            e.printStackTrace();
        } catch (SignatureException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
        return claims;
    }


    //根据负载生成jwt token
    private String createToken(Map<String, Object> claims) {
        //jjwt构建jwt builder
        //设置信息,过期时间,主题,signnature
        return Jwts.builder()
                .setClaims(claims)
            	.setSubject(CLAIM_KEY_SUBJECT)
                .setExpiration(expirationDate())
                .signWith(SignatureAlgorithm.ES512, SECRET)
                .compact();
    }

    //生成token失效时间
    private Date expirationDate() {
        //失效时间为:系统当前毫秒数+我们设置的时间(s)*1000=》毫秒
        //其实就是未来7天
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }

    //判断token是否有效
    public boolean validateToken(String token, UserDetails userDetails) {
        //判断token是否过期
        //判断token是否和userDetails中的一致
        //我们要做的 是先获取用户名
        String username = getUsernameFromToken(token);
        return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
    }

    //判断token、是否失效
    //失效返回true
    private boolean isTokenExpired(String token) {
        Date expiredDate = getExpiredDateFeomToken(token);
        return expiredDate.before(new Date());
    }

    //从荷载中获取时间
    private Date getExpiredDateFeomToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.getExpiration();
    }

    //判断token是否可以被刷新
    //过期(销毁)就可以
    public boolean canBeRefreshed(String token){
        return !isTokenExpired(token);
    }

    //刷新token
    public String refreshToken(String token){
        Claims claims = getClaimsFromToken(token);
        //修改为当前时间
        claims.put(CLAIM_KEY_CREATED,new Date());
        return createToken(claims);
    }

}