04-JWT技术分析及应用实践

JWT简介

背景

在传统的有状态服务应用中,服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如Tomcat中的Session。例如登录:用户登录后,我们把用户的信息保存在服务端session中,并且给用户一个cookie值,记录对应的session,然后下次请求,用户携带cookie值来(这一步有浏览器自动完成),我们就能识别到对应session,从而找到用户的信息。这种方式目前来看最方便,但在分布式应用中,由服务端保存用户状态不是一种很好的选择,因此JWT诞生。

cookie+session这种模式通常是保存在内存中,而且服务从单服务多服务会面临的session共享问题,随着用户量的增多,开销就会越大。

而JWT不是这样的,只需要服务端生成token客户端保存这个token,每次请求携带这个token服务端认证解析就可。

cookie传统请求模型: 重点:服务端保存用户状态(即Session)
在这里插入图片描述
而JWT不是这样的,只需要服务端生成token客户端保存这个token,每次请求携带这个token服务端认证解析就可。

JWT请求模型
在这里插入图片描述

JWT概述

JWT(JSON WEB Token)是一个标准,借助JSON格式数据作为WEB应用请求中的令牌,进行数据的自包含设计,实现各方安全的信息传输,在数据传输过程中还可以对数据进行加密,签名等相关处理。同时JWT也是目前最流行的跨域身份验证解决方案(其官方网址为:https://jwt.io/)。可以非常方便的在分布式系统中实现用户身份认证。

JWT数据结构

JWT通常由三部分构成,分别为Header(头部),Payload(负载),Signature(签名),其格式如下:

xxxxx.yyyyy.zzzzz

例如:由两个点分隔成三部分

eyJhbGciOiJIUzI1NiJ9.eyJwZXJtaXNzaW9ucyI6InN5czpyZXM6Y3JlYXRlLHN5czpyZXM6cmV0cmlldmUiLCJleHAiOjE2MjY5MzIyNTksImlhdCI6MTYyNjkzMDQ1OSwidXNlcm5hbWUiOiJqYWNrIn0.SQrRS5nuID1Xv5GMvUgnr7xrVzB7GcRFrkNak-x16Mw
分割后:
第一部分 Header :eyJhbGciOiJIUzI1NiJ9
第二部分 Payload:eyJwZXJtaXNzaW9ucyI6InN5czpyZXM6Y3JlYXRlLHN5czpyZXM6cmV0cmlldmUiLCJleHAiOjE2MjY5MzIyNTksImlhdCI6MTYyNjkzMDQ1OSwidXNlcm5hbWUiOiJqYWNrIn0
第三部分 Payload :SQrRS5nuID1Xv5GMvUgnr7xrVzB7GcRFrkNak-x16Mw

Header部分

Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。

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

上面代码中,

  • alg属性:表示签名的算法(algorithm),默认是 HMAC SHA256(简写HS256);
  • typ属性:表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。
  • 最后,将这个 JSON 对象使用 Base64URL 算法(详见后文)转成字符串。

Payload部分

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。
JWT规范中规定了7个官方字段,供选用
 iss (issuer):签发人
 exp (expiration time):过期时间
 sub (subject):主题
 aud (audience):受众
 nbf (Not Before):生效时间
 iat (Issued At):签发时间
 jti (JWT ID):编号
除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。
这个 JSON 对象也要使用 Base64URL 算法转成字符串。

Signature部分

Signature 部分是对前两部分的签名,其目的是防止数据被篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret
)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

JWT快速入门

第一步:创建项目,例如:
在这里插入图片描述

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
       <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.3.2.RELEASE</version>
    </parent>
    <groupId>com.cy</groupId>
    <artifactId>03-jt-security-jwt</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--添加jwt依赖-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
    </dependencies>

</project>

第三步:创建配置文件application.yml (暂时不写任何内容)
第四步:定义启动类,代码如下

package com.cy.jt;
@SpringBootApplication
public class SecurityJwtApplication {
    public static void main(String[] args) {
        SpringApplication.run(
                SecurityJwtApplication.class,
                args);
    }
}

第四步:运行启动类,检测是否可成功启动

创建和解析token

编写单元测试,实践Token对象的创建与解析,例如:

@Test
void testCreateAndParseToken(){
    //1.创建令牌
    //1.1定义负载信息
    Map<String,Object> map=new HashMap<>();
    map.put("username", "jack");
    map.put("permissions", "sys:res:create,sys:res:retrieve");
    //1.2定义过期实践
    Calendar calendar=Calendar.getInstance();
    calendar.add(Calendar.MINUTE, 30);
    Date expirationTime=calendar.getTime();
    //1.3定义密钥
    String secret="AAABBBCCCDDD";
    //1.4生成令牌
    String token= Jwts.builder()
            .setClaims(map)
            .setIssuedAt(new Date())
            .setExpiration(calendar.getTime())
            .signWith(SignatureAlgorithm.HS256,secret)
            .compact();
    System.out.println(token);
    //2.解析令牌
    Claims claims = Jwts.parser().setSigningKey(secret)
            .parseClaimsJws(token)
            .getBody();
    System.out.println("claims="+claims);
}

创建JWT工具类

为了简化JWT在项目中的应用,我们通常会构建一个工具类,对token的创建和解析进行封装,例如:

package com.cy.jt.security.util;
public class JwtUtils {
    /**
     * 秘钥
     */
    private static String secret="AAABBBCCCDDDEEE";
    /**
     * 有效期,单位秒
     * 默认30分钟
     */
    private static Long expirationTimeInSecond=1800L;

    /**
     * 从token中获取claim
     *
     * @param token token
     * @return claim
     */
    public static Claims getClaimsFromToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(secret.getBytes())
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            throw new IllegalArgumentException("Token invalided.");
        }
    }

    /**
     * 判断token是否过期
     * @param token token
     * @return 已过期返回true,未过期返回false
     */
    private static Boolean isTokenExpired(String token) {
        Date expiration = getClaimsFromToken(token).getExpiration();
        return expiration.before(new Date());
    }

    /**
     * 为指定用户生成token
     * @param claims 用户信息
     * @return token
     */
    public static String generateToken(Map<String, Object> claims) {
        Date createdTime = new Date();
        Date expirationTime = new Date(System.currentTimeMillis() + expirationTimeInSecond * 1000);
        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(createdTime)
                .setExpiration(expirationTime)
                .signWith(SignatureAlgorithm.HS256,secret)
                .compact();
    }
}

JWT在项目中的应用

AuthController 认证服务

定义AuthController用于处理登录认证业务,代码如下:

package com.cy.jt.security.controller;

@RestController
public class AuthController {
    @RequestMapping("/login")
    public Map<String,Object> doLogin(String username,
                          String password){
        Map<String,Object> map=new HashMap<>();
        if("jack".equals(username)&&"123456".equals(password)){
            map.put("state","200");
            map.put("message","login ok");
            Map<String,Object> claims=new HashMap<>();//负载信息
            claims.put("username",username);
            map.put("Authentication", JwtUtils.generatorToken(claims));
            return map;
        }else{
            map.put("state","500");
            map.put("message","login failure");
            return map;
        }
    }
}

ResourceController 资源服务

定义一个资源服务对象,登录成功以后可以访问此对象中的方法,例如:

package com.cy.jt.security.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ResourceController {

    @RequestMapping("/retrieve")
    public String doRetrieve(){
        //检查用户有没有登录
        //执行业务查询操作
        return "do retrieve resource success";
    }
    @RequestMapping("/update")
    public String doUpdate(){
        //检查用户有没有登录
        //执行业务查询操作
        return "do update resource success";
    }
}

TokenInterceptor 拦截器及配置

假如在每个方法中都去校验用户身份的合法性,代码冗余会比较大,我们可以写一个Spring MVC 拦截器,
在拦截器中进行用户身份检测,例如:

package com.cy.jt.security.interceptor;

import com.cy.jt.security.util.JwtUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 令牌(token:ticker-通票)拦截器
 * 其中,HandlerInterceptor为Spring MVC中的拦截器,
 * 可以在Controller方法执行之前之后执行一些动作.
 * 1)Handler 处理器(Spring MVC中将@RestController描述的类看成是处理器)
 * 2)Interceptor 拦截器
 */
public class TokenInterceptor implements HandlerInterceptor {
    /**
     * preHandle在目标Controller方法执行之前执行
     * @param handler 目标Controller对象
     */
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        //http://localhost:8080/retrieve?Authentication=
        //String token= request.getParameter("Authentication");
        String token=request.getHeader("Authentication");
        //判定请求中是否有令牌
        if(token==null||"".equals(token))
            throw new RuntimeException("please login");
        //判定令牌是否已经过期
        boolean flag=JwtUtils.isTokenExpired(token);
        if(flag)
        throw new RuntimeException("login timeout,please login");
        return true;//true表示放行,false表示拦截到请求以后,不再继续传递
    }
}

拦截器编写好以后,需要将拦截器添加到spring mvc执行链中并设置要拦截的请求,可通过配置类完成这个过程,代码如下:

package com.cy.jt.security.config;
/**
 * 定义Spring Web MVC 配置类
 */
@Configuration
public class SpringWebConfig implements WebMvcConfigurer {
    /**将拦截器添加到spring mvc的执行链中
     * @param registry 此对象提供了一个list集合,可以将拦截器添加到集合中
     * */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
         registry.addInterceptor(new TokenInterceptor())
                 //配置要拦截的url
                 .addPathPatterns("/retrieve","/update");
    }
}

Postman访问测试

第一步:登录访问测试
在这里插入图片描述
第二步:进行资源访问测试,例如:
在这里插入图片描述

总结(Summary)

本章节重点讲解了JWT产生的背景,它的构成和项目中的基本应用,需要在实践中进行分析和理解.

重难点分析

  1. JWT 诞生的背景?(分布式架构应用平台下无状态会话时,规范令牌(通票)数据格式)
  2. JWT 规范定义的数据格式?(头,负载-详细内容,签名,思考一篇文章的构成,)
  3. JWT 规范下JAVA相关API的应用?(jjwt依赖-Jwts)
  4. 基于JWT规范下JAVA API 创建令牌,解析令牌

FAQ分析

  1. JWT 是什么?(一种规范的数据格式)
  2. JWT规范中的数据格式有几部分构成?(3部分,前两部分会进行Base64编码,最会基于签名算法加密)
  3. JWT的负载(Payload-存储实际用户信息的部分)部分可以自定义吗?(Claims)
  4. JWT令牌对象一般是在哪里创建?(服务端,可以创建令牌以后,响应到客户端)
  5. JWT令牌假如要存储在客户端你会存储在哪里?(Cookie,localStorage,sessionStorage)
  6. JWT令牌以怎样的方式有客户端传递到服务端?(请求参数,请求头)

Bug分析

  1. 创建token和解析token时一定要相同的密钥
  2. Token过期了
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1 目标检测的定义 目标检测(Object Detection)的任务是找出图像中所有感兴趣的目标(物体),确定它们的类别和位置,是计算机视觉领域的核心问题之一。由于各类物体有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具有挑战性的问题。 目标检测任务可分为两个关键的子任务,目标定位和目标分类。首先检测图像中目标的位置(目标定位),然后给出每个目标的具体类别(目标分类)。输出结果是一个边界框(称为Bounding-box,一般形式为(x1,y1,x2,y2),表示框的左上角坐标和右下角坐标),一个置信度分数(Confidence Score),表示边界框中是否包含检测对象的概率和各个类别的概率(首先得到类别概率,经过Softmax可得到类别标签)。 1.1 Two stage方法 目前主流的基于深度学习的目标检测算法主要分为两类:Two stage和One stage。Two stage方法将目标检测过程分为两个阶段。第一个阶段是 Region Proposal 生成阶段,主要用于生成潜在的目标候选框(Bounding-box proposals)。这个阶段通常使用卷积神经网络(CNN)从输入图像中提取特征,然后通过一些技巧(如选择性搜索)来生成候选框。第二个阶段是分类和位置精修阶段,将第一个阶段生成的候选框输入到另一个 CNN 中进行分类,并根据分类结果对候选框的位置进行微调。Two stage 方法的优点是准确度较高,缺点是速度相对较慢。 常见Tow stage目标检测算法有:R-CNN系列、SPPNet等。 1.2 One stage方法 One stage方法直接利用模型提取特征值,并利用这些特征值进行目标的分类和定位,不需要生成Region Proposal。这种方法的优点是速度快,因为省略了Region Proposal生成的过程。One stage方法的缺点是准确度相对较低,因为它没有对潜在的目标进行预先筛选。 常见的One stage目标检测算法有:YOLO系列、SSD系列和RetinaNet等。 2 常见名词解释 2.1 NMS(Non-Maximum Suppression) 目标检测模型一般会给出目标的多个预测边界框,对成百上千的预测边界框都进行调整肯定是不可行的,需要对这些结果先进行一个大体的挑选。NMS称为非极大值抑制,作用是从众多预测边界框中挑选出最具代表性的结果,这样可以加快算法效率,其主要流程如下: 设定一个置信度分数阈值,将置信度分数小于阈值的直接过滤掉 将剩下框的置信度分数从大到小排序,选中值最大的框 遍历其余的框,如果和当前框的重叠面积(IOU)大于设定的阈值(一般为0.7),就将框删除(超过设定阈值,认为两个框的里面的物体属于同一个类别) 从未处理的框中继续选一个置信度分数最大的,重复上述过程,直至所有框处理完毕 2.2 IoU(Intersection over Union) 定义了两个边界框的重叠度,当预测边界框和真实边界框差异很小时,或重叠度很大时,表示模型产生的预测边界框很准确。边界框A、B的IOU计算公式为: 2.3 mAP(mean Average Precision) mAP即均值平均精度,是评估目标检测模型效果的最重要指标,这个值介于0到1之间,且越大越好。mAP是AP(Average Precision)的平均值,那么首先需要了解AP的概念。想要了解AP的概念,还要首先了解目标检测中Precision和Recall的概念。 首先我们设置置信度阈值(Confidence Threshold)和IoU阈值(一般设置为0.5,也会衡量0.75以及0.9的mAP值): 当一个预测边界框被认为是True Positive(TP)时,需要同时满足下面三个条件: Confidence Score > Confidence Threshold 预测类别匹配真实值(Ground truth)的类别 预测边界框的IoU大于设定的IoU阈值 不满足条件2或条件3,则认为是False Positive(FP)。当对应同一个真值有多个预测结果时,只有最高置信度分数的预测结果被认为是True Positive,其余被认为是False Positive。 Precision和Recall的概念如下图所示: Precision表示TP与预测边界框数量的比值 Recall表示TP与真实边界框数量的比值 改变不同的置信度阈值,可以获得多组Precision和Recall,Recall放X轴,Precision放Y轴,可以画出一个Precision-Recall曲线,简称P-R
图像识别技术在病虫害检测中的应用是一个快速发展的领域,它结合了计算机视觉和机器学习算法来自动识别和分类植物上的病虫害。以下是这一技术的一些关键步骤和组成部分: 1. **数据收集**:首先需要收集大量的植物图像数据,这些数据包括健康植物的图像以及受不同病虫害影响的植物图像。 2. **图像预处理**:对收集到的图像进行处理,以提高后续分析的准确性。这可能包括调整亮度、对比度、去噪、裁剪、缩放等。 3. **特征提取**:从图像中提取有助于识别病虫害的特征。这些特征可能包括颜色、纹理、形状、边缘等。 4. **模型训练**:使用机器学习算法(如支持向量机、随机森林、卷积神经网络等)来训练模型。训练过程中,算法会学习如何根据提取的特征来识别不同的病虫害。 5. **模型验证和测试**:在独立的测试集上验证模型的性能,以确保其准确性和泛化能力。 6. **部署和应用**:将训练好的模型部署到实际的病虫害检测系统中,可以是移动应用、网页服务或集成到智能农业设备中。 7. **实时监测**:在实际应用中,系统可以实时接收植物图像,并快速给出病虫害的检测结果。 8. **持续学习**:随着时间的推移,系统可以不断学习新的病虫害样本,以提高其识别能力。 9. **用户界面**:为了方便用户使用,通常会有一个用户友好的界面,显示检测结果,并提供进一步的指导或建议。 这项技术的优势在于它可以快速、准确地识别出病虫害,甚至在早期阶段就能发现问题,从而及时采取措施。此外,它还可以减少对化学农药的依赖,支持可持续农业发展。随着技术的不断进步,图像识别在病虫害检测中的应用将越来越广泛。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值