CAS Server使用Maven构建以及自定义扩展使用

介绍

​CAS(Central Authentication Service)中心授权/认证服务,是由耶鲁大学发起的一个开源项目,距今已有20年之久,功能相当丰富,目的在于为Web应用系统提供一种可靠且稳定的单点登录解决方案。

CAS分为两个模块:一个是CAS Server,认证服务器,主要用于票据颁发以及进行票据校验;CAS Client为客户端,当有服务组件需接入CAS时,引入客户端并做一些简单的配置就可以接入了CAS。

前言

本文主要阐述CAS Server服务端Maven构建以及自定义扩展使用。对于CAS Server的执行流程以及原理本文就不详细概述了。

在实际开发情况中,我们需要对CAS Server做一些自定义扩展,便于解决一些实际问题;官方提供了多种使用以及扩展方式,例如:使用cas-overlay-template或者cas-initializr进行第三方扩展;但是都是使用Gradle进行集成以及使用Tomcat容器单独部署,在一些特定的开发场景下会比较麻烦。

项目构建

构建各个版本如下:

  • CAS Server:6.5.9

  • JDK:11

  • Spring Boot:2.6.15

由于CAS官方6.5.9版本默认使用的是SpringBoot 2.6.x和JDK11版本,大多数模块包根据这个版本进行的开发,如果降低版本的话,可能会存在一些不兼容问题。

Maven依赖

如下:

    <properties><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target><jdk.version>11</jdk.version><cas.version>6.5.9</cas.version><spring-boot.version>2.6.15</spring-boot.version></properties>
​<dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><scope>import</scope><type>pom</type></dependency></dependencies></dependencyManagement>
​<dependencies><!-- CAS server dependencis start --><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-webapp-init</artifactId><version>${cas.version}</version></dependency><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-webapp-config</artifactId><version>${cas.version}</version></dependency><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-webapp-init-tomcat</artifactId><version>${cas.version}</version></dependency>
​<dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-core</artifactId><version>${cas.version}</version></dependency>
​<dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-core-cookie</artifactId><version>${cas.version}</version></dependency><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-core-services</artifactId><version>${cas.version}</version></dependency><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-core-web</artifactId><version>${cas.version}</version></dependency><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-core-util</artifactId><version>${cas.version}</version></dependency><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-core-tickets</artifactId><version>${cas.version}</version></dependency><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-core-audit</artifactId><version>${cas.version}</version></dependency><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-core-logout</artifactId><version>${cas.version}</version></dependency><!-- 前端包样式 --><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-support-thymeleaf</artifactId><version>${cas.version}</version><exclusions><exclusion><artifactId>material-components-web</artifactId><groupId>org.webjars.npm</groupId></exclusion><exclusion><artifactId>css-vars-ponyfill</artifactId><groupId>org.webjars.npm</groupId></exclusion></exclusions></dependency><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-support-pm</artifactId><version>${cas.version}</version></dependency><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-support-json-service-registry</artifactId><version>${cas.version}</version></dependency><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-support-person-directory</artifactId><version>${cas.version}</version></dependency><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-support-person-directory-core</artifactId><version>${cas.version}</version></dependency><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-support-validation</artifactId><version>${cas.version}</version></dependency><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-core-web-api</artifactId><version>${cas.version}</version></dependency><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-core-authentication-api</artifactId><version>${cas.version}</version></dependency><!-- CAS server dependencis end --><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-config</artifactId></dependency>
​<!-- 用于自定义WebFlow流程  --><dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-core-webflow-api</artifactId><version>${cas.version}</version></dependency><!-- 控制cas server日志打印管理,默认使用自带的log4j.xml管理日志 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.4</version></dependency></dependencies>

注:其实大多数依赖包存在被其他依赖引用的,但是依赖类型为runtime,会导致在自定义扩展的是时候找不到对应的扩展类。

可以使用Spring Boot Maven Plugin插件进行构建打包或者使用Maven自身打包插件也是可以的。

配置文件

spring.main.allow-bean-definition-overriding=true
# 使用默认的账号名称,使用默认校验方式
cas.authn.accept.enabled=true
cas.authn.accept.users=casuser::mellon
cas.authn.accept.name=Static Credentials
​
cas.tgc.secure=false
# 客户端注册校验
cas.serviceRegistry.initFromJson=true
cas.serviceRegistry.json.watcherEnabled=true
# 指定services注解json文件地址
cas.serviceRegistry.json.location=classpath:/services
# 嵌入式Tomcat配置
server.port=8443
server.servlet.context-path=/cas

配置服务认证注册文件:

在resources文件夹下新建services/${name}-${id}.json文件,name与id为内容的值,内容如下:

{"@class": "org.apereo.cas.services.RegexRegisteredService","serviceId": "^(https|imaps|http)://.*","name": "HTTPandIMAPS","id": 10000001,"description": "This service definition authorizes all application urls that support HTTP and IMAPS protocols.","evaluationOrder": 10000
}
​

该文件主要是针对其他服务注册到CAS Server中的校验。

项目启动

找到cas-server-webapp-init依赖包中的CasWebApplication类,直接启动即可。

自定义扩展

目前就阐述一下在开发过程中可能存在的一些扩展功能。在CAS Server中无法使用@Configuration以及类扫描方式进行注入,只能通过spring.factories文件注册的方式进行注入(即Spring Boot自定配置方式注入)。

自定义登陆校验方式

目前CAS本身也支持很多三方校验扩展,比如MySQL数据库校验、Ldap校验等等,但是那些都存在一定的限制性。

先创建一个自定义校验处理器ExampleAuthenticationHandler,该类继承AbstractUsernamePasswordAuthenticationHandler,实现对应方法即可;主要校验逻辑在authenticateUsernamePasswordInternal方法中,自定义内容如下。

    @Overrideprotected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(UsernamePasswordCredential credential, String originalPassword) throws GeneralSecurityException, PreventedException {LOGGER.info("The is example authentication...");// 登陆校验// ....// 返回成果结果AuthenticationHandlerExecutionResult result = new DefaultAuthenticationHandlerExecutionResult((AuthenticationHandler) credential,new BasicCredentialMetaData((Credential) this.principalFactory.createPrincipal(credential.getUsername())));return result;}

在这个类中可以使用任意方式的进行校验账号密码是否可正常登录;如果校验失败,则直接报错即可(这里可以自定义一些登陆报错异常类)。

需将该校验类注入到CAS本身的校验计划中,新建一个计划配置类,实现AuthenticationEventExecutionPlanConfigurer接口,内容如下:

public class ExampleAuthenticationEventExecutionPlanConfiguration implements AuthenticationEventExecutionPlanConfigurer {
​@Autowiredprivate ServicesManager servicesManager;
​@Overridepublic void configureAuthenticationExecutionPlan(AuthenticationEventExecutionPlan plan) throws Exception {ExampleAuthenticationHandler myAuthenticationHandler = new ExampleAuthenticationHandler(ExampleAuthenticationHandler.class.getName(),servicesManager, new DefaultPrincipalFactory(), 1);plan.registerAuthenticationHandler(myAuthenticationHandler);}
}

需在resources下新建META-INF/spring.factories,并写入如下内容:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.example.config.ExampleAuthenticationEventExecutionPlanConfiguration

自定义API接口

在一些特定的场景下,需要CAS Server提供一些API接口,由于CAS Server使用Spring Security进行接口权限控制,如果使用Spring Security的方式进行放开的话,即继承WebSecurityConfigurerAdapter类,将放开的路径写入会发现毫无作用,只能通过CAS Server提供接口来实现接口的放开。

新建的接口类型需手动配置在配置类中。

新建类实现ProtocolEndpointWebSecurityConfigurer接口,将需要放开的API接口写入:

public class SecurityConfig implements ProtocolEndpointWebSecurityConfigurer {@Overridepublic List<String> getIgnoredEndpoints() {List<String> ignoreList = new ArrayList<>();ignoreList.add("/web/");return ignoreList;}
​
}

将该类写入到spring.factories文件中。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.example.config.ExampleAuthenticationEventExecutionPlanConfiguration,\com.example.config.SecurityConfig

在服务启动的日志中,就能看到/web/开头的接口已被Spring Security放开;

自定义登录参数

当前都是使用username以及password参数进行校验的,在一些业务场景下,登录界面需要会存在需校验验证码、IP地址等一些信息,这就涉及到前端页面登陆改动,以及后端需进行参数绑定,由于CAS Server是通过Spring WebFlow进行页面参数的绑定。

前端自定义界面更改
覆盖页面

如果是界面改动较大的话,可以直接覆盖CAS本身的前端页面,前端页面文件都在cas-server-support-thymeleaf依赖中,

可以将其完全覆盖,如果不愿意覆盖并想修改登陆页面时,可以将登陆页面进行修改;新建一个登录页在login/casLoginView1.html,新建一个类继承DefaultLoginWebflowConfigurer类,用于指定自定义页面

public class ExampleDefaultLoginWebflowConfigurer extends DefaultLoginWebflowConfigurer {
​public ExampleDefaultLoginWebflowConfigurer(FlowBuilderServices flowBuilderServices, FlowDefinitionRegistry flowDefinitionRegistry, ConfigurableApplicationContext applicationContext, CasConfigurationProperties casProperties) {super(flowBuilderServices, flowDefinitionRegistry, applicationContext, casProperties);}
​@Overrideprotected void createLoginFormView(Flow flow) {val propertiesToBind = CollectionUtils.wrapList("username", "password", "source");val binder = createStateBinderConfiguration(propertiesToBind);
​casProperties.getView().getCustomLoginFormFields().forEach((field, props) -> {val fieldName = String.format("customFields[%s]", field);binder.addBinding(new BinderConfiguration.Binding(fieldName, props.getConverter(), props.isRequired()));});// 指定登录页面val state = createViewState(flow, CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM, "login/casLoginView1", binder);
​state.getRenderActionList().add(createEvaluateAction(CasWebflowConstants.ACTION_ID_RENDER_LOGIN_FORM));createStateModelBinding(state, CasWebflowConstants.VAR_ID_CREDENTIAL, UsernamePasswordCredential.class);
​val transition = createTransitionForState(state, CasWebflowConstants.TRANSITION_ID_SUBMIT, CasWebflowConstants.STATE_ID_REAL_SUBMIT);val attributes = transition.getAttributes();attributes.put("bind", Boolean.TRUE);attributes.put("validate", Boolean.TRUE);attributes.put("history", History.INVALIDATE);}
}

在配置类中将该配置注入到容器中,用于覆盖CAS原来注入的类,所以Bean的名称一定修改为defaultWebflowConfigurer。

    @Bean(name = "defaultWebflowConfigurer")@Order(Ordered.HIGHEST_PRECEDENCE)public CasWebflowConfigurer defaultWebflowConfigurer(final ConfigurableApplicationContext applicationContext,final CasConfigurationProperties casProperties,@Qualifier(CasWebflowConstants.BEAN_NAME_LOGIN_FLOW_DEFINITION_REGISTRY)final FlowDefinitionRegistry loginFlowRegistry,@Qualifier(CasWebflowConstants.BEAN_NAME_LOGOUT_FLOW_DEFINITION_REGISTRY)final FlowDefinitionRegistry logoutFlowRegistry,@Qualifier(CasWebflowConstants.BEAN_NAME_FLOW_BUILDER_SERVICES)final FlowBuilderServices flowBuilderServices) {val c = new ExampleDefaultLoginWebflowConfigurer(flowBuilderServices, loginFlowRegistry, applicationContext, casProperties);c.setLogoutFlowDefinitionRegistry(logoutFlowRegistry);c.setOrder(Ordered.HIGHEST_PRECEDENCE);return c;}
通过CAS本身扩展原有页面

如果前端仅仅是加一个字段或者表格,可以通过CAS本身的一个配置进行扩展

在后台配置文件中添加如下配置:

cas.view.custom-login-form-fields.address.messageBundleKey=address
cas.view.custom-login-form-fields.address.required=false

配置含义在登陆界面为扩展一个address字段,属性为非必填,但是前端输入框默认为文本框。

后端绑定自定义属性

如果前端采用扩展原有的登录页面的时候,那么提交过来的数据在后端会自定绑定在UsernamePasswordCredential.customFields中,在自定义校验处理类中,可以获取到customFields的数据。

如果前端是通过覆盖的形式新增登录表单参数时,后端需重写UsernamePasswordCredential用于新的参数绑定,

新建一个类继承UsernamePasswordCredential,并且新增自定义参数,

public class ExampleUsernamePasswordCredential extends UsernamePasswordCredential {
​private String address;
​public String getAddress() {return address;}
​public void setAddress(String address) {this.address = address;}
​public ExampleUsernamePasswordCredential(String username, String password, String address) {super(username, password);this.address = address;}
​@Overridepublic void validate(ValidationContext context) {super.validate(context);}
}
​

在之前的那个指定登录页的ExampleDefaultLoginWebflowConfigurer配置类中,需将address参数绑定到页面上,并且更改绑定的Credential

    @Overrideprotected void createLoginFormView(Flow flow) {val propertiesToBind = CollectionUtils.wrapList("username", "password", "source");val binder = createStateBinderConfiguration(propertiesToBind);
​casProperties.getView().getCustomLoginFormFields().forEach((field, props) -> {val fieldName = String.format("customFields[%s]", field);binder.addBinding(new BinderConfiguration.Binding(fieldName, props.getConverter(), props.isRequired()));});// 绑定address参数binder.addBinding(new BinderConfiguration.Binding("address", null, false));val state = createViewState(flow, CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM, "login/casLoginView", binder);
​state.getRenderActionList().add(createEvaluateAction(CasWebflowConstants.ACTION_ID_RENDER_LOGIN_FORM));// 更改为自定义CredentialcreateStateModelBinding(state, CasWebflowConstants.VAR_ID_CREDENTIAL, ExampleUsernamePasswordCredential.class);
​val transition = createTransitionForState(state, CasWebflowConstants.TRANSITION_ID_SUBMIT, CasWebflowConstants.STATE_ID_REAL_SUBMIT);val attributes = transition.getAttributes();attributes.put("bind", Boolean.TRUE);attributes.put("validate", Boolean.TRUE);attributes.put("history", History.INVALIDATE);}// 用于更改createFlowVariable@Overrideprotected void createRememberMeAuthnWebflowConfig(Flow flow) {if (casProperties.getTicket().getTgt().getRememberMe().isEnabled()) {createFlowVariable(flow, CasWebflowConstants.VAR_ID_CREDENTIAL, RememberMeUsernamePasswordCredential.class);val state = getState(flow, CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM, ViewState.class);val cfg = getViewStateBinderConfiguration(state);cfg.addBinding(new BinderConfiguration.Binding("rememberMe", null, false));} else {// 更改为自定义CredentialcreateFlowVariable(flow, CasWebflowConstants.VAR_ID_CREDENTIAL, ExampleUsernamePasswordCredential.class);}}

后续还会继续分享CAS相关的东西,大家可以一起讨论

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

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

相关文章

Open CASCADE学习|在给定的TopoDS_Shape中查找与特定顶点 V 对应的TopoDS_Edge编号

enum TopAbs_ShapeEnum{TopAbs_COMPOUND,TopAbs_COMPSOLID,TopAbs_SOLID,TopAbs_SHELL,TopAbs_FACE,TopAbs_WIRE,TopAbs_EDGE,TopAbs_VERTEX,TopAbs_SHAPE}; 这段代码定义了一个名为 TopAbs_ShapeEnum 的枚举类型&#xff0c;它包含了表示不同几何形状类型的常量。这些常量通常…

目标检测——色素性皮肤病数据集

一、重要性及意义 首先&#xff0c;色素性皮肤病变是一类常见的皮肤疾病&#xff0c;其发病率有逐年增高的趋势。这些病变可能由遗传或环境因素导致黑素细胞生成异常&#xff0c;如黑色素瘤等。黑色素瘤具有极高的恶性率和致死率&#xff0c;而且恶化可能性大&#xff0c;容易…

openplc Linux 地址映射io,读写驱动数据等使用记录

1. 上一篇记录 openplc使用C语言文件读写驱动实现基本流程。 openPLC_Editor C语言编程 在mp157 arm板上调用io等使用记录_openplc c 编程-CSDN博客 2. 下面通过映射地址的方式控制io和读写驱动数据。 在runtime 环境的 hardware 硬件配置中 选择 python on Linux(PSM)&#…

柱状图中最大的矩形-java

题目描述(力扣题库 84): 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 解题思想: 单调栈: 利用先进后出的思想, 先算出长度更高的柱子所能勾勒…

MXNet的下载安装及问题处理

1、MXNet介绍&#xff1a; MXNet是一个开源的深度学习框架&#xff0c;以其灵活性和效率著称&#xff0c;支持多种编程接口&#xff0c;包括Python、C、R、Julia、Scala等。MXNet支持大规模分布式训练&#xff0c;同时兼顾CPU和GPU的计算资源&#xff0c;尤其擅长于模型并行和数…

1.0-spring入门

文章目录 前言一、版本要求二、第一个spring程序1.引入pom2.代码部分2.1 spring bean2.2 springContext.xml2.3 测试2.4 执行结果 总结 前言 最近想要系统的学习下spring相关的框架,于是乎,来到了B站(真是个好地方),spring会专门开一个专栏出来,记录学习心得,与大家共勉。 Spri…

实景三维技术在推进城市全域数字化转型的作用

4月2日&#xff0c;国家数据局发布《深化智慧城市发展推进城市全域数字化转型的指导意见&#xff08;征求意见稿&#xff09;》&#xff08;下称&#xff1a;《指导意见》&#xff09;&#xff0c;向社会公开征求意见。 《指导意见》作为推进城市数字化转型的重要文件&#xf…

Java学习笔记24(面向对象编程(高级))

1.面向对象编程(高级) 1.1 类变量和类方法 1.类变量 ​ *类变量也叫静态变量/静态属性&#xff0c;是该类的所有对象共享的变量&#xff0c;任何一个该类的对象去访问它时&#xff0c;取到的都是相同的值&#xff0c;同样任何一个该类的对象去修改它时&#xff0c;修改的也是…

C语言数据结构(11)——归并排序

欢迎来到博主的专栏C语言数据结构 博主ID&#xff1a;代码小豪 文章目录 归并排序两个有序数组的合并归并归并排序 归并排序的代码 归并排序 两个有序数组的合并 当前有两个有序数组arr1和arr2&#xff0c;我们创建一个可以容纳arr1和arr2同等元素个数的新数组arr。 让一个…

探索数据结构:特殊的双向队列

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;数据结构与算法 贝蒂的主页&#xff1a;Betty’s blog 1. 双向队列的定义 **双向队列(double‑ended queue)**是一种特殊的队列…

js中使let关键字报错,改用var关键字解决

js中使let关键字报错,改用var关键字解决 项目场景&#xff1a;问题描述原因分析&#xff1a;解决方案&#xff1a;总结 项目场景&#xff1a; 使用 let 关键字报错&#xff0c;报错信息为&#xff1a; Uncaught ReferenceError: maxNum is not defined at getMaxNum (4-3.htm…

Open-GroundingDino和GroundingDino的推理流程实现

1、简单介绍 GroundingDino是一个多模态检测模型&#xff0c;可以输入文本提示输出视觉目标的位置&#xff0c;实现了文本和图像的匹配。相比较于一众的OVD算法&#xff0c;GroundingDino在文本处理上的灵活度高&#xff0c;因为大多OVD算法是采用clip文本编码器&#xff0c;这…

Stable Diffusion介绍

Stable Diffusion是一种前沿的开源深度学习模型框架&#xff0c;专门设计用于从文本描述生成高质量的图像。这种称为文本到图像生成的技术&#xff0c;利用了大规模变换器&#xff08;transformers&#xff09;和生成对抗网络&#xff08;GANs&#xff09;的力量&#xff0c;以…

7(8)-2-CSS 盒子模型

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 CSS 盒子模型1 盒子模型&#xff08;Box Model&#xff09;组成2 边框&#x…

软考程序员2024年5月报名流程及注意事项

2024年5月软考程序员报名入口&#xff1a; 中国计算机技术职业资格网&#xff08;http://www.ruankao.org.cn/&#xff09; 2024年软考报名时间暂未公布&#xff0c;考试时间上半年为5月25日到28日&#xff0c;下半年考试时间为11月9日到12日。不想错过考试最新消息的考友可以…

多线程4

死锁 想获取到第二把锁&#xff0c;就需要执行完第一层大括号&#xff0c;想要执行完第一层大括号&#xff0c;就要先获取到第二层的锁。 synchronized (counter2){ synchronized (counter2){} } 例子:t2先启动&#xff0c;t2进行加锁后一定成功&#xff0c;但是如果t2进行二…

图数据库技术:知识图谱的存储与查询

图数据库技术&#xff1a;知识图谱的存储与查询 一、引言 在探索知识的宇宙中&#xff0c;知识图谱是组织和理解海量信息的星系图。在这张图中&#xff0c;每一个概念、实体与事物不再是孤立的点&#xff0c;而是通过关系与边相互连接&#xff0c;形成一个复杂而有机的网络。图…

计算机网络练习-计算机网络概述与性能指标

计算机网络概述 ----------------------------------------------------------------------------------------------------------------------------- 1. 计算机网络最据本的功能的是( )。 1,差错控制 Ⅱ.路由选择 Ⅲ,分布式处理 IV.传输控制 …

3.网络编程-TCP

目录 TCP 建立连接的过程是怎样的 TCP为什么是三次握手 TCP 断开连接的过程是怎样的 TCP挥手为什么需要四次 为什么TIME_WAIT等待的时间是2MSL TCP详解之滑动窗口 TCP 半连接队列和全连接队列是什么 TCP粘包&#xff0c;拆包是怎么发生的&#xff0c;如何解决 TCP是如何…

书生·浦语大模型实战营之茴香豆:搭建你的 RAG 智能助理

书生浦语大模型实战营之茴香豆&#xff1a;搭建你的 RAG 智能助理 RAG&#xff08;Retrieval Augmented Generation&#xff09;技术&#xff0c;通过检索与用户输入相关的信息&#xff0c;并结合外部知识库来生成更准确、更丰富的回答。解决 LLMs 在处理知识密集型任务时可能遇…