OpenSAML 使用引导 III: Service Provider 的实现之Artifact与断言

前文OpenSAML 使用引导 II : Service Provider 的实现之AuthnRequest介绍从Service Provider(SP)角度出发,讲解如何使用OpenSAML如申请身份鉴别请求,并从IDP出得到断言的引用标识——SAML Artifact,本文将继续讨论Artifact的具体意义,如何使用Artifact换取断言信息,以及断言的使用方法。

相关阅读

  1. SAML2.0入门指南,
  2. OpenSAML 使用引导 I : 简介

源码地址:https://github.com/sunrongxin7666/OpenSAML-ref-project-demo-v3.git

更新 2017-11-20
回答网友xiajale的提问:为什么使用Artifact Binding,详情请见第六节

4. 第四步和第五步:The Artifact and Artifact Resolution

Artifact Resolve
Artifact Resolve Request

这里跳过了关于第三步身份认证的讨论,这是因为关于用户身份的认证完全取决于idp,和SAML协议本身没有关系。

当用户通过身份鉴别之后,Idp会为认证信息(断言)分配一个标识,这个标识被称为ArtifactArtifact将以URL参数的形式和用户一起被发送回SP。

SP为了通过Artifact得到真正的认证信息,需要构建ArtifactResolve对象,ArtifactResolve中包含ArtifactArtifactResolve通过SOAP协议发送给Idp,Idp返回ArtifactResolveResponse,其中就包含了SAML Assertion,即认证信息。
由此可见Artifact对象至关重要,下面就来讨论Artifact的细节。

Artifact

Artifact是对一个SAML对象的引用。SAML规定Artifact中必须包括以下内容:

Artifact

  • TypeCode规定了当前Artifact的类型,不同类型的Artifact对于Remaining Artifact的格式有着不同的要求;
  • EndPointIndex代表着metadata中定义的Web服务终端标识,在用Artifact兑换真正的SAML对象时使用;
  • Artifact是这些部分Base64编码后的连续字节;
  • 推荐的Artifact类型是type4
Artifact-type4
  • SourceID是发送者实体的ID被SHA-1哈希值;
  • MessageHandle是发送方对于SMAL消息真实的引用,为至少16位随机标识,不足20位的应该补全;

Artifact绑定(传输)

Artifact绑定是传输SAML信息(借助于其引用Artifact)的一种方式,通过HTTP客户端(如浏览器)来工作。传递Artifact有两种方式,HTTP POST和HTTP重定向。随后Artifact来换取真正的SAML消息,这一步靠SP和IDP可信信道来完成,比如SOAP信道。Artifact绑定的使用是因为SAML消息中有敏感信息,直接通过浏览器传送SAML消息并不安全。所以通过HTTP重定向传输Artifact,也就是说Artifact被当成URL中的参数传输。

创建ArtifactResolve

当SP得到Artifact之后,根据Artifact构造ArtifactResolve来请求真正的SAML消息。

Artifact artifact = OpenSAMLUtils.buildSAMLObject(Artifact.class); 
artifact.setArtifact(httpServletRequest.getParameter("SAMLart"));

然后构建ArtifactResolve对象

ArtifactResolve artifactResolve = OpenSAMLUtils.buildSAMLObject(ArtifactResolve.class);

接下来开始这是ArtfactResolve的各种属性:

  • Issuer:发送方的身份表示,同
    AuthnRequest中的issuer;
Issuer issuer = OpenSAMLUtils.buildSAMLObject(Issuer.class);
issuer.setValue(SPConstants.SP_ENTITY_ID);
authnRequest.setIssuer(issuer);
  • Time of the Request
artifactResolve.setIssueInstant(new DateTime());
  • ID of the request:
artifactResolve.setID(OpenSAMLUtils.generateSecureRandomId());
  • destination URL
artifactResolve.setDestination(getIPDArtifactResloveDestination());

最后将Artifact加入其中:

artifactResolve.setArtifact(artifact);

形成的Artifact Request XML如下:


Artifact Request

使用SOAP协议发送 ArtifactResolve

SOAP简单的理解,就是这样的一个开放协议SOAP=RPC+HTTP+XML:采用HTTP作为底层通讯协议;RPC作为一致性的调用途径,XML作为数据传送的格式,允许服务提供者和服务客户经过防火墙在INTERNET进行通讯交互。
浅谈SOAP
SOAP 教程

RCP(Rmote Procedure Call):远端程序调用,像调用本地对象那样调用远端的程序(方法),为同步过程,会拥塞调用代码的执行。

RPC vs REST : RPC,是面向服务的,关于行为和动作;REST面向资源的,强调描述应用程序的事务和名词。和REST相比,SOAP的优势在于能保证事物的原子性和消息可靠性,且对于数据完整性和数据隐私性的有很完备标准。

  1. REST Vs SOAP,Soap 和 Rest 的区别
  2. WebService的两种方式SOAP和REST比较
  3. REST与SAOP的比较

AuthnRequest类似,发送ArtifactResolve也是通过message context

MessageContext<ArtifactResolve> contextout 
    = new MessageContext<ArtifactResolve>();
contextout.setMessage(artifactResolve);

不过对于SOAP消息,没有强制的内容需要添加到环境上下文中,但是建议加入数据签名以增强安全性。

SignatureSigningParameters sigParameters = new SignatureSigningParameters();
sigParameters.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
sigParameters.setSigningCredential(SPCredentials.getCredential());
sigParameters.setSignatureCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);

SecurityParametersContext securityParametersContext = contextout.getSubcontext(SecurityParametersContext.class,true);
securityParametersContext.setSignatureSigningParameters(signatureSigningParameters);

因为涉及到两方的通信,还需要创建InOutOperationContext来处理输入输出的信息。

InOutOperationContext<ArtifactResponse, ArtifactResolve> context
    = new ProfileRequestContext<ArtifactResponse, ArtifactResolve>();
context.setOutboundMessageContext(contextout);

为了能发送SOAP消息,还需要设置SOAP Client。这个Client将会调用消息的处理器,编码器以及解码等来传送消息。这些内容将在下一章中详谈。

创建SOAP Client的具体做法为继承AbstractPipelineHttpSOAPClient并实现newPipline方法,来返回管道发送消息。

AbstractPipelineHttpSOAPClient<SAMLObject, SAMLObject> soapClient = new AbstractPipelineHttpSOAPClient() {
    protected HttpClientMessagePipeline newPipeline() throws SOAPException {
        //创建输入输出用的编码器和解码器
        HttpClientRequestSOAP11Encoder encoder
        = new HttpClientRequestSOAP11Encoder();
        HttpClientResponseSOAP11Decoder decoder
        = new HttpClientResponseSOAP11Decoder();
        //创建管道
        BasicHttpClientMessagePipeline pipeline
        = new BasicHttpClientMessagePipeline(
                encoder,
                decoder
        );
        //为输出的内容签名
        pipeline.setOutboundPayloadHandler(
        new SAMLOutboundProtocolMessageSigningHandler());
        return pipeline;
    }
}
// HTTP帮助SOAPClient编码和解码
HttpClientBuilder clientBuilder = new HttpClientBuilder(); soapClient.setHttpClient(clientBuilder.buildClient());
//发送soap消息
soapClient.send(IDPConstants.ARTIFACT_RESOLUTION_SERVICE, context);

SOAP消息发送之后,会同步等待Response返回或者超时。当Response返回时,SAML消息便可或得到:

return context.getInboundMessageContext().getMessage();

ArtifactResponse实例如下:

Artifact Resolve Response XML With Encrypted Assertion
加密断言部分

关于如何加密断言,将在下一个大章节中详细分析。

使用 Message Handlers 处理 SAML 消息

新版本OpenSAML的一大特点就是消息处理器容器(collection of message handlers)。这些消息处理来处消息并提供丰富的方法,如验证消息的有效性,验证签名,签名等等。处理器一般在解码器或者编码器之前调用。以下是一些可用的消息处理器:

  • MessageLifetimeSecurityHandler:生命周期验证,要求SAMLMessageInfoContext包含issue time;
  • SAMLOutboundProtocolMessageSigningHandler: 输出消息签名,要求SecurityParametersContext包含singing参数
  • ReceivedEndpointSecurityHandler:验证消息目的地址,要求base message context包含SAML消息,必需的信息可以从中提取出来;

消息处理器可以直接调用:

SAMLOutboundProtocolMessageSigningHandler handler = new SAMLOutboundProtocolMessageSigningHandler();
handler.initialize();
handler.invoke(context);

也可以在Client的实现中隐式调用,比如在PipelineHttpSOAPClient中那样。

当有多个处理器被调用时,可以使用BasicMessageHandlerChain来批量初始化和调用。

List handlers = new ArrayList<MessageHandler>(); 
handlers.add(handler1);
handlers.add(handler2);

BasicMessageHandlerChain<ArtifactResponse> handlerChain = new BasicMessageHandlerChain<ArtifactResponse>();
handlerChain.setHandlers(handlers);

以下是使用消息处理器的实例:
创建环境上下文:

MessageContext context = new MessageContext<ArtifactResponse>();
context.setMessage(artifactResponse);

将消息配置给SAMLMessageInfoContext,获得Issue内容:

SAMLMessageInfoContext messageInfoContext 
    = context.getSubcontext(SAMLMessageInfoContext.class, true);
messageInfoContext.setMessageIssueInstant(
    artifactResponse.getIssueInstant());

设置MessageLifetimeSecurityHandler

MessageLifetimeSecurityHandler lsHandler = new MessageLifetimeSecurityHandler();
lsHandler.setClockSkew(1000);
lsHandler.setMessageLifetime(2000);
lsHandler.setRequiredRule(true);

设置ReceivedEndpointSecurityHandler

ReceivedEndpointSecurityHandler receivedEndpointSecurityHandler = new ReceivedEndpointSecurityHandler();
receivedEndpointSecurityHandler.setHttpServletRequest(request);

将以上处理器放入处理器链中,批量处理:

List handlers = new ArrayList<MessageHandler>();
handlers.add(lifetimeSecurityHandler); 
handlers.add(receivedEndpointSecurityHandler); 

BasicMessageHandlerChain<ArtifactResponse> handlerChain = new BasicMessageHandlerChain<ArtifactResponse>(); 
handlerChain.setHandlers(handlers); 

handlerChain.initialize(); 
handlerChain.doInvoke(context); 

5. 断言Assertion

SAML断言是认证的实体,其包含关于用户和身份鉴别的信息。以下来简单介绍下断言的内容。

断言用来承载安全信息,接收者可以根据这样的信息来做出安全访问控制决定。断言中所包含的安全信息被称为断言声明(assertion statement),共有三种类型:

  • 认证声明:包含关于用户身份鉴别的信息,比如用户被认证身份的时间和方法;
  • 属性声明: 包含关于用户相关的属性信息,比如用户的姓名、手机号、邮箱和地址等等。这些属性被IDP定义为KEY—Value的形式
  • 授权决定声明:确认当前用户是否授权来访问一个特殊的资源。身份鉴别决定只能提供最基本的鉴别能力,更为复杂情况推荐使用XACML(一种用于决定请求/响应的通用访问控制策略语言和执行授权策略的框架)。

5.1 断言的使用

断言在接收时是经过加密和签名的,这里对其的时候都是假设已经解密并通过签名验证的。


断言1

断言2

断言3

断言里包含很多的信息,下面将会展示从断言里提取需要的信息。

用户身份验证的时间在AuthnStatement字段的AuthnInstant 属性中

logger.info("Authentication instant: "
    +assertion.getAuthnStatements().get(0).getAuthnInstant());

用户被认证的方式在AuthnStatement子字段AuthnContextAuthnContextClassRef属性值,标识认证的方式:

logger.info("Authentication method: "
    + assertion.getAuthnStatements().get(0).getAuthnContext()
    + .getAuthnContextClassRef().getAuthnContextClassRef());

AttributeStatement之中包含很多属性,都是Key_Value的形式:

for(Attribute attribute :
    assertion.getAttributeStatements()
        .get(0).getAttributes()) {
    logger.info("Attribute name: " + attribute.getName());
    for (XMLObject attributeValue : 
        attribute.getAttributeValues())
    {
        logger.info("Attribute value:"
            + ((XSString)attributeValue).getValue());
    }
}

6. 为什么使用Artifact Binding模式

SAML协议中如何使用Artifact Binding模式请求并获得断言流程已经为大家介绍完毕。不过有的读者可能有疑问, 为什么要使用Artifact Binding模式,HTTP POST或者HTTP重定向直接获取断言信息不是更简单吗?的确是更简单,但是这样做有安全性的隐患

其实SAML2.0中支持很多种绑定方式,如下都方式在OpenSAML中都有实现:

  • SAML SOAP Binding (based on SOAP 1.1)
  • Reverse SOAP (PAOS) Binding
  • HTTP Redirect (GET) Binding
  • HTTP POST Binding
  • HTTP Artifact Binding
  • SAML URI Binding

目前,基于浏览器的SSO中,HTTP POST和HTTP Redirect的模式用的最多,但是浏览器本身有很多的限制和缺陷,使得有时候不便直接通过浏览器获得身份认证断言:

  1. URL中的查询字符串(Query String)或者POST payload有长度限制,无法装下断言信息(断言信息是XML格式,在签名加密之后往往很大);
  2. 可能被注入JavaScript脚本,易遭受攻击,如跨站脚本攻击和跨站请求攻击;所以无法断定浏览器发出的请求是合法的,断言内的一些敏感信息不希望暴露给浏览器;
  3. 很多网站现在都是前后端分离开发,如果通过浏览器获得断言信息,前端代码还需要将断言信息通过API发送给后端,增加了断言信息在网络中的传输流程,也增加断言被窃取和篡改的风险(对于XML结构的数字签名有漏洞XML wrapping attacks)。

使用Artifact Binding就可以避免以上问题:

  • 浏览器只是获得断言信息的引用(Artifact),而不是真正的断言,避免敏感信息暴露给浏览器,从而防止浏览器中潜在的风险和被攻击点;
  • Artifact换取断言的过程使用SOAP协议,不受HTTP请求中长度的限制;
  • SP向IDP换取断言的过程是服务器到服务器之间通信,可以使用HTTPS来确认双发的身份(浏览器中HTTPS往往只是单方向验证服务的身份,而没有验证浏览器的),非法的SP将无法获得真正的断言信息,并通信的过程加密传输,保证断言的安全;
  • Artifact是一次性,被使用过后就是无效的,无法重放攻击;
  • 断言信息的请求和使用相分离,减少对于浏览器的依赖,更适用于前后端分离的SP;

其实如果熟悉OAuth中的授权码模式和默认模式的区别,SAML中使用Artifact或是HTTP Redirect也是异曲同工,依据使用场景不同而定。

更多关于SAML协议的是实现的内容,请参见本人编写的一系列教程文章,其介绍如何使用OpenSAML,欢迎阅读指正:

  1. OpenSAML 使用引导 I : 简介
  2. OpenSAML 使用引导 II : Service Provider 的实现之AuthnRequest
  3. OpenSAML 使用引导 III: Service Provider 的实现之Artifact与断言
  4. OpenSAMl 使用引导IV: 安全特性
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容