利用JWT生成Token的原理及公钥和私钥加密和解密的原则 您所在的位置:网站首页 密钥密匙的使用方法 利用JWT生成Token的原理及公钥和私钥加密和解密的原则

利用JWT生成Token的原理及公钥和私钥加密和解密的原则

2024-06-08 09:51| 来源: 网络整理| 查看: 265

文章目录 开篇:什么是JWT头部载荷签名1.使用io.jsonwebtoken包的方式pom.xml导入的jar包User实体JjwtUtil类TestJjwt测试类 2.使用com.auth0包的方式2.1 pom.xml文件2.2 JWTInterceptor拦截器2.3 spring-context.xml配置文件2.4 web.xml配置文件2.5 JWTUtil类2.6 UsersController层 JWT的原理参考文章 加密和签名–公钥和私钥谁来进行加密,解密的参考文章 为啥公钥加密可以被私钥解密的参考文章。 cookie,session,token的参考文章 主要的参考文章,点击这里 传统session和Jwt和JJWt的使用,参考文章

开篇:

实现Token的方式有很多,本篇介绍的是利用Json Web Token(JWT)生成的Token.JWT生成的Token有什么好处呢?

安全性比较高,加上密匙加密而且支持多种算法。携带的信息是自定义的,而且可以做到验证token是否过期。验证信息可以由前端保存,后端不需要为保存token消耗内存。 小知识:Base64是一种编码,也就是说,它是可以被翻译回原来的样子来的。它并不是一种加密过程。 什么是JWT

JSON Web Token 简称JWT。 一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。 JWT生成的token是这样的

eyJpc3MiOiJKb2huI.eyJpc3MiOiJ.Kb2huIFd1IEp

生成的token,是3段,用.连接。下面有解释。其中:前两段头部和载荷都是通过Base64进行编码的,后最后一段是将前两段连接在一起,然后在通过相应的加密算法(这里HS256)进行加密过后的字符串。

头部

用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象。 头部(header)是一个JSON对象,一般使用Base64URL编码,承载两部分信息,存放一些声明信息,如:用什么加密,用什么编码【作用:指定JWT使用的签名】 ① 声明类型typ,表示这个令牌(token)的类型(type),JWT令牌统一写为JWT ② 声明加密的算法alg,通常直接使用HMAC SHA256,也可以使用RSA,支持很多算法(HS256、HS384、HS512、RS256、RS384、RS512、ES256、ES384、ES512、PS256、PS384)

加密算法详解,点击这里 HS开头的是HMAC-SHAX。是利用HAMC对SHA摘要算法进行加盐(秘钥)哈希。 其他三个均是利用非对称算法加密进行签名,再对结果进行摘要得出的结果。 在这里插入图片描述

例如:

{ "typ": "JWT", "alg": "HS256" } 载荷

其实就是自定义的数据,一般存储用户Id,过期时间等信息。也就是JWT的核心所在,因为这些数据就是使后端知道此token是哪个用户已经登录的凭证。而且这些数据是存在token里面的,由前端携带,所以后端几乎不需要保存任何数据。 注意:在载荷里面不应该加入任何敏感的数据,因为它直接可以通过Base64就能进行解码了。 载荷也叫消息体(payload)是一个JSON对象,一般使用Base64URL编码,存放主要的有用信息,如:登陆的用户名,登录时间,登录的过期时间,还可以放一些自定义信息【作用:指定JWT的请求数据】 这些有效信息包含三个部分:标准中注册的声明、公共的声明和私有的声明 一、标准中注册的声明: ① iss(issuer): jwt签发者 ② sub(subject): jwt所面向的用户,放登录的用户名等 ③ aud(audience): jwt接收者 ④ exp(expiration time): jwt的过期时间,这个过期时间必须要大于签发时间 ⑤ nbf(Not Before): 生效时间,定义在什么时间之前 ⑥ iat(issuedAt): jwt的签发时间 ⑦ jti(JWT ID): jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击 二、公共的声明:公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解密 三、私有的声明:私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息

例如:

{ "uid": "xxxxidid", //用户id "exp": "12121212" //过期时间 } 签名

签名其实就是: 1.头部和载荷各自base64加密后用.连接起来,然后就形成了xxx.xx的前两段token。 2.最后一段token的形成是,前两段加入一个密匙用HS256算法或者其他算法加密形成。

所以token3段的形成就是在签名处形成的。 最主要的目的:服务器应用在接受到JWT后,会首先对头部和载荷的内容用同一算法再次签名,如果服务器应用对头部和载荷再次以同样方法签名之后发现,自己计算出来的签名和接受到的签名不一样,那么就说明这个Token的内容被别人动过的,我们应该拒绝这个Token,返回一个HTTP 401 Unauthorized响应。

1.使用io.jsonwebtoken包的方式 pom.xml导入的jar包 io.jsonwebtoken jjwt 0.6.0 com.alibaba fastjson 1.2.47 User实体 package com.jjwt.entity; /** * @ClassName: User * @Description: 用户实体 */ public class User { private int id; private String username; private String password; public User() { super(); } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User [id=" + id + ", username=" + username + ", password=" + password + "]"; } } JjwtUtil类 package com.jwt.utils; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.apache.tomcat.util.codec.binary.Base64; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.UUID; // 使用io.jsonwebtoken包 public class JjwtUtil { // jti:jwt的唯一身份标识 public static final String JWT_ID = UUID.randomUUID().toString(); // 加密密文,私钥 public static final String JWT_SECRET = "jiamimiwen"; // 过期时间,单位毫秒 public static final int EXPIRE_TIME = 60 * 60 * 1000; // 一个小时 // public static final long EXPIRE_TIME = 7 * 24 * 3600 * 1000; // 一个星期 // 由字符串生成加密key public static SecretKey generalKey() { // 本地的密码解码 byte[] encodedKey = Base64.decodeBase64(JWT_SECRET); // 根据给定的字节数组使用AES加密算法构造一个密钥 SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES"); return key; } // 创建jwt public static String createJWT(String issuer, String audience, String subject) throws Exception { // 设置头部信息 // Map header = new HashMap(); // header.put("typ", "JWT"); // header.put("alg", "HS256"); // 或 // 指定header那部分签名的时候使用的签名算法,jjwt已经将这部分内容封装好了,只有{"alg":"HS256"} SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证的方式) Map claims = new HashMap(); claims.put("username", "admin"); claims.put("password", "010203"); // jti用户id,例如:20da39f8-b74e-4a9b-9a0f-a39f1f73fe64 String jwtId = JWT_ID; // 生成JWT的时间 long nowTime = System.currentTimeMillis(); Date issuedAt = new Date(nowTime); // 生成签名的时候使用的秘钥secret,切记这个秘钥不能外露,是你服务端的私钥,在任何场景都不应该流露出去,一旦客户端得知这个secret,那就意味着客户端是可以自我签发jwt的 SecretKey key = generalKey(); // 为payload添加各种标准声明和私有声明 JwtBuilder builder = Jwts.builder() // 表示new一个JwtBuilder,设置jwt的body // .setHeader(header) // 设置头部信息 .setClaims(claims) // 如果有私有声明,一定要先设置自己创建的这个私有声明,这是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明 .setId(jwtId) // jti(JWT ID):jwt的唯一身份标识,根据业务需要,可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击 .setIssuedAt(issuedAt) // iat(issuedAt):jwt的签发时间 .setIssuer(issuer) // iss(issuer):jwt签发者 .setSubject(subject) // sub(subject):jwt所面向的用户,放登录的用户名,一个json格式的字符串,可存放userid,roldid之类,作为用户的唯一标志 .signWith(signatureAlgorithm, key); // 设置签名,使用的是签名算法和签名使用的秘钥 // 设置过期时间 long expTime = EXPIRE_TIME; if (expTime >= 0) { long exp = nowTime + expTime; builder.setExpiration(new Date(exp)); } // 设置jwt接收者 if (audience == null || "".equals(audience)) { builder.setAudience("Tom"); } else { builder.setAudience(audience); } return builder.compact(); } // 解密jwt public static Claims parseJWT(String jwt) throws Exception { SecretKey key = generalKey(); // 签名秘钥,和生成的签名的秘钥一模一样 Claims claims = Jwts.parser() // 得到DefaultJwtParser .setSigningKey(key) // 设置签名的秘钥 .parseClaimsJws(jwt).getBody(); // 设置需要解析的jwt return claims; } } TestJjwt测试类 package com.jwt.test; import java.text.SimpleDateFormat; import com.alibaba.fastjson.JSON; import com.jwt.entity.User; import com.jwt.utils.JjwtUtil; import io.jsonwebtoken.Claims; // 使用io.jsonwebtoken包 public class TestJjwt { public static void main(String[] args) { User user = new User(); user.setId(10); user.setUsername("张三"); user.setPassword("123123"); // jwt所面向的用户,放登录的用户名等 String subject = JSON.toJSONString(user); try { // "Jack"是jwt签发者,"李四"是jwt接收者 String jwt = JjwtUtil.createJWT("Jack", "李四", subject); System.out.println("JWT:" + jwt); System.out.println("JWT长度:" + jwt.length()); System.out.println("\njwt三个组成部分中间payload部分的解密:"); Claims c = JjwtUtil.parseJWT(jwt); System.out.println("jti用户id:" + c.getId()); System.out.println("iat登录时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(c.getIssuedAt())); System.out.println("iss签发者:" + c.getIssuer()); System.out.println("sub用户信息列表:" + c.getSubject()); System.out.println("exp过期时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(c.getExpiration())); System.out.println("aud接收者:" + c.getAudience()); System.out.println("登录的用户名:" + c.get("username")); // 或 System.out.println("登录的用户名:" + c.get("username", String.class)); System.out.println("登录的密码:" + c.get("password", String.class)); } catch (Exception e) { e.printStackTrace(); } } }

打印的结果

JWT:eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ7XCJpZFwiOjEwLFwicGFzc3dvcmRcIjpcIjEyMzEyM1wiLFwidXNlcm5hbWVcIjpcIuW8oOS4iVwifSIsImF1ZCI6IuadjuWbmyIsInBhc3N3b3JkIjoiMDEwMjAzIiwiaXNzIjoiSmFjayIsImV4cCI6MTU2NzUwOTYyMiwiaWF0IjoxNTY3NTA2MDIyLCJqdGkiOiJjYjkyMjlkMi1mMDRiLTQ2NmUtOGY4Ny1iMGM4OWU3YWQ5NDEiLCJ1c2VybmFtZSI6ImFkbWluIn0.UG7aVfmQO28bRTrCyD1u2C8pKYXONZ2FZ_R7aFYhJN0 JWT长度:352 jwt三个组成部分中间payload部分的解密: jti用户id:cb9229d2-f04b-466e-8f87-b0c89e7ad941 iat登录时间:2019-09-03 18:20:22 iss签发者:Jack sub用户信息列表:{"id":10,"password":"123123","username":"张三"} exp过期时间:2019-09-03 19:20:22 aud接收者:李四 登录的用户名:admin 登录的用户名:admin 登录的密码:010203 2.使用com.auth0包的方式 2.1 pom.xml文件 5.1.5.RELEASE junit junit 4.12 test org.springframework spring-core ${spring.version} org.springframework spring-beans ${spring.version} org.springframework spring-context ${spring.version} org.springframework spring-context-support ${spring.version} org.springframework spring-web ${spring.version} org.springframework spring-webmvc ${spring.version} org.springframework spring-tx ${spring.version} org.springframework spring-jdbc ${spring.version} org.springframework spring-expression ${spring.version} org.springframework spring-test ${spring.version} mysql mysql-connector-java 5.1.47 com.auth0 java-jwt 3.5.0 io.jsonwebtoken jjwt 0.6.0 com.alibaba fastjson 1.2.47

User实体使用上面的

2.2 JWTInterceptor拦截器 package com.jwt.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import com.jwt.utils.JWTUtil; import io.jsonwebtoken.Claims; public class JWTInterceptor implements HandlerInterceptor { @Autowired private JWTUtil jWTUtil; public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object obj, Exception e) throws Exception { } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object obj, ModelAndView mav) throws Exception { } // 拦截每个请求 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) { System.out.println("开始进入拦截器检验jwt头部是否含有Authorization方法!"); // 通过url得到token请求头是否包含Authorization String jwt = request.getHeader("Authorization"); System.out.println(jwt); try { // 检测请求头是否为空 if (jwt == null) { System.out.println("用户未登录,验证失败"); } else { Claims c = jWTUtil.parseJWT(jwt); System.out.println("用户[ " + c.get("username") + " ]已是登录状态"); System.out.println("结束进入拦截器检验jwt头部是否含有Authorization方法!"); return true; } System.out.println("token解析错误,验证失败"); response.getWriter().write("未登录,请重新登录后操作"); } catch (Exception e) { e.printStackTrace(); } return false; } } 2.3 spring-context.xml配置文件 2.4 web.xml配置文件 jwt org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:spring-context.xml 1 jwt / characterEncodingFilter org.springframework.web.filter.CharacterEncodingFilter encoding UTF-8 forceRequestEncoding true characterEncodingFilter /* 2.5 JWTUtil类 package com.jwt.utils; import java.util.Date; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.apache.tomcat.util.codec.binary.Base64; import org.springframework.stereotype.Component; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.JWTVerifier; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; /** * * @ClassName: JWTUtil * @Description: 实现对用户名和密码的加密处理,校验token是否正确,获取用户名等操作 * Algorithm algorithm = Algorithm.HMAC256(password) 是对密码进行加密后再与用户名混淆在一起 * 在签名时可以通过 .withExpiresAt(date) 指定token的过期时间 * @param: */ @Component public class JWTUtil { // 过期时间,单位毫秒 private static final long EXPIRE_TIME = 60 * 1000; // 1分钟 // private static final long EXPIRE_TIME = 15 * 60 * 1000; // 15分钟 // 加密密文,私钥 private static final String TOKEN_SECRET = "jiamimiwen"; // 由字符串生成加密key public SecretKey generalKey() { System.out.println("进入由字符串生成加密key方法!"); // 本地的密码解码 byte[] encodedKey = Base64.decodeBase64(TOKEN_SECRET); // 根据给定的字节数组使用AES加密算法构造一个密钥 SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES"); return key; } // 生成签名 public String sign(int id, String username, String password) { System.out.println("生成签名方法开始执行!"); try { // 设置过期时间,单位毫秒 Date expTime = new Date(System.currentTimeMillis() + EXPIRE_TIME); // 私钥和加密算法 Algorithm algorithm = Algorithm.HMAC256(password); //使用用户输入的密码 // Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); // 设置头部信息,也可以不用设置头部信息jwt会自动生成 // Map header = new HashMap(); // header.put("typ", "JWT"); // header.put("alg", "HS256"); // 或 // header.put("Type", "JWT"); // header.put("alg", "HS256"); // 生成JWT的时间 Date issuedAt = new Date(System.currentTimeMillis()); // 返回token字符串 System.out.println("生成签名方法结束执行!"); return JWT.create() // 表示new一个Jwt,设置jwt的body // .withHeader(header) // 设置头部信息 .withClaim("id", id) // 数据库中用户的id .withClaim("username", username) // 前端输入的用户名 .withIssuedAt(issuedAt) // jwt的签发时间 .withExpiresAt(expTime) // jwt过期时间 .sign(algorithm); } catch (Exception e) { e.printStackTrace(); return null; } } /** * * @Title: verify * @Description: 检验token是否正确 * @param: @param token 密钥 * @param: @param username 登录名 * @param: @param password 密码 * @param: @return * @return: boolean * @throws */ public boolean verify(String token, String username, String password) { System.out.println("进入检验token是否正确方法!"); try { Algorithm algorithm = Algorithm.HMAC256(password); //使用用户输入的密码 // Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build(); // JWTVerifier verifier = JWT.require(algorithm).build(); verifier.verify(token); return true; } catch (Exception e) { return false; } } // 获取登录名 public String getUsername(String token) { System.out.println("进入获取登录名方法!"); try { DecodedJWT jwt = JWT.decode(token); return jwt.getClaim("username").asString(); } catch (JWTDecodeException e) { return null; } } // 解密jwt public Claims parseJWT(String jwt) throws Exception { System.out.println("进入解密jwt方法!"); SecretKey key = generalKey(); // 签名秘钥,和生成的签名的秘钥一模一样 Claims claims = Jwts.parser() // 得到DefaultJwtParser .setSigningKey(key) // 设置签名的秘钥 .parseClaimsJws(jwt).getBody(); // 设置需要解析的jwt return claims; } } 2.6 UsersController层 package com.jwt.controller; import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.jwt.entity.User; import com.jwt.utils.JWTUtil; @RestController @RequestMapping("user") public class UserController { @Autowired private JWTUtil jWTUtil; @PostMapping("dologin") public Map dologin(@RequestParam String username, @RequestParam String password) { // public Map dologin(@RequestBody User user) { System.out.println("dologin方法开始执行!"); int id = 10; User user = new User(); user.setId(id); user.setUsername(username); user.setPassword(password); // String username = user.getUsername(); // String password = user.getPassword(); Map map = new HashMap(); if ("admin".equals(username) && "123456".equals(password)) { String token = jWTUtil.sign(id, username, password); if (token != null) { map.put("code", "200"); map.put("message", "认证成功!"); map.put("token", token); map.put("data", user); System.out.println("dologin方法结束执行----认证成功!"); return map; } } else { map.put("code", "000"); map.put("message", "认证失败!"); System.out.println("dologin方法结束执行----认证失败!"); } return map; } @GetMapping("list") public Map list() { System.out.println("list方法开始执行!"); Map map = new HashMap(); User user = new User(); user.setId(1001); user.setUsername("张三"); user.setPassword("123123"); map.put("users", user); System.out.println("list方法结束执行!"); return map; } }

在这里插入图片描述



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有