微信公众号开发——实现用户微信网页授权流程

news/2024/5/20 3:09:30/文章来源:https://blog.csdn.net/zhuocailing3390/article/details/128288957
😊 @ 作者: 一恍过去
💖 @ 主页: https://blog.csdn.net/zhuocailing3390
🎊 @ 社区: Java技术栈交流
🎉 @ 主题: 微信公众号开发——实现用户微信网页授权流程
⏱️ @ 创作时间: 2022年12月16日

目录

  • 准备工作
  • 授权说明
  • yaml配置
  • 接口常量定义
  • 定义工具类
  • 用户授权获取Code
  • 通过Code 换取授权access_token及用户信息
  • 获取用户信息
  • 流程测试

准备工作

1、在本地进行联调时,为让微信端能够访问到本地服务,需要进行内网穿透,参考《本地服务器内网穿透实现(NATAPP)》
2、配置网页授权获取用户基本信息,用于告诉微信发起授权的后端服务器地址

  • 正式公众号:在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”进行配置操作;
  • 测试沙箱环境:在 测试环境 中,进行配置网页授权
    在这里插入图片描述
    在这里插入图片描述

授权说明

微信授权时,分为snsapi_basesnsapi_userinfo两种授权方式

  • snsapi_base: 用来获取进入页面的用户的 openid 的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面,不会有对于的认为操作);
  • snsapi_userinfo: 是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。

网页授权流程最主要分三步:
1、引导用户进入授权页面同意授权,获取code
2、通过 code 换取网页授权access_token(与基础支持中的access_token不同)和openid
3、通过网页授权access_token和 openid 获取用户基本信息(支持 UnionID 机制)

后端接口流程说明:
当前Demo,后端只会对前端暴漏一个接口,微信授权回调地址直接重定向到后端地址,由后端进行后续授权以及获取用户信息操作;
暴漏给前端的接口只需要传入两个参数,分别为:socpe(授权类型)、baseUrl(授权成功后重定向到前端的页面地址)

比如:

1、前端调用`wechant/code`接口,传入`socpe=snsapi_userinfo、baseUrl=http://lhz.com/h5`
2、微信回调的授权地址,直接为后端地址
3、获取code、通过code获取access_token的流程由后端完成
4、后端获取到openid信息后,重定向到`http://lhz.com/h5`,比如`return "redirect:" + baseUrl + "?openid=" + openid;`

yaml配置

wx:# 来源于测试平台appid: wx79ec4331f29311b9secret: 1c79a199560f94096f26b8caa2a73a08apiUrl: https://api.weixin.qq.com/openApiUrl: https://open.weixin.qq.com/authRedirectUri: http://6uks3d.natappfree.cc/wechat/auth

接口常量定义

InterfaceConstant:

public interface InterfaceConstant {/*** 用户同意授权,获取code*/String OAUTH2_AUTHORIZE = "connect/oauth2/authorize";/*** 通过 code 换取网页授权access_token*/String OAUTH2_ACCESS_TOKEN = "sns/oauth2/access_token";/*** 获取用户信息*/String OAUTH2_USERINFO = "sns/userinfo";
}

定义工具类

MapUtils:

public class MapUtils {/*** Map转换为 Entity** @param params 包含参数的Map* @param t      需要赋值的实体* @param <T>    类型*/public static <T> T mapToEntity(Map<String, Object> params, T t) {if (null == params) {return t;}Class<?> clazz = t.getClass();Field[] declaredFields = clazz.getDeclaredFields();try {for (Field declaredField : declaredFields) {declaredField.setAccessible(true);String name = declaredField.getName();if (null != params.get(name)) {declaredField.set(t, params.get(name));}}} catch (Exception e) {throw new RuntimeException("属性设置失败!");}return t;}/*** 将对象转换为HashMap** @param t   转换为Map的对象* @param <T> 转换为Map的类* @return Map*/public static <T> Map<String, Object> entityToMap(T t) {Class<?> clazz = t.getClass();List<Field> allField = getAllField(clazz);Map<String, Object> hashMap = new LinkedHashMap<>(allField.size());try {for (Field declaredField : allField) {declaredField.setAccessible(true);Object o = declaredField.get(t);if (null != o) {hashMap.put(declaredField.getName(), o);}}} catch (Exception e) {throw new RuntimeException("属性获取失败!");}return hashMap;}/*** 获取所有属性** @param clazz class* @param <T>   泛型* @return List<Field>*/public static <T> List<Field> getAllField(Class<T> clazz) {List<Field> fields = new ArrayList<>();Class<?> superClazz = clazz;while (null != superClazz) {fields.addAll(Arrays.asList(superClazz.getDeclaredFields()));superClazz = superClazz.getSuperclass();}return fields;}/*** 将Map参数转换为字符串** @param map* @return*/public static String mapToString(Map<String, Object> map) {StringBuffer sb = new StringBuffer();map.forEach((key, value) -> {sb.append(key).append("=").append(value.toString()).append("&");});String str = sb.toString();str = str.substring(0, str.length() - 1);return str;}/*** 将Bean对象转换Url请求的字符串** @param t* @param <T>* @return*/public static <T> String getUrlByBean(T t) {String pre = "?";Map<String, Object> map = entityToMap(t);return pre + mapToString(map);}}

用户授权获取Code

接口说明:

由于授权操作安全等级较高,所以在发起授权请求时,微信会对授权链接做正则强匹配校验,如果链接的参数顺序不对,授权页面将无法正常访问;链接属性如下:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

重定向说明:

通过微信接口connect/oauth2/authorize获取授权code后,微信将进行一次重定向回调,回调地址就是请求设置的redirect_uri值;

需要注意的是,微信重定向回调地址,一定要在微信公众号平台进行配置

定义请求实体类 Oauth2AuthorizeRep:

@Data
public class Oauth2AuthorizeRep {/*** 公众号的唯一标识*/private String appid;/*** 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理*/private String redirect_uri;/*** 返回类型,请填写code*/private String response_type = "code";/*** 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid)* snsapi_userinfo (弹出授权页面,可通过 openid 拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )*/private String scope;/*** 重定向后会带上 state 参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节*/private String state;
}

Controller层示例:

@Slf4j
@Api(tags = "公众号/订阅号开发")
@Controller
public class WeChantController {/*** 由后端来进行授权操作(需要在微信页面打开)** @param baseUrl 前端页面地址 用于授权完成后,后端重定向到前端页面* @param scope   应用授权作用域,此处为了模拟两种情况,进行传值:*                snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid)*                snsapi_userinfo (弹出授权页面,可通过 openid 拿到昵称、性别、所在地。 即使在未关注的情况下,只要用户授权,也能获取其信息 )* @return*/@GetMapping(value = "/code")@ApiOperation(value = "用户请求进行授权及获取信息", notes = "用户请求进行授权及获取信息")public String code(@RequestParam("baseUrl") String baseUrl, String scope) throws UnsupportedEncodingException {log.info("------ 用户请求进行授权及获取信息 ------");//通过code获取用户其信息String url = weChantService.getAuthCode(baseUrl, scope);return "redirect:" + url;}
}

Service层示例:

@Service
@Slf4j
public class WeChantService {/*** 获取用户授权码** @param baseUrl* @param scope* @return*/public String getAuthCode(String baseUrl, String scope) throws UnsupportedEncodingException {String appId = wxBean.getAppid();// 设置回调地址 http://6uks3d.natappfree.cc/wechat/auth,该地址为后端地址String redirectUri = wxBean.getAuthRedirectUri();// urlEncode处理redirectUri = URLEncoder.encode(redirectUri, "utf-8");// 组装url,在url中让state属性存方baseUrl的值String url = wxBean.getOpenApiUrl() + InterfaceConstant.OAUTH2_AUTHORIZE;// 封装url请求参数Oauth2AuthorizeRep rep = new Oauth2AuthorizeRep();rep.setAppid(appId);rep.setRedirect_uri(redirectUri);rep.setScope(scope);// 设置回调参数,需要进行urlEncode处理Map<String, String> stateMap = new HashMap<>(4);stateMap.put("baseUrl", baseUrl);stateMap.put("scope", scope);String stateMapStr = JSON.toJSONString(stateMap);stateMapStr = new String(Base64.getEncoder().encode(stateMapStr.getBytes(StandardCharsets.UTF_8)));rep.setState(stateMapStr);// 参数的顺序必须是:appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirecturl = url + MapUtils.getUrlByBean(rep) + "#wechat_redirect";// 重定向url,微信会自动访问redirectUri,进行回调return url;}
}

请求示例:
需要在微信中打开以下链接(实际情况是前端在微信环境中进行调用),这里进行Demo演示,手动在微信中进行访问:http://6uks3d.natappfree.cc/wechat/code?baseUrl=https://www.baidu.com&scope=snsapi_base
其中http://6uks3d.natappfree.cc为通过NatApp配置的内网穿透域名,保证微信功能访问到本地项目;

通过Code 换取授权access_token及用户信息

接口说明:

由于公众号的 secret 和获取到的access_token安全级别都非常高,必须只保存在服务器,不允许传给客户端。
该接口就是用户授权获取Code中定义的redirectUri值,微信重定向时会进行调用

需要注意的是,微信重定向回调地址,一定要在微信公众号平台进行配置

定义请求实体类 Oauth2AccessTokenRep:

@Data
public class Oauth2AccessTokenRep {/*** 公众号的唯一标识*/private String appid;/*** 公众号的appsecret*/private String secret;/*** 填写第一步获取的 code 参数*/private String code;/*** 填写为authorization_code*/private String grant_type = "authorization_code";
}

定义响应实体类 Oauth2AccessTokenRes:

@Data
public class Oauth2AccessTokenRes {/*** 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同*/private String access_token;/*** access_token接口调用凭证超时时间,单位(秒)*/private Integer expires_in;/*** 用户刷新access_token*/private String refresh_token;/*** 用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID*/private String openid;/*** 用户授权的作用域,使用逗号(,)分隔*/private String scope;/*** 是否为快照页模式虚拟账号,只有当用户是快照页模式虚拟账号时返回,值为1*/private String is_snapshotuser;/*** 用户统一标识(针对一个微信开放平台帐号下的应用,同一用户的 unionid 是唯一的),只有当 scope 为"snsapi_userinfo"时返回* 并且公众号与微信开放平台进行了绑定才会返回*/private String unionid;
}

Controller层示例:

该接口接收微信的授权回调,获取code及state值,并且在完成授权后,重定向到指定的前端页面

@Slf4j
@Controller
public class WeChantController {/*** @param code* @param state 存放的前端页面地址,授权后回调用* @return*/@GetMapping(value = "/auth")@ApiOperation(value = "前端根据code获取信息", notes = "前端根据code获取信息")public String auth(@RequestParam(value = "code", required = false) String code, @RequestParam(value = "state", required = false) String state) throws UnsupportedEncodingException {log.info("------ 回显Code:{} ------", code);// 解析回传的 state值state = new String(Base64.getDecoder().decode(state.getBytes(StandardCharsets.UTF_8)));Map map = JSON.parseObject(state, Map.class);String baseUrl = map.get("baseUrl").toString();String scope = map.get("scope").toString();// 通过code获取用户openidString openid = weChantService.getUserAuth(code, scope);// 直接跳转到前端地址return "redirect:" + baseUrl + "?openid=" + openid;}
}

Service层示例:

@Service
@Slf4j
public class WeChantService {/*** 用户授权,并且返回openid返回给前端** @param code* @param scope* @return*/public String getUserAuth(String code, String scope) {String appId = wxBean.getAppid();String secret = wxBean.getSecret();String url = wxBean.getApiUrl() + InterfaceConstant.OAUTH2_ACCESS_TOKEN;// 封装url请求参数Oauth2AccessTokenRep rep = new Oauth2AccessTokenRep();rep.setAppid(appId);rep.setSecret(secret);rep.setCode(code);url = url + MapUtils.getUrlByBean(rep);Map map = restHttpRequest.doHttp(url, HttpMethod.GET, null);if (map == null) {throw new RuntimeException("授权失败!");}Oauth2AccessTokenRes res = new Oauth2AccessTokenRes();MapUtils.mapToEntity(map, res);log.info("Oauth2AccessTokenRes:" + JSON.toJSONString(res));// 获取access_token过期时间long expiresToken = res.getExpires_in() - 100;String access_token = res.getAccess_token();String openid = res.getOpenid();log.info("------ access_token:{} ------", access_token);log.info("------ openid:{} ------", openid);// 根据openid和access_token获取用户信息,如果"snsapi_userinfo"授权方式,再调用接口获取用户信息if (scope.equals(SNS_API_USERINFO)) {getAndInsertUserInfo(openid, access_token);}return openid;}
}

获取用户信息

定义请求实体类 Oauth2UserInfoRep:

@Data
public class Oauth2UserInfoRep {/*** 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同*/private String access_token;/*** 用户的唯一标识*/private String openid;/*** 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语*/private String lang = "zh_CN";}

定义响应实体类 Oauth2UserInfoRes:

@Data
public class Oauth2UserInfoRes {/*** 用户昵称*/private String nickname;/*** 用户的唯一标识*/private String openid;/*** 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知*/private Integer sex;/*** 用户个人资料填写的省份*/private String province;/*** 普通用户个人资料填写的城市*/private String city;/*** 国家,如中国为CN*/private String country;/*** 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),* 用户没有头像时该项为空。若用户更换头像,原有头像 URL 将失效。*/private String headimgurl;/*** 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)*/private List<String> privilege;/*** 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。*/private String unionid;
}

Service层示例:

该接口在后端通过code获取accessTokenopenid后,直接调用该方法;

@Service
@Slf4j
public class WeChantService {private void getAndInsertUserInfo(String openid, String accessToken) {// 获取用户信息String url = wxBean.getApiUrl() + InterfaceConstant.OAUTH2_USERINFO;Oauth2UserInfoRep rep = new Oauth2UserInfoRep();rep.setAccess_token(accessToken);rep.setOpenid(openid);url = url + MapUtils.getUrlByBean(rep);Map userMap = restHttpRequest.doHttp(url, HttpMethod.GET, null);Oauth2UserInfoRes res = new Oauth2UserInfoRes();MapUtils.mapToEntity(userMap, res);// 打印信息log.info("UserInfo:" + JSON.toJSONString(res));}
}

流程测试

注意:由于使用了NatApp进行免费的内网穿透,可以会出现域名变更情况,如果域名发生了变更需要重新在公众号平台配置网页授权域名。

1、微信中访问后端接口,地址:http://6uks3d.natappfree.cc/wechat/code?baseUrl=https://www.baidu.com&scope=snsapi_userinfo

2、弹出授权页面如下
在这里插入图片描述
3、同意授权后,重定向到指定的页面(测试时使用了百度页面)
在这里插入图片描述
4、查看百度页面的链接信息,发现url后面带上了openid,就表示授权及重定向成功,比如:https://www.baidu.com/?openid=oTnaY6332ssfv4WiQBU0dES-WxJg

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

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

相关文章

哈希表及其与Java类集的关系

目录 1.哈希表的概念 2.哈希冲突 3.如何避免哈希冲突? 3.1哈希函数设计 3.2 负载因子的调节 4.解决哈希冲突 4.1闭散列 4.1.1线性探测 4.1.2二次探测 4.2开散列(哈希桶) 5.HashMap 6.HashSet 1.哈希表的概念 假设有一组数据,要让你去搜索其中的一个关键码,这种场…

嵌入式软件工程师技能树——Linux应用编程+网络编程+驱动开发+操作系统+计算机网络

文章目录Linux驱动开发1、Linux内核组成2、用户空间与内核的通讯方式有哪些&#xff1f;3、系统调用read/write流程4、内核态用户态的区别5、bootloader内核 根文件的关系6、BootLoader的作用7、BootLoader两个启动阶段1、汇编实现&#xff0c;完成依赖于CPU体系架构的设置&…

异常检测方法总结

在数据挖掘中&#xff0c;异常检测&#xff08;英语&#xff1a;anomaly detection&#xff09;对不匹配预期模式或数据集中其他项目的项目、事件或观测值的识别。 通常异常项目会转变成银行欺诈、结构缺陷、医疗问题、文本错误等类型的问题。异常也被称为离群值、新奇、噪声、…

[Android移动安全渗透基础教程] 易受攻击的移动应用程序

也许每个人出生的时候都以为这世界都是为他一个人而存在的&#xff0c;当他发现自己错的时候&#xff0c;他便开始长大 少走了弯路&#xff0c;也就错过了风景&#xff0c;无论如何&#xff0c;感谢经历 0x01 如何设置 GoatDroid (FourGoats) 1.1 简介&#xff08;概述&#…

第十四届蓝桥杯集训——JavaC组第十三篇——for循环

第十四届蓝桥杯集训——JavaC组第十三篇——for循环 目录 第十四届蓝桥杯集训——JavaC组第十三篇——for循环 for循环(重点) 倒序迭代器 for循环死循环 for循环示例 暴力循环 等差数列求和公式 基础循环展开 循环控制语句 break结束 continue继续 for循环(重点) f…

MySQL主从复制太慢,怎么办?

本文分析了MySQL主从延迟的原因以及介绍了MTS方案。点击上方“后端开发技术”&#xff0c;选择“设为星标” &#xff0c;优质资源及时送达mysql主从同步延迟原因导致备库延迟的原因主要有如下几种&#xff1a;通常备库所在机器的性能要比主库所在的机器性能差&#xff0c;执行…

nodejs092学生考勤请假管理系统vue

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.3 B/S结构 4 2.4 MySQL数据库 4 前端技术&#xff1a;nodejsvueelementui 前端&#xff1a;HTML5,CSS3、JavaScript、VUE 系统…

ChatGpt详细注册流程

ChatGpt详细注册流程ChatGpt的网址&#xff1a;直接点击我 点击链接后向下滑动看到TRY CHATGPT如下图所示&#xff1a; 点击TRY CHATGPT后会跳转如下图界面&#xff1a; 点击Log in(登录)如下图&#xff1a; 因为首次登录你肯定是没有账号的所以需要先点击红框框出的Sign up…

七个步骤覆盖 API 接口测试

接口测试作为最常用的集成测试方法的一部分&#xff0c;通过直接调用被测试的接口来确定系统在功能性、可靠性、安全性和性能方面是否能达到预期&#xff0c;有些情况是功能测试无法覆盖的&#xff0c;所以接口测试是非常必要的。首先需要对接口测试的基本信息做一些了解&#…

Java+Swing实现的五子棋游戏

JavaSwing实现的五子棋游戏一、系统介绍二、功能展示1.游戏展示三、系统实现1.ChessFrame .java四、其它1.其他系统实现2.获取源码一、系统介绍 五子棋游戏实现人机对战、人人对战两个模式。 二、功能展示 1.游戏展示 三、系统实现 1.ChessFrame .java package five;impor…

JDK的使用——Java开发第一步

JDK的使用——Java开发第一步 1 什么是JDK JDK是 Java 语言的软件开发工具包&#xff0c;是整个java开发的核心&#xff0c;使用Java开发第一步就是要在计算机上安装JDK。 JDK主要包含三个部分&#xff1a; 1 JAVA开发工具(jdk\bin) 2 基础开发库(jdk\jre\lib) 3 基础开发库…

java TCP发送数据

TCP是一种可靠的网络协议 他在通信的两端都建立了Socke对象 从而形成了两端的虚拟链路 一旦建立了虚拟链路 两端就可以通过链路通信 TCP会将通信两端分为 客户端和服务端 客户端通过Socke实现 服务端通过ServerSocke实现 那么 我们就来实现一下发送数据的方法 我们创建一个测…

【C语言数据结构(基础版)】第三站:链表(二)

目录 一、单链表的缺陷以及双向链表的引入 1.单链表的缺陷 2.双向链表的引入 3.八大链表结构 &#xff08;1&#xff09;单向和双向 &#xff08;2&#xff09;带头和不带头 &#xff08;3&#xff09;循环和不循环 &#xff08;4&#xff09;八种链表结构 二、带头双向…

web网页设计期末课程大作业:美食餐饮文化主题网站设计——HTML+CSS+JavaScript美食餐厅网站设计与实现 11页面

&#x1f468;‍&#x1f393;静态网站的编写主要是用HTML DIVCSS JS等来完成页面的排版设计&#x1f469;‍&#x1f393;,常用的网页设计软件有Dreamweaver、EditPlus、HBuilderX、VScode 、Webstorm、Animate等等&#xff0c;用的最多的还是DW&#xff0c;当然不同软件写出的…

[附源码]Node.js计算机毕业设计高校教学过程管理系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

java毕业设计——基于java+Socket+sqlserver的网络通讯系统设计与实现(毕业论文+程序源码)——网络通讯系统

基于javaSocketsqlserver的网络通讯系统设计与实现&#xff08;毕业论文程序源码&#xff09; 大家好&#xff0c;今天给大家介绍基于javaSocketsqlserver的网络通讯系统设计与实现&#xff0c;文章末尾附有本毕业设计的论文和源码下载地址哦。 文章目录&#xff1a; 基于jav…

基于java+springmvc+mybatis+vue+mysql的少儿编程管理系统

项目介绍 在国家重视教育影响下&#xff0c;教育部门的密确配合下&#xff0c;对教育进行改革、多样性、质量等等的要求&#xff0c;使教育系统的管理和运营比过去十年前更加理性化。依照这一现实为基础&#xff0c;设计一个快捷而又方便的网上少儿编程教育网站系统是一项十分…

820爆炸案(模拟案件)

文章目录模拟案件背景相关密码快压word邮箱BitLocker系统证书bestcrypt涉案图片模拟案件背景 8月20日18&#xff1a;00某市汽车站发生一起爆炸案件&#xff0c;经初步侦查&#xff0c;炸弹系通过手机远程引爆&#xff0c;办案人员经过综合研判分析&#xff0c;确定了引爆炸弹的…

并发编程之深入理解ReentrantLock和AQS原理

AQS&#xff08;AbstractQueuedSynchronizer&#xff09;在并发编程中占有很重要的地位&#xff0c;可能很多人在平时的开发中并没有看到过它的身影&#xff0c;但是当我们有看过concurrent包一些JDK并发编程的源码的时候&#xff0c;就会发现很多地方都使用了AQS&#xff0c;今…

Linux | 网络概念理解 | 对网络的初识

文章目录重新看待计算机体系结构软件分层的思想网络中的分层协议的理解局域网的理解MAC地址 && IP地址报头的作用端口号&#xff08;port&#xff09;重新看待计算机体系结构 计算机由硬件组成&#xff0c;而不同硬件之间要怎么通信&#xff0c;或者说要怎么进行数据的…