SpringBoot整合JWT示例教程

1. JWT简介

JSON Web Token (JWT) 是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间作为 JSON 对象安全地传输信息。由于这些信息是经过数字签名的,因此可以被验证和信任。JWT 通常用于身份验证和信息交换场景,特别是在 Web 应用程序的认证和授权机制中。

组成
JWT 由三部分组成:Header、Payload 和 Signature。这三部分分别用点(.)分隔,形成一个字符串。

Header(头部):
Header 通常由两部分组成:令牌的类型(JWT)和所使用的签名算法(例如,HMAC SHA256 或 RSA)。

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

这个 JSON 对象被 Base64Url 编码后,形成 JWT 的第一部分。
Payload(负载):

  • Payload 包含声明(claims),声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型:
  • Registered claims(注册声明):预定义的声明,如 iss(发行者)、exp(过期时间)、sub(主题)、aud(受众)。
  • Public claims(公共声明):可以自由定义的声明,但为了避免冲突,建议在 IANA JSON Web Token Claims 注册表中注册或使用 URI 作为声明名称的前缀。
  • Private claims(私有声明):自定义的声明,用于共享信息,比如用户角色、权限等。
    例:
{"sub": "1234567890","name": "John Doe","admin": true
}

这个 JSON 对象也被 Base64Url 编码后,形成 JWT 的第二部分。
Base64编码方式是可逆的,也就是透过编码后发放的Token内容是可以被解析的。一般而言不建议在Payload放敏感讯息,比如使用者的密码。
Signature(签名):
签名部分用于验证消息在传输过程中未被篡改。
首先,需要指定一个密钥,然后使用指定的签名算法对编码后的 Header 和 Payload 以及一个密钥进行签名。签名的过程实际上是对头部以及负载内容进行签名,防止内容被窜改。如果有人对头部以及负载的内容解码之后进行修改,再进行编码,最后加上之前的签名组合形成新的JWT的话,那么服务器端会判断出新的头部和负载形成的签名和JWT附带上的签名是不一样的。如果要对新的头部和负载进行签名,在不知道服务器加密时用的密钥的话,得出来的签名也是不一样的。

例子:

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret
)

生成的签名也被 Base64Url 编码,形成 JWT 的第三部分。

基于JWT的认证流程
在这里插入图片描述

  • 前端通过Web表单将自己的用户名和密码发送到后端的接口。该过程一般是HTTP的POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探;
  • 后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT(Token);
  • 后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage(浏览器本地缓存)或cookie上,退出登录时前端删除保存的JWT即可;
  • 前端在每次请求时将JWT放入HTTP的Header中的Authorization位。(解决XSS和XSRF问题)HEADER
  • 后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确﹔检查Token是否过期等;
  • 验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果。

注:Base64编码方式是可逆的,也就是透过编码后发放的Token内容是可以被解析的。一般而言,不建议在有效载荷内放敏感讯息,比如使用者的密码。

2. 准备工作

环境:JDK8 + SpringBoot2.6.13

2.1 生成秘钥对

需要使用到jdk的keytool工具,在jdk安装目录的bin目录内,在cmd控制窗口执行JDK中keytool的命令:

keytool -genkeypair -alias test -keyalg RSA -keysize 2048 -validity 365 -keystore test.jks -storepass test123 -keypass test123 -dname "CN=Sakura, OU=xxb, O=ncu, L=nc, ST=JX, C=CN"

参数解释

  • genkeypair: 生成一个密钥对(包括公钥和私钥)。
  • alias test: 为生成的密钥对指定一个别名 test。别名是用来识别密钥条目的。
  • keyalg RSA: 指定密钥对的算法为 RSA。RSA 是一种常用的公钥加密算法。
  • keysize 2048: 指定密钥的大小为 2048 位。密钥越长,安全性越高,但性能开销也越大。
  • validity 365: 指定证书的有效期为 365 天。
  • keystore test.jks: 指定密钥库文件的名称为 test.jks。如果文件不存在,keytool 会创建一个新的文件。
  • storepass test123: 指定密钥库的密码为 test123。这是保护整个密钥库的密码。
  • keypass test123: 指定密钥的密码为 test123。这是保护单个密钥条目的密码。
  • dname “CN=Sakura, OU=xxb, O=ncu, L=nc, ST=JX, C=CN”: 指定证书的详细信息,依次为名字与姓氏,组织单位,城市,区县,国家代码,使用逗号分隔的格式。

执行完命令后,会警告:

JKS 密钥库使用专用格式。建议使用 keytool -importkeystore -srckeystore test.jks -destkeystore test.jks -deststoretype pkcs12迁移到行业标准格式 PKCS12。
执行下上述命令即可:

keytool -importkeystore -srckeystore test.jks -destkeystore test.jks -deststoretype pkcs12

最后,将生成的test.jks文件放到springboot的resources目录(即类路径下)。

2.2 SpringBoo项目配置

项目目录如下:
在这里插入图片描述
maven 依赖:

server:port: 9000  # 服务端口# 自定义JWT配置
<dependencies><!-- web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 注解执行器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><!-- validation --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- hutool --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.11</version></dependency><!--加密--><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-crypto</artifactId></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-rsa</artifactId><version>1.0.9.RELEASE</version></dependency></dependencies>

application.yml配置文件:

server:port: 9000  # 服务端口# 自定义JWT配置
app:jwt:location: classpath:test.jks  # JWT密钥存放位置,classpath为resource文件夹alias: test  # 别名password: test123  # 密码tokenTTL: 30m  # Token有效期为30minauth:excludePaths: # 排除的路径,不需要认证的路径- /auth/login

JwtApplication .java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@SpringBootApplication
@EnableConfigurationProperties
@ConfigurationPropertiesScan("com.jwt.demo.config")
public class JwtApplication {public static void main(String[] args) {SpringApplication.run(JwtApplication.class, args);}}

AuthProperties.java

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;import java.util.List;@Data
@ConfigurationProperties(prefix = "app.auth")
public class AuthProperties {/**** 指定需要拦截的请求路径*/private List<String> includePaths;/*** 指定需要放行的请求路径*/private List<String> excludePaths;
}

JwtProperties .java

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.Resource;import java.time.Duration;@Data
@ConfigurationProperties(prefix = "app.jwt")
public class JwtProperties {private Resource location;private String password;private String alias;private Duration tokenTTL = Duration.ofMinutes(10);}

SecurityConfig .java

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;import java.security.KeyPair;@Configuration
@EnableConfigurationProperties(JwtProperties.class)
public class SecurityConfig {@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}/*** 根据配置文件读取jks文件的密钥对*/@Beanpublic KeyPair keyPair(JwtProperties properties){// 获取秘钥工厂KeyStoreKeyFactory keyStoreKeyFactory =new KeyStoreKeyFactory(properties.getLocation(),properties.getPassword().toCharArray());//读取钥匙对return keyStoreKeyFactory.getKeyPair(properties.getAlias(),properties.getPassword().toCharArray());}
}

JwtTool .java

import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTValidator;
import cn.hutool.jwt.signers.JWTSigner;
import cn.hutool.jwt.signers.JWTSignerUtil;
import com.jwt.demo.constants.UserConstants;
import com.jwt.demo.exception.UnauthorizedException;
import org.springframework.stereotype.Component;import java.security.KeyPair;
import java.time.Duration;
import java.util.Date;@Component
public class JwtTool {private final JWTSigner jwtSigner;public JwtTool(KeyPair keyPair) {this.jwtSigner = JWTSignerUtil.createSigner(UserConstants.ALGORITHM, keyPair);}/*** 创建 access-token** @param userId 用户id* @param ttl    有效时间* @return access-token*/public String createToken(Long userId, Duration ttl) {// 1.生成jwsreturn JWT.create().setPayload(UserConstants.PAY_LOAD, userId) // 设置载荷.setExpiresAt(new Date(System.currentTimeMillis() + ttl.toMillis()))    // 设置过期时间.setSigner(jwtSigner).sign();}/*** 解析token** @param token token* @return 解析刷新token得到的用户信息*/public Long parseToken(String token) {// 1.校验token是否为空if (token == null) {throw new UnauthorizedException("未登录");}// 2.校验并解析jwtJWT jwt;try {jwt = JWT.of(token).setSigner(jwtSigner);} catch (Exception e) {throw new UnauthorizedException("无效的token", e);}// 2.校验jwt是否有效if (!jwt.verify()) {// 验证失败throw new UnauthorizedException("无效的token");}// 3.校验是否过期try {JWTValidator.of(jwt).validateDate();} catch (ValidateException e) {throw new UnauthorizedException("token已经过期");}// 4.数据格式校验Object userPayload = jwt.getPayload(UserConstants.PAY_LOAD);if (userPayload == null) {// 数据为空throw new UnauthorizedException("无效的token");}// 5.数据解析try {Long userId = Long.valueOf(userPayload.toString());return userId;} catch (RuntimeException e) {// 数据格式有误throw new UnauthorizedException("无效的token");}}
}

UserConstants .java

/*** 常量类** @date 2024-07-12 11:27*/
public interface UserConstants {/*** JWT 载荷字段*/String PAY_LOAD = "user";/*** 加密算法RSA256*/String ALGORITHM = "rs256";/*** token对应的请求头字段名称*/String AUTHORAZATION = "authorization";
}

3. 登录拦截器

编写登录拦截器逻辑,并注入到Spring的拦截器链。
AuthInterceptor .java

import cn.hutool.core.text.AntPathMatcher;
import cn.hutool.core.util.StrUtil;
import com.jwt.demo.config.AuthProperties;
import com.jwt.demo.constants.UserConstants;
import com.jwt.demo.exception.UnauthorizedException;
import com.jwt.demo.utils.JwtTool;
import com.jwt.demo.utils.UserContext;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Component
@RequiredArgsConstructor
public class AuthInterceptor implements HandlerInterceptor {// 采用构造器注入的方式注入配置类private final AuthProperties authProperties;private final JwtTool jwtTool;private final AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 判断是否需要进行登录拦截if (isExcludedPath(request.getRequestURI())) {// 若不需要登录拦截,则放行return true;}// 若需要登录拦截,则获取tokenString header = String.valueOf(request.getHeader(UserConstants.AUTHORAZATION));String token = null;// 判断token是否存在if (StrUtil.isNotBlank(header)) {token = header;}// 校验并解析tokenLong userId = null;try {userId = jwtTool.parseToken(token);} catch (UnauthorizedException e) {// 拦截该请求response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.setContentType("application/json;charset=UTF-8");response.getWriter().write("{\"error\": \"Unauthorized\", \"message\": \"" + e.getMessage() + "\"}");return false; // 返回false以阻止请求的进一步处理}// 传递用户信息,放置在ThreadLocal中UserContext.setUser(userId);// 放行该请求return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 清空ThreadLocalUserContext.removeUser();}/*** 判断是否需要进行登录拦截** @param path 当前请求的路径* @return 是否是不需要登录拦截的路径*/private boolean isExcludedPath(String path) {// 判断是否是不需要登录拦截的路径for (String excludePath : authProperties.getExcludePaths()) {// 选择antPathMatcher实现路径匹配if (antPathMatcher.match(excludePath, path)) {return true;}}return false;}
}

MvcConfig .java

import com.jwt.demo.interceptor.AuthInterceptor;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** MVC配置** @author: hong.jian* @date 2024-03-02 20:02*/
@Configuration
@ConditionalOnClass(DispatcherServlet.class)
@RequiredArgsConstructor
public class MvcConfig implements WebMvcConfigurer {private final AuthInterceptor authInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 将自定义的拦截器进行注册registry.addInterceptor(authInterceptor);}
}

编写控制器AuthController
AuthController.java

import com.jwt.demo.config.JwtProperties;
import com.jwt.demo.domain.dto.LoginFormDTO;
import com.jwt.demo.domain.po.User;
import com.jwt.demo.domain.vo.UserLoginVO;
import com.jwt.demo.utils.JwtTool;
import com.jwt.demo.utils.UserContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author: hong.jian* @date 2024-07-12 10:36*/
@RestController
@RequiredArgsConstructor
@Slf4j
@RequestMapping("/auth")
public class AuthController {private final JwtTool jwtTool;private final JwtProperties jwtProperties;/*** 用户登录后生成token** @param loginDTO 登录表单* @return 含token的用户信息*/@PostMapping("/login")public UserLoginVO login( LoginFormDTO loginDTO) {// 1.获取表单信息(省略)String username = loginDTO.getUsername();String password = loginDTO.getPassword();// 2. 登录逻辑校验(需要对接DB,这里使用静态数据模拟)User user = User.builder().id(111L).username("Sakura").build();// 3.生成TOKENString token = jwtTool.createToken(user.getId(), jwtProperties.getTokenTTL());// 4.封装VO返回UserLoginVO vo = UserLoginVO.builder().userId(user.getId()) // 用户id.username(user.getUsername())   // 用户名.token(token)   // token.build();log.info("UserLoginVO:{}", vo);return vo;}/*** 用户登录后生成token* 测试接口*/@GetMapping("/test")public void test() {// 直接从ThreadLocal获取用户信息log.info("userId:{}", UserContext.getUser());}}

4. 测试

控制台日志:

2024-07-12 21:29:51.329  INFO 25212 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 9000 (http) with context path ''
2024-07-12 21:29:51.335  INFO 25212 --- [  restartedMain] com.jwt.demo.JwtApplication              : Started JwtApplication in 2.352 seconds (JVM running for 3.21)
2024-07-12 21:29:51.710  INFO 25212 --- [nio-9000-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-07-12 21:29:51.710  INFO 25212 --- [nio-9000-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2024-07-12 21:29:51.711  INFO 25212 --- [nio-9000-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
2024-07-12 21:29:58.489  INFO 25212 --- [nio-9000-exec-9] com.jwt.demo.controller.AuthController   : UserLoginVO:UserLoginVO(token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VyIjoxMTEsImV4cCI6MTcyMDc5Mjc5OH0.KjmVdTh1RYUOZ_okycZJoj86qkfqlRuSPrwmjMNYS2uS0IwzM1Ab2D4m53F6z4x2zZxEt4aReC-Rnb_HpAx1uj0-unxAlsbe5mW9ok1GhtWp7EuW0k1rgQRA0nx6DUPwUmxhOXIyM9tdJsN0Sae5KQ5mimKORtB6n-VhIDo-cKqdTvtwKUVSbSiCHoQRryUBI2333TjdwkrYg2o-Fdwt80LkHxWOwoGelqmThDlvIvY-Nfkb0-EFIq1IlA027QBN3-TJdohy_3ATWWXOS1h4zuNzTzeN_ML4BZI-SWa2EajQl1eBpgYWZttWTcduV2WGDhsH-zsafC2IvW9tpz6b3A, userId=111, username=Sakura)
2024-07-12 21:30:49.347  INFO 25212 --- [io-9000-exec-10] com.jwt.demo.controller.AuthController   : userId:111

在这里插入图片描述
登录后请求需要带上token
在这里插入图片描述

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

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

相关文章

openharmony上传图片,并获取返回路径

适用条件&#xff1a; openharmony开发 4.0 release版本&#xff0c;对应能力API10 一直不断尝试&#xff0c;一会用官方提供的上传文件&#xff0c;一会用第三方库的axios都不行&#xff0c; 一会报错‘没权限&#xff0c;一会报错’路径错误&#xff0c;还有报错‘401参数错…

本地部署,去除动漫图像背景Anime Remove Background

目录 摘要 引言 深度学习在动漫角色中的应用 1.​U-Net 2.Mask R-CNN 3.ISNet 模型 4.MODNet 模型 5.InSPyReNet 模型 本地部署 运行效果 测验结果​ Tip&#xff1a; 摘要 动漫图像背景去除是一项在图像处理和计算机视觉领域具有重要应用的技术&#xff0c;广泛应用于…

奇舞周刊第533期:单点登录(SSO)实现详解

奇舞推荐 ■ ■ ■ 单点登录&#xff08;SSO&#xff09;实现详解&#xff01;&#xff01; 单点登录是什么&#xff1f;你是怎么理解的&#xff1f;单点登录是如何实现的。 纯前端怎么实现检测版本更新&#xff0c;请看这篇&#xff01; 在传统的多页Web应用中&#xff0c;每次…

CSS实现table表格:隔行换色的效果

表格是网页中十分重要的组成元素。表格用来存储数据&#xff0c;包含标题、表头、行和单元格。在HTML语言中&#xff0c;表格标记使用符号<table>表示。定义表格光使用<table>是不够的&#xff0c;还需要定义表格中的行、列、标题等内容。推荐博文&#xff1a;《HT…

Mac M1安装配置Hadoop+Flink SQL环境

Flink 1.18.1 Hadoop 3.4.0 一、准备工作 系统&#xff1a;Mac M1 (MacOS Sonoma 14.3.1) JDK&#xff1a;jdk1.8.0_381 &#xff08;注意&#xff1a;尽量一定要用JDK8&#xff0c;少用高版本&#xff09; Scala&#xff1a;2.12 JDK安装在本机的/opt/jdk1.8.0_381.jdk/C…

Uniapp鸿蒙项目实战

Uniapp鸿蒙项目实战 24.7.6 Dcloud发布了uniapp兼容鸿蒙的文档&#xff1a;Uniapp开发鸿蒙应用 在实际使用中发现一些问题&#xff0c;开贴记录一下 设备准备 windows电脑准备&#xff08;家庭版不行&#xff0c;教育版、企业版、专业版也可以&#xff0c;不像uniapp说的只有…

html5——CSS3_文本样式属性

目录 字体样式 字体类型 字体大小 字体风格 字体的粗细 文本样式 文本颜色 排版文本段落 文本修饰和垂直对齐 文本阴影 字体样式 字体类型 p{font-family:Verdana,"楷体";} body{font-family: Times,"Times New Roman", "楷体";} …

html5——CSS高级选择器

目录 属性选择器 E[att^"value"] E[att$"http"] E[att*"http"] 关系选择器 子代&#xff1a; 相邻兄弟&#xff1a; 普通兄弟&#xff1a; 结构伪类选择器 链接伪类选择器 伪元素选择器 CSS的继承与层叠 CSS的继承性 CSS的层叠性 …

华为HCIP Datacom H12-821 卷37

1.多选题 下面关于Network- Summary-LSA 描述正确的是 A、Network- Summary-LSA中的Metric被设置成从该ABR到达目的网段的开销值 B、Network- Sumary-LSA中的Net mask 被设置成目的网段的网络掩码 C、Network- Summary-LSA 是由ASBR产生的 D、Network- Summary-LSA 中的Li…

php安装Imagick扩展 处理pdf为图片

这个方法是使用源码编译安装&#xff0c;适用于php编译安装和包安装。如果有pecl&#xff0c;直接安装就行&#xff0c;我这是因为多个环境怕直接使用pecl工具导致混乱。 由于浏览器显示大量pdf不方便&#xff0c;我这先将pdf转化为图片再显示 如果没有安装php&#xff0c;这是…

网络层的角色与重要性:互联网通信的关键

本章讨论网络层及网络互连问题&#xff0c;也就是讨论多个网络通过路由器互连成为一个互连网络的各种问题。在介绍网络层提供的两种不同服务后&#xff0c;我们开始讲解本章的核心内容——网际协议&#xff08;IP&#xff09;&#xff0c;这是本书的一项重点内容。只有较深入地…

登录/注册

目录 1.HTML 2.CSS 3.JS 4.资源 5.运行结果 6.下载链接 7.注意事项 1.HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-sca…

Zabbix配置JAVA JMX监控

JAVA JMX监控简介 官方文档&#xff1a;https://www.zabbix.com/documentation/current/zh/manual/concepts/java Zabbix Java gateway以 Zabbix 守护进程方式原生支持监控 JMX 应用程序。Zabbix Java gateway 的守护进程是用 Java 编写。为了在特定主机上找到 JMX 计数器的值…

Java设计模式的7个设计原则

Java设计模式的7个设计原则是面向对象设计领域中的重要指导方针&#xff0c;它们旨在提高软件系统的可维护性、可扩展性、可复用性和灵活性。以下是这7个设计原则的详细解释&#xff1a; 1. 开闭原则&#xff08;Open-Closed Principle, OCP&#xff09; 定义&#xff1a;一个…

LCM通讯的使用

本文主要介绍LCM通讯的基本使用&#xff0c;内容主要整理自官网 https://lcm-proj.github.io/lcm/index.html LCM&#xff0c;即Library for Communication and Marshalling&#xff0c;是一组用于消息传递与数据封装的库和工具&#xff0c;它主要的面向对象是要求高带宽、低延…

Cesium--获取当前相机中心与地面的射线焦点

本文记录获取当前相机中心与地面的射线焦点的方法&#xff0c;可用于视角缩放过程中&#xff0c;控制视角自动平滑切换到二维等场景&#xff1a; 方法一定是视角中心能与地面有交集&#xff0c;如果对着地平线或对着天空肯定是没效果的。直接放代码&#xff1a; //调整相机到正…

Objective-C 自定义渐变色Slider

文章目录 一、前情概要二、具体实现 一、前情概要 系统提供UISlider&#xff0c;但在开发过程中经常需要自定义&#xff0c;本次需求内容是实现一个拥有渐变色的滑动条&#xff0c;且渐变色随着手指touch的位置不同改变区域&#xff0c;类似如下 可以使用CAGradientLayer实现渐…

利用宝塔安装一套linux开发环境

更新yum&#xff0c;并且更换阿里镜像源 删除yum文件 cd /etc/yum.repos.d/ 进入yum核心目录 ls sun.repo rm -rf * 删除之前配置的本地源 ls 配置阿里镜像源 wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo 配置扩展包 wge…

paloalto防火墙CLI修改MGT IP

怎么样通过Cli修改MGT口的IP、掩码、网关、DNS呢&#xff1f; 1&#xff09;console连接上CLi&#xff0c;输入configure进入系统视图 输入exit&#xff0c;退出到用户视图 2&#xff09;在CLI修改带外管理MGT的IP地址、掩码、网关、DNS&#xff0c;默认带外管理是开启https、…

[Elasticsearch]ES近似实时搜索的原因|ES非实时搜索的原因|ES Near real-time search

Elasticsearch-专栏&#x1f448;️ 往期回顾&#xff1a; ES单一查询定义&#x1f448;️ ES深分页问题&#x1f448;️ ES商城搜索实战&#x1f448;️ ES环境搭建:单节点模式/集群模式&#x1f448;️ ES开启认证&#x1f448;️ 近似实时搜索&#xff08;Near real-t…