Available as of Camel 2.3
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.
If you are not familiar with the Spring Security authentication and authorization system, please review the current reference documentation on the SpringSource web site linked above.
Access to a route is controlled by an instance of a
SpringSecurityAuthorizationPolicy
object. A policy object contains the
name of the Spring Security authority (role) required to run a set of endpoints and references
to Spring Security AuthenticationManager
and
AccessDecisionManager
objects used to determine whether the current
principal has been assigned that role. Policy objects may be configured as Spring beans or by
using an <authorizationPolicy>
element in Spring XML.
The <authorizationPolicy>
element may contain the following
attributes:
Name | Default Value | Description |
---|---|---|
id
|
null
|
The unique Spring bean identifier which is used to reference the policy in routes (required) |
access
|
null
|
The Spring Security authority name that is passed to the access decision manager (required) |
authenticationManager
|
authenticationManager
|
The name of the Spring Security AuthenticationManager object in the
context |
accessDecisionManager
|
accessDecisionManager
|
The name of the Spring Security AccessDecisionManager object in the
context |
authenticationAdapter
|
DefaultAuthenticationAdapter | Camel 2.4 The name of a camel-spring-securityAuthenticationAdapter object in the
context that is used to convert a javax.security.auth.Subject into a
Spring Security Authentication instance. |
useThreadSecurityContext
|
true
|
If a javax.security.auth.Subject cannot be found in the In message
header under Exchange.AUTHENTICATION, check the Spring Security
SecurityContextHolder for an Authentication
object. |
alwaysReauthenticate
|
false
|
If set to true, the SpringSecurityAuthorizationPolicy will always
call AuthenticationManager.authenticate() each time the policy is
accessed. |
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>
Now that the underlying security objects are set up, we can use them 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:org/apache/camel/component/spring/security/commonSecurity.xml"/> <authorizationPolicy id="admin" access="ROLE_ADMIN" authenticationManager="authenticationManager" accessDecisionManager="accessDecisionManager" xmlns="http://camel.apache.org/schema/spring-security"/> <camelContext id="myCamelContext" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="direct:start"/> <!-- The exchange should be authenticated with the role of ADMIN before it is send to mock:endpoint --> <policy ref="admin"> <to uri="mock:end"/> </policy> </route> </camelContext> </beans>
In this example, the endpoint mock:end
will not be executed unless a
Spring Security Authentication
object that has been or can be authenticated
and contains the ROLE_ADMIN
authority can be located by the
adminSpringSecurityAuthorizationPolicy
.
The process of obtaining security credentials that are used for authorization is not
specified by this component. You can write your own processors or components which get
authentication information from the exchange depending on your needs. For example, you might
create a processor that gets credentials from an HTTP request header originating in the camel-jetty component. No matter how the credentials are
collected, they need to be placed in the In message or the
SecurityContextHolder
so the camel-spring-security component can access them:
import javax.security.auth.Subject; import org.apache.camel.*; import org.apache.commons.codec.binary.Base64; import org.springframework.security.authentication.*; public class MyAuthService implements Processor { public void process(Exchange exchange) throws Exception { // get the username and password from the HTTP header // http://en.wikipedia.org/wiki/Basic_access_authentication String userpass = new String(Base64.decodeBase64(exchange.getIn().getHeader("Authorization", String.class))); String[] tokens= userpass.split(":"); // create an Authentication object UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(tokens[0], tokens[1]); // wrap it in a Subject Subject subject = new Subject(); subject.getPrincipals().add(token); // 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); } }
The SpringSecurityAuthorizationPolicy
will automatically authenticate
the Authentication
object if necessary.
There are two issues to be aware of when using the
SecurityContextHolder
instead of or in addition to the
Exchange.AUTHENTICATION
header. First, the context holder uses a
thread-local variable to hold the Authentication
object. Any routes that
cross thread boundaries, like seda or jms, will lose the Authentication
object. Second, the Spring
Security system appears to expect that an Authentication
object in the
context is already authenticated and has roles (see the Technical Overview section 5.3.1 for more details).
The default behavior of camel-spring-security is to look
for a Subject
in the Exchange.AUTHENTICATION
header.
This Subject
must contain at least one principal, which must be a subclass
of org.springframework.security.core.Authentication
. You can customize the
mapping of Subject
to Authentication
object by providing
an implementation of the
org.apache.camel.component.spring.security.AuthenticationAdapter
to your
<authorizationPolicy>
bean. This can be useful if you are working with
components that do not use Spring Security but do provide a Subject
. At
this time, only the camel-cxf component populates the
Exchange.AUTHENTICATION
header.
If authentication or authorization fails in the
SpringSecurityAuthorizationPolicy
, a
CamelAuthorizationException
will be thrown. This can be handled using
Camel's standard exception handling methods, like the Exception clause. The CamelAuthorizationException
will have a
reference to the ID of the policy which threw the exception so you can handle errors based on
the policy as well as the type of exception:
<onException> <exception>org.springframework.security.authentication.AccessDeniedException</exception> <choice> <when> <simple>${exception.policyId} == 'user'</simple> <transform> <constant>You do not have ROLE_USER access!</constant> </transform> </when> <when> <simple>${exception.policyId} == 'admin'</simple> <transform> <constant>You do not have ROLE_ADMIN access!</constant> </transform> </when> </choice> </onException>
Maven users will need to add the following dependency to their pom.xml
for this component:
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-spring-security</artifactId> <version>2.4.0</version> </dependency>
This dependency will also pull in
org.springframework.security:spring-security-core:3.0.3.RELEASE
and
org.springframework.security:spring-security-config:3.0.3.RELEASE
.