SAML2.0使用

article/2025/11/4 22:36:41

最近在工作中和海外一家公司对接单点登录,用到了SAML2.0协议,目前公司的单点登录
还是比较老的CASE3.5版本,不支持SAML2,要支持也要定制优,由于后面肯定是要升级,所
以不在源码上做调整支持,单独建了个SSO应用作为CASE客户端,并包装客户的接口,登录
还是用CASE认证。
由于客户没有完全实现SAML2.0(SP)的功能, IDP由我司CASE提供,我司SSO应用其实是半个SP的功能,提供给页面访问并认证,认证成功后response给客户,客户校验通过后才返回页面,
在这里插入图片描述

  1. 推荐一个很好的SAML工具,想测试一个SAML2的接口很容易,支持多种加密
    工具链接[https://www.samltool.com/generic_sso_res.php]

    Reponse各种示例SAML Response 各种示例签名工具:
    在这里插入图片描述

2.代码示例
下面是groovy代码,java类似

import org.springframework.boot.context.properties.ConfigurationProperties@ConfigurationProperties(prefix = "sso")
class SSOProperties {String responseAdpString audienceAdpString relayStateAdpboolean testAdpString[] apps
}
@Service("adpService")
@Slf4j
@EnableConfigurationProperties([SSOProperties.class])
class AdpService implements SamlService{@ResourceSSOProperties ssoProperties@ResourceAccountService accountService
def getSamlResponse() {String username = AssertionHolder.getAssertion().getPrincipal().getName()log.info("username: ${username}")String jobNo = accountService.getJobNo(username)def model = [:]//签名消息def xml = signedResponse(jobNo)//返回页面form提交的参数model.put("samlResponse", Base64.encodeBytes(xml.getBytes()))model.put("relayState", ssoProperties.getRelayStateAdp())model.put("redirectUrl", ssoProperties.getResponseAdp())model}
def signedResponse(String userId){String destination = ssoProperties.getResponseAdp()final Response samlResponse = SamlHelper.buildResponse(UUIDFactory.INSTANCE.getUUID(), destination)DateTime notBefore =  new DateTime(2018, 10, 19, 1, 0, 0, 0, ISOChronology.getInstanceUTC())DateTime notOnOrAfter = new DateTime(2021, 10, 19, 1, 0, 0, 0, ISOChronology.getInstanceUTC())String audienceURI = ssoProperties.getAudienceAdp()Assertion assertion = SamlHelper.buildAssertion(samlResponse, userId,audienceURI,  notBefore, notOnOrAfter)AttributeStatement attributeStatement = SamlHelper.buildAttributeStatement("PersonImmutableID", userId)assertion.getAttributeStatements().add(attributeStatement)SamlHelper.signXMLObject(assertion)samlResponse.getAssertions().add(assertion)def xml = SamlHelper.buildXMLObjectToString(samlResponse)return xml}}
@Slf4j
abstract class SamlHelper {static final XMLObjectBuilderFactory builderFactorystatic {try {DefaultBootstrap.bootstrap()} catch (ConfigurationException e) {log.error(e.getMessage(), e)}Security.addProvider(new BouncyCastleProvider())builderFactory = Configuration.getBuilderFactory()}static String buildXMLObjectToString(XMLObject xmlObject) {Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(xmlObject)Element authDOMtry {authDOM = marshaller.marshall(xmlObject)StringWriter rspWrt = new StringWriter()XMLHelper.writeNode(authDOM, rspWrt)String messageXML = rspWrt.toString()return messageXML} catch (MarshallingException e) {throw new RuntimeException(e)}}static XMLObject buildStringToXMLObject(String xmlObjectString) {try {BasicParserPool parser = new BasicParserPool()parser.setNamespaceAware(true)String xmlString = decode64SAMLResponse(xmlObjectString)Document doc = (Document) parser.parse(new ByteArrayInputStream(xmlString.getBytes()))Element samlElement = (Element) doc.getDocumentElement()Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory().getUnmarshaller(samlElement)return unmarshaller.unmarshall(samlElement)} catch (XMLParserException e) {throw new RuntimeException(e)} catch (UnmarshallingException e) {throw new RuntimeException(e)}}static AuthnRequest buildAuthnRequest(String ticket, String setAssertionConsumerServiceURL) {NameID nameid = (NameID) buildXMLObject(NameID.DEFAULT_ELEMENT_NAME)nameid.setFormat(NameID.UNSPECIFIED)nameid.setValue(ticket)Subject subject = (Subject) buildXMLObject(Subject.DEFAULT_ELEMENT_NAME)subject.setNameID(nameid)Audience audience = (Audience) buildXMLObject(Audience.DEFAULT_ELEMENT_NAME)audience.setAudienceURI(Constants.LOCALDOMAIN)AudienceRestriction ar = (AudienceRestriction) buildXMLObject(AudienceRestriction.DEFAULT_ELEMENT_NAME)ar.getAudiences().add(audience)Conditions conditions = (Conditions) buildXMLObject(Conditions.DEFAULT_ELEMENT_NAME)conditions.getAudienceRestrictions().add(ar)AuthnContextClassRef classRef = (AuthnContextClassRef) buildXMLObject(AuthnContextClassRef.DEFAULT_ELEMENT_NAME)classRef.setAuthnContextClassRef("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport")RequestedAuthnContext rac = (RequestedAuthnContext) buildXMLObject(RequestedAuthnContext.DEFAULT_ELEMENT_NAME)rac.getAuthnContextClassRefs().add(classRef)AuthnRequest request = (AuthnRequest) buildXMLObject(AuthnRequest.DEFAULT_ELEMENT_NAME)request.setSubject(subject)request.setConditions(conditions)request.setRequestedAuthnContext(rac)request.setForceAuthn(false)request.setAssertionConsumerServiceURL(setAssertionConsumerServiceURL)request.setAttributeConsumingServiceIndex(0)request.setProviderName("IDP Provider")request.setID("_" + UUIDFactory.INSTANCE.getUUID())request.setVersion(SAMLVersion.VERSION_20)request.setIssueInstant(new DateTime(2005, 1, 31, 12, 0, 0, 0, ISOChronology.getInstanceUTC()))request.setDestination(Constants.LOCALDOMAIN)request.setConsent("urn:oasis:names:tc:SAML:2.0:consent:obtained")Issuer rIssuer = (Issuer) buildXMLObject(Issuer.DEFAULT_ELEMENT_NAME)rIssuer.setFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:entity")rIssuer.setValue(Constants.LOCALDOMAIN)request.setIssuer(rIssuer)return request}static Response buildResponse(String requestId, String destination) {Response response = (Response) buildXMLObject(Response.DEFAULT_ELEMENT_NAME)Namespace namespace = new Namespace("urn:oasis:names:tc:SAML:2.0:assertion", "saml2")response.addNamespace(namespace)response.setID(UUIDFactory.INSTANCE.getUUID())
//        response.setInResponseTo(requestId)response.setDestination(destination)Calendar now = DateUtil.getUTCCalendar()response.setIssueInstant(new DateTime(now.get(Calendar.YEAR), (now.get(Calendar.MONTH) + 1), now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY), now.get(Calendar.MINUTE), now.get(Calendar.SECOND), 0, ISOChronology.getInstanceUTC()))Issuer rIssuer = (Issuer) buildXMLObject(Issuer.DEFAULT_ELEMENT_NAME)// rIssuer.setFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:entity")rIssuer.setValue(Constants.ISSUER)rIssuer.removeNamespace(new Namespace("urn:oasis:names:tc:SAML:2.0:assertion", "saml2"))response.setIssuer(rIssuer)Status status = (Status) buildXMLObject(Status.DEFAULT_ELEMENT_NAME)StatusCode statusCode = (StatusCode) buildXMLObject(StatusCode.DEFAULT_ELEMENT_NAME)statusCode.setValue("urn:oasis:names:tc:SAML:2.0:status:Success")response.setStatus(status)status.setStatusCode(statusCode)return response}static Assertion buildAssertion(Response response, String nameIdValue, String audienceURI, DateTime notBefore, DateTime notOnOrAfter) {Calendar now = DateUtil.getUTCCalendar()Assertion assertion = (Assertion) buildXMLObject(Assertion.DEFAULT_ELEMENT_NAME)assertion.setID(UUIDFactory.INSTANCE.getUUID())assertion.setIssueInstant(new DateTime(now.get(Calendar.YEAR), (now.get(Calendar.MONTH) + 1), now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY), now.get(Calendar.MINUTE), now.get(Calendar.SECOND), ISOChronology.getInstanceUTC()))
//        assertion.removeNamespace(new Namespace("urn:oasis:names:tc:SAML:2.0:assertion", "saml2"))assertion.addNamespace(new Namespace("http://www.w3.org/2001/XMLSchema-instance", "xsi"))assertion.addNamespace(new Namespace("http://www.w3.org/2001/XMLSchema", "xs"))AuthnStatement authnStatement = (AuthnStatement) buildXMLObject(AuthnStatement.DEFAULT_ELEMENT_NAME)authnStatement.setAuthnInstant(new DateTime(now.get(Calendar.YEAR), (now.get(Calendar.MONTH) + 1), now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY), now.get(Calendar.MINUTE), now.get(Calendar.SECOND), ISOChronology.getInstanceUTC()))authnStatement.setSessionIndex(UUIDFactory.INSTANCE.getUUID())now.add(Calendar.MINUTE, 2)DateTime sessionNotOnOrAfter = new DateTime(now.get(Calendar.YEAR), (now.get(Calendar.MONTH) + 1), now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY), now.get(Calendar.MINUTE), now.get(Calendar.SECOND), ISOChronology.getInstanceUTC())authnStatement.setSessionNotOnOrAfter(sessionNotOnOrAfter)AuthnContext authnContext = (AuthnContext) buildXMLObject(AuthnContext.DEFAULT_ELEMENT_NAME)AuthnContextClassRef classRef = (AuthnContextClassRef) buildXMLObject(AuthnContextClassRef.DEFAULT_ELEMENT_NAME)classRef.setAuthnContextClassRef("urn:oasis:names:tc:SAML:2.0:ac:classes:Password")authnContext.setAuthnContextClassRef(classRef)authnStatement.setAuthnContext(authnContext)assertion.getAuthnStatements().add(authnStatement)Issuer aIssuer = (Issuer) buildXMLObject(Issuer.DEFAULT_ELEMENT_NAME)
//        aIssuer.setFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:entity")aIssuer.setValue(Constants.ISSUER)assertion.setIssuer(aIssuer)Subject subject = (Subject) buildXMLObject(Subject.DEFAULT_ELEMENT_NAME)NameID nameID = (NameID) buildXMLObject(NameID.DEFAULT_ELEMENT_NAME)nameID.setFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified")nameID.setSPNameQualifier(audienceURI)nameID.setValue(nameIdValue)SubjectConfirmation subjectConfirmation = (SubjectConfirmation) buildXMLObject(SubjectConfirmation.DEFAULT_ELEMENT_NAME)subjectConfirmation.setMethod("urn:oasis:names:tc:SAML:2.0:cm:bearer")subject.setNameID(nameID)SubjectConfirmationData subjectConfirmationData = (SubjectConfirmationData) buildXMLObject(SubjectConfirmationData.DEFAULT_ELEMENT_NAME)subjectConfirmationData.setNotOnOrAfter(sessionNotOnOrAfter)subjectConfirmationData.setRecipient(response.getDestination())
//        subjectConfirmationData.setInResponseTo(response.getInResponseTo())subjectConfirmation.setSubjectConfirmationData(subjectConfirmationData)subject.getSubjectConfirmations().add(subjectConfirmation)assertion.setSubject(subject)Conditions conditions = (Conditions) buildXMLObject(Conditions.DEFAULT_ELEMENT_NAME)conditions.setNotBefore(notBefore)conditions.setNotOnOrAfter(notOnOrAfter)AudienceRestriction audienceRestriction = (AudienceRestriction) buildXMLObject(AudienceRestriction.DEFAULT_ELEMENT_NAME)Audience audience = (Audience) buildXMLObject(Audience.DEFAULT_ELEMENT_NAME)audience.setAudienceURI(audienceURI)audienceRestriction.getAudiences().add(audience)conditions.getAudienceRestrictions().add(audienceRestriction)assertion.setConditions(conditions)assertion}static void signXMLObject(SignableXMLObject signableXMLObject) {SignatureBuilder signatureBuilder = (SignatureBuilder) builderFactory.getBuilder(Signature.DEFAULT_ELEMENT_NAME)BasicCredential basicCredential = new BasicCredential()basicCredential.setPrivateKey(CertificateHelper.getRSAPrivateKey())Signature signature = signatureBuilder.buildObject()signature.setCanonicalizationAlgorithm(Constants.CANON_ALGORITHM)signature.setSignatureAlgorithm(Constants.SIGNATURE_METHOD)signature.setSigningCredential(basicCredential)signableXMLObject.setSignature(signature)MarshallerFactory marshallerFactory = Configuration.getMarshallerFactory()Marshaller marshaller = marshallerFactory.getMarshaller(signableXMLObject)try {marshaller.marshall(signableXMLObject)Signer.signObject(signature)} catch (MarshallingException e) {log.error(e.getMessage(), e)throw new RuntimeException("XML Marshalling failure")} catch (SignatureException e) {log.error(e.getMessage(), e)throw new RuntimeException("Signature failure")}}static Attribute buildStringAttribute(String name, String value) {Attribute attribute = (Attribute) buildXMLObject(Attribute.DEFAULT_ELEMENT_NAME)attribute.setName(name)attribute.setNameFormat("urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified")XMLObjectBuilder<?> stringBuilder = builderFactory.getBuilder(XSString.TYPE_NAME)XSString ldapAttribValue = (XSString) stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME)ldapAttribValue.removeNamespace(new Namespace("http://www.w3.org/2001/XMLSchema", "xs"))ldapAttribValue.setValue(value)attribute.getAttributeValues().add(ldapAttribValue)return attribute}static AttributeStatement buildAttributeStatement() {return (AttributeStatement) buildXMLObject(AttributeStatement.DEFAULT_ELEMENT_NAME)}static AttributeStatement buildAttributeStatement(String name, String value) {AttributeStatement attributeStatement = buildAttributeStatement()Attribute attribute = buildStringAttribute(name, value)attributeStatement.getAttributes().add(attribute)attributeStatement}static String buildArtifactResolve(Artifact artifact) {ArtifactResolve artifactResolve = (ArtifactResolve) buildXMLObject(ArtifactResolve.DEFAULT_ELEMENT_NAME)artifactResolve.setArtifact(artifact)return buildXMLObjectToString(artifactResolve)}static SSODescriptor buildSSODescriptor(String xmlFilePath, Class<?> descriptorType) {EntityDescriptor entityDescriptor = (EntityDescriptor) unmarshallElementWithXMLFile(xmlFilePath)if (descriptorType.getClass().getName().equals(IDPSSODescriptor.class.getName())) {return entityDescriptor.getIDPSSODescriptor("urn:oasis:names:tc:SAML:2.0:protocol")}return entityDescriptor.getSPSSODescriptor("urn:oasis:names:tc:SAML:2.0:protocol")}static X509Certificate getX509Certificate(String xmlFilePath) {SSODescriptor _SPSSODescriptor = buildSSODescriptor(xmlFilePath, SPSSODescriptor.class)List<KeyDescriptor> keyDescriptors = _SPSSODescriptor.getKeyDescriptors()KeyDescriptor keyDescriptor = keyDescriptors.get(0)KeyInfo keyInfo = keyDescriptor.getKeyInfo()List<X509Data> x509Datas = keyInfo.getX509Datas()List<X509Certificate> x509Certificates = x509Datas.get(0).getX509Certificates()X509Certificate x509Certificate = x509Certificates.get(0)return x509Certificate}static String decode64SAMLResponse(String samlResponse) {byte[] decodedBytes = Base64.decode(samlResponse)return new String(decodedBytes)}static def buildXMLObject(QName objectQName) {XMLObjectBuilder<?> builder = Configuration.getBuilderFactory().getBuilder(objectQName)return builder.buildObject(objectQName.getNamespaceURI(), objectQName.getLocalPart(), objectQName.getPrefix())}static XMLObject unmarshallElementWithXMLFile(String elementFile) {try {BasicParserPool parser = new BasicParserPool()parser.setNamespaceAware(true)Document doc = parser.parse(SamlHelper.class.getResourceAsStream(elementFile))Element samlElement = doc.getDocumentElement()Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory().getUnmarshaller(samlElement)return unmarshaller.unmarshall(samlElement)} catch (XMLParserException e) {throw new RuntimeException(e)} catch (UnmarshallingException e) {throw new RuntimeException(e)}}/*** 加密断言* @param assertion* @param receiverCredential* @return*/static EncryptedAssertion encrypt(Assertion assertion, X509Credential receiverCredential) {Credential symmetricCredentialEncryptedAssertion encrypted = nulltry {symmetricCredential = SecurityHelper.getSimpleCredential(SecurityHelper.generateSymmetricKey(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128))EncryptionParameters encParams = new EncryptionParameters()encParams.setAlgorithm(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128)encParams.setEncryptionCredential(symmetricCredential)KeyEncryptionParameters kek = new KeyEncryptionParameters()kek.setAlgorithm(EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSA15)kek.setEncryptionCredential(receiverCredential)Encrypter encrypter = new Encrypter(encParams, kek)encrypter.setKeyPlacement(KeyPlacement.INLINE)encrypted = encrypter.encrypt(assertion)} catch (NoSuchAlgorithmException | KeyException e) {log.error(e.getMessage(), e)} catch (EncryptionException e) {log.error(e.getMessage(), e)}return encrypted}/*** 解密断言* @param enc* @param credential* @param federationMetadata* @return*/static Assertion decrypt(EncryptedAssertion enc, Credential credential, String federationMetadata) {KeyInfoCredentialResolver keyResolver = new StaticKeyInfoCredentialResolver(credential)EncryptedKey key = enc.getEncryptedData().getKeyInfo().getEncryptedKeys().get(0)Decrypter decrypter = new Decrypter(null, keyResolver, new InlineEncryptedKeyResolver())decrypter.setRootInNewDocument(true)SecretKey dkeyAssertion assertion = nulltry {dkey = (SecretKey) decrypter.decryptKey(key, enc.getEncryptedData().getEncryptionMethod().getAlgorithm())Credential shared = SecurityHelper.getSimpleCredential(dkey)decrypter = new Decrypter(new StaticKeyInfoCredentialResolver(shared), null, null)decrypter.setRootInNewDocument(true)assertion = decrypter.decrypt(enc)} catch (DecryptionException e) {log.error(e.getMessage(), e)}return assertion}/*** 签名断言* @param enc* @param credential* @param federationMetadata* @return*/static Signature signature() {SignatureBuilder signatureBuilder = (SignatureBuilder) builderFactory.getBuilder(Signature.DEFAULT_ELEMENT_NAME)BasicCredential basicCredential = new BasicCredential()Signature signature = signatureBuilder.buildObject()basicCredential.setPrivateKey(CertificateHelper.getRSAPrivateKey())signature.setCanonicalizationAlgorithm(Constants.CANON_ALGORITHM)signature.setSignatureAlgorithm(Constants.SIGNATURE_METHOD)return signature}/*** 验签断言* @param enc* @param credential* @param federationMetadata* @return*/static boolean validate(String base64Response) {SignableXMLObject signableXMLObject = (SignableXMLObject) buildStringToXMLObject(base64Response)return validate(signableXMLObject)}static boolean validate(SignableXMLObject signableXMLObject) {BasicCredential basicCredential = new BasicCredential()basicCredential.setPublicKey(CertificateHelper.getRSAPublicKey())SignatureValidator signatureValidator = new SignatureValidator(basicCredential)Signature signature = signableXMLObject.getSignature()try {signatureValidator.validate(signature)return true} catch (ValidationException e) {log.warn("验证签名错误" + e.getMessage())return false}}static Artifact buildArtifact() {String artifactId = UUIDFactory.INSTANCE.getUUID()Artifact artifact = (Artifact) buildXMLObject(Artifact.DEFAULT_ELEMENT_NAME)artifact.setArtifact(artifactId)return artifact}static ArtifactResolve buildArtifactResolve() {String artifactResolveId = UUIDFactory.INSTANCE.getUUID()ArtifactResolve artifactResolve = (ArtifactResolve) buildXMLObject(ArtifactResolve.DEFAULT_ELEMENT_NAME)artifactResolve.setID(artifactResolveId)Issuer aIssuer = (Issuer) buildXMLObject(Issuer.DEFAULT_ELEMENT_NAME)aIssuer.setFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:entity")aIssuer.setValue(Constants.LOCALDOMAIN)artifactResolve.setIssuer(aIssuer)artifactResolve.setVersion(SAMLVersion.VERSION_20)
//        artifactResolve.setDestination(Constants.SP_ARTIFACT_RESOLUTION_SERVICE)artifactResolve.setIssueInstant(new DateTime(2005, 1, 31, 12, 0, 0, 0, ISOChronology.getInstanceUTC()))return artifactResolve}static ArtifactResponse buildArtifactResponse() {String artifactResponseId = UUIDFactory.INSTANCE.getUUID()ArtifactResponse artifactResponse = (ArtifactResponse) buildXMLObject(ArtifactResponse.DEFAULT_ELEMENT_NAME)artifactResponse.setID(artifactResponseId)artifactResponse.setVersion(SAMLVersion.VERSION_20)artifactResponse.setIssueInstant(new DateTime(2005, 1, 31, 12, 0, 0, 0, ISOChronology.getInstanceUTC()))return artifactResponse}static AttributeQuery buildAttributeQuery() {AttributeQuery attributeQuery = (AttributeQuery) buildXMLObject(AttributeQuery.DEFAULT_ELEMENT_NAME)return attributeQuery}static Status getStatusCode(boolean success) {Status status = (Status) buildXMLObject(Status.DEFAULT_ELEMENT_NAME)StatusCode statusCode = (StatusCode) buildXMLObject(StatusCode.DEFAULT_ELEMENT_NAME)statusCode.setValue(success ? StatusCode.SUCCESS_URI : StatusCode.AUTHN_FAILED_URI)status.setStatusCode(statusCode)return status}}

页面部分代码

<form id="myForm" method="POST" th:action="${redirectUrl}"><input type="hidden" name="SAMLResponse" th:value="${samlResponse}" /><input type="hidden" name="RelayState" th:value="${relayState}" /><input type="submit" hidden value="Submit" /></form>
</body>
<script type="text/javascript" th:inline="javascript">window. function(){document.getElementById('myForm').submit();}</script>

POM.xml部分

<!-- case --><dependency><groupId>net.unicon.cas</groupId><artifactId>cas-client-autoconfig-support</artifactId><version>1.5.0-GA</version></dependency><!-- case --><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-web</artifactId></dependency><!-- spring boot --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>fluent-hc</artifactId><version>4.3.6</version></dependency><dependency><groupId>commons-httpclient</groupId><artifactId>commons-httpclient</artifactId><version>3.1</version></dependency><dependency><groupId>org.opensaml</groupId><artifactId>opensaml</artifactId><version>2.6.4</version></dependency>

http://chatgpt.dhexx.cn/article/pxDnyZuC.shtml

相关文章

TCP第四次挥手后为什么要等待2MSL后才断开链接?等待时间为什么是2MSL?

为何要等待2MSL&#xff1f; 1.假如第四次挥手失败了&#xff0c;因为丢失而未到达服务器会怎样呢&#xff1f;这样&#xff0c;服务器会一直收不到客户端的回应&#xff0c;也就无法得知客户端是否收到了即将要断开连接的请求。客户端此刻还蒙在鼓里&#xff0c;还在等待服务器…

为什么TIME_WAIT状态是2MSL?(2个原因)

为什么TIME_WAIT的时间是2MSL&#xff1f; 先来看看上文中TIME_WAIT状态存在的两个理由。 首先看理由1&#xff0c;为了可靠地实现全双工连接的终止&#xff0c;假设图2-5中客户端发送的最后一个ACK丢失&#xff0c;服务端将重传FIN&#xff0c;为了能够收到这个超时重传的FIN…

Time-wait状态(2MSL)

本文转自&#xff1a;https://blog.csdn.net/overstack/article/details/8833894&#xff0c;尊重原创 三次握手四次挥手图&#xff1a; time_wait之后会等2msl。 什么是2MSL&#xff1a; MSL是Maximum Segment Lifetime,译为“报文最大生存时间”&#xff0c;他是任何报文在…

【Linux网络编程】TCP状态转换、半关闭、2MSL时长

------------->【Linux系统编程/网络编程】(学习目录汇总) <-------------- 目录 1. 三次握手、四次挥手过程中的状态变化2. TCP状态转换图3. 半关闭4. 2MSL时长4.1 为什么要2MSL时长&#xff1f;4.2 端口复用 1. 三次握手、四次挥手过程中的状态变化 先结合下图回顾一下…

TIME_WAIT状态(2MSL)的作用

主动关闭的Socket端会进入TIME_WAIT状态&#xff0c;并且持续2MSL时间长度&#xff0c;MSL就是maximum segment lifetime(最大分节生命期&#xff09;&#xff0c;这是一个IP数据包能在互联网上生存的最长时间&#xff0c;超过这个时间将在网络中消失。MSL在RFC 1122上建议是2分…

【CSS】关于CSS的几种移动端布局方式

关于CSS的几种移动端布局方式 一、移动端布局01.meta视口标签设置02.移动布局的分类有哪些&#xff1f;03.为什么需要二倍图&#xff1f;&#xff08;1&#xff09;物理像素和物理像素比&#xff08;2&#xff09;二倍图&#xff08;根据需要确定多倍图&#xff09;&#xff08…

css:居中的几种布局方式

居中布局的方式 初始状态 <!DOCTYPE html> <html lang"en"> <head><style>.outer {width: 100px;height: 100px;border: 1px solid #f00;}.inner {width: 30px;height: 30px;background-color: #000;}</style> </head> <bo…

常见的CSS页面布局方式

详情&#xff1a;CSS页面结构是我们日常生活中最常使用到的&#xff0c;当然目前可能大家用的最多的是elementUI实现布局&#xff0c;简单方柏霓&#xff0c;下面介绍几种常见的原生页面布局的方式 公共的样式部分 <style>* {margin: 0;padding: 0;}.layout {margin-bot…

css的几种布局方式都在这

说道布局方式&#xff0c;是我们经常遇到的问题&#xff0c;下面我们就来讲解css的常见的一些布局方式。 1.双飞翼布局&#xff08;两边定宽&#xff0c;中间自适应&#xff09; 主要是通过浮动与margin实现&#xff0c;代码如下&#xff1a; <!DOCTYPE html PUBLIC &quo…

css横向布局的几种方式

首先我们先看看 html部分 bodyTip 内的三个标签我们需要让他们横向显示并且根据浏览器宽度平均显示 <body> <!--头部--> <div class"head">我是头部 </div> <div class"bodyTip"><!--左边--><div class"lef…

CSS 多种布局方式

​css布局是工作中最常碰到的&#xff0c;同时也是笔试 or 面试中会被问到的问题&#xff0c;故在本文整理了css多种布局方式&#xff0c;以供参考。 此篇较长四千五百字左右&#xff0c;读者可分三部分阅读&#xff0c;水平居中布局&#xff0c;垂直居中布局&#xff0c;水平…

HTML+CSS第十课:常见的3种网页布局方式:表格布局、DIV+CSS布局、框架布局

知识点:网页布局的方式 1、网页布局 常见的页面布局方式:表格布局、DIV+CSS布局、框架布局。 表格布局:用来显示较多的数据,如OA系统、ERP系统或CRM系统。(一般用在局部)DIV+CSS布局:相对来说最灵活的布局方式,完全实现内容和样式的分离。框架布局:通常用在网站后台…

css常见布局方式

css常见布局方式 0、前言1、两栏布局1.1 浮动 margin1.2 浮动 BFC&#xff08;overflow: hidden&#xff09;1.3 定位 margin-left1.4 给父容器设置flex布局&#xff0c;左盒子固定宽度&#xff0c;然后给右子元素设置 flex: 1。1.5 table布局 2、三栏布局2.1 float布局2.2 …

CSS布局的三种方式

绝对定位 绝对定位&#xff1a; ​ 属性&#xff1a;position 值&#xff1a;absolute <style> p.abs{position: absolute;left: 150px;top: 50px; }</style><p >正常文字1</p> <p >正常文字2</p> <p class"abs" >绝对定…

CSS五种布局方式

是CSS知识体系的重中之重 早期以table为主&#xff08;简单&#xff09; 后来以技巧性布局为主&#xff08;难&#xff09; 现在有flexbox/grid(偏简单) 响应式布局是必备知识 常用布局方法 table表格布局 <!DOCTYPE html> <html lang"en"> <head&…

CSS+DIV三种布局方式

在学习了盒模型、块级元素和行内元素得到概念后&#xff0c;我们来说一下CSS的一个比较重要的用途&#xff1a;布局。以前我们学过表格可以起到布局页面的作用&#xff0c;比如布局表单&#xff0c;但实际工作表格的布局通常也仅仅是用来布局表单。绝大多数的模具工作是由CSSDI…

Css 常用布局方式

1.CSS 参考手册 2.元素的分类 首先我们要知道一共有几种元素 1.行内元素&#xff08;可以与其他行内元素位于同一行&#xff0c;不会以新行开始高度、宽度不能设置&#xff09; 2.块级元素&#xff08;每个块级元素都从新的一行开始&#xff0c;其后的元素也另起一行。默认…

css五大布局方式详解

css布局方式 table布局float布局flex布局响应式布局Grid布局 table布局 table布局在如今已经很少使用&#xff0c;原因是&#xff1a;table布局比其它html标记占更多的字节&#xff0c;会阻挡浏览器渲染引擎的渲染顺序&#xff0c;会影响其内部的某些布局属性的生效。 使用…

css中常见的布局方式

1.流体布局 流体布局是网页缩小和放大时网页布局会随着浏览器的大小而改变。 两边的宽度是固定的&#xff0c;中间的宽度是可以根据屏幕的大小进行改变的 思路: 给左右两边的盒子设置固定的宽高并设置左右浮动&#xff0c;中间盒子通过设置margin-left和margin-right(margin的…

CSS的三大布局方式(流式布局,浮动布局和层布局)

文章目录 前言一、标准文档流二、三种布局方式1.流式布局2.浮动布局&#xff08;1&#xff09;字围效果&#xff08;2&#xff09;圣杯布局 3.层布局定位的分类&#xff1a;&#xff08;1&#xff09; 相对定位 position:relative&#xff08;2&#xff09; 绝对定位 position:…