JWT的原理及实际使用

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: JWT的原理及实际使用

                                                      🎬 艳艳耶✌️:个人主页

                                                      🔥 个人专栏 :《Spring与Mybatis集成整合》《Vue.js使用》

               ⛺️ 生活的理想,为了不断更新自己 !


 



1.JWT是什么?

        JWT全称为JSON Web Token,是一种用于身份验证和授权的开放标准。它是一个紧凑且安全的方式,通过在不同系统之间传递可信任的信息来实现用户身份验证。JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。

2. jwt工具类介绍

JWT工具类是一个用于生成、解析和验证JSON Web Token的辅助类。它提供了一系列方法来简化JWT的处理过程,使得开发者可以方便地在应用中使用JWT进行身份验证和授权。

JWT工具类通常包括以下功能:

  • 生成JWT:提供方法用于生成JWT,传入有效载荷和加密密钥等参数,生成符合规范的JWT字符串。
  • 解析JWT:提供方法用于解析JWT,将JWT字符串解析成头部和载荷的JSON对象,并对签名进行验证。
  • 验证JWT:提供方法用于验证JWT的有效性,包括验证签名是否正确、验证令牌是否过期等。
  • 获取载荷信息:提供方法用于获取JWT中的载荷信息,比如用户ID、角色等。
  • 刷新JWT:提供方法用于刷新JWT,生成新的JWT并返回给客户端,用于延长认证的有效期。
  • 配置参数:提供方法用于配置JWT的相关参数,比如加密算法、密钥、过期时间等。

使用JWT工具类可以简化JWT的处理逻辑,减少代码重复性,并增加系统的安全性。通过封装好的方法,开发者可以方便地在不同的场景中使用JWT进行身份验证和授权,提高开发效率。但需要注意,在使用JWT工具类时,应确保密钥的安全性,避免泄露导致令牌被篡改或伪造。

代码展示:

package com.sy.ssm.util;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
/**
 * JWT验证过滤器:配置顺序 CorsFilte->JwtUtilsr-->StrutsPrepareAndExecuteFilter
 *
 */
public class JwtUtils {
  /**
   * JWT_WEB_TTL:WEBAPP应用中token的有效时间,默认30分钟
   */
  public static final long JWT_WEB_TTL = 30 * 60 * 1000;
  /**
   * 将jwt令牌保存到header中的key
   */
  public static final String JWT_HEADER_KEY = "jwt";
  // 指定签名的时候使用的签名算法,也就是header那部分,jwt已经将这部分内容封装好了。
  private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
  private static final String JWT_SECRET = "f356cdce935c42328ad2001d7e9552a3";// JWT密匙
  private static final SecretKey JWT_KEY;// 使用JWT密匙生成的加密key
  static {
    byte[] encodedKey = Base64.decodeBase64(JWT_SECRET);
    JWT_KEY = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
  }
  private JwtUtils() {
  }
  /**
   * 解密jwt,获得所有声明(包括标准和私有声明)
   * 
   * @param jwt
   * @return
   * @throws Exception
   */
  public static Claims parseJwt(String jwt) {
    Claims claims = Jwts.parser()
        .setSigningKey(JWT_KEY)
        .parseClaimsJws(jwt)
        .getBody();
    return claims;
  }
  /**
   * 创建JWT令牌,签发时间为当前时间
   * 
   * @param claims
   *            创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
   * @param ttlMillis
   *            JWT的有效时间(单位毫秒),当前时间+有效时间=过期时间
   * @return jwt令牌
   */
  public static String createJwt(Map<String, Object> claims, 
      long ttlMillis) {
    // 生成JWT的时间,即签发时间 2021-10-30 10:02:00 -> 30 10:32:00
    long nowMillis = System.currentTimeMillis();
    //链式语法:
    // 下面就是在为payload添加各种标准声明和私有声明了
    // 这里其实就是new一个JwtBuilder,设置jwt的body
    JwtBuilder builder = Jwts.builder()
        // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
        .setClaims(claims)
        // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
        // 可以在未登陆前作为身份标识使用
        .setId(UUID.randomUUID().toString().replace("-", ""))
        // iss(Issuser)签发者,写死
        .setIssuer("zking")
        // iat: jwt的签发时间
        .setIssuedAt(new Date(nowMillis))
        // 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可放数据{"uid":"zs"}。此处没放
        // .setSubject("{}")
        // 设置签名使用的签名算法和签名使用的秘钥
        .signWith(SIGNATURE_ALGORITHM, JWT_KEY)
        // 设置JWT的过期时间
        .setExpiration(new Date(nowMillis + ttlMillis));
    return builder.compact();
  }
  /**
   * 复制jwt,并重新设置签发时间(为当前时间)和失效时间
   * 
   * @param jwt
   *            被复制的jwt令牌
   * @param ttlMillis
   *            jwt的有效时间(单位毫秒),当前时间+有效时间=过期时间
   * @return
   */
  public static String copyJwt(String jwt, Long ttlMillis) {
    //解密JWT,获取所有的声明(私有和标准)
    //old
    Claims claims = parseJwt(jwt);
    // 生成JWT的时间,即签发时间
    long nowMillis = System.currentTimeMillis();
    // 下面就是在为payload添加各种标准声明和私有声明了
    // 这里其实就是new一个JwtBuilder,设置jwt的body
    JwtBuilder builder = Jwts.builder()
        // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
        .setClaims(claims)
        // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
        // 可以在未登陆前作为身份标识使用
        //.setId(UUID.randomUUID().toString().replace("-", ""))
        // iss(Issuser)签发者,写死
        // .setIssuer("zking")
        // iat: jwt的签发时间
        .setIssuedAt(new Date(nowMillis))
        // 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可放数据{"uid":"zs"}。此处没放
        // .setSubject("{}")
        // 设置签名使用的签名算法和签名使用的秘钥
        .signWith(SIGNATURE_ALGORITHM, JWT_KEY)
        // 设置JWT的过期时间
        .setExpiration(new Date(nowMillis + ttlMillis));
    return builder.compact();
  }
}

3. jwt集成spa项目

后台代码:

用户登录方法,放开用户信息生成jwt串保存到响应头中

代码展示:

package com.sy.ssm.controller;
import com.sy.ssm.service.IUserService;
import com.sy.ssm.util.JsonResponseBody;
import com.sy.ssm.util.PageBean;
import com.sy.ssm.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.sy.ssm.jwt.*;
@Controller
@RequestMapping("/user")
public class UserController {
    @Autowired
    private IUserService userService;
    @RequestMapping("/userLogin")
    @ResponseBody
    public JsonResponseBody<?> userLogin(UserVo userVo, HttpServletResponse response){
        if(userVo.getUsername().equals("admin")&&userVo.getPassword().equals("123")){
            //私有要求claim
//            Map<String,Object> json=new HashMap<String,Object>();
//            json.put("username", userVo.getUsername());
            //生成JWT,并设置到response响应头中
//            String jwt=JwtUtils.createJwt(json, JwtUtils.JWT_WEB_TTL);
//            response.setHeader(JwtUtils.JWT_HEADER_KEY, jwt);
            return new JsonResponseBody<>("用户登陆成功!",true,0,null);
        }else{
            return new JsonResponseBody<>("用户名或密码错误!",false,0,null);
        }
    }
    @RequestMapping("/userRegister")
    @ResponseBody
    public JsonResponseBody<?> userRegister(UserVo userVo, HttpServletResponse response){
        //状态新注册默认为0
        userVo.setStatus("0");
        //因为ID为String类型需要手动设置,当然可以根据自己的需要改为Int类型
        userVo.setId("15");
        int i = userService.insertSelective(userVo);
        if(i>0){
            return new JsonResponseBody<>("用户注册完成!快去登入吧!",true,0,null);
        }else{
            return new JsonResponseBody<>("用户注册失败!重新输入。",false,0,null);
        }
    }
    @RequestMapping("/queryUserPager")
    @ResponseBody
    public JsonResponseBody<List<Map<String,Object>>>
            queryUserPager(UserVo userVo, HttpServletRequest request){
        try {
            PageBean pageBean=new PageBean();
            pageBean.setRequest(request);
            List<Map<String, Object>> users = userService.queryUserPager(userVo, pageBean);
            return new JsonResponseBody<>("OK",true,pageBean.getTotal(),users);
        } catch (Exception e) {
            e.printStackTrace();
            return new JsonResponseBody<>("分页查询用户信息失败!",false,0,null);
        }
    }
}

关闭JwtFilter中的OFF开关,开启jwt验证

代码展示:

package com.sy.ssm.jwt;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import io.jsonwebtoken.Claims;
/**
 * * JWT验证过滤器,配置顺序 :CorsFilter-->JwtFilter-->struts2中央控制器
 * 
 * @author Administrator
 *
 */
public class JwtFilter implements Filter {
  // 排除的URL,一般为登陆的URL(请改成自己登陆的URL)
  private static String EXCLUDE = "^/user/userLogin?.*$";
  private static Pattern PATTERN = Pattern.compile(EXCLUDE);
  private boolean OFF = true;// true关闭jwt令牌验证功能
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
  }
  @Override
  public void destroy() {
  }
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;
    HttpServletResponse resp = (HttpServletResponse) response;
    //获取当前请求路径。只有登录的请求路径不进行校验之外,其他的URL请求路径必须进行JWT令牌校验
    //http://localhost:8080/ssh2/bookAction_queryBookPager.action
    //req.getServletPath()==/bookAction_queryBookPager.action
    String path = req.getServletPath();
    if (OFF || isExcludeUrl(path)) {// 登陆直接放行
        chain.doFilter(request, response);
        return;
    }
    // 从客户端请求头中获得令牌并验证
    //token=头.载荷.签名
    String jwt = req.getHeader(JwtUtils.JWT_HEADER_KEY);
    Claims claims = this.validateJwtToken(jwt);
    //在这里请各位大哥大姐从JWT令牌中提取payload中的声明部分
    //从声明部分中获取私有声明
    //获取私有声明中的User对象 -> Modules
    Boolean flag=false;
    if (null == claims) {
      // resp.setCharacterEncoding("UTF-8");
      resp.sendError(403, "JWT令牌已过期或已失效");
      return;
    } else {
      //1.获取已经解析后的payload(私有声明)
      //2.从私有声明中当前用户所对应的权限集合List<String>或者List<Module>
      //3.循环权限(Module[id,url])
      // OK,放行请求 chain.doFilter(request, response);
      // NO,发送错误信息的JSON
      // ObjectMapper mapper=new ObjectMapper()
      // mapper.writeValue(response.getOutputStream(),json)
      String newJwt = JwtUtils.copyJwt(jwt, JwtUtils.JWT_WEB_TTL);
      resp.setHeader(JwtUtils.JWT_HEADER_KEY, newJwt);
      chain.doFilter(request, response);
    }
  }
  /**
   * 验证jwt令牌,验证通过返回声明(包括公有和私有),返回null则表示验证失败
   */
  private Claims validateJwtToken(String jwt) {
    Claims claims = null;
    try {
      if (null != jwt) {
        //该解析方法会验证:1)是否过期 2)签名是否成功
        claims = JwtUtils.parseJwt(jwt);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return claims;
  }
  /**
   * 是否为排除的URL
   * 
   * @param path
   * @return
   */
  private boolean isExcludeUrl(String path) {
    Matcher matcher = PATTERN.matcher(path);
    return matcher.matches();
  }
  // public static void main(String[] args) {
  // String path = "/sys/userAction_doLogin.action?username=zs&password=123";
  // Matcher matcher = PATTERN.matcher(path);
  // boolean b = matcher.matches();
  // System.out.println(b);
  // }
}

web.xml中要配置corsfilter,允许jwt使用请求头及响应头

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         id="WebApp_ID" version="3.0">
  <display-name>Archetype Created Web Application</display-name>
  <!--1.实现Spring与Web集成,实现Spring上下文的初始化工作-->
  <!-- spring上下文配置文件 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring.xml</param-value>
  </context-param>
  <!-- 读取Spring上下文的监听器 -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!--2.配置中文乱码过滤器,使用Spring自带的过滤器-->
  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <async-supported>true</async-supported>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <!--CrosFilter跨域过滤器-->
  <filter>
    <filter-name>corsFilter</filter-name>
    <filter-class>com.zking.ssm.util.CorsFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>corsFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <!--JwtFilter-->
  <filter>
    <filter-name>jwtFilter</filter-name>
    <filter-class>com.zking.ssm.jwt.JwtFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>jwtFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <!--Spring MVC核心控制器-->
  <servlet>
    <servlet-name>SpringMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--此参数可以不配置,默认值为:/WEB-INF/springmvc-servlet.xml-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <!--web.xml 3.0的新特性,是否支持异步-->
    <async-supported>true</async-supported>
  </servlet>
  <servlet-mapping>
    <servlet-name>SpringMVC</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

前端代码:

state.js

export default {
  eduName: '米西米西花不垃圾',
  jwt: ''
}

mutations.js

export default {
  // type(事件类型): 其值为setEduName
  // payload:官方给它还取了一个高大上的名字:载荷,其实就是一个保存要传递参数的容器
  setEduName: (state, payload) => {
    state.eduName = payload.eduName;
  },
  setJwt: (state, payload) => {
    state.jwt = payload.jwt;
  }
}

getters.js

export default{
  getedName:(state)=>{
    return state.edName;
  },
  getJwt:(state)=>{
    return state.jwt;
  }
}

在spa项目中找到main.js文件 :

/* eslint-disable no-new */
window.mm = new Vue({
  el: '#app',
  router,
  store,
  data() {
    return {
      bus: new Vue()
    }
  },
  components: {
    App
  },
  template: '<App/>'
})

在spa项目中找到http.js文件 ( src/api/http.js)进行编写请求拦截器 & 响应拦截器  :

// 请求拦截器
axios.interceptors.request.use(function(config) {
  let jwt = window.mm.$store.getters.getJwt;
  if(jwt){
    config.headers['jwt'] = jwt;
  }
  return config;
}, function(error) {
  return Promise.reject(error);
});
// 响应拦截器
axios.interceptors.response.use(function(response) {
  //将响应头中的jwt字符串放入state.js中
  let jwt = response.headers['jwt'];
  if (jwt) {
    window.mm.$store.commit('setJwt', {
      jwt: jwt
    });
  }
  return response;
}, function(error) {
  return Promise.reject(error);
});

效果展示:

                                           今日分享到此结束!!!  

目录
相关文章
|
6月前
|
存储 JSON 算法
无懈可击的身份验证:深入了解JWT的工作原理
无懈可击的身份验证:深入了解JWT的工作原理
960 0
|
3月前
|
安全 Nacos 数据安全/隐私保护
【技术干货】破解Nacos安全隐患:连接用户名与密码明文传输!掌握HTTPS、JWT与OAuth2.0加密秘籍,打造坚不可摧的微服务注册与配置中心!从原理到实践,全方位解析如何构建安全防护体系,让您从此告别数据泄露风险!
【8月更文挑战第15天】Nacos是一款广受好评的微服务注册与配置中心,但其连接用户名和密码的明文传输成为安全隐患。本文探讨加密策略提升安全性。首先介绍明文传输风险,随后对比三种加密方案:HTTPS简化数据保护;JWT令牌减少凭证传输,适配分布式环境;OAuth2.0增强安全,支持多授权模式。每种方案各有千秋,开发者需根据具体需求选择最佳实践,确保服务安全稳定运行。
310 0
|
5月前
|
存储 算法 Java
闲鱼面试:说说JWT工作原理?
闲鱼面试:说说JWT工作原理?
55 0
闲鱼面试:说说JWT工作原理?
|
4月前
|
存储 JSON 算法
JWT原理与在身份验证中的应用
JWT原理与在身份验证中的应用
|
存储 JSON 算法
JWT的原理及实际应用
JWT的原理及实际应用
173 1
|
6月前
|
存储 JSON 算法
net core jwt的基本原理和实现
这篇文章介绍了.NET Core中JWT(JSON Web Token)的基本原理和实现。JWT是一种用于安全传输信息的开放标准,由头部、负载和签名三部分组成。在.NET Core中实现JWT,需要安装`Microsoft.AspNetCore.Authentication.JwtBearer`包,然后在`Startup.cs`配置JWT认证服务,包括设置密钥和验证参数。生成JWT令牌后,客户端存储并将其包含在请求头中发送给服务器进行验证和授权。JWT提供了一种无需服务器存储会话数据的安全身份验证和授权机制。
|
前端开发
什么是JWT?深入理解JWT从原理到应用(下)
什么是JWT?深入理解JWT从原理到应用(下)
108 0
|
存储 JSON 前端开发
了解什么是JWT的原理及实际应用
了解什么是JWT的原理及实际应用
1348 0
|
6月前
|
JSON 安全 网络安全
超详细的用户认证、权限、安全原理详解(认证、权限、JWT、RFC 7235、HTTPS、HSTS、PC端、服务端、移动端、第三方认证等等)
超详细的用户认证、权限、安全原理详解(认证、权限、JWT、RFC 7235、HTTPS、HSTS、PC端、服务端、移动端、第三方认证等等)
1042 0
|
6月前
|
JSON 算法 前端开发
gin框架JWT验证实践(原理介绍,代码实践)
gin框架JWT验证实践(原理介绍,代码实践)
187 0

热门文章

最新文章