Wiki Markup |
---|
{span:style=font-size:2em;font-weight:bold} JAX-RS: SAML {span} |
Table of Contents |
---|
Introduction
...
Maven dependencies
Code Block | ||||
---|---|---|---|---|
| ||||
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-security-xml</artifactId>
<version>2.5.0</version>
</dependency>
|
...
Enveloped SAML assertions
Payload:
Code Block | ||||
---|---|---|---|---|
| ||||
<env:Envelope xmlns:env="http://org.apache.cxf/rs/env">
<Book ID="67ca6441-0c4e-4430-af0e-9463ce9226aa">
<id>125</id>
<name>CXF</name>
</Book>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<!-- Book signature, omitted for brewity -->
</ds:Signature>
<!-- SAML assertion with an enveloped signature -->
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_62D574706635C0B9F413203247720501" IssueInstant="2011-11-03T12:52:52.050Z" Version="2.0" xsi:type="saml2:AssertionType">
<saml2:Issuer>https://idp.example.org/SAML2</saml2:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#_62D574706635C0B9F413203247720501">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>IDD9nFocVm/7FpUbiGI3ZvpY2ps=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>JA2I7u/SmNsXGgWNdrLSovkipiM3JmGHsmpoP0EeIOwPwnLMx0WvV0C3xNGNiT1jOBe2uv8+WchtPoppGTC2JTJVX/t8PmKQCYZo4kVJo6Nmsjbn5kp7ejWuOYynvrUheQeTLU8e5CQmuS6L4VYaMVV2ETtb0VvpKjoQKHOC+co=</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate><!-- Omitted for brewity --> </ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
<saml2:Subject>
<saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" NameQualifier="www.mock-sts.com">uid=sts-client,o=mock-sts.com</saml2:NameID>
<saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:sender-vouches"/>
</saml2:Subject>
<saml2:Conditions NotBefore="2011-11-03T12:52:52.063Z" NotOnOrAfter="2011-11-03T12:52:52.063Z">
<saml2:AudienceRestriction>
<saml2:Audience>https://sp.example.com/SAML2</saml2:Audience>
</saml2:AudienceRestriction>
</saml2:Conditions>
<saml2:AuthnStatement AuthnInstant="2011-11-03T12:52:51.981Z" SessionIndex="123456">
<saml2:AuthnContext><saml2:AuthnContextClassRef/></saml2:AuthnContext>
</saml2:AuthnStatement>
<saml2:AttributeStatement>
<saml2:Attribute FriendlyName="subject-role"
Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role"
NameFormat="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">
<saml2:AttributeValue xsi:type="xs:string">user</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="http://claims/authentication"
NameFormat="http://claims/authentication-format">
<saml2:AttributeValue xsi:type="xs:string">password</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
</saml2:Assertion>
</env:Envelope>
|
...
Here is another payload showing the whole enveloped signed including Book and SAML Assertion, this time only a single signature will be available:
Code Block | ||||
---|---|---|---|---|
| ||||
<env:Envelope xmlns:env="http://org.apache.cxf/rs/env" ID="e795cdd1-c19d-4a5c-8d86-e8a781af4787">
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_C76E3D5BBEE4C4D87913203281641141" IssueInstant="2011-11-03T13:49:24.114Z" Version="2.0" xsi:type="saml2:AssertionType">
<saml2:Issuer>https://idp.example.org/SAML2</saml2:Issuer>
<saml2:Subject>
<saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" NameQualifier="www.mock-sts.com">uid=sts-client,o=mock-sts.com</saml2:NameID>
<saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:sender-vouches"/>
</saml2:Subject>
<saml2:Conditions NotBefore="2011-11-03T13:49:24.127Z" NotOnOrAfter="2011-11-03T13:49:24.127Z">
<saml2:AudienceRestriction>
<saml2:Audience>https://sp.example.com/SAML2</saml2:Audience>
</saml2:AudienceRestriction>
</saml2:Conditions>
<saml2:AuthnStatement AuthnInstant="2011-11-03T13:49:24.044Z" SessionIndex="123456">
<saml2:AuthnContext>
<saml2:AuthnContextClassRef/>
</saml2:AuthnContext>
</saml2:AuthnStatement>
<saml2:AttributeStatement>
<saml2:Attribute FriendlyName="subject-role" Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role" NameFormat="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">user</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="http://claims/authentication" NameFormat="http://claims/authentication-format">
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">password</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
</saml2:Assertion>
<Book>
<id>125</id>
<name>CXF</name>
</Book>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><ds:Reference URI="#e795cdd1-c19d-4a5c-8d86-e8a781af4787"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>GR1pHd2JpxYiCzl6ouCmTZjq/AA=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>C2qUDOFwart2GHFjX6kB3E3z73AMXtRR/6Qjgyp6XP/vTn/Fr2epDNub3q+gNdT0KgjLE2rSynM3QTcpHov9C8l9a8VQquItaalr0XA7BJcxdFMxB7KEATKR9XtrmIEkiw9efM8M83iVux/ufCOWrt0Te2RLz+nRwzyEY49VQOQ=</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate><!-- Omitted for brewity --></ds:X509Certificate></ds:X509Data><ds:KeyValue><ds:RSAKeyValue><ds:Modulus>vu747/VShQ85f16DGSc4Ixh9PVpGguyEqrCsK8q9XHOYX9l9/g5wEC6ZcR2FwfNsoaHcKNPjd5sSTzVtBWmQjfBEfIqwTR7vuihOxyNTwEzVwIJzvo7p8/aYxk+VdBtQxq4UweIcf/iFkUbM1cZ1oiXRQzciRBi+C1BQCQE0qzs=</ds:Modulus><ds:Exponent>AQAB</ds:Exponent></ds:RSAKeyValue></ds:KeyValue></ds:KeyInfo></ds:Signature></env:Envelope>
|
Server configuration fragment:
Code Block | ||||
---|---|---|---|---|
| ||||
<bean id="serviceBean" class="org.apache.cxf.systest.jaxrs.security.BookStore"/>
<bean id="samlHandler" class="org.apache.cxf.rs.security.saml.SamlEnvelopedInHandler"/>
<!-- only needed if the detached signature signing the application data is expected -->
<bean id="xmlSigHandler" class="org.apache.cxf.rs.security.xml.XmlSigInHandler"/>
<jaxrs:server
address="https://localhost:${testutil.ports.jaxrs-saml}/samlxml">
<jaxrs:serviceBeans>
<ref bean="serviceBean"/>
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref bean="xmlSigHandler"/>
<ref bean="samlHandler"/>
</jaxrs:providers>
<jaxrs:properties>
<entry key="ws-security.signature.properties"
value="org/apache/cxf/systest/jaxrs/security/alice.properties"/>
</jaxrs:properties>
</jaxrs:server>
|
Client code:
Code Block | ||||
---|---|---|---|---|
| ||||
private WebClient createWebClient(String address,
boolean selfSigned) {
JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
bean.setAddress(address);
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("ws-security.callback-handler",
"org.apache.cxf.systest.jaxrs.security.saml.KeystorePasswordCallback");
properties.put("ws-security.saml-callback-handler",
"org.apache.cxf.systest.jaxrs.security.saml.SamlCallbackHandler");
properties.put("ws-security.signature.username", "alice");
properties.put("ws-security.signature.properties",
"org/apache/cxf/systest/jaxrs/security/alice.properties");
if (selfSigned) {
properties.put("ws-security.self-sign-saml-assertion", "true");
}
bean.setProperties(properties);
bean.getOutInterceptors().add(new SamlEnvelopedOutInterceptor(!selfSigned));
XmlSigOutInterceptor xmlSig = new XmlSigOutInterceptor();
if (selfSigned) {
xmlSig.setStyle(XmlSigOutInterceptor.DETACHED_SIG);
}
return bean.createWebClient();
}
|
...
Logging output:
Code Block | ||||
---|---|---|---|---|
| ||||
Address: https://localhost:9000/samlheader/bookstore/books/123
Http-Method: GET
Headers: {Accept=[application/xml], Authorization=[SAML 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], ...}
|
...
Server configuration is similar to the one from the Enveloped SAML Assertions section, the only difference is that a SAML handler needs to be replaced:
Code Block | ||||
---|---|---|---|---|
| ||||
<bean id="serviceBean" class="org.apache.cxf.systest.jaxrs.security.BookStore"/>
<bean id="samlHandler" class="org.apache.cxf.rs.security.saml.SamlHeaderInHandler"/>
<!-- same as in the Enveloped SAML Assertions section -->
|
Client code:
Code Block | ||||
---|---|---|---|---|
| ||||
private WebClient createWebClient(String address,
boolean selfSigned) {
JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
bean.setAddress(address);
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("ws-security.callback-handler",
"org.apache.cxf.systest.jaxrs.security.saml.KeystorePasswordCallback");
properties.put("ws-security.saml-callback-handler",
"org.apache.cxf.systest.jaxrs.security.saml.SamlCallbackHandler");
properties.put("ws-security.signature.username", "alice");
properties.put("ws-security.signature.properties",
"org/apache/cxf/systest/jaxrs/security/alice.properties");
if (selfSigned) {
properties.put("ws-security.self-sign-saml-assertion", "true");
}
bean.setProperties(properties);
bean.getOutInterceptors().add(new SamlHeaderOutInterceptor());
return bean.createWebClient();
}
|
...
Logging output:
Code Block | ||||
---|---|---|---|---|
| ||||
Address: https://localhost:9000/samlform/bookstore/books
Encoding: ISO-8859-1
Http-Method: POST
Content-Type: application/x-www-form-urlencoded
Headers: {Accept=[application/xml], Cache-Control=[no-cache], connection=[keep-alive], Content-Length=[2206], content-type=[application/x-www-form-urlencoded], Host=[localhost:9000], Pragma=[no-cache], User-Agent=[Apache CXF ${project.version}]}
Payload: name=CXF&id=125&SAMLToken=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
|
...
Server configuration is similar to the one from the Enveloped SAML Assertions section, the only difference is that a SAML handler needs to be replaced:
Code Block | ||||
---|---|---|---|---|
| ||||
<bean id="serviceBean" class="org.apache.cxf.systest.jaxrs.security.BookStore"/>
<bean id="samlHandler" class="org.apache.cxf.rs.security.saml.SamlFormInHandler"/>
<!-- same as in the Enveloped SAML Assertions section -->
|
The client code is the same as in the SAML assertions in Authorization header section except than an instance of SamlFormOutInterceptor has to be registered:
Code Block | ||||
---|---|---|---|---|
| ||||
bean.getOutInterceptors().add(new SamlFormOutInterceptor());
|
...
When SAML assertions are received on the server side, they are validated to make sure that the enveloped signatures are correct. SubjectConfirmation methods (sender-vouches, holder-of-key, bearer) are also checked.
The validation can be delegated to STS if needed. By default, server side SAML handlers have a "samlValidator" property set to an instance of org.apache.ws.security.validate.SamlAssertionValidator which does a thorough validation of the assertion. If needed org.apache.cxf.ws.security.trust.STSSamlAssertionValidator STSTokenValidator can be set instead which will use STS to validate the assertion.
Custom validators extending WSS4J SamlAssertionValidator and doing the additional application-specific validation can be registered if needed.
...
SAML assertions may contain so-called claims which are represented by a sequence of SAML AttributeStatements containing one or more Attributes, for example:
Code Block | ||||
---|---|---|---|---|
| ||||
<saml2:Assertion>
<!-- ... -->
<saml2:AttributeStatement>
<saml2:Attribute NameFormat="http://schemas.xmlsoap.org/ws/2005/05/identity/claims"
Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role"
FriendlyName="subject-role">
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">user</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute NameFormat="http://claims/authentication"
Name="http://claims/authentication-format">
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">password</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
<!-- ... -->
</saml2:Assertion>
|
...
Here is a simple code fragment:
Code Block | ||||
---|---|---|---|---|
| ||||
import org.apache.cxf.rs.security.saml.authorization.Claim;
import org.apache.cxf.rs.security.saml.authorization.Claims;
@Path("/bookstore")
public class SecureClaimBookStore {
@POST
@Path("/books")
@Produces("application/xml")
@Consumes("application/xml")
@Claims({
@Claim({"admin" }),
@Claim(name = "http://claims/authentication-format",
format = "http://claims/authentication",
value = {"fingertip", "smartcard" })
})
public Book addBook(Book book) {
return book;
}
}
|
...
Note that in the above example, a Claim with the name "http://claims/authentication-format" has two values, 'fingertip' and 'smartcard'. By default, in order to meet this Claim, Subject needs to have a Claim which has either a 'fingertip' or 'smartcard' value. If it is expected that Subject needs to have a Claim which has both 'fingertip' and 'smartcard' values, then the following change needs to be done:
Code Block | ||||
---|---|---|---|---|
| ||||
import org.apache.cxf.security.claims.authorization.Claim;
import org.apache.cxf.security.claims.authorization.Claims;
@Path("/bookstore")
public class SecureClaimBookStore {
@POST
@Path("/books")
@Produces("application/xml")
@Consumes("application/xml")
@Claims({
@Claim({"admin" }),
@Claim(name = "http://claims/authentication-format",
format = "http://claims/authentication",
value = {"fingertip", "smartcard" },
matchAll = true)
})
public Book addBook(Book book) {
return book;
}
}
|
Claims can be specified using individual @Claim annotation, they can be set at the class level and overridden at the method level and finally a lax mode of check can be specified:
Code Block | ||||
---|---|---|---|---|
| ||||
import org.apache.cxf.security.claims.authorization.Claim;
import org.apache.cxf.security.claims.authorization.Claims;
@Path("/bookstore")
@Claim({"user"})
public class SecureClaimBookStore {
@POST
@Path("/books")
@Produces("application/xml")
@Consumes("application/xml")
@Claims({
@Claim({"admin" }),
@Claim(name = "http://claims/authentication-format",
format = "http://claims/authentication",
value = {"fingertip", "smartcard" },
matchAll = true)
})
public Book addBook(Book book) {
return book;
}
@GET
@Claim(name = "http://claims/authentication-format",
format = "http://claims/authentication",
value = {"password" },
mode = ClaimMode.LAX)
public Book getBook() {
//...
}
@GET
public BookList getBookList() {
//...
}
}
|
...
org.apache.cxf.rs.security.saml.authorization.ClaimsAuthorizingInterceptor enforces the CBAC rules. This filter can be overridden and configured with the rules directly which can be useful if no Claim-related annotations are expected in the code. Map nameAliases and formatAliases properties are supported to make @Claim annotations look a bit simpler, for example:
Code Block | ||||
---|---|---|---|---|
| ||||
@Claim(name = "auth-format", format = "authentication", value = {"password" })
|
...
Have a look please at this server configuration example:
Code Block | ||||
---|---|---|---|---|
| ||||
<bean id="serviceBeanClaims" class="org.apache.cxf.systest.jaxrs.security.saml.SecureClaimBookStore"/>
<bean id="samlEnvHandler" class="org.apache.cxf.rs.security.saml.SamlEnvelopedInHandler">
<property name="securityContextProvider">
<bean class="org.apache.cxf.systest.jaxrs.security.saml.CustomSecurityContextProvider"/>
</property>
</bean>
<bean id="claimsHandler"
class="org.apache.cxf.rs.security.saml.authorization.ClaimsAuthorizingFilter">
<property name="securedObject" ref="serviceBeanClaims"/>
</bean>
<jaxrs:server address="/saml-claims">
<jaxrs:serviceBeans>
<ref bean="serviceBeanClaims"/>
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref bean="samlEnvHandler"/>
<ref bean="claimsHandler"/>
</jaxrs:providers>
</jaxrs:server>
|
...
For example, given this code:
Code Block | ||||
---|---|---|---|---|
| ||||
import org.springframework.security.annotation.Secured;
@Path("/bookstore")
@Claim({"user"})
public class SecureBookStore {
@POST
@Secured("admin")
public Book addBook(Book book) {
return book;
}
}
|
where @Secured can be replaced with @RoledAllowed if needed, the following configuration will do it:
Code Block | ||||
---|---|---|---|---|
| ||||
<bean id="serviceBeanRoles" class="org.apache.cxf.systest.jaxrs.security.saml.SecureBookStore"/>
<bean id="samlEnvHandler" class="org.apache.cxf.rs.security.saml.SamlEnvelopedInHandler">
<property name="securityContextProvider">
<bean class="org.apache.cxf.systest.jaxrs.security.saml.CustomSecurityContextProvider"/>
</property>
</bean>
<bean id="authorizationInterceptor" class="org.apache.cxf.interceptor.security.SecureAnnotationsInterceptor">
<property name="securedObject" ref="serviceBean"/>
<property name="annotationClassName"
value="org.springframework.security.annotation.Secured"/>
</bean>
<bean id="rolesHandler" class="org.apache.cxf.jaxrs.security.SimpleAuthorizingFilter">
<property name="interceptor" ref="authorizationInterceptor"/>
</bean>
<jaxrs:server address="/saml-roles">
<jaxrs:serviceBeans>
<ref bean="serviceBeanRoles"/>
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref bean="samlEnvHandler"/>
<ref bean="rolesHandler"/>
</jaxrs:providers>
<!-- If default role qualifier and format are not supported:
<jaxrs:properties>
<entry key="org.apache.cxf.saml.claims.role.nameformat"
value="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"/>
<entry key="org.apache.cxf.saml.claims.role.qualifier"
value="urn:oid:1.3.6.1.4.1.5923.1.1.1.1"/>
</jaxrs:properties>
-->
</jaxrs:server>
|
...