Camel provides several forms and levels of security capabilities.
These various forms of security may be used in conjunction with each
other or separately.
The Camel security levels can be divided
into:
For more information visit Camel Security page .
Camel provides Policy driven security capabilities that may be wired
into routes or route segments. A Policy in Camel utilizes a strategy
pattern for applying interceptors on Camel Processors. It offering the
ability to apply cross-cutting concerns (for example. security,
transactions etc) on sections/segments of a Camel route.
The
components offering authentication and authorization Services
utilizing Route Policies are :
The shiro-security component in Camel is a security focused component,
based on the Apache Shiro security project. Apache Shiro is a powerful
and flexible open-source security framework that cleanly handles
authentication, authorization, enterprise session management and
cryptography. The objective of the Apache Shiro project is to provide
the most robust and comprehensive application security framework
available while also being very easy to understand and extremely
simple to use.
The component needs to be declared in the
pom.xml as the following:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-shiro</artifactId>
<version>x.x.x</version>
<!-- use the same version as your Camel core version -->
</dependency>
To use Shiro security on a Camel route, a ShiroSecurityPolicy object must be instantiated with security configuration details (including users, passwords, roles etc). This configuration is contained on an Ini file (properties file) or an Ini object. This object is then applied to a Camel route in order to check the user's authentication and authorization. This ShiroSecurityPolicy Object may also be registered in the Camel registry (JNDI or ApplicationContextRegistry) and then utilized on other routes in the Camel Context.
The configuration Ini file is a standard Shiro configuration file
containing user/role details as shown below :
[users] # user 'ringo' with password 'starr' and the 'sec-level1' role ringo = starr, sec-level1 george = harrison, sec-level2 john = lennon, sec-level3 paul = mccartney, sec-level3 [roles] # 'sec-level3' role has all permissions, indicated by the # wildcard '*' sec-level3 = * # The 'sec-level2' role can do anything with access of permission # readonly (*) to help sec-level2 = zone1:* # The 'sec-level1' role can do anything with access of permission # readonly sec-level1 = zone1:readonly:*
The following source code shows the ShiroSecurityPolicy class instanciation using the Shiro configuration file, the passPhrase and a specified permission list. And finally, adds the ShiroSecurityPolicy object in the Camel Registry.
// Shiro configuration file path
private static final String iniResourcePath = "classpath:shiro/securityPolicy.ini";
// Object used to encrypt/decrypt the token in the message.
private static final byte[] passPhrase = {
(byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B, (byte) 0x0C, (byte) 0x0D, (byte) 0x0E, (byte) 0x0F,
(byte) 0x10, (byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17};
...
public static void main(String[] args)
{
// Creation of the permission list supported by the policy.
List<Permission> permissionsList = new ArrayList<Permission>();
Permission permission = new WildcardPermission("zone1:readwrite:*");
permissionsList.add(permission);
ShiroSecurityPolicy policy = new ShiroSecurityPolicy(iniResourcePath, passPhrase, true, permissionsList);
// Add the policy to the Camel Registry.
SimpleRegistry reg = new SimpleRegistry();
reg.put("securityPolicy",policy);
...
}
This policy will be applied to a specified route and determine if the message can pass through it. The policy object is retrieved directly from the Camle Registry.
from("direct:secureEndpoint").to("log:Incoming payload").policy(
(Policy) context.getRegistry().lookup("securityPolicy")).to("log:success");
In this case, the message needs to contain the user credential that
will be checked in the policy. This user credential is contained in an
Security Token object. A TokenInjector object is created using this
token and encrypts it using the passPhrase. The message is processed
using the TokecnInjector object to inject the encrypted token on it.
The following source code shows this operation:
// The ShiroSecurityToken constructor receives the userName and the password as parameters.
ShiroSecurityToken shiroSecurityToken = new ShiroSecurityToken("paul", "mccartney");
// Encrypting the token using the passPhrase in the TokenInjector object.
ShiroSecurityTokenInjector shiroSecurityTokenInjector = new ShiroSecurityTokenInjector(shiroSecurityToken, passPhrase);
...
final CamelContext context = new DefaultCamelContext(reg);
try
{
context.addRoutes(new RouteBuilder()
{
public void configure() throws Exception
{
...
from("direct:client").process(shiroSecurityTokenInjector).to("direct:secureEndpoint");
...
}
});
We can the Camel Exception management to catch exceptions thrown by The ShiroSecurityPolicy object as follow :
onException(UnknownAccountException.class).handled(true).to("log:UnknownAccountException").to(
"file:Error/UnknownAccountException");
onException(IncorrectCredentialsException.class).handled(true).to("log:IncorrectCredentialsException")
.to("file:Error/IncorrectCredentialsException");
onException(LockedAccountException.class).handled(true).to("log:LockedAccountException").to(
"file:Error/LockedAccountException");
onException(AuthenticationException.class).handled(true).to("log:AuthenticationException").to(
"file:Error/AuthenticationException");
onException(CamelAuthorizationException.class).handled(true).to(
"log:CamelAuthorizationException ${in.header}").to("file:Error/CamelAuthorizationException");
onException(NullPointerException.class).handled(true).to("log:Message not secure").to(
"file:Error/NotSecureMessage");
public class CamelShiroExample
{
// Shiro configuration file path
private static final String iniResourcePath = "classpath:shiro/securityPolicy.ini";
// Object used to encrypt/decrypt the token in the message.
private static final byte[] passPhrase = {
(byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B, (byte) 0x0C, (byte) 0x0D, (byte) 0x0E, (byte) 0x0F,
(byte) 0x10, (byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17};
// list of token injectors used in this example
private static ShiroSecurityTokenInjector shiroSecurityTokenInjector;
private static ShiroSecurityTokenInjector shiroSecurityTokenInjector2;
private static ShiroSecurityTokenInjector shiroSecurityTokenInjector3;
/**
* @param args
* @throws Exception
*/
public static void main(String[] args)
{
// Creation of the permission list supported by the policy.
List<Permission> permissionsList = new ArrayList<Permission>();
Permission permission = new WildcardPermission("zone1:readwrite:*");
permissionsList.add(permission);
ShiroSecurityPolicy policy = new ShiroSecurityPolicy(iniResourcePath, passPhrase, true, permissionsList);
// Add the policy to the Camel Registry.
SimpleRegistry reg = new SimpleRegistry();
reg.put("securityPolicy", policy);
// Instanciating the Shiro tokens
// The ShiroSecurityToken constructor receives the userName and the password as
// parameters.
ShiroSecurityToken shiroSecurityToken = new ShiroSecurityToken("paul", "mccartney");
ShiroSecurityToken shiroSecurityToken2 = new ShiroSecurityToken("ringo", "starr");
ShiroSecurityToken shiroSecurityToken3 = new ShiroSecurityToken("ringo", "stirr");
// Instanciating the Shiro TokenInjectors objects
shiroSecurityTokenInjector = new ShiroSecurityTokenInjector(shiroSecurityToken, passPhrase);
shiroSecurityTokenInjector2 = new ShiroSecurityTokenInjector(shiroSecurityToken2, passPhrase);
shiroSecurityTokenInjector3 = new ShiroSecurityTokenInjector(shiroSecurityToken3, passPhrase);
final CamelContext context = new DefaultCamelContext(reg);
try
{
context.addRoutes(new RouteBuilder()
{
public void configure() throws Exception
{
onException(UnknownAccountException.class).handled(true).to("log:UnknownAccountException").to(
"file:Error/UnknownAccountException");
onException(IncorrectCredentialsException.class).handled(true).to("log:IncorrectCredentialsException")
.to("file:Error/IncorrectCredentialsException");
onException(LockedAccountException.class).handled(true).to("log:LockedAccountException").to(
"file:Error/LockedAccountException");
onException(AuthenticationException.class).handled(true).to("log:AuthenticationException").to(
"file:Error/AuthenticationException");
onException(CamelAuthorizationException.class).handled(true).to(
"log:CamelAuthorizationException ${in.header}").to("file:Error/CamelAuthorizationException");
onException(NullPointerException.class).handled(true).to("log:Message not secure").to(
"file:Error/NotSecureMessage");
from("direct:client").process(shiroSecurityTokenInjector).to("direct:secureEndpoint");
from("direct:client1").process(shiroSecurityTokenInjector2).to("direct:secureEndpoint");
from("direct:client2").process(shiroSecurityTokenInjector3).to("direct:secureEndpoint");
from("direct:secureEndpoint").to("log:Incoming payload").policy(
(Policy) context.getRegistry().lookup("securityPolicy")).to("log:success");
}
});
context.start();
ProducerTemplate template = context.createProducerTemplate();
Endpoint endpoint = context.getEndpoint("direct:client");
Exchange exchange = endpoint.createExchange();
exchange.getIn().setBody("Data");
template.send(endpoint, exchange);
Endpoint endpoint1 = context.getEndpoint("direct:client1");
Exchange exchange1 = endpoint1.createExchange();
exchange1.getIn().setBody("Data1");
template.send(endpoint1, exchange1);
Endpoint endpoint2 = context.getEndpoint("direct:client2");
Exchange exchange2 = endpoint2.createExchange();
exchange2.getIn().setBody("Data2");
template.send(endpoint2, exchange2);
Endpoint endpoint3 = context.getEndpoint("direct:secureEndpoint");
Exchange exchange3 = endpoint3.createExchange();
exchange3.getIn().setBody("Data3");
template.send(endpoint3, exchange3);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
The camel-spring-security component provides role-based authorization
for Camel routes. It leverages the authentication and user services
provided by Spring Security (formerly Acegi Security) and adds a
declarative, role-based policy system to control whether a route can
be executed by a given principal.
The component needs to be
declared in the pom.xml as the following:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-security</artifactId>
<version>x.x.x</version>
<!-- use the same version as your Camel core version -->
</dependency>
A Spring Security AuthenticationManager and AccessDecisionManager are required to use this component. Here is an example of how to configure these objects in Spring XML using the Spring Security namespace:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:spring-security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="true"/>
<property name="decisionVoters">
<list>
<bean class="org.springframework.security.access.vote.RoleVoter"/>
</list>
</property>
</bean>
<spring-security:authentication-manager alias="authenticationManager">
<spring-security:authentication-provider user-service-ref="userDetailsService"/>
</spring-security:authentication-manager>
<spring-security:user-service id="userDetailsService">
<spring-security:user name="jim" password="jimspassword" authorities="ROLE_USER, ROLE_ADMIN"/>
<spring-security:user name="bob" password="bobspassword" authorities="ROLE_USER"/>
</spring-security:user-service>
</beans>
we have to import the Spring Security configuration in your Spring configuration file in order to configure an authorization policy and use that policy to control access to a route:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:spring-security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd
http://camel.apache.org/schema/spring-security
http://camel.apache.org/schema/spring-security/camel-spring-security.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
<!-- import the spring security configuration -->
<import
resource="classpath:META-INF/spring/security/springSecurity-configuration.xml" />
<authorizationPolicy id="admin" access="ROLE_ADMIN"
authenticationManager="authenticationManager" accessDecisionManager="accessDecisionManager"
xmlns="http://camel.apache.org/schema/spring-security" />
<authorizationPolicy id="user" access="ROLE_USER"
authenticationManager="authenticationManager" accessDecisionManager="accessDecisionManager"
xmlns="http://camel.apache.org/schema/spring-security" />
<bean id="bobProcessor"
class="com.sungard.isb.camel.security.springsecurity.MyAuthService">
<property name="userName" value="bob" />
<property name="password" value="bobspassword" />
</bean>
<bean id="jimProcessor"
class="com.sungard.isb.camel.security.springsecurity.MyAuthService">
<property name="userName" value="jim" />
<property name="password" value="jimspassword" />
</bean>
<camelContext id="myCamelContext" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="direct:client" />
<process ref="jimProcessor"/>
<to uri="direct:secureEndpoint" />
</route>
<route>
<from uri="direct:client1" />
<process ref="bobProcessor"/>
<to uri="direct:secureEndpoint" />
</route>
<route>
<from uri="direct:client2" />
<to uri="direct:secureEndpoint" />
</route>
<route>
<from uri="direct:secureEndpoint" />
<policy ref="user">
<to uri="log:success user role" />
</policy>
<policy ref="admin">
<to uri="log:success admin role" />
</policy>
<onException>
<exception>
org.apache.camel.CamelAuthorizationException
</exception>
<handled>
<constant>true</constant>
</handled>
<choice>
<when>
<simple>${exception.policyId} == 'user'</simple>
<to uri="log:Fail user role" />
</when>
<when>
<simple>${exception.policyId} == 'admin'</simple>
<to uri="log:Fail admin role" />
</when>
<when>
<simple>${exception.policyId} == 'null'</simple>
<to uri="log:Fail user/admin role" />
</when>
</choice>
<to uri="log:AccessDeniedException" />
</onException>
</route>
</camelContext>
</beans>
The MyAuthService class injects the user credentials in the message header:
public class MyAuthService implements Processor {
private String userName;
private String password;
public void process(Exchange exchange) throws Exception {
// create an Authentication object
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userName, password);
// wrap it in a Subject
Subject subject = new Subject();
subject.getPrincipals().add(authToken);
// place the Subject in the In message
exchange.getIn().setHeader(Exchange.AUTHENTICATION, subject);
// you could also do this if useThreadSecurityContext is set to true
// SecurityContextHolder.getContext().setAuthentication(authToken);
}
public String getUserName()
{
return userName;
}
public void setUserName(String userName)
{
this.userName = userName;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
}
public class SpringSecurityExample
{
public static void main(String[] args)
{
ApplicationContext context = new ClassPathXmlApplicationContext(
"classpath:META-INF/spring/camel-context-springSecurity.xml");
SpringCamelContext camelContext = (SpringCamelContext) context.getBean("myCamelContext");
ProducerTemplate template = camelContext.createProducerTemplate();
Endpoint endpoint = camelContext.getEndpoint("direct:client");
Exchange exchange = endpoint.createExchange();
exchange.getIn().setBody("Data");
template.send(endpoint, exchange);
Endpoint endpoint1 = camelContext.getEndpoint("direct:client1");
Exchange exchange1 = endpoint1.createExchange();
exchange1.getIn().setBody("Data1");
template.send(endpoint1, exchange1);
Endpoint endpoint2 = camelContext.getEndpoint("direct:client2");
Exchange exchange2 = endpoint2.createExchange();
exchange2.getIn().setBody("Data2");
template.send(endpoint2, exchange2);
}
}
Camel offers encryption/decryption services to secure payloads or
selectively apply encryption/decryption capabilities on
portions/sections of a payload.
These capabilites are offered
by the following components :
We will focus on the Crypto component.
The Crypto Data Format integrates the Java Cryptographic Extension into Camel, providing simple and flexible encryption and decryption of messages using the Camel marshall and unmarshal formatting mechanism. It assumes marshalling to mean encryption to cyphertext and unmarshalling decryption back to the original plaintext. At its most basic all that is required to encrypt/decrypt an exchange is a shared secret key. If one or more instances of the Crypto data format are configured with this key the format can be used to encrypt the payload in one route (or part of one) and decrypted in another.
The following example shows how to encrypt and decrypt Camel messages into the Camel route:
KeyGenerator generator = KeyGenerator.getInstance("DES");
CryptoDataFormat cryptoFormat = new CryptoDataFormat("DES", generator.generateKey());
from("direct:basic-encryption").to("log:Initial message")
.marshal(cryptoFormat)
.to("log:Encrypted message")
.unmarshal(cryptoFormat)
.to("log:Unencrypted message");
public class CryptoExample
{
public static void main(String[] args)
{
final CamelContext context = new DefaultCamelContext();
try
{
context.addRoutes(new RouteBuilder()
{
public void configure() throws Exception
{
KeyGenerator generator = KeyGenerator.getInstance("DES");
CryptoDataFormat cryptoFormat = new CryptoDataFormat("DES", generator.generateKey());
from("direct:basic-encryption").to("log:Initial message")
.marshal(cryptoFormat)
.to("log:Encrypted message")
.unmarshal(cryptoFormat)
.to("log:Unencrypted message");
}
});
context.start();
ProducerTemplate template = context.createProducerTemplate();
Endpoint endpoint = context.getEndpoint("direct:basic-encryption");
Exchange exchange = endpoint.createExchange();
exchange.getIn().setBody("Data");
template.send(endpoint, exchange);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
Some components in Camel offer an ability to secure their endpoints (using interceptors etc) and therefore ensure that they offer the ability to secure payloads as well as provide authentication/authorization capabilities at endpoints created using the components. Some such components are
For more information refer to Camel Official Website
Camel offers the Properties component to externalize configuration values to properties files. Those values could contain sensitive information such as usernames and passwords. Those values can be encrypted and automatic decrypted by Camel.
Jasypt is a simplified encryption library which makes encryption and
decryption easy. Camel integrates with Jasypt to allow sensitive
information in Properties files to be encrypted. By dropping
camel-jasypt on the classpath those encrypted values will automatic be
decrypted on-the-fly by Camel. This ensures that human eyes can't
easily spot sensitive information such as usernames and passwords.
The component needs to be declared in the pom.xml as the following:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jasypt</artifactId>
<version>x.x.x</version>
<!-- use the same version as your Camel core version -->
</dependency>
In Java DSL you need to configure Jasypt as a JasyptPropertiesParser instance and set it on the Properties component as show below:
// create the jasypt properties parser
JasyptPropertiesParser jasypt = new JasyptPropertiesParser();
// and set the master password
jasypt.setPassword("secret");
// create the properties component
PropertiesComponent pc = new PropertiesComponent();
pc.setLocation("classpath:org/apache/camel/component/jasypt/myproperties.properties");
// and use the jasypt properties parser so we can decrypt values
pc.setPropertiesParser(jasypt);
// add properties component to camel context
context.addComponent("properties", pc);
The properties file myproperties.properties then contain the encrypted value, such as shown below. Notice how the password value is encrypted and the value has the tokens surrounding ENC(value here)
# refer to a mock endpoint name by that encrypted password
cool.result=mock:{{cool.password}}
# here is a password which is encrypted
cool.password=ENC(bsW9uV37gQ0QHFu7KO03Ww==)
In Spring XML you need to configure the JasyptPropertiesParser which is shown below. Then the Camel Properties component is told to use jasypt as the properties parser, which means Jasypt have its chance to decrypt values looked up in the properties.
<!-- define the jasypt properties parser with the given password to be used -->
<bean id="jasypt" class="org.apache.camel.component.jasypt.JasyptPropertiesParser">
<property name="password" value="secret"/>
</bean>
<!-- define the camel properties component -->
<bean id="properties" class="org.apache.camel.component.properties.PropertiesComponent">
<!-- the properties file is in the classpath -->
<property name="location" value="classpath:org/apache/camel/component/jasypt/myproperties.properties"/>
<!-- and let it leverage the jasypt parser -->
<property name="propertiesParser" ref="jasypt"/>
</bean>
The Properties component can also be inlined inside the <camelContext> tag which is shown below. Notice how we use the propertiesParserRef attribute to refer to Jasypt.</camelContext>
<!-- define the jasypt properties parser with the given password to be used -->
<bean id="jasypt" class="org.apache.camel.component.jasypt.JasyptPropertiesParser">
<!-- password is mandatory, you can prefix it with sysenv: or sys: to indicate it should use
an OS environment or JVM system property value, so you dont have the master password defined here -->
<property name="password" value="secret"/>
</bean>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<!-- define the camel properties placeholder, and let it leverage jasypt -->
<propertyPlaceholder id="properties"
location="classpath:org/apache/camel/component/jasypt/myproperties.properties"
propertiesParserRef="jasypt"/>
<route>
<from uri="direct:start"/>
<to uri="{{cool.result}}"/>
</route>
</camelContext>