当前位置: 首页 > news >正文

后端之路——登录校验前言(Cookie\ Session\ JWT令牌)

前言:Servlet

【登录校验】这个功能技术的基础是【会话技术】,那么在讲【会话技术】的时候必然要谈到【Cookie】和【Session】这两个东西,那么在这之前必须要先讲一下一个很重要但是很多人都会忽略的一个知识点:【Servlet

什么是Servlet?

        Servlet是用java编写的应用在服务器端的程序;对于它的定义,“广义上”是一个个很大的【类】,“狭义上”是【接口】

.

Servlet容器是什么?

        我们经常听说的Tomcat、Weblogic......这些都是各种【Servlet容器】,而【Servlet容器】就是Servlet的运行环境,也可以理解是Servlet的引擎,为请求和响应的这些操作提供网络服务。

        那么我们知道,我们运行网络服务的时候都要启动Tomcat服务器,Tomcat来解析处理【请求】、【响应】,并处理成报文信息,但是这些服务器的缺点是底层代码写死了,很不灵活,而且有时处理完的数据格式也不够规范;那么这时候就诞生了Servlet,它被用来“扩展服务器的性能”,它能够灵活的处理请求、响应数据并以规范形式返回。

        那么说回Servlet,其实最简单最简单的理解,就是一个很大很规范的【类】,它里面包含了很多其他【子类】(准确来说是接口),这些子类包括:CookieSessionHttpServletRequestHttpServletResponse、ServletConfig、ServletContext......

        那么这些【子类】里也写好了很详细、规范的各种处理网络服务的方法,当我们需要处理一些网络服务逻辑的时候,就需要调用Servlet里的【子类】的【实例化对象】的【方法】

        打个比方:处理前端客户端发送请求的数据并生成对应的请求头报文时,就需要调用【HttpServletRequest】的实例化对象的方法;  处理服务器端返回回去的响应头报文的时候,就需要调用【HttpServletResponse】的实例化对象的方法......等等

那么现在我们重新来看一下(B/S架构)浏览器客户端与服务器端传输的流程:

1、浏览器客户端发送请求到服务器端

2、服务器端接收到信息,交给Servlet

3、Servlet(通过调用里面的一些子类的方法)处理逻辑,然后生成响应信息,给回服务器

4、服务器将响应结果返回浏览器客户端

一、会话技术

1、何为会话?

浏览器与服务器之间的一次连接就是一次会话

2、会话跟踪

【会话跟踪】就是:识别多个请求是否来自于同一个浏览器,然后在同一个浏览器的多个请求之间共享数据

3、会话跟踪方案:

浏览器与服务器之间的交互使用的是【http】协议,但是【http】协议是无状态的,也就是所有请求都是相互独立的,并不能在多个请求之间共享数据

那么就有了这么几种【会话跟踪技术】:

1、客户端会话跟踪技术:Cookie

.

2、服务端会话跟踪技术:Session

3、token令牌技术:JWT

这里不讲解HTTP是啥,这里有一篇讲的比较详细:HTTP 协议详解(史上最全)-CSDN博客

HTTP协议里的【请求头】和【响应头】的文章:Request Headers 和Response Headers——请求头和响应头-CSDN博客

只需要知道HTTP协议是一个浏览器与服务器联系传输数据的超文本传输协议,它两规定了一种传输数据的格式,然后里面有【响应报文Response Headers】和【请求报文Request Headers】这两部分,下面是简单讲解:

请求报文Request Headers】:

前端通过【浏览器/客户端】发送请求传给【服务器】的数据信息

一个【请求报文Request Headers】里包括了【请求行】【请求头】【请求体】

【请求体】就是前端传过来的具体的数据信息

【请求头】是服务器获取客户端信息的一些依据(用什么格式获取?从哪个地址获取?哪个浏览器发的?......)

响应头报文Reponse Headers】:

【服务器】返回给【浏览器/客户端】的响应数据信息

一样,一个【响应报文Response Headers】里也包括了【响应行】【响应头】【响应体】

【响应体】就是针对前端发来的请求数据而相应回去的具体数据值

【响应头】包含了服务器的响应讯息,如http版本,压缩方式,响应文件类型,文件编码等

(1)Cookie技术(Cookie是一个Servlet里的“子类”)

虽然每个请求和响应之间的Http协议是独立的,但是Http协议的【请求头】和【响应头】支持携带【Cookie】这个信息,那多个请求间就可以通过Cookie这个标识来获取用户信息数据

Cookie简单说:就是存放在客户端(浏览器)】的会话信息

—— Cookie是通过在【请求报文Request Headers】里的【请求头】处传递的

—— 传递关系是:【客户端(浏览器)——传Cookie——>服务器

—— Set-Cookie是通过【响应报文Response Headers】里的【响应头】处传递的

—— 传递关系是:【服务器——返回Set-Cookie——>客户端(浏览器)

简单用代码直观一点展示Cookie和Set-Cookie:(切记别记忆这些代码,简单了解即可)

Set-Cookie:

【HttpServletResponse】这个接口的实现类是专门设置【响应报文】信息的(切记:要导入的包一定要选这个【javax.servlet.http】)

.

然后【HttpServletResponse】的实现类对象的【.addCookie( )】方法能设置一个Cookie的值,需要往里面传一个【Cookie对象】(切记:这个【Cookie对象】对应的类型必须也是【javax.servlet.http】)

.

最后【Cookie对象】里数据形式是“键值对Key=value”:【name=value】,所以要传一个name参数、一个value值,分别代表 “键” 和 “值”

/*** 模拟【服务器】设置【用户的Cookie】的操作* @param response* @return*/
@GetMapping("/setCookie")
public Result setCookie(HttpServletResponse response){//调用这个响应方法就能设置一个Cookie值,Cookie对象里是【键值对】信息:name=valueresponse.addCookie(new javax.servlet.http.Cookie("userName","岑梓铭"));return Result.success();
}

怎么看效果?

1、首先输入网址,千万先别回车

2、摁F12打开网页检查,然后再在网址那回车,就会看到一个网络响应

3、单击它,然后就能在【响应报文Response Header】处看到【Set-Cookie】

Cookie:

【HttpServletRequest】这个接口的实现类是专门设置【请求报文】信息的(切记:要导入的包一定要选这个【javax.servlet.http】)

.

然后【HttpServletRequest】的实现类对象的【.getCookies( )】方法能返回所有Cookie的值,返回值是一个数组;需要用一个【Cookie对象类型的数组】接收(切记:这个【Cookie对象】类型对应的类型必须也是【javax.servlet.http】)

.

最后【Cookie对象】里数据形式是“键值对Key=value”:【name=value】,所以要获取Cookie数组里每一个Cookie的 “键”,就要调用【Cookie对象】的【.getName( )】方法

@GetMapping("/getCookie")
public Result getCookie(HttpServletRequest request){//发送请求后,获取返回的【所有的Cookies】javax.servlet.http.Cookie[] cookies = request.getCookies();//遍历所有Cookie,如果有对应这个【键(name)】的,就返回对应的【值(value)】for(javax.servlet.http.Cookie cookie : cookies){if(cookie.getName().equals( "userName" )){System.out.println("userName: " + "【" + cookie.getValue() + "】");}}return Result.success();
}

 怎么看效果?还是一样

1、首先输入网址,千万先别回车

2、摁F12打开网页检查,然后再在网址那回车,就会看到一个网络响应

3、单击它,然后就能在【响应报文Response Header】处看到【Set-Cookie】

缺点

1、移动端环境不是浏览器,浏览器才有Cookie这玩意

.

2、用户可以随意自己设置禁用Cookie,会用电脑的应该不用我解释,浏览器设置那里有

.

3、Cookie不能跨域(跨域就是【协议、IP/域名、端口】至少其中一样不一样,就是两个域,就存在跨域访问,那么我们都知道前端、后端是分别部署到两个不同的服务器的,不同服务器的地址肯定是不一样的,浏览器在发请求、返回响应时必然会要访问前端和后端的两个服务器,就会跨域)

总结

(2)Session技术(Session也是Servlet里的一个“子类”)

Session的本质其实就是对Cookie的优化,是存放在【服务器端】的会话信息

为什么说是Cookie的优化?因为它的逻辑其实是这样:

        首先浏览器发请求,产生一个会话,然后服务器这边就立刻产生一个【Session会话信息】和一个对应这个Session会话信息的【sessionId】,然后把【Session会话信息】存在服务器,只会把【sessionId】存进Cookie,通过Set-Cookie响应回给浏览器

。。

        然后下次浏览器再次发送请求想获取这个【Session会话信息】的时候,传递过去的是【装着sessionId的Cookie】,然后服务器检查Cookie里的【sessionId】,再到session里找有没有对应这个【sessionId】的【Session会话信息】,找到了的话,把【sessionId】和【Session会话信息】一起塞进【Cookie】返回给浏览器

模拟服务器【保存session】并【生成sessionId】的逻辑
/*** 模拟【服务器端】生成并响应回【session】的操作* @param session* @return*/
@GetMapping("/setSession")
public Result setSession(HttpSession session){//打印一下当前session的哈希码值,这个哈希码值代表指向了哪一个session会话,是【整数】//但是注意区分,这个不是sessionId,sessionId是HttpSession对象的唯一标识符,是【字符串】log.info("session_hashCode: {}",session.hashCode());//调用这个方法可以往session会话里存入一个数据,然后对应生成一个sessionID并塞进Cookie里session.setAttribute("LoginName","岑梓铭");return Result.success();
}

模拟浏览器通过sessionId【接收session信息】的逻辑
/*** 模拟【浏览器】发请求后获得服务器生成的【session】的操作* @param request* @return*/
@GetMapping("/getSession")
public Result getSession(HttpServletRequest request){//调用HttpServletRequest对象的getSession()方法可以获取到session会话对象HttpSession session = request.getSession();//打印一下当前session的哈希码值,这个哈希码值代表指向了哪一个session会话,是【整数】log.info("session_hashCode: {}",session.hashCode());//Session的getAttribute方法,里面传入“键”参数,就能返回对应的seesion会话对象里的具体值//逻辑是浏览器这边Cookie里只有sessionId,然后通过Session的getAttribute方法把Cookie里的sessionId给到服务器端//最终经过服务器检测sessionId,然后将session会话里对应“userName”的会话具体数据再塞进Cookie,返回给浏览器Object userInfo = session.getAttribute("LoginName");log.info("userInfo: {}",userInfo);return Result.success(userInfo);
}

 缺点

1、服务器集群情况下(也就是连接多个服务器的情况下),不同的服务器之间存着不同的会话信息,那就算浏览器的Cookie里有sessionId,那第二台服务器那里能靠第一台服务器seesion的 “钥匙” 来 “开“ 第二台服务器seesion的 “大门” 呢?

.

2、session会话信息都存在服务器,随着浏览器请求增多,服务器内存越来越不够用

.

3、既然它还是基于Cookie优化而来的,那必然也继承了Cookie的缺点

总结

(3)JWT令牌技术

——概念:

JWT,全称:【Json Web Token】,简单来说就是一长串带有【数字签名、签名算法、具体自定义信息......等等】json字符串,每一次请求响应都会带着它,通过计算来校验、得出其中的身份信息

一个JWT字符串主要包括三大部分:

        第一部分:Header(头),记录令牌类型、名算法等。例如:{"alg":"HS256","type":"JWT"}

        第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。例如:{"id":"1","username":"Tom"}

        第三部分:Siqnature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。

而组成这个字符串的原理是:

1、前面两部分是【Base64编码】,是一种由【A-Z  a-z  1-9  还有/】组成的来表示二进制数据的编码

2、最后一部分那一段是根据前面指定的一种【签名算法】计算后得到的编码

应用场景:

其实很简单,流程就是:

1、浏览器发送请求到服务器

2、服务器拦截请求,查看有没有token?没有就拒绝访问数据,并生成一个JWT令牌

3、下一次再来检查到有JWT令牌了,那就校验JWT令牌对不对,不对就拒绝访问;对就开放访问权限。

生成JWT令牌的方法:
1、引入依赖

在pom.xml文件引入下面依赖

<!-- JWT令牌 -->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>

注意,如果点了右上角的【maven刷新】按钮只后还是爆红,又可能只是连接中央库下载安装这个依赖包的时候网络不好,毕竟这些依赖都是在国外的公司的中央库,那么控制台那会有一个 “蓝色” 的提示——“尝试使用 -U 标记(强制更新快照)运行 Maven导入”,直接点它让Maven帮我们换个方案下载安装就行了

2、在Test类测试一下生成JWT令牌

只需要记住6步:

1、先设置一个哈希表集合,因为jwt令牌的【有效信息部分】要用【哈希表集合类型】接收

2、创建一个jwt的方法是【Jwts.builder( )】

3、一个Jwt令牌的第一部分是指定 “签名算法类型”“签名密钥”;那么【.signWith( )方法】就是设置jwt令牌第一部分。以我个人理解,“签名算法类型” 就是指根据不同类型的不同算法,“签名密钥” 就是以你自定义输入的一串字符串作为一个 “密钥”,用你的这个 “签名密钥” 才能在解析jwt令牌时知道你要解析的是哪一个(至于“签名算法类型”具体哪些类型有啥区别我也不知道,尽量先都用HS256这个类型就行)

4、一个Jwt令牌的第二部分是【有效荷载】,也就是用户的一些【有效信息部分】,【Jwts.addClaims( )】方法则是在创建一个jwt对象之后接收这个【有效信息部分】的方法,接收【哈希表集合】类型数据

5、jwt令牌要有个【有效时间】,就跟你们平时登录时的验证码一样,不然的话没时间限制那不是留够了时间给黑客破解吗。【.setExiration( )】方法就是设置【有效时间】,需要接受的是Date时间类型(System.currentTimeMillis()是目前的系统时间,加一个有效时间期限就行)

6、jwt是一个对象,要用【.compact( )】方法才能转化成【字符串】

@Test
void testGenJWT(){//先设置一个哈希表集合,因为jwt令牌的【有效信息部分】要用【哈希表集合类型】接收Map<String , Object> claims = new HashMap<>(); //值用Object因为可能是数字、可能是字符串claims.put("id",1);claims.put("name","岑梓铭");String jwt = Jwts.builder() //builder就是创建一个JWT令牌.signWith(SignatureAlgorithm.HS256,"yjtlwkbz") //设置【签名算法的类型(比如HS256)】、【签名内容(比如yjtlwkbz)】.addClaims(claims) //有效荷载部分,接收哈希表集合类型,存入用户有效信息.setExpiration(new Date(System.currentTimeMillis() + 3600*1000)) //设置有效时间1h(3600秒 * 1000毫秒).compact(); //把结果生成字符串System.out.println(jwt);
}

然后提示几点:

1、因为顶上的【@SpringBootTest】注解会影响整个项目,直接注释了,然后点对应这个当前这个【@Test】测试方法运行最快

.

2、如果刚刚编写生成jwt令牌代码时爆红爆错,检查这几个问题:

—— 报错classNotFoundException的下载jaxb-api依赖,2.1版本(版本别填错了)

—— 使用Base64编码字符串长度至少为43位,位数报错的可以把 “签名内容” 改成任意的大于等于43位的字符串(比如我代码里的"yjtlwkbz"改成"ahjahsdgaysdgkuywdgjwdbasbcjhcjasyasgkjjsh")

3、还有有的人可能会出现【test】包下的test类(class文件)全变成java文件了,没法运行,右键也不能新建class类文件,那可能是IDE出了点问题,清楚IDE缓存再重新启动一次就行,见下图

然后运行完成后,我们把控制台生成的【jwt令牌】复制,到这个网站可以查看解析我们的【jwt令牌】的信息:JSON Web Tokens - jwt.io

解析JWT令牌的方法

更简单,三步:

1、【Jwts.parser( )】方法解析jwt令牌

2、【.setSigningKey( )】方法就是根据你前面生成jwt令牌时的那个【签名密钥】来 “打开解密大门”

3、【.parseClaimsJws( )】把刚刚生成的【jwt令牌】整个塞进去就能被解析了

注意,别选成【.parseClaimsJwt( )】了,这两是两个东西,选下图这个

4、【.getBody( )】能够获取出【有效荷载】部分那些具体信息,并封装在一个Claims类型对象

@Test
void testGetJWT(){Claims claims = Jwts.parser().setSigningKey("yjtlwkbz")  //对应生成jwt的.signWith(SignatureAlgorithm.HS256,"yjtlwkbz")那个密钥//对应刚刚运行生成的jwt令牌.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoi5bKR5qKT6ZOtIiwiaWQiOjEsImV4cCI6MTcyMDQwOTg1Nn0.OmpQBJoP50BjjPnItLBGCmhgAVTcDzYGsTBMT7qohoE").getBody(); //获取有效荷载部分System.out.println(claims);
}

注意几点:

1、有效信息最后要用一个Claims对象来接收

2、一个jwt令牌有时效和使用次效,你如果超过了你设置的失效期限、或者已经运行执行了一次解析jwt令牌,那么这串jwt令牌就作废了,需要你再次运行【生成jwt令牌】,然后再运行【解析jwt令牌】获取信息,否则会报错

3、【.parseClaimsJws( )】别写成了【.parseClaimsJwt( )】

二、利用jwt令牌技术校验身份

现在我们学完了最先进的jwt令牌技术,那么就来实践一下如何运用它。

1、先为了前后端请求响应方便,封装好一个jwt令牌工具类

很简单,我们前面已经知道怎么【生成】和【解析】jwt令牌了,那么在封装工具类里只要改几点:

/

1、【生成jwt令牌】的时候首先要接收一个前端传过来的装着用户有效信息的【哈希表集合】参数;并最后要把生成的令牌字符串return出去

.

2、【解析jwt令牌】的时候需要接收生成的jwt字符串;并把解析获得【有效荷载信息】return回前端

package com.czm.tliaswebmanagement.utils;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;import java.util.Date;
import java.util.HashMap;
import java.util.Map;//别忘了加这个注解,让类放入IOC容器
@Component
public class JwtUtils {private static String signKey = "yjtlwkbz"; //定义【签名密钥】是“yjtlwkbz”private static Long time = (long)60*5 * 1000; //定义有效时间是5分钟/*** 接收前端有效信息,生成jwt令牌并返回给前端* @param claims* @return*/public String generateJWT(Map<String,Object> claims){String jwt = Jwts.builder() //builder就是创建一个JWT令牌.signWith(SignatureAlgorithm.HS256,signKey) //设置【签名算法的类型】、【签名内容】.addClaims(claims) //有效荷载部分,接收哈希表集合类型,存入用户有效信息.setExpiration(new Date(System.currentTimeMillis() + time)) //设置有效时间.compact(); //把结果生成字符串return jwt;}/*** 接收前端传来的jwt令牌,解析并返回有效荷载* @param jwt* @return*/public Claims parseJWT(String jwt){Claims claims = Jwts.parser().setSigningKey(signKey).parseClaimsJws(jwt).getBody();return claims;}
}

2、然后完成登录接口(三层架构)代码编写

1、首先根据接口文档规定,来确定前端传入的是什么格式数据

比如这个文档,以它为例子,那么确定前端传入【JSON格式】的【用户登录信息】,那么就要用一个Emp对象(我前几篇一直用的案例,员工对象)来接收这些参数值,然后用【@RequestBody】解析JSON成对象。然后post请求跟接口是“/login”,那就【@PostMapping("/login")】

(contrller)

2、第二步,在controller层调用service、并把刚刚解析的参数Emp传给service,service调用mapping进行sql查询,根据这个【用户登录信息】参数查询完数据库之后返回结果,再一级一级返回controller,老生常谈的流程我就不细说了。

(controllerr)

(service )

(mapping)        

3、然后在controller层再用一个【新的Emp对象】接收【查询完返回的结果】,如果查询到结果就说明数据库有这个账户,那么调用【JWT工具类】为这个账户【生成一个jwt令牌】(生成jwt的逻辑,在JWT工具类已经帮我们做好了,我们只需要传一个【装有用户信息】的【Map哈希表集合类的参数】给JWT工具类就行了)

4、最后,在查询到账户的情况下,将生成含有用户信息的JWT令牌返回给前端即可;如果查不到信息,就说明账户密码有误,查无此人,那就直接返回失败。

controller的完整代码:(其他的层的就不展示了)

package com.czm.tliaswebmanagement.controller;import com.czm.tliaswebmanagement.pojo.Emp;
import com.czm.tliaswebmanagement.pojo.Result;
import com.czm.tliaswebmanagement.service.EmpService;
import com.czm.tliaswebmanagement.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.HashMap;
import java.util.Map;@Slf4j
//@RequestMapping("/emps")
@RestController
public class EmpController {@Autowiredprivate EmpService empService;//获取JWT令牌工具类@Autowiredprivate JwtUtils jwtUtils;/*** 登录接口*/@PostMapping("/login")public Result login(@RequestBody Emp emp){log.info("传过来的员工账号密码信息:{}",emp);//先调用service查找数据库有无此账户Emp e = empService.login(emp);//判断能否根据用户名、密码在数据库查到此人//有的话,生成属于它的令牌,并返回给他if(e != null){//因为生成jwt令牌需要的是【哈希表集合】类型,所以用一个【哈希表集合】装查到的员工信息Map<String , Object> claims = new HashMap<>();//这里经过service、mapper查询回来的员工信息e,获取出他的id、name、username作为有效荷载信息claims.put("id",e.getId());claims.put("name",e.getName());claims.put("username",e.getUsername());//调用jwt工具类生成jwt方法获取jwt令牌String jwt = jwtUtils.generateJWT(claims);//然后把jwt令牌返回给前端return Result.success(jwt);}//那么如果数据库都没查到这个账户,就说明账号密码输入错误,返回错误就行了return Result.error("登陆失败,查无此人");}
}

然后我要解释一下这个jwt令牌到底在哪里传输

就在一个叫【token的玩意里存着,你可以理解为【token是一张磁卡,然后 jwt 就是类似这个磁卡的信号信息,你用【token这个磁卡刷门禁、刷刷卡机的时候,把 jwt 信息传过去验证。

然后这个【token可以放请求体、也可以是请求头,不过一般都是放请求头

那么现在前端只要登陆成功,就已经能获取到后端为之生成的jwt令牌了,现在只需要下次再携带这个jwt令牌发送请求,后端就可针对这个令牌进行判断:是否给这个用户放行使用软件、网页了,那么这就涉及到【过滤器filter】和【拦截器Interceptor】,下一篇再讲

相关文章:

后端之路——登录校验前言(Cookie\ Session\ JWT令牌)

前言&#xff1a;Servlet 【登录校验】这个功能技术的基础是【会话技术】&#xff0c;那么在讲【会话技术】的时候必然要谈到【Cookie】和【Session】这两个东西&#xff0c;那么在这之前必须要先讲一下一个很重要但是很多人都会忽略的一个知识点&#xff1a;【Servlet】 什么是…...

【蓄势·致远】 同为科技(TOWE)2024年年中会议

2024年7月2日-8日&#xff0c;同为科技&#xff08;TOWE&#xff09;召开2024年年中工作会议。会议回顾上半年总体工作情况&#xff0c;分析研判发展形势&#xff0c;规划部署下半年工作。 为期一周的工作会议&#xff0c;由同为科技&#xff08;TOWE&#xff09;创始人、董事长…...

通过git将文件push到github 远程仓库

1.先git clone 代码地址 git clone htttp://github.com/用户名/test.git 2. 添加文件 例如&#xff1a;touch 1.txt 3.将文件添加到暂存区 git add 1.txt 4.提交 git commit -m "commit 1.txt" 5.与远程仓库建立关联 git remote add 远程仓库名 远程仓库…...

如何判断服务器是否被攻击

如何判断服务器是否被攻击 一、异常流量模式 一种判断服务器是否遭到攻击的方法是监控网络流量。异常的流量模式&#xff0c;例如流量突然剧增或减少&#xff0c;都可能是攻击的迹象。通常&#xff0c;大量的入站流量表明分布式拒绝服务&#xff08;DDoS&#xff09;攻击的可能…...

泽众一站式性能测试平台P-One监控指标的意义

在当今数字化和信息化高度发展的时代&#xff0c;企业把保障系统稳定运行、优化业务流程和提升用户体验摆在首要位置。然而&#xff0c;在现如今复杂的分布式系统中&#xff0c;各个组件和服务之间的交互频繁且紧密&#xff0c;当系统出现性能瓶颈时&#xff0c;传统的监测手段…...

前端Canvas入门——一些注意事项

创建渐变的三种方法&#xff1a; createLinearGradient() - 线性渐变 createRadialGradient() - 径向渐变&#xff08;放射性渐变&#xff09; createConicGradient() - 锥形渐变 这三种的核心观点都是&#xff1a; 创建一个gradient对象&#xff0c;然后调用addColorStop()方法…...

移动互联安全扩展要求测评项

安全物理环境-无线接入点的位置选择 应为无线接入设备的安装选择合理位置&#xff0c;避免过度覆盖和电磁干扰。 无线接入设备的安装位置选择不当&#xff0c;易被攻击者利用&#xff0c;特别是攻击者会通过无线信号过度覆盖的弱点进行无线渗透攻击&#xff0c;因此要选择合理…...

【代码随想录】【算法训练营】【第64天】 [卡码117]软件构建 [卡码47]参加科学大会

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 卡码网。 day 64&#xff0c;周三&#xff0c;继续ding~ 题目详情 [卡码117] 软件构建 题目描述 卡码117 软件构建 解题思路 前提&#xff1a; 思路&#xff1a; 重点&#xff1a; 代码实现 C语言 [卡码…...

【python算法学习1】用递归和循环分别写下 fibonacci 斐波拉契数列,比较差异

问题&#xff1a; fibonacci 斐波拉契数列&#xff0c;用递归和循环的方法分别写,比较递归和循环的思路和写法的差别 最直接的思路&#xff0c;是写递归方法 循环方法的稍微有点绕&#xff0c;我觉得问题主要是出在&#xff0c;总结循环的通项公式更麻烦&#xff0c;难在数学…...

【邀请函】庭田科技邀您第五届中国国际复合材料科技大会

第五届中国国际复合材料科技大会暨第七届国际复合材料产业创新成果技术展示&#xff08;ICIE7-新疆&#xff09;将于7月25-27日在新疆乌鲁木齐-国际会展中心举行。上海庭田信息科技有限公司将携多款仿真模拟软件亮相本次大会&#xff0c;诚挚欢迎各位到场咨询了解&#xff01; …...

win32:第一个窗口程序-应用程序入口点(part.6)

第一个窗口程序的最后一部分&#xff1a;应用程序入口函数wWinMain&#xff1b;这是Windows应用程序的主函数&#xff0c;负责初始化应用程序、注册窗口类、创建主窗口并进入消息循环处理消息。 int APIENTRY wWinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInst…...

c++ 多边形 xyz 数据 获取 中心点方法,线的中心点取中心值搞定 已解决

有需求需要对。多边形 获取中心点方法&#xff0c;绝大多数都是 puthon和java版本。立体几何学中的知识。 封装函数 point ##########::getCenterOfGravity(std::vector<point> polygon) {if (polygon.size() < 2)return point();auto Area [](point p0, point p1, p…...

ext_errno:拓展errno

类似于C库的errno机制&#xff0c;报告错误发生的原因以及所在的位置&#xff0c;通过查询来获取。...

【CUDA】 Trust基本特性介绍及性能分析

Trust简介 Thrust 是一个实现了众多基本并行算法的 C 模板库,类似于 C 的标准模板库(standard template library, STL)。该库自动包含在 CUDA 工具箱中。这是一个模板库,仅仅由一些头文件组成。在使用该库的某个功能时,包含需要的头文件即可。该库中的所有类型与函数都在命名空…...

颈肩肌筋膜炎中医治疗

颈肩肌筋膜炎&#xff0c;又称颈肩肌纤维织炎或肌肉风湿症&#xff0c;是一种涉及筋膜、肌肉、肌腱和韧带等软组织的无菌性炎症。以下将分别从症状和治疗两方面进行详细介绍。 一、颈肩肌筋膜炎的症状 颈肩肌筋膜炎的主要症状包括&#xff1a; 1、肩背部疼痛&#xff1a;患者…...

Java 通配符 在短信发送之中 通配符参数动态获取解决方案

目录 1、通配符应用场景 2、实现方案分析 2.1、可能针对不同模板中核定参数硬编码到程序之中写死 2.2、通配置模板之中动态获得对应的参数 3、通过正则表达式验证与替换参数${}参考示例 4、参考文章 1、通配符应用场景 我们在使用通配符场景&#xff0c;主要是应用于短信…...

Mybatis-Plus中LambdaQueryWrapper

基本用法 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 假设有一个 User 实体类 LambdaQueryWrapper<User> queryWrapper new LambdaQueryWrapper<>(); // 添加查询条件 queryWrapper.eq(User::getName, "John&quo…...

C++ 入门05:类和对象

往期回顾&#xff1a; C 入门02&#xff1a;控制结构和循环-CSDN博客C 入门03&#xff1a;函数与作用域-CSDN博客C 入门04&#xff1a;数组与字符串-CSDN博客 一、前言 在前面文章的学习中&#xff0c;我们了解了 C 的基本结构、变量、输入输出、控制结构、循环、函数、作用域…...

4G LTE教程

整体架构 物理层&#xff08;第 1 层&#xff09; 物理层通过空中接口传输来自 MAC 传输信道的所有信息。负责 RRC 层的链路自适应 (AMC)、功率控制、小区搜索&#xff08;用于初始同步和切换目的&#xff09;和其他测量&#xff08;LTE 系统内部和系统之间&#xff09;。 介…...

C++:哈希表

哈希表概念 哈希表可以简单理解为&#xff1a;把数据转化为数组的下标&#xff0c;然后用数组的下标对应的值来表示这个数据。如果我们想要搜索这个数据&#xff0c;直接计算出这个数据的下标&#xff0c;然后就可以直接访问数组对应的位置&#xff0c;所以可以用O(1)的复杂度…...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 &#xff08;1&#xff09;连接查询&#xff08;JOIN&#xff09; 内连接&#xff08;INNER JOIN&#xff09;&#xff1a;返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

MySQL用户和授权

开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务&#xff1a; test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

代码随想录刷题day30

1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币&#xff0c;另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额&#xff0c;返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

Python Ovito统计金刚石结构数量

大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

NPOI操作EXCEL文件 ——CAD C# 二次开发

缺点:dll.版本容易加载错误。CAD加载插件时&#xff0c;没有加载所有类库。插件运行过程中用到某个类库&#xff0c;会从CAD的安装目录找&#xff0c;找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库&#xff0c;就用插件程序加载进…...

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分&#xff1a; 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...

前端中slice和splic的区别

1. slice slice 用于从数组中提取一部分元素&#xff0c;返回一个新的数组。 特点&#xff1a; 不修改原数组&#xff1a;slice 不会改变原数组&#xff0c;而是返回一个新的数组。提取数组的部分&#xff1a;slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...