JWT 详解

前言:

本博客为转载整合博客(主打一个:我们只做博客的搬运工),参考博客主要有:
https://blog.csdn.net/weixin_45070175/article/details/118559272?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170459940116800215060893%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=170459940116800215060893&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogtop_positive~default-1-118559272-null-null.nonecase&utm_term=jwt&spm=1018.2226.3001.4450

以及讲义:
链接:https://pan.baidu.com/s/1-m74G-15V8a36y9D7tqaMA?pwd=d95d
提取码:d95d
–来自百度网盘超级会员V5的分享

以及B站视频:https://www.bilibili.com/video/BV1i54y1m7cP

如有雷同,肯定是我搬运的!如有侵权,还请联系笔者。

1.什么是JWT

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

​ 官网地址: https://jwt.io/introduction/

翻译: jsonwebtoken(JWT)是一个开放标准(rfc7519),它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON对象安全地传输信息。此信息可以验证和信任,因为它是数字签名的。jwt可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名

通俗解释

JWT简称JSON Web Token,也就是通过JSON形式作为Web应用中的令牌,用于在各方之间安全地将信息作为JSON对象传输。在数据传输过程中还可以完成数据加密、签名等相关处理。

2.JWT能做什么

1.授权

  • 这是使用JWT的最常见方案。一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌允许的路由,服务和资源。单点登录是当今广泛使用JWT的一项功能,因为它的开销很小并且可以在不同的域中轻松使用。

2.信息交换

  • JSON Web Token是在各方之间安全地传输信息的好方法。因为可以对JWT进行签名(例如,使用公钥/私钥对),所以您可以确保发件人是他们所说的人。此外,由于签名是使用标头和有效负载计算的,因此您还可以验证内容是否遭到篡改。

3.为什么是JWT

基于传统的Session认证

1.认证方式

-我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。

2.认证流程

在这里插入图片描述

3.暴露问题

  • 1.每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大

  • 2.用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。

  • 3.因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

  • 4.在前后端分离系统中就更加痛苦:如下图所示
    也就是说前后端分离在应用解耦后增加了部署的复杂性。通常用户一次请求就要转发多次。如果用session 每次携带sessionid 到服务器,服务器还要查询用户信息。同时如果用户很多。这些信息存储在服务器内存中,给服务器增加负担。还有就是CSRF(跨站伪造请求攻 击)攻击,session是基于cookie进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。还有就是 sessionid就是一个特征值,表达的信息不够丰富。不容易扩展。而且如果你后端应用是多节点部署。那么就需要实现session共享机制。 不方便集群应用。

基于JWT认证

在这里插入图片描述

1.认证流程

  • 首先,前端通过Web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。

  • 后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT(Token)。形成的JWT就是一个形同lll.zzz.xxx的字符串。 token head.payload.singurater

  • 后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可。

  • 前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题) HEADER

  • 后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选)。

  • 验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果。

2.jwt优势

  • 简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快

  • 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库

  • 因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。

  • 不需要在服务端保存会话信息,特别适用于分布式微服务。

4.JWT的结构是什么?

1.令牌组成

  • 1.标头(Header)
  • 2.有效载荷(Payload)
  • 3.签名(Signature)

因此,JWT通常如下所示:xxxxx.yyyyy.zzzzz
Header.Payload.Signature

2.Header

  • 标头通常由两部分组成:令牌的类型(type)即JWT和所使用的签名算法(algorithm ),例如HMAC SHA256或RSA。它会使用 Base64 编码组成 JWT 结构的第一部分。

  • 注意:Base64是一种编码,也就是说,它是可以被翻译回原来的样子来的。它并不是一种加密过程。


```json
{"alg": "HS256","typ": "JWT"
}

3.Payload

-令牌的第二部分是有效负载,其中包含声明。声明是有关实体(通常是用户)和其他数据的声明。同样的,它会使用 Base64 编码组成 JWT 结构的第二部分

{"sub": "1234567890","name": "John Doe","admin": true
}

这里面存的实际可以理解为后端传输给前端的用户信息,所以这里的信息一般是非敏感信息,比如说用户名,用户性别,年龄等。
注意:这里一般不会传输用户的密码等信息,不然存在信息泄露的风险。

4.Signature

前面两部分都是使用 Base64 进行编码的,即前端可以解开知道里面的信息。Signature 需要使用编码后的 header 和 payload 以及我们提供的一个密钥secret(也可以说是盐值、签名),然后使用 header 中指定的签名算法(HS256)进行签名。签名的作用是保证 JWT 没有被篡改过。

  • 如:
    HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload),secret)

这里有几个问题需要再强调一下:

签名目的

所谓的“签名的作用是保证 JWT 没有被篡改过。”实际是这样操作的。

在首次登录后,后端会构建这样一个字符串
在这里插入图片描述

然后将其传给前端。
前端浏览器也好,安卓客户端也好进行存储,下一次相同用户访问时,前端会将这个字符串放入header(一般来说,也可能是cookie)里,然后后端在请求中将这个字符串重新取出,后端会根据“·”对这个字符串进行拆分,拿到header、payload、Signature这三个部分,然后使用同样的加密算法比如前面说的HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload),secret)。看得到的结果与前端传过来的Signature是否一致,这样就可以验证JWT 有没有被篡改过。

放在一起

  • 输出是三个由点分隔的Base64-URL字符串,可以在HTML和HTTP环境中轻松传递这些字符串,与基于XML的标准(例如SAML)相比,它更紧凑。
  • 简洁(Compact)
    可以通过URL, POST 参数或者在 HTTP header 发送,因为数据量小,传输速度快
  • 自包含(Self-contained)
    负载中包含了所有用户所需要的信息,避免了多次查询数据库

5.使用JWT

1.引入依赖

<!--引入jwt-->
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.4.0</version>
</dependency>

2.生成token

Calendar instance = Calendar.getInstance();
instance.add(Calendar.SECOND, 90);
//生成令牌
String token = JWT.create().withClaim("username", "张三")//设置自定义用户名.withExpiresAt(instance.getTime())//设置过期时间.sign(Algorithm.HMAC256("token!Q2W#E$RW"));//设置签名 保密 复杂
//输出令牌
System.out.println(token);

生成结果
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsicGhvbmUiLCIxNDMyMzIzNDEzNCJdLCJleHAiOjE1OTU3Mzk0NDIsInVzZXJuYW1lIjoi5byg5LiJIn0.aHmE3RNqvAjFr_dvyn_sD2VJ46P7EGiS5OBMO_TI5jg

这里说明一下:

withClaim这个属性可以翻译为伴随属性,实际就是token里面的payload这个字符串里面的内容。默认是Map形式进行存储。有几个需要存储的数据就几个withClaim。

.sign(Algorithm.HMAC256("token!Q2W#E$RW"));

这是签名:"token!Q2W#E$RW"就是盐值或者说签名,这个字符串是一定只能在服务端自留的,不能泄露,一般项目中是在配置文件中配置好。

Algorithm.HMAC256是一种常用的加盐加密算法,这也是默认的加密算法。

在上述设置中我们是没有设置header的,实际生成器会自动按照默认值设置,也就是前面说的默认值。

3.根据令牌和签名解析数据

//jwt验证
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("token!Q2W#E$RW")).build();
//将token解析为能够获取信息的格式
DecodedJWT decodedJWT = jwtVerifier.verify(token);
System.out.println("用户名: " + decodedJWT.getClaim("username").asString());
System.out.println("过期时间: "+decodedJWT.getExpiresAt());

注意:
1.取数据需要注意,如果前面我们payload里面存储了两个值,(比如添加一行代码 .withClaim("sex", "男")//设置自定义用户名)但是我们并不能使用类似

String name  = decodedJWT.getClaim("username");
String sex = decodeJWT.getClaim("sex");

直接取值,getClaim本质上是一个Claim类型,在本质一点是一个Map类型,所以需要先get一下再去使用。

String name  = decodedJWT.get().getClaim("username");
String sex = decodeJWT.get().getClaim("sex");

4.常见异常信息

  • SignatureVerificationException: 签名不一致异常
  • TokenExpiredException: 令牌过期异常
  • AlgorithmMismatchException: 算法不匹配异常
  • InvalidClaimException: 失效的payload异常

6.封装工具类

public class JWTUtils {private static final String  SING = "!Q@W3e4r%T^Y";/*** 生成token  header.payload.sing*/public static String getToken(Map<String,String> map, Date expire){//创建jwt builderJWTCreator.Builder builder = JWT.create();//payloadmap.forEach((k,v)->{builder.withClaim(k,v);});String token = builder.withExpiresAt(expire)//指定令牌过期时间.sign(Algorithm.HMAC256(SING));//signreturn token;}/*** 验证token 合法性**/public static DecodedJWT verify(String token){return JWT.require(Algorithm.HMAC256(SING)).build().verify(token);}
}

7.JWT的种类

其实JWT(JSON Web Token)指的是一种规范,这种规范允许我们使用JWT在两个组织之间传递安全可靠的信息,JWT的具体实现可以分为以下几种:

nonsecure JWT:未经过签名,不安全的JWT
JWS:经过签名的JWT
JWE:payload部分经过加密的JWT

1.nonsecure JWT

未经过签名,不安全的JWT。其header部分没有指定签名算法

{"alg": "none","typ": "JWT"
}

并且也没有Signature部分

2.JWS

JWS ,也就是JWT Signature,其结构就是在之前nonsecure JWT的基础上,在头部声明签名算法,并在最后添加上签名。创建签名,是保证jwt不能被他人随意篡改。我们通常使用的JWT一般都是JWS

为了完成签名,除了用到header信息和payload信息外,还需要算法的密钥,也就是secretKey。加密的算法一般有2类:

  • 对称加密:secretKey指加密密钥,可以生成签名与验签
  • 非对称加密:secretKey指私钥,只用来生成签名,不能用来验签(验签用的是公钥)

JWT的密钥或者密钥对,一般统一称为JSON Web Key,也就是JWK

到目前为止,jwt的签名算法有三种:

  • HMAC【哈希消息验证码(对称)】:HS256/HS384/HS512
  • RSASSA【RSA签名算法(非对称)】(RS256/RS384/RS512)
  • ECDSA【椭圆曲线数据签名算法(非对称)】(ES256/ES384/ES512)

8.Java中使用JWT

官网推荐了6个.ava使用JWT的开源库,其中比较推荐使用的是java-jwtjjwt-root
在这里插入图片描述

1.java-jwt

1.1对称加密

这个在上面的案例中已经展示了,这里就不再做说明。

1.2非对称加密

private static final String RSA_PRIVATE_KEY = "...";
private static final String RSA_PUBLIC_KEY = "...";/*** 生成token* @param payload token携带的信息* @return token字符串*/
public static String getTokenRsa(Map<String,String> payload){// 指定token过期时间为7天Calendar calendar = Calendar.getInstance();calendar.add(Calendar.DATE, 7);JWTCreator.Builder builder = JWT.create();// 构建payloadpayload.forEach((k,v) -> builder.withClaim(k,v));// 利用hutool创建RSARSA rsa = new RSA(RSA_PRIVATE_KEY, null);// 获取私钥RSAPrivateKey privateKey = (RSAPrivateKey) rsa.getPrivateKey();// 签名时传入私钥String token = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.RSA256(null, privateKey));return token;
}/*** 解析token* @param token token字符串* @return 解析后的token*/
public static DecodedJWT decodeRsa(String token){// 利用hutool创建RSARSA rsa = new RSA(null, RSA_PUBLIC_KEY);// 获取RSA公钥RSAPublicKey publicKey = (RSAPublicKey) rsa.getPublicKey();// 验签时传入公钥JWTVerifier jwtVerifier = JWT.require(Algorithm.RSA256(publicKey, null)).build();DecodedJWT decodedJWT = jwtVerifier.verify(token);return decodedJWT;
}

2.jjwt-root

2.1 对称签名

引入依赖

<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>

使用方法类似,可参考下列代码

public class JwtUtils {// token时效:24小时public static final long EXPIRE = 1000 * 60 * 60 * 24;// 签名所需的密钥,对于不同的加密算法来说含义不同public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";/*** 根据用户id和昵称生成token* @param id  用户id* @param nickname 用户昵称* @return JWT规则生成的token*/public static String getJwtToken(String id, String nickname){String JwtToken = Jwts.builder().setHeaderParam("typ", "JWT").setHeaderParam("alg", "HS256").setExpiration(new Date(System.currentTimeMillis() + EXPIRE)).claim("id", id).claim("nickname", nickname)// HS256算法实际上就是MD5加盐值,此时APP_SECRET就代表盐值.signWith(SignatureAlgorithm.HS256, APP_SECRET).compact();return JwtToken;}/*** 判断token是否存在与有效* @param jwtToken token字符串* @return 如果token有效返回true,否则返回false*/public static boolean checkToken(String jwtToken) {if(StringUtils.isEmpty(jwtToken)) return false;try {Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** 判断token是否存在与有效* @param request Http请求对象* @return 如果token有效返回true,否则返回false*/public static boolean checkToken(HttpServletRequest request) {try {// 从http请求头中获取token字符串String jwtToken = request.getHeader("token");if(StringUtils.isEmpty(jwtToken)) return false;Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** 根据token获取会员id* @param request Http请求对象* @return 解析token后获得的用户id*/public static String getMemberIdByJwtToken(HttpServletRequest request) {String jwtToken = request.getHeader("token");if(StringUtils.isEmpty(jwtToken)) return "";Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);Claims claims = claimsJws.getBody();return (String)claims.get("id");}
}

注意:

jjwt在0.10版本以后发生了较大变化,pom依赖要引入多个

<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.2</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.2</version><scope>runtime</scope>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred --><version>0.11.2</version><scope>runtime</scope>
</dependency>

标准规范中对各种加密算法的secretKey的长度有如下要求:

HS256:要求至少 256 bits (32 bytes) HS384:要求至少384 bits (48 bytes)
HS512:要求至少512 bits (64 bytes) RS256 and PS256:至少2048 bits RS384 and
PS384:至少3072 bits RS512 and PS512:至少4096 bits ES256:至少256 bits (32
bytes) ES384:至少384 bits (48 bytes) ES512:至少512 bits (64 bytes)

在jjwt0.10版本之前,没有强制要求,secretKey长度不满足要求时也可以签名成功。但是0.10版本后强制要求secretKey满足规范中的长度要求,否则生成jws时会抛出异常

之前的签名和验签方法都是传入密钥的字符串,已经过时。最新的方法需要传入Key对象。

public class JwtUtils {// token时效:24小时public static final long EXPIRE = 1000 * 60 * 60 * 24;// 签名哈希的密钥,对于不同的加密算法来说含义不同public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHOsdadasdasfdssfeweee";/*** 根据用户id和昵称生成token* @param id  用户id* @param nickname 用户昵称* @return JWT规则生成的token*/public static String getJwtToken(String id, String nickname){String JwtToken = Jwts.builder().setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + EXPIRE)).claim("id", id).claim("nickname", nickname)// 传入Key对象.signWith(Keys.hmacShaKeyFor(APP_SECRET.getBytes(StandardCharsets.UTF_8)), SignatureAlgorithm.HS256).compact();return JwtToken;}/*** 判断token是否存在与有效* @param jwtToken token字符串* @return 如果token有效返回true,否则返回false*/public static Jws<Claims> decode(String jwtToken) {// 传入Key对象Jws<Claims> claimsJws = Jwts.parserBuilder().setSigningKey(Keys.hmacShaKeyFor(APP_SECRET.getBytes(StandardCharsets.UTF_8))).build().parseClaimsJws(jwtToken);return claimsJws;}
}

2.2 非对称签名

生成jwt串的时候需要指定私钥,解析jwt串的时候需要指定公钥

private static final String RSA_PRIVATE_KEY = "...";
private static final String RSA_PUBLIC_KEY = "...";/*** 根据用户id和昵称生成token* @param id  用户id* @param nickname 用户昵称* @return JWT规则生成的token*/
public static String getJwtTokenRsa(String id, String nickname){// 利用hutool创建RSARSA rsa = new RSA(RSA_PRIVATE_KEY, null);RSAPrivateKey privateKey = (RSAPrivateKey) rsa.getPrivateKey();String JwtToken = Jwts.builder().setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + EXPIRE)).claim("id", id).claim("nickname", nickname)// 签名指定私钥.signWith(privateKey, SignatureAlgorithm.RS256).compact();return JwtToken;
}/*** 判断token是否存在与有效* @param jwtToken token字符串* @return 如果token有效返回true,否则返回false*/
public static Jws<Claims> decodeRsa(String jwtToken) {RSA rsa = new RSA(null, RSA_PUBLIC_KEY);RSAPublicKey publicKey = (RSAPublicKey) rsa.getPublicKey();// 验签指定公钥Jws<Claims> claimsJws = Jwts.parserBuilder().setSigningKey(publicKey).build().parseClaimsJws(jwtToken);return claimsJws;
}

9.实际开发中的应用

在实际的SpringBoot项目中,一般我们可以用如下流程做登录:

  • 在登录验证通过后,给用户生成一个对应的随机token(注意这个token不是指jwt,可以用uuid等算法生成),然后将这个token作为key的一部分,用户信息作为value存入Redis,并设置过期时间,这个过期时间就是登录失效的时间
  • 将第1步中生成的随机token作为JWT的payload生成JWT字符串返回给前端
  • 前端之后每次请求都在请求头中的Authorization字段中携带JWT字符串
  • 后端定义一个拦截器,每次收到前端请求时,都先从请求头中的Authorization字段中取出JWT字符串并进行验证,验证通过后解析出payload中的随机token,然后再用这个随机token得到key,从Redis中获取用户信息,如果能获取到就说明用户已经登录
public class JWTInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String JWT = request.getHeader("Authorization");try {// 1.校验JWT字符串DecodedJWT decodedJWT = JWTUtils.decode(JWT);// 2.取出JWT字符串载荷中的随机token,从Redis中获取用户信息...return true;}catch (SignatureVerificationException e){System.out.println("无效签名");e.printStackTrace();}catch (TokenExpiredException e){System.out.println("token已经过期");e.printStackTrace();}catch (AlgorithmMismatchException e){System.out.println("算法不一致");e.printStackTrace();}catch (Exception e){System.out.println("token无效");e.printStackTrace();}return false;}
}

在实际开发中需要用下列手段来增加JWT的安全性:

  • 因为JWT是在请求头中传递的,所以为了避免网络劫持,推荐使用HTTPS来传输,更加安全
  • JWT的哈希签名的密钥是存放在服务端的,所以只要服务器不被攻破,理论上JWT是安全的。因此要保证服务器的安全
  • JWT可以使用暴力穷举来破解,所以为了应对这种破解方式,可以定期更换服务端的哈希签名密钥(相当于盐值)。这样可以保证等破解结果出来了,你的密钥也已经换了

以上。

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

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

相关文章

[ComfyUI进阶教程] 使用IPAdapater FaceID换脸

IPapapter 的团队推出IP-Adapter-FaceID模型,相较于旧模型大幅提高从图片还原人像的相似度。一张照片换脸现在可以更精准了。 使用人脸识别模型中的人脸 ID 嵌入代替 CLIP 图像嵌入,此外,使用 LoRA 来提高 ID 一致性。 IP-Adapter-FaceID 只需文字提示即可生成以人脸为条件的…

【基础篇】九、程序计数器 JVM栈

文章目录 0、运行时数据区域1、程序计数器2、JVM栈3、JVM栈--栈帧--局部变量表4、JVM栈--栈帧--操作数栈5、JVM栈--栈帧--桢数据6、栈溢出7、设置栈空间大小8、本地方法栈 0、运行时数据区域 JVM结构里&#xff0c;类加载器下来&#xff0c;到了运行时数据区域&#xff0c;即Ja…

视图与索引连表查询(内/外联)和子查询

目录 一、视图 1.1、概念&#xff1a; 1.2、场景&#xff1a; 1.3、用视图的意义 1.2、创建(增加)视图 1.3、修改视图 1.4、删除视图 1.5、查看视图 ​编辑 二、索引 2.1、概念 2.2、优缺点 优点&#xff1a; 缺点&#xff1a; 2.3、应用场景 2.4、会失效 2.5、…

JavaWeb——新闻管理系统(Jsp+Servlet)之jsp新闻查询

java-ee项目结构设计 1.dao:对数据库的访问&#xff0c;实现了增删改查 2.entity:定义了新闻、评论、用户三个实体&#xff0c;并设置对应实体的属性 3.filter&#xff1a;过滤器&#xff0c;设置字符编码都为utf8&#xff0c;防止乱码出现 4.service:业务逻辑处理 5.servlet:处…

【WPF】使用 WriteableBitmap 提升 Image 性能

【WPF】使用 WriteableBitmap 提升 Image 性能 前言WriteableBitmap 背景WriteableBitmap 渲染原理WriteableBitmap 使用技巧案例核心源码测试结果 前言 由于中所周不知的原因&#xff0c;WPF 中想要快速的更新图像的显示速率一直以来都是一大难题。在本文中&#xff0c;我将分…

Android学习(一):Android Studio安装与配置

Android学习&#xff08;一&#xff09;&#xff1a;Android Studio安装与配置 一、安装 下载地址 下载zip文件&#xff0c;免安装。 二、下载资源 启动后&#xff0c;出现该弹框&#xff0c;点击Cancel。 点击Next 默认&#xff0c;点击Next。 点击Next。 点击Finish 开始…

性能优化-OpenMP基础教程(二)

本文主要介绍OpenMP并行编程技术&#xff0c;编程模型、指令和函数的介绍、以及OpenMP实战的几个例子。希望给OpenMP并行编程者提供指导。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;高性能&#xff08;HPC&am…

学习Redis缓存

学习Redis缓存 NoSQL和SQL的区别缓存缓存作用缓存成本添加Redis缓存 Redis特征Redis中数据结构Redis通用命令String类型Key的层级格式Hash类型Redis的Java客户端 NoSQL和SQL的区别 缓存 缓存就是数据交换的缓冲区&#xff0c;是存储数据的临时地方&#xff0c;一般读写性比较高…

async和await关键字

目录 async 关键字await 关键字使用 async 和 await 解决回调地狱问题错误处理总结 在JavaScript中&#xff0c; async和 await是用于简化基于 Promise的异步编程的关键字。在ES2017&#xff08;也称为ES8&#xff09;中引入后&#xff0c;它们迅速成为管理异步代码的首选方…

SQL高级:事务

在前面的内容中,我们学习了很多SQL的高级语法,包括窗口函数,存储过程等。在这篇文章中,我们要学习一个很重要的概念,事务。 事务的定义 为了讲清楚事务,很多人拿银行转账来举例,不得不说这真的是一个非常恰当的例子。一个账户要增加对应的金额,另一个账户需要减少对应…

RT-DETR Gradio 前端展示页面

效果展示 使用方法 Gradio 是一个开源库,旨在为机器学习模型提供快速且易于使用的网页界面。它允许开发者和研究人员轻松地为他们的模型创建交互式的演示,使得无论技术背景如何的人都可以方便地试用和理解这些模型。使用Gradio,你只需几行代码就可以生成一个网页应用程序,…

【C程序设计】C函数指针与回调函数

函数指针 函数指针是指向函数的指针变量。 通常我们说的指针变量是指向一个整型、字符型或数组等变量&#xff0c;而函数指针是指向函数。 函数指针可以像一般函数一样&#xff0c;用于调用函数、传递参数。 函数指针变量的声明&#xff1a; typedef int (*fun_ptr)(int,i…

mysql之视图mysql连接案例索引

文章目录 一、视图1.1 含义1.2 操作1.2.1 创建视图1.2.2 视图的修改1.2.3 删除视图1.2.4 查看视图 二、连接案例01)查询" 01 "课程比" 02 "课程成绩高的学生的信息及课程分数02)查询同时存在" 01 "课程和" 02 "课程的情况03&#xff0…

【信息论与编码】习题-判断题-第二部分

目录 判断题 第二部分24. 信道矩阵 代表的信道的信道容量C125. 信源熵具有严格的下凸性。26. 率失真函数对允许的平均失真度具有上凸性。27. 信道编码定理是一个理想编码的存在性定理&#xff0c;即&#xff1a;信道无失真传递信息的条件是信息率小于信道容量 。28. 信道的输出…

修改 Ubuntu 的配置

目录 一、修改地址 1. 修改本机IP 二、修改网关 1. 查看网关地址 2. 设置默认网关 三、重启网络 1. 重启网络 2. 刷新网络 四、修改主机名 1. 查看主机名 2. 修改主机名 一、修改地址 1. 修改本机IP sudo ifconfig en…

【视频图像篇】模糊图像增强技术之视频平均帧处理

【视频图像篇】模糊图像增强技术之视频平均帧处理 0、目录 1、实验环境 2、集成和超级分辨率 3、色彩清晰化 4、翻转 总结 1、实验环境 系统环境Windows 11 专业版&#xff0c;[23H2&#xff08;22631.2715&#xff09;Impress&#xff0c;[v8.0.3.2] 2、集成和超级分辨…

如何使用VsCode编译C语言?

下载VsCode (1) 解压到D盘跟目录 (2) 运行[vscode.reg]&#xff0c;注册右键菜单 (3) 进入[pack]文件夹&#xff0c;运行[install.bat]。安装基本插件。 下载mingw32 (1) 解压任意目录 (2) 我的电脑右键–高级系统设置–高级–环境变量–系统变量–Path(双击)–空白行(双击)–…

MySQL之视图索引执行计划

目录 一.视图 二.执行计划 2.1.什么是执行计划 2.2.执行计划的作用 三.使用外连接、内连接和子查询进行举例 四.思维导图 好啦今天就到这里了哦&#xff01;&#xff01;&#xff01;希望能帮到你哦&#xff01;&#xff01;&#xff01; 一.视图 含义 &#xff1a;在数…

二手买卖、废品回收小程序 在app.json中声明permission scope.userLocation字段 教程说明

处理二手买卖、废品回收小程序 在app.json中声明permission scope.userLocation字段 教程说明 sitemapLocation 指明 sitemap.json 的位置&#xff1b;默认为 ‘sitemap.json’ 即在 app.json 同级目录下名字的 sitemap.json 文件 找到app.json这个文件 把这段代码加进去&…