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

后端之路——登录校验

前言: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

这里不讲解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令牌了,现在只需要下次再携带这个jwt令牌发送请求,后端就可针对这个令牌进行判断:是否给这个用户放行使用软件、网页了,那么这就涉及到【过滤器filter】和【拦截器Interceptor】,下一篇再讲

相关文章:

后端之路——登录校验

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

无线网卡怎么连接台式电脑?让上网更便捷!

随着无线网络的普及&#xff0c;越来越多的台式电脑用户希望通过无线网卡连接到互联网。无线网卡为台式电脑提供了无线连接的便利性&#xff0c;避免了有线网络的束缚。本文将详细介绍无线网卡怎么连接台式电脑的四种方法&#xff0c;包括使用USB无线网卡、内置无线网卡以及使用…...

【45 Pandas+Pyecharts | 去哪儿海南旅游攻略数据分析可视化】

文章目录 &#x1f3f3;️‍&#x1f308; 1. 导入模块&#x1f3f3;️‍&#x1f308; 2. Pandas数据处理2.1 读取数据2.2 查看数据信息2.3 日期处理&#xff0c;提取年份、月份2.4 经费处理2.5 天数处理 &#x1f3f3;️‍&#x1f308; 3. Pyecharts数据可视化3.1 出发日期_…...

Vue3项目给ElementPlus设置中文的两个方案

介绍 在Vue3项目将ElementPlus切换为中文 1、在App.vue的文件中修改 <template><el-config-provider :locale"zhCn"><router-view></router-view></el-config-provider> </template><script lang"ts" setup>im…...

C#开发单实例应用程序并响应后续进程启动参数

C#默认的WinForm模板是不支持设置单实例的&#xff0c;也没有隔壁大哥VB.NET那样有个“生成单个实例应用程序”的勾选选项&#xff08;VB某些时候要比C#更方便&#xff09;&#xff0c;实现单实例可以有多种方法&#xff1a; 检测同名进程&#xff1a;Process.GetProcessesByNa…...

STM32智能机器人导航系统教程

目录 引言环境准备智能机器人导航系统基础代码实现&#xff1a;实现智能机器人导航系统 4.1 数据采集模块 4.2 数据处理与导航算法 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;机器人导航应用与优化问题解决方案与优化收尾与总结 1. 引言 智能机器…...

Android 15 适配之16K Page Size :为什么它会是最坑的一个适配点

首先什么是 Page Size &#xff1f;一般意义上&#xff0c;页面(Page)指的就是 Linux 虚拟内存管理中使用的最小数据单位&#xff0c;页面大小(Page Size)就是虚拟地址空间中的页面大小&#xff0c; Linux 中进程的虚拟地址空间是由固定大小的页面组成。 Page Size 对于虚拟内…...

下载linux的吐槽

本来这几天放假了&#xff0c;想下一个linux玩一玩 教程&#xff08;我就是根据这个教程进行下载的&#xff0c;但是呢在进行修改BIOS 模式的 地方遇见了困难&#xff0c;也许是电脑修过的原因&#xff0c;我狂按F12 以及 FnF12都没有BIOS设置&#xff0c;只有一个让我选择用w…...

【HTML入门】第四课 - 换行、分割横线和html的注释

这一小节&#xff0c;我们继续说HTML的入门知识&#xff0c;包括换行、横线分割以及注释&#xff08;html的注释&#xff09;。 目录 1 换行 2 分割横线 3 html注释 1 换行 html中分为块元素和行内元素。这一小节呢&#xff0c;先不说这些元素们&#xff0c;我们先说一下换…...

基于Hadoop平台的电信客服数据的处理与分析④项目实现:任务15:数据生产

任务描述 电信数据生产是一个完整且严密的体系&#xff0c;这样可以保证数据的鲁棒性。在本项目的数据生产模块中&#xff0c;我们来模拟生产一些电信数据。同时&#xff0c;我们必须清楚电信数据的格式和数据结构&#xff0c;这样才能在后续的数据产生、存储、分析和展示环节…...

Kotlin中的数据类型

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…...

提高交易决策质量,Anzo Capital昂首资本只需两个交易策略

要想提高交易决策质量&#xff0c;其实很简单&#xff0c;Anzo Capital昂首资本只需两个交易策略&#xff0c;结合价格行为和VSA(成交量与价格分析)就可以达成我们的目的。首先&#xff0c;理解这两个概念&#xff1a; 1. 价格行为&#xff1a;价格行为是市场价格变动的方式&a…...

Ubuntu TensorRT安装

什么是TensorRT 一般的深度学习项目&#xff0c;训练时为了加快速度&#xff0c;会使用多 GPU 分布式训练。但在部署推理时&#xff0c;为了降低成本&#xff0c;往往使用单个 GPU 机器甚至嵌入式平台&#xff08;比如 NVIDIA Jetson&#xff09;进行部署&#xff0c;部署端也…...

spring mvc学习

第四章 Spring MVC 第一节 Spring MVC 简介 1. Spring MVC SpringMVC是一个Java 开源框架&#xff0c; 是Spring Framework生态中的一个独立模块&#xff0c;它基于 Spring 实现了Web MVC&#xff08;数据、业务与展现&#xff09;设计模式的请求驱动类型的轻量级Web框架&am…...

第4集《修习止观坐禅法要》

请打开讲义第七面&#xff0c;四、悟道。 我们前面讲到智者大师出家以后&#xff0c;他除了持戒以外&#xff0c;一方面拜忏&#xff0c;一方面就是打坐&#xff0c;来调伏他过去的烦恼跟罪业&#xff0c;以为他未来圆顿止观的一个基础&#xff0c;这以下讲到他开悟的情况&…...

IPython 日志的开关:精通 %logoff 命令的实用指南

IPython 日志的开关&#xff1a;精通 %logoff 命令的实用指南 在 IPython 的强大功能中&#xff0c;日志记录是一个不可或缺的工具&#xff0c;它帮助用户记录会话历史&#xff0c;以便日后分析和重现。%logoff 命令作为日志记录功能的补充&#xff0c;允许用户在需要时停止日…...

Redis 分布式集群方案 Cluster

引言 相比于Codis&#xff0c;Redis Cluster是Redis官方提供的解决方案。相比于Codis的不同&#xff0c;他是去中心化的&#xff0c;如图所示&#xff0c;该集群有三个Redis节点组成&#xff0c;每个节点负责整个集群的一部分数据&#xff0c;每个节点负责的数据多少可能不一样…...

Redis的两种持久化方案

Redis 提供了多种持久化机制来保证数据在发生意外情况下&#xff08;如断电或服务器崩溃&#xff09;不丢失。以下是几种主要的 Redis 持久化方案及其特点&#xff1a; 1. RDB (Redis Database Backup) RDB 是 Redis 创建的数据库快照&#xff0c;它可以将数据集快照以二进制…...

Spring中常见知识点及使用

Spring Framework 是 Java 生态系统中最流行的开源框架之一&#xff0c;它提供了一系列强大的功能&#xff0c;用于构建企业级应用。以下是一些常见的 Spring 知识点及其使用方法&#xff1a; 1. 依赖注入&#xff08;Dependency Injection&#xff09; 依赖注入是 Spring 的…...

Excel 宏录制与VBA编程 ——VBA编程技巧篇二 (合并内容相同连续单元格、取消合并单元格并在每个单元格中保留内容)

1、合并内容相同的连续单元格 如果需要合并如图所示的工作表中B列中部门相同的连续单元格 VBA代码&#xff1a; Sub Mergerng()Dim IntRow As IntegerDim i As IntegerApplication.DisplayAlerts FalseWith Sheet1IntRow .Range("A65536").End(xlUp).RowFor i In…...

理解和应用工业设备字典文件:一篇详细指南

理解和应用工业设备字典文件&#xff1a;一篇详细指南 在工业自动化领域&#xff0c;设备和模块的配置和管理是一个复杂而重要的任务。为了简化这个过程&#xff0c;字典文件被广泛应用于描述离线对象字典。本文将详细解释字典文件的用途、格式&#xff0c;并举例说明如何引用…...

Python酷库之旅-第三方库Pandas(010)

目录 一、用法精讲 22、pandas.read_hdf函数 22-1、语法 22-2、参数 22-3、功能 22-4、返回值 22-5、说明 22-6、用法 22-6-1、数据准备 22-6-2、代码示例 22-6-3、结果输出 23、pandas.HDFStore.put方法 23-1、语法 23-2、参数 23-3、功能 23-4、返回值 23-5…...

海康威视监控web实时预览解决方案

海康威视摄像头都试rtsp流&#xff0c;web页面无法加载播放&#xff0c;所以就得转换成web页面可以播放的hls、rtmp等数据流来播放。 一&#xff1a;萤石云 使用萤石云平台&#xff0c;把rtsp转化成ezopen协议&#xff0c;然后使用组件UIKit 最佳实践 萤石开放平台API文档 …...

ubuntu运行qq音乐闪退

ubuntu运行qq音乐闪退 修改/usr/share/applications中的qqmusic.desktop&#xff0c;在Exec后加上 --no-sandbox,如下图所示&#xff1a; 该文件有可能是只读&#xff0c;权限不够的话用sudo vim qqmusic.desktop...

人脸检测(Python)

目录 环境&#xff1a; 初始化摄像头&#xff1a; 初始化FaceDetector对象&#xff1a; 获取摄像头帧&#xff1a; 获取数据&#xff1a; 绘制数据&#xff1a; 显示图像&#xff1a; 完整代码&#xff1a; 环境&#xff1a; cvzone库&#xff1a;cvzone是一个基于…...

Offer150-23:链表中环的入口节点

题目描述:如果一个链表中包含环&#xff0c;找了环的入口节点。例如&#xff0c;在下图所示的链表中&#xff0c;环的入口节点是节点4。 分析&#xff1a;第一步需要确定一个链表中是否包含环&#xff0c;可以用快慢指针来解决这个问题。定义两个指针&#xff0c;同时从链表的头…...

【linux】服务器创建RAID1

【linux】服务器创建RAID1 文章目录 【linux】服务器创建RAID1一、配置介绍raid介绍raid类型RAID 0:RAID 1:RAID 5:RAID 6:二、配置RAID硬件RAID:软件RAID:三、软件配置RAID1(以linux为例)1.先进入管理员模式2.安装mdadm工具3.创建raid1数组4.查看RAID数组状态5.格式化和挂载…...

记录自己Ubuntu加Nvidia驱动从入门到入土的一天

前言 记录一下自己这波澜壮阔的一天&#xff0c;遇到了很多问题&#xff0c;解决了很多问题&#xff0c;但是还有很多问题&#xff0c;终于在晚上的零点彻底放弃&#xff0c;重启windows。 安装乌班图 1.安装虚拟机 我开始什么操作系统的基础都没有&#xff0c;网上随便搜了…...

基于现有Docker镜像构建新的Docker镜像

1.拉取ubuntu 22.04的系统镜像 docker pull ubuntu:22.04 拉取成功后在DockerDesktop中可发现该镜像 2.启动刚才接取的ubuntu镜像 docker run --name Ubuntu22.04 -it -d -p 22:22 -p 80:80 -p 443:443 340d9b015b194dc6e2a13938944e0d016e57b9679963fdeb9ce021daac430221 启…...

Java 静态变量、静态代码块、普通代码块、构造方法的执行顺序

今天碰到这个问题&#xff0c;看了课程以及资料&#xff0c;做出解答。这是我自己绘制的图&#xff0c;按从上到下&#xff0c;从左到右的顺序执行。如有问题请联系我修正。 要点&#xff1a; 1、执行顺序分为两步&#xff0c;类加载和初始化阶段。 2、因为静态变量和静态代码块…...