10.单点登录原理及JWT实现
单点登录原理及JWT实现
一、单点登录效果
首先我们看通过一个具体的案例来加深对单点登录的理解。案例地址:https://gitee.com/xuxueli0323/xxl-sso?_from=gitee_search 把案例代码直接导入到IDEA中

然后分别修改下server和samples中的配置信息


在host文件中配置
127.0.0.1 sso.server.com
127.0.0.1 client1.com
127.0.0.1 client2.com
然后分别启动server和两个simple服务。

访问测试:

其中一个节点登录成功后其他节点就可以访问了

自行测试。
二、单点登录实现
清楚了单点登录的效果后,我们就可以自己来创建一个单点登录的实现了。来加深下单点登录的理解了。
1.创建项目
通过Maven创建一个聚合工程,然后在工程中创建3个子模块,分别为认证服务和客户端模块。

引入相同的依赖

2.client1
我们先在client1中来提供相关的接口。我们提供一个匿名访问的接口和一个需要认证才能访问的接口。
@Controller
public class UserController {@ResponseBody@GetMapping("/hello")public String hello(){return "hello";}@GetMapping("/queryUser")public String queryUser(Model model){model.addAttribute("list", Arrays.asList("张三","李四","王五"));return "user";}
}
user.html中的代码为:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>$Title$</title>
</head>
<body><h1>用户管理:</h1><ul><li th:each="user:${list}">[[${user}]]</li></ul>
</body>
</html>
访问测试:

没有认证就能访问,所以得加上验证的逻辑。
@GetMapping("/queryUser")public String queryUser(Model model, HttpSession session){Object userLogin = session.getAttribute("userLogin");if(userLogin != null){// 说明登录过了,直接放过model.addAttribute("list", Arrays.asList("张三","李四","王五"));return "user";}// 说明没有登录,需要跳转到认证服务器认证 为了能在登录成功后跳回到当前页面,传递参数return "redirect:http://sso.server:8080/loginPage?redirect=http://client1.com:8081/queryUser";}
可以看到当我们访问queryUser请求的时候,因为没有登录所以会重定向到认证服务中的服务,做登录处理。这时就需要进入到server服务中处理
3.server服务
在服务端我们需要提供两个接口,一个调整到登录界面,一个处理认证逻辑以及一个登录页面
@Controller
public class LoginController {/*** 跳转到登录界面的逻辑* @return*/@GetMapping("/loginPage")public String loginPage(@RequestParam(value = "redirect" ,required = false) String url, Model model){model.addAttribute("url",url);return "login";}/*** 处理登录请求* @return*/@PostMapping("/ssoLogin")public String login(@RequestParam("userName") String userName,@RequestParam("password") String password,@RequestParam(value = "url",required = false) String url){if("zhangsan".equals(userName) && "123".equals(password)){// 登录成功return "redirect:"+url;}// 登录失败重新返回登录页面return "redirect:loginPage";}}
登录页面代码逻辑
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>sso-server-login</title>
</head>
<body><h1>Server登录页面</h1><form action="/ssoLogin" method="post" >账号:<input type="text" name="userName" ><br/>密码:<input type="password" name="password"><br/><input type="hidden" name="url" th:value="${url}"><input type="submit" value="提交"></form>
</body>
</html>
然后当我们在client1中访问需要认证的服务的时候就会跳转到登录界面

提交登录操作。当我们提交登录成功的情况,应该要重定向会原来的访问地址,但实际情况和我们所想的有点出入:

原来的queryUser中的逻辑为:

4. 认证凭证
上面的问题是我们在认证服务登录成功了,但是client1中并不知道登录成功了,所以认证成功后需要给client1一个认证成功的凭证。也就是Token信息。
/*** 处理登录请求* @return*/@PostMapping("/ssoLogin")public String login(@RequestParam("userName") String userName,@RequestParam("password") String password,@RequestParam(value = "url",required = false) String url){if("zhangsan".equals(userName) && "123".equals(password)){// 通过UUID生成Token信息String uuid = UUID.randomUUID().toString().replace("-","");// 把生成的信息存储在Redis服务中redisTemplate.opsForValue().set(uuid,"zhangsan");// 登录成功return "redirect:"+url+"?token="+uuid;}// 登录失败重新返回登录页面return "redirect:loginPage";}
生成的Token同步保存在了Redis中,然后在重定向的地址中携带了token信息。然后在client1中处理
@GetMapping("/queryUser")public String queryUser(Model model,HttpSession session,@RequestParam(value = "token",required = false) String token){if(token != null){// token有值 说明认证了// TODO 基于token 去服务器获取用户信息session.setAttribute("userLogin","张三");}Object userLogin = session.getAttribute("userLogin");if(userLogin != null){// 说明登录过了,直接放过model.addAttribute("list", Arrays.asList("张三","李四","王五"));return "user";}// 说明没有登录,需要跳转到认证服务器认证 为了能在登录成功后跳回到当前页面,传递参数return "redirect:http://sso.server.com:8080/loginPage?redirect=http://client1.com:8081/queryUser";}
然后我们就可以来访问client1中的服务了

5. client2
控制器逻辑:
@Controller
public class OrderController {@GetMapping("/order")public String getOrder(HttpSession session, Model model){Object userLogin = session.getAttribute("userLogin");if(userLogin != null){// 说明认证了model.addAttribute("list", Arrays.asList("order1","order2","order3"));return "order";}return "redirect:http://sso.server.com:8080/loginPage?redirect=http://client2.com:8082/order";}
}
order.html页面内容:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>$Title$</title>
</head>
<body><h1>订单管理:</h1><ul><li th:each="order:${list}">[[${order}]]</li></ul>
</body>
</html>
通过前面的介绍我们可以发现clent1认证后可以访问了,但是client2提交请求的时候还是会跳转到server服务,做认证的处理。

造成这个的原因是client1认证成功后在Session中保存了认证信息,但是在client2是获取不到的,这时我们可以在Server服务登录成功后在浏览器的Cookie中存储一个token信息,然后在其他服务跳转到要进入登录页面之前的接口服务中判断Cookie中是否有值,如果有则认为是其他服务登录过的,直接放过。

提交请求的时候校验


搞定
三、JWT实现

1.JWT介绍
1.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 .
JSON Web 令牌(JWT)是一种开放标准(RFC 7519) ,它定义了一种紧凑和自包含的方式,用于作为 JSON 对象在各方之间安全地传输信息。可以验证和信任此信息,因为它是数字签名的。JWTs 可以使用 secret (使用 HMAC 算法)或使用 RSA 或 ECDSA 的公钥/私钥对进行签名。
通俗的解释:JWT简称 JSON Web Token,也就是JSON形式作为Web应用中的令牌信息,用于在各方之间安全的将信息作为JSON对象传输,在数据传输过程中可以完成数据加密,签名等操作。
1.2 基于Session认证
我们最先接触到的认证方式就是基于Session的认证方式,每一个会话在服务端都会存储在HttpSession中,相当于一个Map,然后通过Cookie的形式给客户端返回一个jsessionid,然后每次访问的时候都需要从HttpSession中根据jsessionid来获取,通过这个逻辑来判断是否是认证的状态。

存在的问题:
- 每个用户都需要做一次记录,而Session一般情况下都会存在内存中,增大了服务器的开销
- 集群环境下Session需要同步,或者分布式Session来处理
- 因为是基于Cookie来传输的,如果Cookie被解惑,用户容易受到CSRF攻击。
- 前后端分离项目中会更加的麻烦
1.3 基于JWT的认证
具体流程如下:

认证的流程:
- 用户通过表单把账号密码提交到后端服务后,如果认证成功就会生成一个对应的Token信息
- 之后用户请求资源都会携带这个Token值,后端获取到后校验通过放行,校验不通过拒绝
jwt的优势:
- 简介:可以通过URL,POST参数或者HTTP header发送,因为数据量小,传输速度快。
- 自包含:负载中包含了所有用户所需的信息,避免多次查询数据
- 夸语音:以JSON形式保存在客户端。
- 不需要服务端保存信息,适合分布式环境。
1.4 JWT的结构
令牌的组成:
- 标头(Header)
- 有效载荷(Payload)
- 签名(Signature)
因此JWT的格式为: xxxx.yyyy.zzzz Header.Payload.Signature
Header:
header通常由两部分组成:令牌的类型【JWT】和所使用的签名算法。例如HMAC、SHA256或者RSA,它会使用 Base64 编码组成 JWT结构的第一部分。注意:Base64是一种编码,是可以被翻译回原来的样子的。
{"alg":"HS256","typ":"JWT"
}
Payload:
令牌的第二部分是有效负载,其中包含声明,声明是有关实体(通常是用户信息)和其他数据的声明,它会使用Base64来编码,组成JWT结构的第二部分。
{"userId":"123","userName":"波波烤鸭","admin":true
}
因为会通过Base64编码,所以不要把敏感信息写在Payload中。
Signature:
签名部分,前面两部分都是使用 Base64 进行编码的,即前端可以解开header和payload中的信息,Signature需要使用编码后的 header 和 payload 以及我们提供的一个秘钥,然后使用 header 中指定的前面算法(HS256) 进行签名,签名的作用是保证 JWT 没有被篡改过

2.JWT实现
2.1 JWT基本实现
生成Token令牌
/*** 生成Token信息*/@Testvoid generatorToke() {Map<String,Object> map = new HashMap<>();map.put("alg","HS256");map.put("typ","JWT");Calendar calendar = Calendar.getInstance();calendar.add(Calendar.SECOND,60);String token = JWT.create().withHeader(map) // 设置header.withClaim("userid", 666) // 设置 payload// 设置过期时间.withExpiresAt(calendar.getTime()).withClaim("username", "波波烤鸭") // 设置 payload.sign(Algorithm.HMAC256("qwaszx")); // 设置签名 保密System.out.println(token);}
根据Token来验证是否正确。
/*** 验证Token信息*/@Testpublic void verifier(){String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NTMwNTE5ODUsInVzZXJpZCI6NjY2LCJ1c2VybmFtZSI6IuazouazoueDpOm4rSJ9.0LW5MFihMeYNfRfez0a68ncaKQ13j5pSnVZTB7m1CDw";JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("qwaszx")).build();DecodedJWT verify = jwtVerifier.verify(token);System.out.println(verify.getClaim("userid").asInt());System.out.println(verify.getClaim("username").asString());}
验证中场景的异常信息:
- SignatureVerificationException 签名不一致异常
- TokenExpiredException Token过期异常
- AlgorithmMismatchException 算法不匹配异常
- InvalidClaimException 失效的payload异常
2.2 JWT封装
为了简化操作我们可以对上面的操作进一步封装来简化处理
package com.bobo.jwt.utils;import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;import java.util.Calendar;
import java.util.Map;/*** JWT操作的工具类*/
public class JWTUtils {private static final String SING = "123qwaszx";/*** 生成Token header.payload.sing 组成* @return*/public static String getToken(Map<String,String> map){Calendar instance = Calendar.getInstance();instance.add(Calendar.DATE,7); // 默认过期时间 7天JWTCreator.Builder builder = JWT.create();// payload 设置map.forEach((k,v)->{builder.withClaim(k,v);});// 生成Token 并返回return builder.withExpiresAt(instance.getTime()).sign(Algorithm.HMAC256(SING));}/*** 验证Token* @return* DecodedJWT 可以用来获取用户信息*/public static DecodedJWT verify(String token){// 如果不抛出异常说明验证通过,否则验证失败return JWT.require(Algorithm.HMAC256(SING)).build().verify(token);}
}
2.3 SpringBoot应用
首先是在登录方法中,如果登录成功,我们需要生成对应的Token信息,然后将Token信息响应给客户端。
@PostMapping("/login")public Map<String,Object> login(User user){Map<String,Object> res = new HashMap<>();if("zhang".equals(user.getUserName()) && "123".equals(user.getPassword())){// 登录成功Map<String,String> map = new HashMap<>();map.put("userid","1");map.put("username","zhang");String token = JWTUtils.getToken(map);res.put("flag",true);res.put("msg","登录成功");res.put("token",token);return res;}res.put("flag",false);res.put("msg","登录失败");return res;}

然后就是用户提交请求的时候需要携带Token信息,然后我们在controller中处理请求之前需要对token做出校验。如果验证通过就继续处理请求,否则就拦截该请求。
@PostMapping("/queryUser")public Map<String,Object> queryUser(@RequestParam("token") String token){// 获取用信息之前校验Map<String,Object> map = new HashMap<>();try{DecodedJWT verify = JWTUtils.verify(token);map.put("state",true);map.put("msg","请求成功");return map;}catch (SignatureVerificationException e){e.printStackTrace();map.put("msg","无效签名");}catch (TokenExpiredException e){e.printStackTrace();map.put("msg","Token过期");}catch (AlgorithmMismatchException e){e.printStackTrace();map.put("msg","算法不一致");}catch (Exception e){e.printStackTrace();map.put("msg","Token无效");}map.put("state",false);return map;}

但是上面的情况我们看到在controller中添加了大幅度的Token校验的代码,增大的冗余代码,这时我们可以考虑把Token校验的代码放在拦截器中处理。我们创建一个自定义的拦截器.
/*** 自定义的拦截器* 对特定的情况校验是否携带的有Token信息,如果不携带直接拒绝* 然后对Token校验合法性*/
public class JWTInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getParameter("token");// 获取用信息之前校验Map<String,Object> map = new HashMap<>();try{DecodedJWT verify = JWTUtils.verify(token);return true;}catch (SignatureVerificationException e){e.printStackTrace();map.put("msg","无效签名");}catch (TokenExpiredException e){e.printStackTrace();map.put("msg","Token过期");}catch (AlgorithmMismatchException e){e.printStackTrace();map.put("msg","算法不一致");}catch (Exception e){e.printStackTrace();map.put("msg","Token无效");}map.put("state",false);// 把Map转换为JSON响应String json = new ObjectMapper().writeValueAsString(map);response.setContentType("application/json;charset=UTF-8");response.getWriter().println(json);return false;}
}
要让拦截器生效我们还需要添加对应的配置类。
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new JWTInterceptor()).addPathPatterns("/queryUser") // 需要拦截的请求.addPathPatterns("/saveUser") // 需要拦截的请求.excludePathPatterns("/login"); // 需要排除的请求}
}
然后添加一个测试的方法 /saveUser
@PostMapping("/saveUser")public String saveUser(){System.out.println("------------>");return "success";}
测试访问,把过期时间缩短到1分钟

正常的访问

Token过期后再访问

搞定
相关文章:
10.单点登录原理及JWT实现
单点登录原理及JWT实现 一、单点登录效果 首先我们看通过一个具体的案例来加深对单点登录的理解。案例地址:https://gitee.com/xuxueli0323/xxl-sso?_fromgitee_search 把案例代码直接导入到IDEA中 然后分别修改下server和samples中的配置信息 在host文件中配置 …...
图表控件LightningChart.NET 系列教程(十一):LightningChart 组件——添加至 Blend WPF 项目
LightningChart.NET 是一款高性能 WPF 和 Winforms 图表,可以实时可视化多达1万亿个数据点。可有效利用CPU和内存资源,实时监控数据流。同时,LightningChart使用突破性创新技术,以实时优化为前提,大大提升了实时渲染的效率和效果&…...
libGDX:灯光效果实现一(实现一个点光源)
国内的libGDX文章很少,特别是libGDX实现灯光效果,所以就开始总结灯光效果的实现 绿色的框 是为了方便看到Body位置,使用Box2DDebugRenderer渲染的 工欲善其事,必先利其器,工具集合 gdx-setup.jar 1. 从libGDX官网下载…...
Java生态/Redis中如何使用Lua脚本
文章目录一、安装LUA1)简单使用二、lua语法简介1、注释1)单行注释2)多行注释2、关键字3、变量1)全局变量2)局部变量4、数据类型1)Lua数组2)字符串操作5、if-else6、循环1)for循环1&g…...
网络编程 socket 编程(一)
1. C/S 架构 C/S 架构即客户端/服务端架构,B/S 架构(浏览器与服务端)也是 C/S 架构的一种。 C/S 架构与 socket 的关系:学习 socket 可以完成 C/S 架构的开发。 2. osi 七层 一个完整的计算机系统由硬件、操作系统以及应用软件…...
【SpringCloud】SpringCloud教程之Nacos实战(一)
目录Nacos是什么?一.Nacos下载二.安装Nacos三.Nacos原理四.Nacos快速入门五.Nacos服务多级存储模式六.Nacos根据集群设置负载均衡1.根据同集群优先访问2.根据权重配置负载均衡七.Nacos的环境隔离八.Nacos和Eureka的区别前提:以订单服务和用户服务为例&am…...
高通Android 12/13 默认应用程序授予权限
1、一提到权限很多Android开发者都会想到 比如拨打电话 读取手机通讯录 定位 这些都是需要申请权限,Google Android 6.0之后(sdk 23) 需要app动态申请权限 或者权限组 2、我这里打个比方 比如需要在fm应用 默认打开mic权限 3、我们需要知道…...
代码随想录|day6|哈希表篇-- 242.有效的字母异位词 、349. 两个数组的交集 、202. 快乐数、1. 两数之和
总链接https://docs.qq.com/doc/DUEtFSGdreWRuR2p4?u329948d2f0044f34b7cbe72503f0b572 242.有效的字母异位词 链接:代码随想录 class Solution { public:bool isAnagram(string s, string t) {//两种做法,一种是int f[26]的数组,一种是map /*第一种&a…...
k8s学习之路 | Day20 k8s 工作负载 Deployment(下)
文章目录3. HPA 动态扩缩容3.1 HPA3.2 安装 metrics-server3.3 验证指标收集3.4 扩缩容的实现3.5 增加负载3.6 降低负载3.7 更多的度量指标4. 金丝雀部署4.1 蓝绿部署4.2 金丝雀部署4.3 金丝雀部署的实现5. Deployment 状态与排查5.1 进行中的 Deployment5.2 完成的 Deployment…...
考研复试——操作系统
文章目录操作系统1. 操作系统的特征:2. 进程与线程的关系以及区别3. 简述进程和程序的区别4. 进程的常见状态?以及各种状态之间的转换条件?5. 进程的调度算法有哪些?6. 什么是死锁?产生条件?如何避免死锁&a…...
Java ~ Collection/Executor ~ LinkedBlockingDeque【源码】
一 LinkedBlockingDeque(链接阻塞双端队列)类源码及机制详解 类 LinkedBlockingDeque(链接阻塞双端队列)类(下文简称链接阻塞双端队列)是BlockingDeqeue(阻塞双端队列)接口的唯一实现…...
【前缀和】截断数组、K倍区间、激光炸弹
Halo,这里是Ppeua。平时主要更新C语言,C,数据结构算法......感兴趣就关注我吧!你定不会失望。 🌈个人主页:主页链接 🌈算法专栏:专栏链接 我会一直往里填充内容哒! &…...
函数编程:强大的 Stream API
函数编程:强大的 Stream API 每博一文案 只要有人的地方,世界就不会是冰冷的,我们可以平凡,但绝对不可以平庸。—————— 《平凡的世界》人活着,就得随时准备经受磨难。他已经看过一些书,知道不论是普通…...
企业架构图之业务架构图
在TOGAF的世界里面,所有的架构思想都可以通过下面三种类型的图形进行表示。 目录(Catalogs)矩阵(Matrix)图 (Diagram) 其架构图的本质就是用来进行沟通交流,通过架构图和业务团队进…...
监控易网络管理:网络流量分析
1、什么是网络流量分析2、网络流量分析的作用3、为什么要用网络流量分析功能,如何开启什么是网络流量分析简单的来说,网络流量分析就是捕捉网络中流动的数据包,并通过查看包内部数据以及进行相关的协议、流量、分析、统计等,协助发…...
RHCSA-文件内容显示(3.6)
查看命令 cat:显示文件内容 cat -n:显示文件内容的同时显示编号 tac:倒叙查看 head 文件名 (默认显示前10行):显示前10行 tail:显示末尾行数信息 more:查看文件信息,从头…...
Qt多线程文件查找器
⭐️我叫恒心,一名喜欢书写博客的研究生在读生。 原创不易~转载麻烦注明出处,并告知作者,谢谢!!! 这是一篇近期会不断更新的博客欧~~~ 有什么问题的小伙伴 欢迎留言提问欧。 Qt多线程文件查找器 前言 最近在实现一些代码功能的时候,需要找一些多线程样例来学习,于是就…...
源码阅读笔记 InputFormat、FileInputFormat、CombineTextInputFormat
1. InputFormat InputFormat是MapReduce框架提供的用来处理job输入的基类 它主要定义了三个功能: 1.验证job输入是否合法 2.对输入文件进行逻辑切片(InputSplit),然后将每个切片分发给单独的MapTask 3.提供切片读取器(Re…...
二值图像骨架线提取
二值图像骨架线提取HilditchThin算法Rosenfeld算法OpenCV_Contrib中的算法示例其他细化算法查表法HilditchThin的另一种算法参考二值图像骨架线提取算法:HilditchThin算法、Rosenfeld算法、OpenCV_Contrib中的算法 HilditchThin算法 1、使用的8邻域标记为ÿ…...
规划数据指标体系方法(上)——OSM 模型
之前我已经有写过文章讲了数据指标体系的搭建思路,但有同学还是不太清楚要从何入手,今天我就来跟大家讲一讲搭建数据指标体系之前第一步要先做的事情——规划数据指标体系。 规划数据指标体系,在业内有三种比较常见的方法,分别是&…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...
