jwt学习笔记


jwt是json web token的缩写,常用于安全校验

jwt的组成

jwt有三部分组成,用.拼接

这三部分是

  • Header(数据类型和加密算法类型)

    {
        'typ': 'JWT',
        'alg': 'HS256'
    }
    
  • Payload (存放有效信息)

    {
        'sub': '1234567890',
        'name': 'john',
        'admin': true
    }
    
  • signatrue 对前两个部分进行再编码

    var encodeString = base64UrlEncode(header) + '.' + 
    base64UrlEncode(payload)
    
    var signatrue = HMACSHA256(encodedString, 'secret')
    

依赖


    io.jsonwebtoken
    jjwt
    0.9.1


    javax.xml.bind
    jaxb-api
    2.3.0


    com.sun.xml.bind
    jaxb-impl
    2.3.0


    com.sun.xml.bind
    jaxb-core
    2.3.0


    javax.activation
    activation
    1.1.1

创建一个jwt

package zjc;

import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.UUID;

public class Test {
	private long time = 1000 * 60 * 60 * 24;
	private String signature = "dasdia";

	@org.junit.Test
	public void test() {
		JwtBuilder jwtBuilder = Jwts.builder(); // 构建jwt对象
		String token = jwtBuilder
						// header
						.setHeaderParam("typ", "JWT")
						.setHeaderParam("alg", "HS256")
						// payload
						.claim("username", "张三")
						.claim("role", "admin")
						.setSubject("admin-test")
						.setExpiration(new Date(System.currentTimeMillis() + time)) // 有效期
						.setId(UUID.randomUUID().toString())
						// signature
						.signWith(SignatureAlgorithm.HS256, signature)
						.compact();
		System.out.println(token);
	}
}

运行获得以下token

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IuW8oOS4iSIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2MzQwMTU0MDgsImp0aSI6IjRjMWJiZGZlLTRkNzMtNDJmOS05NzE3LTZjOWE5NGI3NTBhNSJ9.CcDS6k2vcfz_ajUlhHtGhJEm_VzzohzLXJaAIylB-Eo

同样的,有token和signature就可以从token中获取数据

@org.junit.Test
    public void parse() {
    String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IuW8oOS4iSIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2MzQwMTU0MDgsImp0aSI6IjRjMWJiZGZlLTRkNzMtNDJmOS05NzE3LTZjOWE5NGI3NTBhNSJ9.CcDS6k2vcfz_ajUlhHtGhJEm_VzzohzLXJaAIylB-Eo";
    JwtParser parser = Jwts.parser();
    Jws claimsJws = parser.setSigningKey(signature).parseClaimsJws(token);
    Claims claims = claimsJws.getBody();
    System.out.println(claims.get("username"));
    System.out.println(claims.get("role"));
    System.out.println(claims.getId());
    System.out.println(claims.getSubject());
    System.out.println(claims.getExpiration());
}

运行结果如下

image-20211011131814795

springboot中使用jwt

  1. 添加上述依赖

  2. 创建用户Model

    image-20211011134808605

    package zjc.jwt_001.entity;
    
    import lombok.Data;
    
    @Data
    public class User {
    	private String username;
    	private String password;
    	private String token;
    }
    
    
  3. 创建jwt工具类

    image-20211011134902291

    package zjc.jwt_001.util;
    
    import io.jsonwebtoken.JwtBuilder;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    
    import java.util.Date;
    import java.util.UUID;
    
    public class JwtUtil {
        private static long time = 1000 * 60 * 60 * 24;
        private static String signature = "dasdia";
    
        public static String createToken() {
            JwtBuilder jwtBuilder = Jwts.builder(); // 构建jwt对象
            return jwtBuilder
                // header
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                // payload
                .claim("username", "张三")
                .claim("role", "admin")
                .setSubject("admin-test")
                .setExpiration(new Date(System.currentTimeMillis() + time)) // 有效期
                .setId(UUID.randomUUID().toString())
                // signature
                .signWith(SignatureAlgorithm.HS256, signature)
                .compact();
        }
    }
    
    
  4. 添加相应的controller

    image-20211011134818006

    package zjc.jwt_001.controller;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import zjc.jwt_001.entity.User;
    import zjc.jwt_001.util.JwtUtil;
    
    @RestController
    public class UserController {
    
        private final String USERNAME = "admin";
        private final String PASSWORD = "123456";
    
        @GetMapping("/login")
        public User login(User user) {
            if (USERNAME.equals(user.getUsername()) && PASSWORD.equals(user.getPassword())) {
                user.setToken(JwtUtil.createToken());
                return user; // 验证成功
            }
            return null;
        }
    }
    
    
  5. 跨域设置

    package zjc.jwt_001.config;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class CorsConfiguration implements WebMvcConfigurer {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");
        }
    }
    
    
  6. 测试访问

    访问localhost:8080/login

    其中用户名和密码设定如下

    image-20211011135046717

    访问页面

    image-20211011135105749

    获取如下数据

    {
        "username": "admin",
        "password": "123456",
        "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IuW8oOS4iSIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2MzQwMTc2MDIsImp0aSI6IjQwMzk2ZWI0LTRkOTEtNDgwYS05YzExLWNiNGUzNzJjZmE5YiJ9.HqMgqiT6CQkZNNBm44KwQsJA8IFa0B6yvBC5wx53jM8"
    }
    

springboot + vue

实现功能:

前端登录并且登陆成功之后储存登录信息

创建一个登陆页面






token校验

因为token有有效期,所以需要在页面跳转时对token进行是否失效的校验

后端编写校验工具

在JwtUtil类中添加静态方法

public static boolean checkToken(String token) {
    if (token == null) { // token不存在
        return false;
    }
    try {
        Jws claimsJws = Jwts.parser().setSigningKey(signature).parseClaimsJws(token);
    } catch (Exception e) {
        return false;
    }
    return true; // 看是否能解码token,不能解码说明token无效
}

创建校验token的controller

@GetMapping("/checkToken")
public boolean checkToken(HttpServletRequest request) {
    String token = request.getHeader("token");
    return JwtUtil.checkToken(token);
}

前端在router增加过滤

image-20211011161855922

增加以下函数

router.beforeEach((to, from, next) => {
  if (to.path.startsWith('/login')) {
    window.localStorage.removeItem('access-admin') // 如果去登陆页面那就清除本地登录信息
    next()
  } else {
      // 如果是去其他页面那就校验本地登录信息,如果校验结果为true那就继续,为false就跳转到/error页面
    let admin = JSON.parse(window.localStorage.getItem('access-admin'))
    if (!admin) {
      next({path: '/login'})
    } else {
      axios({
        url: 'http://localhost:8080/checkToken',
        method: 'get',
        headers: {
          token: admin.token
        }
      }).then(resp => {
        if (!resp.data) {
          console.log('校验失败')
          next({path: '/error'})
        }
      })
      next()
    }
  }
})

创建error页面