后端之路——登录校验前言(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】,下一篇再讲

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

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

相关文章

STM32-外部中断浅析

本篇解释了STM32中断原理 MCU为什么需要中断 中断&#xff0c;是嵌入式系统中很重要的一个功能&#xff0c;在系统运行过程中&#xff0c;当出现需要立刻处理的情况时&#xff0c;暂停当前任务&#xff0c;转而处理紧急任务&#xff0c;处理完毕后&#xff0c;恢复之前的任务…

vue3项目图片压缩+rem+自动重启等plugin使用与打包配置

一、Svg配置 每次引入一张 SVG 图片都需要写一次相对路径&#xff0c;并且对 SVG 图片进行压缩优化也不够方便。 vite-svg-loader插件加载SVG文件作为Vue组件&#xff0c;使用SVGO进行优化。 插件网站https://www.npmjs.com/package/vite-svg-loader 1. 安装 pnpm i vite-svg…

谷粒商城学习笔记-使用renren-fast-vue框架时安装依赖包遇到的问题及解决策略

文章目录 1&#xff0c;npm error Class extends value undefined is not a constuctor or null2&#xff0c;npm warn cli npm v10.8.1 does not support Node.js v16.20.2.3&#xff0c;npm error code CERT_HAS_EXPIRED学习心得 这篇文章记录下使用renren-fast-vue&#xff…

Unity3D游戏 RPG

丛林探险游戏 人物进行探险游戏 拥有登录&#xff0c;首页&#xff0c;3D物体旋转浏览的功能&#xff0c;还能进行种植树等功能

11 个例子讲清spark提交命令参数

目录 提交命名参数详情为什么有这么多参数如何开始学习一些具体的例子1. 基本的Spark应用提交2. 提交带有依赖的Python脚本3. 运行Spark SQL作业4. 提交Spark Streaming作业5. 使用外部包运行Spark作业6. 动态资源分配7. 使用多个配置文件8. GPU 支持9. 自定义日志配置10. 使用…

swiftui中NavigationStack布局navigationBarTitleDisplayMode作用,以及内容顶部空白区域解决办法

写了一个小demo用于学习NavigationStack和toolbar/ToolbarItem知识&#xff0c;但是在写一个瀑布流布局的时候&#xff0c;设置了顶部的toolbar&#xff0c;然后内容区域的顶部出现了一大片空白区域&#xff0c;这样的效果并不是很美观很好看&#xff0c;所以就想着研究解决一下…

人工智能的新时代:从模型到应用的转变

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Chrome 127内置AI大模型攻略

Chrome 127 集成Gemini:本地AI功能 Google将Gemini大模型整合进Chrome浏览器,带来全新免费的本地AI体验: 完全免费、无限制使用支持离线运行,摆脱网络依赖功能涵盖图像识别、自然语言处理、智能推荐等中国大陆需要借助魔法,懂都懂。 安装部署步骤: 1. Chrome V127 dev …

城市地下综合管廊物联网远程监控

城市地下综合管廊物联网远程监控 城市地下综合管廊&#xff0c;作为现代都市基础设施的重要组成部分&#xff0c;其物联网远程监控系统的构建是实现智慧城市建设的关键环节。这一系统集成了先进的信息技术、传感器技术、通信技术和数据处理技术&#xff0c;旨在对埋设于地下的…

数据分析与挖掘实战案例-电商产品评论数据情感分析

数据分析与挖掘实战案例-电商产品评论数据情感分析 文章目录 数据分析与挖掘实战案例-电商产品评论数据情感分析1. 背景与挖掘目标2. 分析方法与过程2.1 评论预处理1. 评论去重2. 数据清洗 2.2 评论分词1. 分词、词性标注、去除停用词2. 提取含名词的评论3. 绘制词云查看分词效…

Java---包装类与泛型

1.包装类 1.1 包装类 在Java中&#xff0c;由于基本数据类型不是继承Object类&#xff0c;为了在泛型代码中可以支持基本数据类型&#xff0c;Java给每个基本数据类型各自提供了一个包装类。 如下图 除了char和int基本数据类型的包装类型有点特别&#xff0c;其他的都是首字…

MySQL Binlog详解:提升数据库可靠性的核心技术

文章目录 1. 引言1.1 什么是MySQL Bin Log&#xff1f;1.2 Bin Log的作用和应用场景 2. Bin Log的基本概念2.1 Bin Log的工作原理2.2 Bin Log的三种格式 3. 配置与管理Bin Log3.1 启用Bin Log3.2 配置Bin Log参数3.3 管理Bin Log文件3.4 查看Bin Log内容3.5 使用mysqlbinlog工具…

LabVIEW自动探头外观检测

开发了一套基于LabVIEW的软件系统&#xff0c;结合视觉检测技术&#xff0c;实现探头及连接器外观的自动检测。通过使用高分辨率工业相机、光源和机械手臂&#xff0c;系统能够自动定位并检测探头表面的细微缺陷&#xff0c;如划痕、残胶、异色、杂物等。系统支持多种探头形态&…

栈 栈是一种数据结构&#xff0c;只允许在固定一端进行插入和删除功能&#xff0c;进行插入和删除的一端叫做栈顶&#xff0c;另一端叫做栈底&#xff0c;遵循后入先出的规则&#xff0c;就像穿烤串和吃烤串一样 其中&#xff0c;插入数据叫做进栈/压栈/入栈&#xff0c;数据插…

Nacos 进阶篇---集群:选举心跳健康检查劳动者(九)

一、引言 本章将是我们第二阶段&#xff0c;开始学习集群模式下&#xff0c;Nacos 是怎么去操作的 &#xff1f; 本章重点&#xff1a; 在Nacos服务端当中&#xff0c;会去开启健康心跳检查定时任务。如果是在Nacos集群下&#xff0c;大家思考一下&#xff0c;有没有必要所有的…

MySQL存储过程创建

DQL call create_order_infos(7,2,3); delimiter $$ CREATE PROCEDURE create_order_infos( in in_user_id int, in in_product_id int, in in_count int ) BEGIN -- 业务逻辑 SELECT in_user_id 用户id,in_product_id 产品id,in_count 购买数量; end $$ delimiter ; 结果 c…

7.8洛谷 字符串

P5650 基础字符串练习题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 思路 如果 S[i] 0&#xff0c;则 dp[i] max(dp[i-1] 1, 1)&#xff08;因为增加了 0&#xff0c;减少了 1&#xff09;。如果 S[i] 1&#xff0c;则 dp[i] max(dp[i-1] - 1, -1)&#xff08;因为减…

九浅一深Jemalloc5.3.0 -- ④浅*配置

目前市面上有不少分析Jemalloc老版本的博文&#xff0c;但最新版本5.3.0却少之又少。而且5.3.0的架构与5之前的版本有较大不同&#xff0c;本着“与时俱进”、“由浅入深”的宗旨&#xff0c;我将逐步分析最新release版本Jemalloc5.3.0的实现。 另外&#xff0c;单讲实现代码是…

这几类人,千万不要买纯电车

文 | AUTO芯球 作者 | 响铃 纯电车的冤大头真是太多了&#xff0c; 我之前劝过&#xff0c;有些人不适合买纯电车&#xff0c; 你们看&#xff0c;果然吧&#xff0c;麦卡锡最近的一份报告就披露了 去年啊&#xff0c;22%的人在买了电车后后悔了&#xff0c; 这些人说了&a…

亿康源精英盛宴暨亿康源启动成功举办

&#xff08;本台记者报&#xff09;2024年7月7日下午&#xff0c;亿康源精英盛宴暨启动仪式在杭州市中维歌德大酒店盛大举行。此次盛会不仅吸引了行业内的专业人才、著名投资界大咖和科技领域的杰出企业家&#xff0c;还汇聚了众多关注大健康产业的各界人士&#xff0c;共同见…