登录校验(令牌,过滤器,拦截器使用详解)

文章目录

  • 前言
  • 一、会话技术
    • 1. Cookie
    • 2. Session
    • 3. 令牌
  • 二、JWT令牌
    • 1. 简介
  • 二、过滤器Filter
    • 1. 简介
    • 2. 快速入门
    • 3. 执行流程
    • 4. 使用示例
    • 5. 为什么自定义的Filter类不需要使用@Component
  • 四、拦截器Interceptor
    • 1. 介绍
    • 2. 入门程序
    • 3. Interceptor详解


前言

该篇详细对SpringBoot项目的登录检验功能进行了详细分析,包含jwt令牌,token的生产和检验,过滤器,拦截器等技术的使用。


一、会话技术

在这里插入图片描述

1. Cookie

cookie 是客户端会话跟踪技术,它是存储在客户端浏览器的,当使用 cookie 来跟踪会话,就可以在浏览器第一次发起请求来请求服务器的时候,在服务器端来设置一个cookie。
服务器端在给客户端在响应数据的时候,会自动的将 cookie 响应给浏览器,浏览器接收到响应回来的 cookie 之后,会自动的将 cookie 的值存储在浏览器本地。接下来在后续的每一次请求当中, 都会将浏览器本地所存储的 cookie 自动地携带到服务端。
在服务端就可以对cookie进行判,如果不存在这个cookie值,就说明客户端之前是没有访问登录接口的;如果存在 cookie 的值,就说明客户端之前已经登录完成了。这样就可以基于 cookie 在同一次会话的不同请求之间来共享数据。
cookie 是 HTP 协议所支持的技术,各大浏览器厂商都支持了这一标准。在 HTTP 协议官方给我们提供了一个响应头和请求头:
- 响应头 Set-Cookie :设置Cookie数据的
- 请求头 Cookie:携带Cookie数据的
- 所以浏览器服务器可以完成上面的三个自动
- 服务器会 自动 的将 cookie 响应给浏览器。
- 浏览器接收到响应回来的数据之后,会 自动 的将 cookie 存储在浏览器本地。
- 在后续的请求当中,浏览器会 自动 的将 cookie 携带到服务器端。

在这里插入图片描述

程序实现分析如下:

@Slf4j
@RestController
public class SessionController {//设置Cookie    响应@GetMapping("/c1")public Result cookie1(HttpServletResponse response){response.addCookie(new Cookie("login_username","itheima")); //设置Cookie/响应Cookiereturn Result.success();}//获取Cookie   请求@GetMapping("/c2")public Result cookie2(HttpServletRequest request){Cookie[] cookies = request.getCookies();for (Cookie cookie : cookies) {if(cookie.getName().equals("login_username")){System.out.println("login_username: "+cookie.getValue()); //输出name为login_username的cookie}}return Result.success();}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. Session

前面介绍的时候,我们提到Session,它是服务器端会话跟踪技术,所以它是存储在服务器端的。而 Session 的底层其实就是基于我们刚才所介绍的 Cookie 来实现的。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3. 令牌

这里我们所提到的令牌,其实它就是一个用户身份的标识,看似很高大上,很神秘,其实本质就是一个字符串。
在这里插入图片描述
如果通过令牌技术来跟踪会话,我们就可以在浏览器发起请求。在请求登录接口的时候,如果登录成功,我就可以生成一个令牌,令牌就是用户的合法身份凭证。接下来我在响应数据的时候,我就可以直接将令牌响应给前端。
接下来我们在前端程序当中接收到令牌之后,就需要将这个令牌存储起来。这个存储可以存储在 cookie 当中,也可以存储在其他的存储空间(比如:localStorage)当中。
接下来,在后续的每一次请求当中,都需要将令牌携带到服务端。携带到服务端之后,接下来我们就需要来校验令牌的有效性。如果令牌是有效的,就说明用户已经执行了登录操作,如果令牌是无效的,就说明用户之前并未执行登录操作。
此时,如果是在同一次会话的多次请求之间,我们想共享数据,我们就可以将共享的数据存储在令牌当中就可以了。
在这里插入图片描述
在这里插入图片描述

二、JWT令牌

1. 简介

前面我们介绍了基于令牌技术来实现会话追踪。这里所提到的令牌就是用户身份的标识,其本质就是一个字符串。令牌的形式有很多,我们使用的是功能强大的 JWT令牌。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2. 生成和校验
首先我们先来实现JWT令牌的生成。要想使用JWT令牌,需要先引入JWT的依赖:

<!-- JWT依赖-->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>

在引入完JWT来赖后,就可以调用工具包中提供的API来完成JWT令牌的生成和校验

工具类:Jwts

package com.itheima;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.junit.jupiter.api.Test;import java.util.Date;
import java.util.HashMap;
import java.util.Map;/*** @Author: 风清* @CreateTime: 2024-08-06 09:21*/
public class testJwt {//生成JWT代码实现:@Testpublic void testGenJwt(){//  claims 一个键值对集合,用于存储 JWT 的载荷部分中的数据。例如用户 ID、角色或其他任何想要存储的信息。Map<String,Object> claims = new HashMap<>();claims.put("id",1);claims.put("password",123456);String string = Jwts.builder().signWith(SignatureAlgorithm.HS256, "MTAwODY=").addClaims(claims).setExpiration(new Date(System.currentTimeMillis() + 2 * 60 * 60 * 1000)).compact();//public static JwtBuilder builder()   ---  用于创建JWT的构建器/*public JwtBuilder signWith(加密算法, 密钥)  设置加密信息     SignatureAlgorithm ---算法,常用HS256public  JwtBuilder signWith(SignatureAlgorithm var1, byte[] var2);public  JwtBuilder signWith(SignatureAlgorithm var1, String var2);public  JwtBuilder signWith(SignatureAlgorithm var1, Key var2);      Key---base64编码   推荐*///  public  JwtBuilder addClaims(Map<String, Object> var1) 添加数据体(以键值对的格式)//  public  JwtBuilder setExpiration(Date var1) 设置密钥时限//  public String compact()  将加密后的JwtBuilder对象转为String密钥System.out.println(string);}@Test//校验JWT令牌(解析生成的令牌):public void testParseJwt(){//Claims接口继承了Map接口,也是一个键值对的集合Claims claims = Jwts.parser().setSigningKey("MTAwODY=").parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJwYXNzd29yZCI6MTIzNDU2LCJpZCI6MSwiZXhwIjoxNzIyOTE3MDEyfQ.fgWip-sWmzBX6DdpJW_9vfs0jQg5mrNcf8FIlJv9GNE").getBody();//public static JwtParser parser()  创建一个新的解析器/*public JwtParser setSigningKey(密钥); 设置用于验证 JWT 签名的密钥,需要与加密保持一致public JwtParser setSigningKey(byte[] var1);public JwtParser setSigningKey(String var1);public JwtParser setSigningKey(Key var1);  Key---base64编码   推荐*///Jws<Claims> parseClaimsJws(String var1)  解析 JWT,并获取 ClaimsJws 对象//  (Jwt<H extends Header, B>) B getBody() 获取载荷部分 (payload) 中的数据,即 Claims 对象。System.out.println(claims);}
}

在这里插入图片描述

二、过滤器Filter

1. 简介

Filter表示过滤器,是 JavaWeb三大组件(Servlet、Filter、Listener)之一,属于JavaEE。
- 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能
- 使用了过滤器之后,要想访问web服务器上的资源,必须先经过滤器,过滤器处理完毕之后,才可以访问对应的资源。
- 过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等。
在这里插入图片描述

2. 快速入门

  • 第1步,定义过滤器 :1.定义一个类,实现 Filter 接口,并重写其所有方法。

  • 第2步,配置过滤器:Filter类上加 @WebFilter 注解,配置拦截资源的路径。引导类上加 @ServletComponentScan 开启Servlet组件支持。
    在这里插入图片描述

    • init方法:过滤器的初始化方法。在web服务器启动的时候会自动的创建Filter过滤器对象,在创建过滤器对象的时候会自动调用init初始化方法,这个方法只会被调用一次。
    • doFilter方法:这个方法是在每一次拦截到请求之后都会被调用,所以这个方法是会被调用多次的,每拦截到一次请求就会调用一次doFilter()方法。
    • destroy方法: 是销毁的方法。当我们关闭服务器的时候,它会自动的调用销毁方法destroy,而这个销毁方法也只会被调用一次。

3. 执行流程

在这里插入图片描述
过滤器拦截到请求之后,如果希望继续访问后面的web资源,就要执行放行操作,调用 FilterChain对象当中的doFilter()方法,在调用doFilter()方法之前所编写的代码属于放行之前的逻辑。
在放行后访问完 web 资源之后还会回到过滤器当中,回到过滤器之后如有需求还可以执行放行之后的逻辑,放行之后的逻辑写在doFilter()这行代码之后。

@WebFilter(urlPatterns = "/*") 
public class DemoFilter implements Filter {@Override //初始化方法, 只调用一次public void init(FilterConfig filterConfig) throws ServletException {System.out.println("init 初始化方法执行了");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("DemoFilter   放行前逻辑.....");//放行请求filterChain.doFilter(servletRequest,servletResponse);//上面的请求和响应参数System.out.println("DemoFilter   放行后逻辑.....");}@Override //销毁方法, 只调用一次public void destroy() {System.out.println("destroy 销毁方法执行了");}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. 使用示例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

过滤器实现

/*** 令牌校验过滤器*/
@Slf4j
@WebFilter(urlPatterns = "/*")
public class TokenFilter implements Filter {@Overridepublic void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {/*方法中的ServletRequest、ServletResponse接口分别被HttpServletRequest、HttpServletResponse接口继承了,所以可以强转*/HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) resp;//1. 获取请求url。String url = request.getRequestURL().toString();//2. 判断请求url中是否包含login,如果包含,说明是登录操作,放行。if(url.contains("login")){ //登录请求log.info("登录请求 , 直接放行");chain.doFilter(request, response);return;}//3. 获取请求头中的令牌(token)。String jwt = request.getHeader("token");//4. 判断令牌是否存在,如果不存在,返回错误结果(未登录)。if(!StringUtils.hasLength(jwt)){ //jwt为空log.info("获取到jwt令牌为空, 返回错误结果");response.setStatus(HttpStatus.SC_UNAUTHORIZED);return;}//5. 解析token,如果解析失败,返回错误结果(未登录)。try {JwtUtils.parseJWT(jwt);} catch (Exception e) {e.printStackTrace();log.info("解析令牌失败, 返回错误结果");response.setStatus(HttpStatus.SC_UNAUTHORIZED);return;}//6. 放行。log.info("令牌合法, 放行");chain.doFilter(request , response);}}

5. 为什么自定义的Filter类不需要使用@Component

在 Java Web 应用中,自定义的过滤器(Filter)类通常不需要使用 @Component 注解,这是因为过滤器的生命周期和配置是由 Web 容器(如 Tomcat)管理的,而不是由 Spring 容器管理。下面是几个原因说明为什么自定义的 Filter 类不需要使用 @Component 注解:

  1. 生命周期管理:
    • 过滤器的生命周期(如初始化、销毁)是由 Web 容器管理的,而不是 Spring 容器。
    • 当 Web 容器启动时,它会根据配置(如 web.xml 或 @WebFilter 注解)自动创建过滤器实例,并调用 init 方法来初始化过滤器。
    • 当 Web 容器关闭时,它会调用过滤器的 destroy 方法来释放资源。
  2. 配置方式:
    • 过滤器可以通过在 web.xml 文件中配置 和 元素来注册。
    • 从 Java 7 开始,可以通过使用 @WebFilter 注解直接在过滤器类上进行配置。
    • 这两种配置方式都不涉及 Spring 容器的管理。
  3. Spring 与 Web 容器的关系: Spring 容器负责管理 Spring Beans 的生命周期,而 Web 容器负责管理 Web 组件(如 Servlet、Filter 和 Listener)的生命周期。
    • 当使用 Spring MVC 或 Spring Boot 时,虽然 Spring 容器和 Web 容器紧密集成,但过滤器仍然由 Web 容器管理。
  4. 使用 @Component 的场景:
    • @Component 注解通常用于将类标记为 Spring Bean,使其成为 Spring 管理的对象。
    • 如果你的过滤器需要与 Spring 容器中的其他 Bean 进行交互,或者需要通过 Spring 进行依赖注入,那么你可以考虑使用 @Component 注解,并且还需要在配置类中使用 @ServletComponentScan 来让 Spring 发现这些组件。

四、拦截器Interceptor

1. 介绍

拦截器

  • 是一种动态拦截方法调用的机制,类似于过滤器。
  • 拦截器是Spring框架中提供的,用来动态拦截控制器方法的执行。
  • 是JavaEE规范中的,所以不需要导包,只需要实现然后交给Bean容器管理即可

拦截器的作用:
在这里插入图片描述

  • 拦截请求,在指定方法调用前后,根据业务需要执行预先设定的代码。

在拦截器当中,我们通常也是做一些通用性的操作,比如:我们可以通过拦截器来拦截前端发起的请求,将登录校验的逻辑全部编写在拦截器当中。在校验的过程当中,如发现用户登录了(携带JWT令牌且是合法令牌),就可以直接放行,去访问spring当中的资源。如果校验时发现并没有登录或是非法令牌,就可以直接给前端响应未登录的错误信息。

2. 入门程序

在这里插入图片描述

自定义拦截器

//实现HandlerInterceptor接口重写方法
@Component
public class DemoInterceptor implements HandlerInterceptor {//目标资源方法执行前执行。 返回true:放行    返回false:不放行@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle .... ");return true; //true表示放行}//目标资源方法执行后执行@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle ... ");}//视图渲染完毕后执行,最后执行@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion .... ");}
}

注意:

  • preHandle方法:目标资源方法执行前执行。 返回true:放行 返回false:不放行
  • postHandle方法:目标资源方法执行后执行
  • afterCompletion方法:视图渲染完毕后执行,最后执行

注册配置拦截器

/*
然后创建一个配置类 WebConfig, 实现 WebMvcConfigurer 接口,
并重写 addInterceptors 方法(该接口提供了各种插件方法,根据需求
重写,这里需要添加拦截器,就重写了addInterceptors添加拦截器的
方法)
*/
@Configuration  
public class WebConfig implements WebMvcConfigurer {//自定义的拦截器对象@Autowiredprivate DemoInterceptor demoInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//注册自定义拦截器对象,并指定拦截器路径registry.addInterceptor(demoInterceptor).addPathPatterns("/**");//设置拦截器拦截的请求路径( /** 表示拦截所有请求)}
}

在这里插入图片描述

3. Interceptor详解

在这里插入图片描述

  1. 拦截路径
    在这里插入图片描述
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/428394.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

串口助手的qt实现思路

要求实现如下功能&#xff1a; 获取串口号&#xff1a; foreach (const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()) {qDebug() << "Port: " << serialPortInfo.portName(); // e.g. "COM1"qDebug() <<…

GeoPandas在地理空间数据分析中的应用

GeoPandas是一个开源的Python库&#xff0c;专门用于处理和分析地理空间数据。它建立在Pandas库的基础上&#xff0c;扩展了Pandas的数据类型&#xff0c;使得用户能够在Python中方便地进行GIS操作。GeoPandas的核心数据结构是GeoDataFrame&#xff0c;它是Pandas的DataFrame的…

射击靶标检测系统源码分享

射击靶标检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

VScode开发GD32移植(标准库通用),保姆级!!!!!!!

VScode开发GD32移植(标准库通用)&#xff0c;保姆级&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 文章目录 VScode开发GD32移植(标准库通用)&#xff0c;保姆级&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#…

哪个牌子的麦克风好用?无线麦克风避坑指南:五大常见问题

随着短视频行业的兴起&#xff0c;和视频拍摄有关的外设也被推到了风口浪尖上&#xff0c;而麦克风作为视频拍摄或者现场直播使用的主要拾音工具&#xff0c;自然成为了大家非常关注的一个摄影外设工具&#xff0c;毕竟一款好的拾音工具能够给视频创作者或者直播博主带来更好的…

基于PHP的CRM管理系统源码/客户关系管理CRM系统源码/php源码/附安装教程

源码简介&#xff1a; 这是一款基于PHP开发的CRM管理系统源码&#xff0c;全称客户关系管理CRM系统源码&#xff0c;它是由php源码开发的&#xff0c;还附带了一整套详细的安装教程哦&#xff01; 功能亮点&#xff1a; 1、公海管理神器&#xff1a;不仅能搞定公海类型&…

OpenCV特征检测(6)对初步检测到的角点位置进行亚像素级别的精炼函数cornerSubPix()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 细化角点的位置。 该函数迭代以找到角点或径向鞍点的亚像素级准确位置&#xff0c;如 93中所述&#xff0c;并如下图所示。 亚像素级准确的角点…

Linux:虚拟文件系统/proc和self进程

相关阅读 Linuxhttps://blog.csdn.net/weixin_45791458/category_12234591.html?spm1001.2014.3001.5482 /proc目录 在Linux操作系统中&#xff0c;目录/proc是一个虚拟文件系统&#xff0c;称为procfc&#xff0c;用于访问内核和系统的实时状态信息。这个文件系统不同于常规…

SpringMVC1~~~

快速入门 spring容器文件 在src下就是applicationContext-mvc.xml&#xff0c;需要在web.xml指定<init-param>&#xff0c;给DispatcherServlet指定要去操作的spring容器文件 在WEB-INF下就是xxx-servlet.xml&#xff0c;不需要在web.xml指定<init-param>,如果我们…

Qt:智能指针QScopedPointer 的使用(以及如何写一个QScopedPointer )

前言 本文讲述QScopedPointer 的使用&#xff0c;以及自己如何写一个QScopedPointer . 正文 QScopedPointer 的常用方法 以下是 QScopedPointer 的一些常用方法及其详细说明&#xff1a; 构造函数&#xff1a; QScopedPointer<T> ptr(new T);用于创建一个 QScopedPoi…

使用 Elasticsearch Reindex API 迁移数据

使用 Elasticsearch Reindex API 迁移数据 在 Elasticsearch 中&#xff0c;随着需求的变化&#xff0c;可能需要对索引进行重建或更新。这通常涉及创建新索引、迁移数据等步骤。本文介绍如何使用 Reindex API 将旧索引中的数据迁移到新索引中 一、步骤概述 创建新索引&#…

R18 NES 之SSB-less SCell operation for inter-band CA

在TR 21.918 Summary of Rel-18 Work Items 中可以看到SSB-less SCell operation for inter-band CA 是Network energy savings for NR 的一部分,其中还包括cell DTX/DRX 等等其他内容。 网络节能是 5G/NR 成功的关键,可以减少对环境的影响(温室气体排放)并节省运营成本。R…

『功能项目』伤害数字UI显示【53】

我们打开上一篇52眩晕图标显示的项目&#xff0c; 本章要做的事情是在Boss受到伤害时显示伤害数字 首先打开Boss01预制体空间在Canvas下创建一个Text文本 设置Text文本 重命名为DamageUI 设置为隐藏 编写脚本&#xff1a;PlayerCtrl.cs 运行项目 本章做了怪物受伤血量的显示UI…

详细分析Java中的ObjectMapper基本知识(附Demo)

目录 1. 基本知识2. 基本操作2.1 转换Java对象为JSON2.2 转换JSON为Java对象 3. 拓展 1. 基本知识 ObjectMapper 是 Jackson 数据处理库中的核心类之一&#xff0c;主要用于将 Java 对象转换为 JSON 和将 JSON 转换为 Java 对象 Jackson 是当前最流行的 JSON 处理库之一&…

DOCKER 数据库管理软件自己开发--———未来之窗行业应用跨平台架构

- 数据异地容灾服务--未来之窗智慧数据服务 DATA REMOTE DISASTER RECOVERY SERVICE -CyberWin Future Docker-数据查看 CyberWin DATA Viewer 1.docker 样式 mysqli://root:密码172.17.0.2:端口/数据库 阿雪技术观 拥抱开源与共享&#xff0c;见证科技进步奇迹&#xff0c;…

mat (Eclipse Memory Analyzer Tool)使用以及详解

前言 在Java开发中&#xff0c;内存问题往往不易被发现&#xff0c;但它们可能导致应用性能下降甚至崩溃。Eclipse Memory Analyzer Tool&#xff08;MAT&#xff09;是一个强大的开源工具&#xff0c;专门用于分析Java堆转储&#xff08;heap dumps&#xff09;文件&#xff…

2024华为杯研赛E题保姆级教程思路分析

E题题目&#xff1a;高速公路应急车道紧急启用模型 今年的E题设计到图像/视频处理&#xff0c;实际上&#xff0c;E题的难度相对来说较低&#xff0c;大家不用畏惧视频的处理&#xff0c;被这个吓到。实际上&#xff0c;这个不难&#xff0c;解决了视频的处理问题&#xff0c;…

L3 逻辑回归

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 在周将使用 LogisticRegression 函数对经典的鸢尾花 (Iris) 数据集进行分类。将详细介绍逻辑回归的数学原理。 1. 逻辑回归的数学原理 逻辑回归是一种线性分…

如何短期提高品牌声量?说几个有效策略

在如今竞争激烈的市场环境中&#xff0c;品牌声量成为了衡量一个品牌市场影响力的关键指标。一个强大的品牌声量不仅可以增加品牌的可见度&#xff0c;还能有效提升品牌的市场竞争力。但是&#xff0c;如何有效提升品牌声量&#xff0c;成为很多企业面临的挑战。首先我们要明确…

R语言机器学习算法实战系列(二) SVM算法(Support Vector Machine)

文章目录 介绍原理应用方向下载数据加载R包导入数据数据预处理数据描述数据切割标准化数据设置参数训练模型预测测试数据评估模型模型准确性混淆矩阵模型评估指标ROC CurvePRC Curve特征的重要性保存模型总结系统信息介绍 支持向量机(Support Vector Machine,简称SVM)是一种…