后端之路——登录校验

前言: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=value
    response.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 {
    @Autowired
    private EmpService empService;

    //获取JWT令牌工具类
    @Autowired
    private 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】,下一篇再讲

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

redis-cli 连接Redis

Redis-cli介绍 redis-cli 是原生 Redis 自带的命令行工具&#xff0c;您可以在云主机或本地设备上通过 redis-cli 连接 Redis 数据库&#xff0c;进行数据管理。 redis-cli 客户端的使用方法&#xff0c;请参考官方文档。 连接命令 redis-cli -h <redis_instance_address…

【算法笔记自学】第 7 章 提高篇(1)——数据结构专题(1)

7.1栈的应用 #include <iostream> #include <string> #include <stack> using namespace std;int main() {int n, x;string action;cin >> n;stack<int> s;for (int i 0; i < n; i) {cin >> action;if (action "push") {ci…

Qt/C++项目积累: 2.主机监控器 - 2.1 项目介绍

一&#xff1a;项目主体编写背景 在观察程序的运行状态时&#xff0c;其对系统的CPU&#xff0c;内存&#xff0c;硬盘占用无疑是几项重要参考指标&#xff0c;而现有的监控软件&#xff0c;搜索了解到以Zabbix类软件比较出名&#xff0c;其采用标准的SNMP协议的原理来实现监控…

【鸿蒙学习笔记】页面布局

官方文档&#xff1a;布局概述 常见页面结构图 布局元素的组成 线性布局&#xff08;Row、Column&#xff09; 了解思路即可&#xff0c;更多样例去看官方文档 Entry Component struct PracExample {build() {Column() {Column({ space: 20 }) {Text(space: 20).fontSize(15)…

10岁女孩儿童编程规划

目录 1. 背景2. 为什么让她学儿童编程&#xff1f;3. 学习方法&目标4. 整体规划4.1 Python 入门与基础4.1.1 目标4.1.2 学习内容 4.2 C 入门与基础4.2.1 目标4.2.2 学习内容 4.3 算法进阶4.3.1 目标4.3.2 学习内容 4.4 高级编程4.4.1 目标4.4.2 学习内容 4.5 参与编程赛事4…

Java套红:指定位置合并文档-NiceXWPFDocument

需求&#xff1a;做个公文系统&#xff0c;需要将正文文档在某个节点点击套红按钮&#xff0c;实现文档套红 试了很多方法&#xff0c;大多数网上能查到但是实际代码不能找到关键方法&#xff0c;可能是跟包的版本有关系&#xff0c;下面记录能用的这个。 一&#xff1a;添加依…

深入源码,探究#、$号替换符的区别

在Mybatis的日常使用过程中以及在一些技术论坛上我们都能常常听到&#xff0c;不要使用$符号来进行SQL的编写&#xff0c;要使用#符号&#xff0c;否则会有SQL注入的风险。那么&#xff0c;为什么在使用$符号时会有注入的风险呢&#xff0c;以及#号为什么不会有风险呢&#xff…

spark任务,使用 repartition 对数据进行了重新分区,但任务输入数据大小仍存在不均衡

目录 目录 确认 Spark 任务重新分区后的数据不均衡 1. 检查分区大小 2. 使用 DataFrame API 检查分区 3. 使用 Spark UI 查看分区情况 4. 使用日志记录分区信息 可能原因 1. 数据分布不均衡 2. 分区策略 3. 数据预处理 解决方案 1. 检查数据分布 2. 使用 coalesce…

Java反射与Fastjson的危险反序列化

什么是Java反射&#xff1f; 在前文中&#xff0c;我们有一行代码 Computer macBookPro JSON.parseObject(preReceive,Computer.class); 这行代码是什么意思呢&#xff1f;看起来好像就是我们声明了一个名为 macBookPro 的 Computer 类&#xff0c;它由 fastjson 的 parseObje…

工程仪器振弦采集仪的设计与研发进展

工程仪器振弦采集仪的设计与研发进展 工程仪器振弦采集仪是一种用于测量和记录物体振动参数的仪器。它能够实时采集物体的振动信号&#xff0c;并通过内部的传感器将振动信号转化为电信号&#xff0c;然后进行信号放大和处理&#xff0c;最终以数字形式显示或存储。 河北稳控科…

2024图纸加密软件TOP8排行丨企业保护数据安全最佳选择

图纸往往包含了设计人员的创意和智慧&#xff0c;是企业的重要资产。加密可以防止未经授权的复制、分发或使用&#xff0c;保护设计的原创性和独特性不被侵犯。 许多图纸可能含有公司的商业秘密&#xff0c;比如特定的技术参数、生产流程或是产品设计等。这些信息若泄露给竞争…

股票数据分析(K线图、均值图、MACD图、RSI图)--股票日数据

数据 数据是上证指数日行情数据&#xff0c;股票代码000002.sz&#xff0c;原始数据shdata示例如下&#xff1a; 读取数据&#xff1a; import numpy as np import pandas as pd import mplfinance as mpf import matplotlib.pyplot as plt from datetime import datetime imp…

JVM原理(二十):JVM虚拟机内存的三特性详解

1. 原子性、可进行、有序性 1.1. 原子性 Java内存模型围绕着在并发过程中如何处理原子性、可见性和有序性这三个特征来建立的。 Java内存模型来直接保证的原子性变量操作包括read、load、assign、use、store和write这六个。我们大致可以认为&#xff0c;基本数据类型的访问、…

vllm技术分享

vLLM Question&#xff1a; 推理所生成的序列长度大小是无法事先预知的&#xff0c;大部分框架会按照(batch_size, max_seq_len)这样的固定尺寸&#xff0c;在gpu显存上预先为一条请求开辟一块连续的矩形存储空间。这样的分配方法很容易引起“gpu显存利用不足”的问题&#xff…

ICE启动AI:人工智能高频交易平台测试进入尾段

Intercontinental Exchange, Inc.(ICE)宣布,其革命性AI高频交易平台ICE.AI已经完成搭建,目前已全面进入测试最终阶段,该平台利用先进的人工智能技术,主旨在提升交易效率和市场分析的精确度,即将为全球交易者带来前所未有的交易体验。 性能验证: ICE.AI平台在测试阶段主要进行性…

【QT中堆栈布局的实现】

学习分享 1、环境设置&#xff0c;头文件2、.h文件2.1、主界面.h文件2.2、对话界面1.h文件2.3、对话界面2.h文件 3、.cpp文件3.1、对话界面1.cpp3.2、对话界面2.cpp3.3、主界面.cpp3.4、main.cpp 1、环境设置&#xff0c;头文件 该示例使用C14实现&#xff0c;因此在QT项目pro文…

【银河麒麟】系统内存使用异常现象分析及建议

1.现象描述 问题机器系统内存占用长时间90%以上&#xff0c;同时伴随着高iowait&#xff0c;在故障时无法ssh登录&#xff0c;同时也影响生产业务。但之后系统内存占用会突然掉下来&#xff0c;在内存自己掉下来后能ssh登录。 2.显示分析 2.1 sa日志分析 查看问题机器3月15日…

什么是企业服务总线?它包含哪些技术组件?

我们每个人都会去医院&#xff0c;您描述下我们去医院的场景&#xff0c;然后引出这个挂号流程&#xff0c;通过挂号流程中的一个问题或者什么东西来吸引他的好奇心&#xff0c;这样呢&#xff1f;会比现在的预设场景好一些。我举个例子&#xff0c;人工智能怎么帮人看病。如果…

关于put提交不了参数的解决办法

html中form表单只支持GET与POST请求&#xff0c;而DELETE、PUT等method并不支持&#xff0c; 如图所示 参数请求改成RequestBody&#xff0c;用json格式传参即可解决问题

AI直播手机APP震撼发布!3大场景直播,60秒一键开播!

无需繁琐准备&#xff0c;无需复杂操作&#xff0c;60 秒在抖音及其他平台一键开播&#xff0c;青否数字人AI直播APP正式发布&#xff01; 3大AI直播类型&#xff0c;6大核心 AIGC 技术&#xff0c;让新手小白也能轻松搞定数字人在全平台直播&#xff0c;并且有效规避违规风险&…