整合JWT

令牌组成

  • 1.标头(Header)
  • 2.有效载荷(Payload)
  • 3.签名(Signature)
    因此,JWT通常如下所示:xxxxx.yyyyy.zzzzz
    Header.Payload.Signature

jwt组成

Header

标头通常由两部分组成:令牌的类型(即JWT)和所使用的签名算法,例如HMAC SHA256或RSA。它会使用 Base64 编码组成 JWT 结构的第一部分。

  • 注意:Base64是一种编码,也就是说,它是可以被翻译回原来的样子来的。它并不是一种加密过程。
{
  "alg": "HS256",
  "typ": "JWT"
}

Payload

不要在这里放密码;反编译Base64即可解码;

  • 令牌的第二部分是有效负载,其中包含声明。声明是有关实体
  • (通常是用户)和其他数据的声明。同样的,它会使用 Base64 编码组成 JWT 结构的第二部分
{
  "username": "xzxadmin",
  "datetime": "2023-05-01 11:11:11",
  "role": "admin"
}

Signature

  • 前面两部分都是使用 Base64 进行编码的,即前端可以解开知道里面的信息。Signature 需要使用编码后的 header 和 payload以及我们提供的一个密钥,然后使用 header 中指定的签名算法(HS256)进行签名。签名的作用是保证 JWT 没有被篡改过
  • 如:
    HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload),secret);

整合JWT

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

JWT帮助类

public class JwtUtils {

    //常量
    public static final long time = 1000 * 60 * 60 * 24;//token过期时间
    public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";//秘钥

    //生成token字符串的方法
    public static String getJwtToken(String username, String role){

        String JwtToken = Jwts.builder()
                //头部
                .setHeaderParam("typ","JWT")
                .setHeaderParam("alg","HS256")
                //载荷
                .claim("username",username)
                .claim("role",role)
                .setSubject("jwt-user")
                //token过期时间:1小时
                .setExpiration(new Date(System.currentTimeMillis()+time ))
                .setId(UUID.randomUUID().toString())//id字段
                //签名
                .signWith(SignatureAlgorithm.HS256,APP_SECRET)//签名加密算法和
                //连接字符串(.);
                .compact();

        return JwtToken;
    }

    /**
     * 判断token是否存在与有效
     * @param jwtToken
     * @return
     */
    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
     * @return
     */
    public static boolean checkToken(HttpServletRequest request) {
        try {
            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
     * @return
     */
    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-->
<dependency>
  <groupId>com.auth0</groupId>
  <artifactId>java-jwt</artifactId>
  <version>3.4.0</version>
</dependency>
 
<!--引入mybatis-->
<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>2.1.3</version>
</dependency>
 
<!--引入lombok-->
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.12</version>
</dependency>
 
<!--引入druid-->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.1.19</version>
</dependency>
 
<!--引入mysql-->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.38</version>
</dependency>

配置文件

server.port=8989
spring.application.name=jwt
 
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/jwt?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=UTC&useSSL=false
spring.datasource.username=root
spring.datasource.password=1234
 
mybatis.type-aliases-package=com.zuhao.springbootjwt.entity
mybatis.mapper-locations=classpath:com/zuhao/springbootjwt/mapper/*.xml
 
logging.level.com.zuhao.springbootjwt.dao=debug

JWT拦截器

interceptor包下新建拦截器

@Component
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
    //目标资源方法执行前执行。 返回true:放行    返回false:不放行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1,先获取请求头
        String token = request.getHeader("Authorization");
        System.out.println("token:"+token);
        response.setContentType("application/json;charset = UTF-8");
        ObjectMapper mapper = new ObjectMapper();
        //2,判断请求头是否存在

        if (token == null || "".equals(token)){
            //请求头不存在或者请求头为空
            log.info("...token不存在");

            response.getWriter().write("result:缺少token");
            return false;
        }else{
            try {
                boolean isJwt = JwtUtils.checkToken(token);
                //解析不出错就是格式有效;
                /*从redis中查询token*/
                //时间有效,通过;时间无效,过期;

            }  catch (Exception e)  {
                log.info("请求头不正确!!");

                response.getWriter().write("result:token错误:");
                return false;
            }
        }


        return true;
    }

    //==========下面与登录无关,不用写==============

    //目标资源方法执行后执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle ... ");
    }

    //视图渲染完毕后执行,最后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion .... ");
    }
}

config包下添加拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //定义拦截对象
        registry.addInterceptor(loginCheckInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns(
                        "/users/login",
                        "/users/login/**",
                        "/swagger-ui/",
                        "/swagger-ui/**",
                        "/swagger-resources",
                        "/swagger-resources/**",
                        "/v3/**",
                        "/users/hello"

                );
    }
}