Spring Security安全配置
使用Spring Boot与Spring MVC进行Web开发时,如果项目引入spring-boot- starter-security依赖启动器,MVC Security 安全管理功能就会自动生效,其默认的安全配置是在SecurityAutoConfiguration和UserDetailsServiceAutoConfiguration中实现的。其中,SecurityAutoConfiguration会导入并自动化配置SpringBootWebSecurityConfiguration用于启动Web安全管理,UserDetailsServiceAutoConfiguration则用于配置用户身份信息。
通过自定义WebSecurityConfigurerAdapter 类型的Bean组件,可以完全关闭Security 提供的Web应用默认安全配置,但是不会关闭UserDetailsService用户信息自动配置类。如果要关闭UserDetailsService默认用户信息配置,可以自定义UserDetailsService、AuthenticationProvider或AuthenticationManager类型的Bean组件。另外,可以通过自定义WebSecurityConfigurerAdapter类型的Bean组件覆盖默认访问规则。Spring Boot 提供了非常多方便的方法,可用于覆盖请求映射和静态资源的访问规则。
下面我们通过Spring Security API查看WebSecurityConfigurerAdapter的主要方法,具体所示:
configure(AuthenticationManagerBuilder auth):定制用户认证管理器来实现用户认证
configure(HttpSecurity http):定制基于HTTP请求的用户访问控制
从上面可以看到,WebSecurityConfigurerAdapter类中包含两个非常重要的配置方法,分别是configure(AuthenticationManagerBuilder auth)和configure(HttpSecurity http)。Configure(AuthenticationManagerBuilder auth)方法用于构建认证管理器,configure(HttpSecurity http)方法用于对基于HTTP的请求进行请求访问控制。
一、自定义用户认证:
通过自定义WebSecurityConfigurerAdapter类型的Bean组件,并重写configure(AuthenticationManagerBuilder auth)方法,可以自定义用户认证。针对自定义用户认证,Spring Security提供了多种自定义认证方式, 包括有: In-Memory Authentication (内存 身份认证)、JDBCAuthentication ( JDBC身份认证)、LDAP Authentication ( LDAP身份认证)、AuthenticationProvider (身份认证提供商)和UserDetailsService (身份详情服务)。 下面我们选取其中3个比较有代表性的方式讲解如何实现自定义用户认证。
二、自定义用户授权管理:
当一个系统建立之后,通常需要适当地做一些权限控制,使得不同用户具有不同的权限操作系统。例如,一般的项目中都会做一些简单的登录控制,只有特定用户才能登录访问。接下来我们针对Web应用中常见的自定义用户授权管理进行介绍。
自定义用户访问控制:
实际生产中,网站访问多是基于HTTP请求的,上面MVC Security 安全配置介绍时,我们已经分析出通过重写WebSecurityConfigurerAdapter类的configure(HttpSecurity http)方法可以对基于HTTP的请求访问进行控制。下面我们通过对configure(HttpSecurity http)方法剖析,分析自定义用户访问控制的实现过程。
configure(HttpSecurity http)方法的参数类型是HttpSecurity类,HttpSecurity类提供了Http请求的限制以及权限、Session 管理配置、CSRF跨站请求问题等方法,具体如下所示。
HttpSecurity 类的主要方法及说明:
authorizeRequests():开启基于HtpSereleRequest请求访问的限制
formLogin():开启基于表单的用户登录
httpBasic():开启基于HTTP请求的Basic认证登录
logout():开启退出登录的支持
sessionManagement():开启Session管理配置
rememberMe():开启记住我功能
csrf():配置CSRF跨站请求伪造防护功能
此处重点讲解用户访问控制,这里先对authorizeRequests()方法的返回值做进一步查看,其中涉及用户访问控制的主要方法及说明如下所示。
antMatchers(java.lang.String .. antPatterns):开启Ant风格的路径匹配
mvcMatchers(java.lang. String ... patterns):开启MVC风格的路径匹配(与Ant风格类似)
regexMatchers(java.lang String... regexPatterns):开启正则表达式的路径匹配
and():功能连接符
anyRequest():匹配任何请求
rememberMe():开启记住我功能
access(String attribute):匹配给定的SpEL表达式计算结果是否为true
hasAnyRole(String .. roles):匹配用户是否有参数中的任意角色
hasRole(String role):匹配用户是否有某一个角色
hasAnyAuthority(String .. authorities):匹配用户是否有参数中的任意权限
hasAuthority(String authority):匹配用户是否有某一个权限
authenticated():匹配已经登录认证的用户
fullyAuthenticated():匹配完整登录认证的用户(非rememberMe登录用户)
haslpAddress(String ipaddressExpression):匹配某IP地址的访问请求
permitAll():无条件对请求进行放行
上面列举了用户请求访问的常用方法,如果想了解更多方法可以通过查看API文档。
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Overrideprotected void configure(HttpSecurity http) throws Exception {// 授权http.authorizeRequests().antMatchers("/user/setting","/user/upload","/discuss/add","/comment/add/**","/letter/**","/notice/**","/like","/follow","/unfollow").hasAnyAuthority("AUTHORITY_USER","AUTHORITY_ADMIN","AUTHORITY_MODERATOR").antMatchers("/discuss/top","/discuss/wonderful").hasRole("AUTHORITY_USER").anyRequest().permitAll().and().formLogin();}
}
自定义用户登录:
formLogin()是开启基于表单的用户登录,所以下面就围绕formLogin()这个方法来探索并讲解自定义登录的具体实现。
用户登录相关的主要方法及说明如下所示。
loginPage(String loginPage):用户登录页面跳转路径,默认为get请求的/login
successForwardUrl(String forwardUrl):用户登录成功后的重定向地址
successHandler(AuthenticationSuccessHandler successHandler):用户登录成功后的处理
defaultSuccessUrl(String defaultSuccessUrl):用户直接登录后默认跳转地址
failureForwardUrl(String forwardUrl):用户登录失败后的重定向地址
failureUrl(String authenticationFailureUrl):用户登录失败后的跳转地址,默认为/login?error
failureHandler(AuthenticationF ailureHandler authenticationF ailureHandler):用户登录失败后的错误处理
usernameParameter(String usernameParameter):登录用户的用户名参数,默认为username
passwordParameter(String passwordParameter):登录用户的密码参数,默认为password
loginProcessingUrl(String loginProcessingUr):登录表单提交的路径,默认为post请求的login
permitAIl():无条件对请求进行放行
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Overrideprotected void configure(HttpSecurity http) throws Exception {// 自定义用户登录控制http.formLogin().loginPage("/userLogin").permitAll().usernameParameter("name").passwordParameter("pwd").defaultSuccessUrl("/").failureUrl("/userLogin?error");}
}
自定义用户退出:
自定义用户退出主要考虑退出后的会话如何管理以及跳转到哪个页面。HttpSecurity 类的logout()方法用来处理用户退出,它默认处理路径为“logout”的Post类型请求,同时也会清除Session和“Remember Me”(记住我)等任何默认用户配置。下面我们就围绕logout)这个方法来探索并讲解自定义用户退出的具体实现。
logout()方法中涉及用户退出的主要方法及说明如下所示。
logoutUrI(String logoutUrl):用户退出处理控制URL,默认为post请求的logout
logoutSuccessUrl(String logoutSuccessUrI) :用户退出成功后的重定向地址
logoutSuccessHandler(LogoutSuccessHandle logoutSuccessHandler):用户退出成功后的处理器设置
deleteCookies(String ... cookieNamesToClear):用户退出后删除指定Cookie
invalidateHttpSession(boolean invalidateHttpSession):用户退出后是否立即清除Session (默认为true )
clearAuthentication(boolean clearAuthentication):用户退出后是否立即清除Authentication用户认证信息(默认为true)
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Overrideprotected void configure(HttpSecurity http) throws Exception {// 自定义用户退出控制http.logout().logoutUrlUrl("/mylogout").logoutSuccessUrl("/");}
}
记住我功能:
在实际开发中,有些项目为了用户登录方便还会提供记住我( Remember-Me )功能。如果用户登录时勾选了记住我选项,那么在一段有效时间内,会默认自动登录,并允许访问相关页面,这就免去了重复登录操作的麻烦。Spring Security提供了用户登录控制的同时,当然也提供了对应的记住我功能,前面介绍的HttpSecurity类的主要方法rememberMe()就是Spring Security 用来处理记住我功能的。下面我们围绕rememberMe()这个方法来探索并讲解记住我功能的具体实现。
rememberMe()记住我功能相关涉及记住我的主要方法及说明如下所示。
rememberMeParameter(String rememberMeParameter):指示在登录时记住用户的HTTP参数
key(String key):记住我认证生成的Token令牌标识
tokenValiditySeconds(int tokenValiditySeconds):记住我Token令牌有效期,单位为s (秒)
tokenRepository(PersistentTokenRepository tokenRepository):指定要使用的PersistentTokenRepository,用来配置持久化Token令牌
alwaysRemember(boolean alwaysRemember):是否应该始终创建记住我Cookie,默认为false
clearAuthentication(boolean clearAuthentication):是否设置Cookie为安全的,如果设置为true,则必须通过HTTPS进行连接请求
需要说明的是,Spring Security 针对记住我功能提供了两种实现:一种是简单地使用加密来保证基于Cookie中Token的安全;另一种是通过数据库或其他持久化机制来保存生成的Token。下面我们分别对这两种记住我功能的实现进行讲解并演示说明。
基于简单加密Token 的方式:
基于简单加密Token的方式实现记住我功能非常简单,当用户选择记住我并成功登录后,Spring Security将会生成一个Cookie并发送给客户端浏览器。
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Overrideprotected void configure(HttpSecurity http) throws Exception {// 定制Remember-Me记住我功能http.rememberMe().rememberMeParameter("rememberme").tokenValiditySeconds(200);}
}
基于持久化Token的方式:
持久化Token的方式与简单加密Token的方式在实现Remember -Me功能上大体相同,都是在用户选择[记住我]并成功登录后,将生成的Token存入Cookie中并发送到客户端浏览器,在下次用户通过同一客户端访问系统时,系统将直接从客户端Cookie中读取Token进行认证。两者的主要区别在于:基于简单加密Token的方式,生成的Token将在客户端保存一段时间,如果用户不退出登录,或者不修改密码,那么在Cookie失效之前,任何人都可以无限制地使用该Token进行自动登录;而基于持久化Token的方式采用如下实现逻辑。
- 用户选择[记住我]成功登录后,Security会把username、随机产生的序列号、生成的Token进行持久化存储(例如一个数据表中),同时将它们的组合生成一个 Cookie发送给客户端浏览器。
- 当用户再次访问系统时,首先检查客户端携带的Cookie,如果对应Cookie 中包含的username、序列号和Token与数据库中保存的一致,则通过验证并自动登录,同时系统将重新生成一个新的Token替换数据库中旧的Token,并将新的Cookie再次发送给客户端。
- 如果Cookie中的Token不匹配,则很有可能是用户的Cookie被盗用了。由于盗用者使用初次生成的Token进行登录时会生成一个新的 Token,所以当用户在不知情时再次登录就会出现Token不匹配的情况,这时就需要重新登录,并生成新的Token和Cookie。同时Spring Security就可以发现Cookie可能被盗用的情况,它将删除数据库中与当前用户相关的所有Token记录,这样盗用者使用原有的Cookie将不能再次登录。
- 如果用户访问系统时没有携带Cookie, 或者包含的username和序列号与数据库中保存的不-致,那么将会引导用户到登录页面。
从以上实现逻辑可以看出,持久化Token的方式比简单加密Token的方式相对更加安全。使用简单加密Token的方式,一旦用户的Cookie被盗用,在Token有效期内,盗用者可以无限制地自动登录进行恶意操作,直到用户本人发现并修改密码才会避免这种问题;而使用持久化Token的方式相对安全,用户每登录一次都会生成新的Token和Cookie,但也给盗用者留下了在用户进行第2次登录前进行恶意操作的机会,只有在用户进行第2次登录并更新Token和Cookie时,才会避免这种问题。因此,总体来讲,对于安全性要求很高的应用,不推荐使用Remember -Me功能。
CSRF防护功能:
CSRF (Cross-site request forgery,跨站请求伪造),也被称为“One Click Attack”(一键攻击)或者”Session Riding” (会话控制),通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。与传统的XSS攻击( Cross-site Scriping,跨站脚本攻击)相比,CSRF攻击更加难以防范,被认为比XSS更具危险性。CSRF攻击可以在受害者毫不知情的情况下以受害者的名义伪造请求发送给攻击页面,从而在用户未授权的情况下执行在权限保护之下的操作。
例如,一个用户Tom登录银行站点服务器准备进行转账操作,在此用户信息有效期内,Tom
被诱导查看了一个黑客恶意网站,该网站就会获取到Tom登录后的浏览器与银行网站之间尚未过期的Session信息,而Tom浏览器的Cookie中含有Tom银行账户的认证信息,此时黑客就会伪装成Tom认证后的合法用户对银行账户进行非法操作。
在讨论如何抵御CSRF攻击之前,先要明确CSRF攻击的对象,也就是要保护的对象。从上
面的例子可知,CSRF 攻击是黑客借助受害者的Cookie骗取服务器的信任,但是黑客并不能获取Cookie,也看不到Cookie 的具体内容。另外,对于服务器返回的结果,由于浏览器同源策略的限制,黑客无法进行解析。黑客所能做的就是伪造正常用户给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。因此,针对CSRF攻击要保护的对象是那些可以直接产生数据变化的服务,而对于读取数据的服务,可以不进行CSRF保护。例如,银行转账操作会改变账号金额,需要进行CSRF保护。获取银行卡等级信息是读取操作,不会改变数据,可以不需要保护。在业界目前防御CSRF攻击主要有以下3种策略。
(1)验证HTTP Referer字段。
(2)在请求地址中添加Token并验证。
(3)在HTTP头中自定义属性并验证。
Spring Security安全框架提供了CSRF防御相关方法,具体如下所示。
disable():关闭Security默认开启的CSRF防御功能
csrfTokenRepository(CsrfTokenRepositor csrfTokenRepository):指定要使用的CsrfTokenRepository ( Token令牌持久化仓库)。默认是由LazyCsrfTokenRepository包装的HttpSessionCsrfTokenRepository
rgurerProtetionatchereqestMatcher requireCSsrfProtectionMatcher):指定针对什么类型的请求应用CSRF防护功能。默认设置是忽略GET、HEAD、TRACE和OPTIONS请求,而处理并防御其他所有请求
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Overrideprotected void configure(HttpSecurity http) throws Exception {// 可以关闭Spring Security默认开启的CSRF防护功能http.csrf().disable();;}
}
相关文章:

Spring Security安全配置
使用Spring Boot与Spring MVC进行Web开发时,如果项目引入spring-boot- starter-security依赖启动器,MVC Security 安全管理功能就会自动生效,其默认的安全配置是在SecurityAutoConfiguration和UserDetailsServiceAutoConfiguration中实现的。…...

2023Java后端开发之100道常见经典面试题
目录 1.重载和重写的区别? 2.String 和 StringBuffer、StringBuilder 的区别是什么? 3. 与 equals 的区别? 4.抽象类和接口的区别是什么? 5. 面向对象的特点 6.Collection 和 Collections 有什么区别? 7.List、Set、Map 之…...

Redis详解,包括安装命令,应用场景,优缺点,案列分析,各个开发语言如何应用
目录 1、安装命令2、应用场景3、优缺点4、案例分析5、各个开发语言如何应用? Redis 是一个基于内存的开源数据库系统,被广泛应用于 Web 应用、消息队列、缓存、实时统计等领域。下面是 Redis 的详解,包括安装命令、应用场景和优缺点ÿ…...

AI数字人:金融数字化转型的“关键先生”
今年年初ChatGPT的火热,在全球掀起一阵生成式AI(AIGC)热潮。国外的OpenAI、国内的百度等企业,都在AIGC上强力布局。 各种应用场景中,AIGC助力的数字人引起了市场注意。 事实上,数字人不是个新鲜事。早在1…...

mac关闭VPN之后,浏览器就不能够正常上网了(图解)
可能打开谷歌浏览器会显示无法正常连接网络等信息,这个时候可以按照以下步骤: 点击"检查代理服务器地址" 会显示以下这段话,按照这个步骤来操作就可以了。 打开系统偏好设置,点击网络 点击高级 取消掉所有已勾选代…...

YOLOv5改进系列(17)——更换IoU之MPDIoU(ELSEVIER 2023|超越WIoU、EIoU等|实测涨点)
【YOLOv5改进系列】前期回顾: YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析 YOLOv5改进系列(1)——添加SE注意力机制...

基于WSL2、Ubuntu和VS Code的CUDA平台运行C语言程序
一、CUDA程序执行方法 执行步骤为: 安装Visual Studio Code。在Visual Studio Code中安装插件WSL与电脑的WSL2进行连接。点击左下角,然后再选择连接到WSL。 在WSL中创建以 .cu 为后缀的文件。 rootDESKTOP-HR6VO5J:~# mkdir CUDA /…...

构建外卖系统小程序,订单管理功能实现步骤详解
外卖系统小程序是近年来越来越受欢迎的一种订餐方式,方便快捷,并且可以减少人与人之间的接触,更加卫生安全。为了搭建一个完善的外卖系统小程序,订单管理功能是必不可少的一部分。在本文中,我们将详细介绍如何实现订单…...

用asp.net开发h5网页版视频播放网站,类似优酷,jellyfin,emby
之前用jellyfin开源软件搞了一个视频播放服务器,用来共享给家里人看电影和电视剧,jellyfin虽然各方面功能都很强大,但是界面和使用习惯都很不适合,于是就想着利用下班休息时间做一套自己喜欢的视频网站出来. 本来是打算直接用jellyfin的源码进行修改,源码是用C# netcore 写的服…...

Redis—相关背景
Redis—相关背景 🔎Redis—特性In-memory data structures—在内存中存储数据Programmability—可编程性Extensibility—可扩展性Persistence—持久化Clustering—集群High availability—高可用 🔎Redis 为什么快🔎Redis 的使用场景Real-tim…...

SSL 证书过期巡检脚本
哈喽大家好,我是咸鱼 我们知道 SSL 证书是会过期的,一旦过期之后需要重新申请。如果没有及时更换证书的话,就有可能导致网站出问题,给公司业务带来一定的影响 所以说我们要每隔一定时间去检查网站上的 SSL 证书是否过期 如果公…...

leetcode 面试题 01.03. URL化
⭐️ 题目描述 🌟 leetcode链接:面试题 01.03. URL化 思路: 计算出空格的个数,我们可以知道最后一个字符的位置 endPos,再从后 end 向前遍历若不是空格正常拷贝,是空格则替换成 %20,最终当空格…...

uni-app在小米手机上运行【步骤细节】
注意细节重点: 1.手机使用数据线与电脑连接,手机连接模式必须是传输文件模式 2.手机必须打开开发者模式 3.打开开发者模式后,仔细浏览并调整USB调试权限,重点打开USB是否允许安装按钮!!! 操作步…...

微信小程序实现日历功能、日历转换插件、calendar
文章目录 演示htmlJavaScript 演示 效果图 微信小程序实现交互 html <view wx:if"{{calendarArr.length}}"><view class"height_786 df_fdc_aic"><view class"grid_c7_104"><view class"font_weight_800 text_align…...

【浩鲸科技】济南Java后端面经
本文目录 写在前面试题总览题目解析1.说一下SpringBoot中常用的注解2.Redis中的基本数据类型3.TCP的网络协议4.java中常见的锁5.Hashmap的底层数据结构、底层源码、扩容机制源码6.java面向对象的特点 写在前面 关于这个专栏: 本专栏记录一些互联网大厂、小厂的面试实…...

VMware搭建Hadoop集群 for Windows(完整详细,实测可用)
目录 一、VMware 虚拟机安装 (1)虚拟机创建及配置 (2)创建工作文件夹 二、克隆虚拟机 三、配置虚拟机的网络 (1)虚拟网络配置 (2)配置虚拟机 主机名 (3…...

【Rust 基础篇】Rust关联类型:灵活的泛型抽象
导言 Rust是一种以安全性和高效性著称的系统级编程语言,其设计哲学是在不损失性能的前提下,保障代码的内存安全和线程安全。为了实现这一目标,Rust引入了"所有权系统"、"借用检查器"等特性,有效地避免了常见…...

学习笔记21 list
一、概述 有两种不同的方法来实现List接口。ArrayList类使用基于连续内存分配的实现,而LinkedList实现基于linked allocation。 list接口提供了一些方法: 二、The ArrayList and LinkedList Classes 1.构造方法 这两个类有相似的构造方法:…...

微信小程序弱网监控
前言 在真实的项目中,我们为了良好的用户体验,会根据用户当前的网络状态提供最优的资源,例如图片或视频等比较大的资源,当网络较差时,可以提供分辨率更低的资源,能够让用户尽可能快的看到有效信息…...

【Linux】进程通信 — 共享内存
文章目录 📖 前言1. 共享内存2. 创建共享内存2.1 ftok()创建key值:2.2 shmget()创建共享内存:2.3 ipcs指令:2.4 shmctl()接口:2.5 shmat()/shmdt()接口:2.6 共享内存没有访问控制:2.7 通过管道对共享内存进…...

“从零开始学习Spring Boot:快速搭建Java后端开发环境“
标题:从零开始学习Spring Boot:快速搭建Java后端开发环境 摘要:本文将介绍如何从零开始学习Spring Boot,并详细讲解如何快速搭建Java后端开发环境。通过本文的指导,您将能够快速搭建一个基于Spring Boot的Java后端开发…...

行为型-状态模式(State Pattern)
概述 状态模式是一种行为设计模式,它可以让对象在内部状态改变时改变它的行为。简而言之,状态模式允许对象在不同状态下更改其行为,而不需要通过使用大量的条件语句进行手动更改。 优点: 状态模式将与特定状态相关的行为分散到…...

大厂领导为什么喜欢跨层与下属聊天
作为一个在大厂里面浸淫十几年的loser,平时主要精力没用在技术提升上,对于大厂的人情世故各类八卦倒是研究的透彻。 如果你细心观察,会发现一些大的公司里面,领导喜欢跨层与下属去沟通聊天,我待过几家比较大的公司&am…...

Android 面试题 避免OOM(内存优化)三
🔥 OOM介绍(out of memory 内存溢出)🔥 Android和java中都会出现由于不良代码引起的内存泄露,为了使Android应用程序能够快速高效的运行,Android每个应用程序都会有专门Dalvik虚拟机实例来运行,…...

SpringBoot集成Lock4j 底层使用Redission 实现分布锁
Lock4j 在分布式系统中,实现锁的功能对于保证数据一致性和避免并发冲突是非常重要的。Lock4j是一个简单易用的分布式锁框架,而Redisson是一个功能强大的分布式解决方案,可以与Lock4j进行集成。 操作步骤 第一步:添加依赖 首先&…...

TortoiseSVN操作使用
说明 SVN常用于程序代码版本控制,由于业务需求需将生产资料通过SVN进行管控,涉及人员众多,权限分支管理需要细化,特此记录SVN的学习操作. 前言 版本控制是管理信息修改的艺术,它一直是程序员最重要的工具,程序员经常会花时间作出小的修改, 然…...

第五篇-ChatGLM2-6B模型下载
下载chatglm2-6b模型文件 https://huggingface.co/THUDM/chatglm2-6b方法一:huggingface页面直接点击下载 一个一个下载,都要下载方法二:snapshot_download下载文件 可以使用如下代码下载 创建下载环境 conda create --name hfhub pytho…...

【Matlab】基于长短期记忆网络的数据分类预测(Excel可直接替换数据)
【Matlab】基于长短期记忆网络的数据分类预测(Excel可直接替换数据) 1.模型原理2.数学公式3.文件结构4.Excel数据5.分块代码6.完整代码7.运行结果1.模型原理 “基于长短期记忆网络的数据分类预测”是一种利用长短期记忆网络(Long Short-Term Memory, LSTM)进行数据分类任务…...

C++网络编程 TCP套接字基础知识,利用TCP套接字实现客户端-服务端通信
1. TCP 套接字编程流程 1.1 概念 流式套接字编程针对TCP协议通信,即是面向对象的通信,分为服务端和客户端两部分。 1.2 服务端编程流程: 1)加载套接字库(使用函数WSAStartup()),创建套接字&…...

苍穹外卖-day07
苍穹外卖-day07 本项目学自黑马程序员的《苍穹外卖》项目,是瑞吉外卖的Plus版本 功能更多,更加丰富。 结合资料,和自己对学习过程中的一些看法和问题解决情况上传课件笔记 视频:https://www.bilibili.com/video/BV1TP411v7v6/?sp…...