StringCloud_JWT(鉴权)

1. 为什么要学习JWT?

在这里插入图片描述

1.1.简介

JWT,全称是Json Web Token, 是JSON风格轻量级的授权和身份认证规范,可实现无状态、分布式的Web应用授权;它是分布式服务权限控制的标准解决方案!

什么是token:https://www.cnblogs.com/xuxinstyle/p/9675541.html

1.2.数据格式

普通的token:32位UUID
在这里插入图片描述
JWT的token:至少64位
在这里插入图片描述
JWT的token包含三部分数据:

  • Header:头部,通常头部有两部分信息:

    • 声明类型type,这里是JWT(type=jwt)

    • 加密算法,自定义(rs256/base64/hs256)

      我们会对头部进行base64加密(可解密),得到第一部分数据

  • Payload:载荷,就是有效数据,一般包含下面信息:

  • 用户身份信息-userid,username(注意,这里因为采用base64加密,可解密,因此不要存放敏感信息)

  • 注册声明:如token的签发时间,过期时间,签发人等

    这部分也会采用base64加密,得到第二部分数据

  • Signature:base64加密,签名,是整个数据的认证信息。一般根据前两步的数据,再加上服务的的密钥(secret,盐)(不要泄漏,最好周期性更换),通过加密算法生成。用于验证整个数据完整和可靠性

结论:

1 jwt的一个有规则的token

2 它有三部分组成:Header.payload.signature,每部分都是通过base64加密而成的

3 jwt前两个部分都是可以解密的

1.3. JWT详解

1.3.1 base64编码原理(了解)

Base64编码之所以称为Base64,是因为其使用64个字符来对任意数据进行编码,同理有Base32、Base16编码。标准Base64编码使用的64个字符如下:
在这里插入图片描述
这64个字符是各种字符编码(比如ASCII码)所使用字符的子集,并可打印。唯一有点特殊的是最后两个字符。

Base64本质上是一种将二进制数据转成文本数据的方案。对于非二进制数据,是先将其转换成二进制形式,然后每连续6比特(2的6次方=64)计算其十进制值,根据该值在上面的索引表中找到对应的字符,最终得到一个文本字符串。假设我们对Hello!进行Base64编码,按照ASCII表,其转换过程如下图所示:
在这里插入图片描述
可知Hello!的Base64编码结果为SGVsbG8h,原始字符串长度为6个字符串,编码后长度为8个字符,每3个原始字符经编码成4个字符。

但要注意,Base64编码是每3个原始字符编码成4个字符,如果原始字符串长度不能被3整除,怎么办?使用0来补充原始字符串。

以Hello!!为例,其转换过程为:
在这里插入图片描述
Hello!! Base64编码的结果为 SGVsbG8hIQAA 。最后2个零值只是为了Base64编码而补充的,在原始字符中并没有对应的字符,那么Base64编码结果中的最后两个字符 AA 实际不带有效信息,所以需要特殊处理,以免解码错误。

标准Base64编码通常用 = 字符来替换最后的 A,即编码结果为 SGVsbG8hIQ==。因为 = 字符并不在Base64编码索引表中,其意义在于结束符号,在Base64解码时遇到 = 时即可知道一个Base64编码字符串结束。

如果Base64编码字符串不会相互拼接再传输,那么最后的 = 也可以省略,解码时如果发现Base64编码字符串长度不能被4整除,则先补充 = 字符,再解码即可。

解码是对编码的逆向操作,但注意一点:对于最后的两个 = 字符,转换成两个A 字符,再转成对应的两个6比特二进制0值,接着转成原始字符之前,需要将最后的两个6比特二进制0值丢弃,因为它们实际上不携带有效信息。

总结:

1、base64的编码/加密原理

答:原理:将键盘输入的字符用base64编码表示

过程:将键盘输入字符的ascii码值,转成的对应8位二进制,将该二进制6个一组拆分,并计算拆分之后的十进制值,找出十进制值在base64编码表中对应的字母,即完成base64加密

1.3.2 jwt测试-JwtUtil的使用

  1. 导入jar包
 <properties>
           <jjwt.version>0.7.0</jjwt.version>
           <joda-time.version>2.9.6</joda-time.version>
       </properties>
       
       <dependencies>
           <dependency>
               <groupId>io.jsonwebtoken</groupId>
               <artifactId>jjwt</artifactId>
               <version>${jjwt.version}</version>
           </dependency>
           <!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
           <dependency>
               <groupId>joda-time</groupId>
               <artifactId>joda-time</artifactId>
               <version>${joda-time.version}</version>
           </dependency>
       </dependencies>
  1. 导入JwtUtil
package com.czxy.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.joda.time.DateTime;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;

/**
 * @Description: token工具类
 */
public class JWTUtil {
   


    /**
     * 获取token中的参数
     *
     * @param token
     * @return
     */
    public static Claims parseToken(String token,String key) {
   
        if ("".equals(token)) {
   
            return null;
        }

        try {
   
            return Jwts.parser()
                    .setSigningKey(DatatypeConverter.parseBase64Binary(key))
                    .parseClaimsJws(token).getBody();
        } catch (Exception ex) {
   
            return null;
        }
    }

    /**
     * 生成token
     *
     * @param userId
     * @return
     */
    public static String createToken(Integer userId,String key, int expireMinutes) {
   
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        long nowMillis = System.currentTimeMillis();

        //生成签名密钥
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(key);

        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

        //添加构成JWT的参数
        JwtBuilder builder = Jwts.builder()
//                .setHeaderParam("type", "JWT")
//                .setSubject(userId.toString())
                .claim("userId", userId) // 设置载荷信息
                .setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate())// 设置超时时间
                .signWith(signatureAlgorithm, signingKey);

        //生成JWT
        return builder.compact();
    }
}

/**
解密
*/
public static void main(String[] args) {
   
        String token = JWTUtil.createToken(1, "zhangsan","admin", 30);
        System.out.println(token);
        Claims claims = JWTUtil.parseToken(token, "admin");
        System.out.println();
    }
  1. 编码测试

结论:

  1. jwt是采用base64加密/编码的
    在这里插入图片描述

1.4. JWT交互流程

流程图:
在这里插入图片描述
步骤翻译:

• 1、用户登录

• 2、服务的认证,通过后根据secret生成token

• 3、将生成的token返回给用户

• 4、用户每次请求携带token

• 5、服务端利解读jwt签名,判断签名有效后,从Payload中获取用户信息

• 6、处理请求,返回响应结果

因为JWT签发的token中已经包含了用户的身份信息,并且每次请求都会携带,这样服务的就无需保存用户信息,甚至无需去数据库查询,就能知道用户身份,完全符合了Rest的无状态规范。

1.5.结合Zuul的鉴权流程

我们逐步演进系统架构设计。需要注意的是:secret是签名的关键,因此一定要保密,我们放到鉴权中心保存,其它任何服务中都不能获取secret。

在微服务架构中,我们可以把服务的鉴权操作放到网关中,将未通过鉴权的请求直接拦截,如图:
在这里插入图片描述
流程图解:

  1. 第一个流程:用户点击登录—>请求授权中心颁发jwt凭证
  2. 第二个流程:用户的每次请求都携带jwt凭证—>zuul判断jwt是否正确

• 1、用户请求登录

• 2、Zuul将请求转发到授权中心,请求授权

• 3、授权中心校验完成,颁发JWT凭证

• 4、客户端请求其它功能,携带JWT

• 5、Zuul将jwt交给授权中心校验,通过后放行

• 6、用户请求到达微服务

• 7、微服务将jwt交给鉴权中心,鉴权同时解析用户信息

• 8、鉴权中心返回用户数据给微服务

• 9、微服务处理请求,返回响应

2. 项目整体架构

在这里插入图片描述

  • jwt-parent:统一jar包版本控制
  • jwt-eureka:eureka注册中心
  • user-service:用户服务,对外暴露用户操作相关接口,如新增用户等
  • jwt-auth:认证中心
  • jwt-pojo:实体类存放位置
  • jwt-common:工具类、常量类等存放的位置
  • goods-search:商品搜索服务,对外暴露商品搜索相关接口

结论:

  1. 项目的整体架构方式
  2. 搭建认证+授权中心
  3. 通过zuul进行权限过滤

3. 搭建父工程jwt-parent

3.1 创建项目

在这里插入图片描述

3.2 pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.czxy</groupId>
    <artifactId>jwt-parent01</artifactId>
    <packaging>pom</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <modules>
        <module>jwt-pojo01</module>
        <module>jwt-commom01</module>
    </modules>
    <name>jwt-parent01</name>
    <description>Demo project for Spring Boot</description>

    <!-- 父工程中只是统一版本依赖,但是不实际导入jar包 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
        <mybatis.starter.version>1.3.2</mybatis.starter.version>
        <mapper.starter.version>2.0.2</mapper.starter.version>
        <druid.starter.version>1.1.9</druid.starter.version>
        <mysql.version>5.1.32</mysql.version>
        <pa
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值