Authentication and authorization using Spring Security

This recipe will show how using the Spring Security framework you can authenticate credentials passed into a route on an exchange, and determine whether that user/system (Principal in security terms) is authorized to access the route based on their role.

Getting ready

The Java code for this recipe is located in the org.camelcookbook.security.springsecurity package. The Spring XML files are located under src/main/resources/META-INF/spring and prefixed with springSecurity.

To use Camel's Spring Security Component, add the following to the dependencies section of your Maven POM:

<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-spring-security</artifactId>
  <version>${camel-version}</version>
</dependency>

In order for your configuration to be parsed correctly, you will also need the appropriate Spring Security JARs without any of their Core Spring dependencies in order to avoid version clashes.

<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-core</artifactId>
  <version>${spring-security-version}</version>
  <exclusions>
    <exclusion>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
    </exclusion>
    <exclusion>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
    </exclusion>
    <exclusion>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
    </exclusion>
  </exclusions>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-config</artifactId>
  <version>${spring-security-version}</version>
  <exclusions>
    <exclusion>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
    </exclusion>
    <exclusion>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
    </exclusion>
  </exclusions>
</dependency>

The ${spring-security-version} used here is 3.1.4.RELEASE. Yours should correspond to the same version used by the camel-spring-security library.

Note

At the time of writing this book, Camel primarily uses Spring 3.2, but uses an older version of Spring Security—3.1.4. Double check with the Camel documentation and source to verify that you are referencing consistent versions.

How to do it...

The steps that we need to perform to authenticate and authorize an exchange on a route are as follows:

  1. Extract the credentials from the exchange and set them in a place accessed by Spring Security, or its Camel wrapper (there are 2 options).
  2. Define an authentication mechanism that Spring Security will use to check those credentials against, and fetch the roles that then correspond to those credentials.
  3. Define an authorization mechanism that Spring Security will use to check those roles against in order to work out whether the exchange should be processed.
  4. Bridge this mechanism to Camel through a Policy, and use it from the route.

To carry out the steps described, you need to perform a series of actions as follows:

  1. Register two additional XML namespaces in the Spring configuration; one for Spring Security, and another for the Camel Spring Security bindings:
    <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:sec="http://www.springframework.org/schema/security"
      xmlns:camel-sec="http://camel.apache.org/schema/spring-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-3.1.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">
  2. The mechanism by which you obtain credentials is dependent on the consumer transport. As such you will often have to write this part yourself. This can be done using a Processor:
    public class SecurityContextLoader implements Processor {
      @Override
      public void process(Exchange exchange) throws Exception {
        Message in = exchange.getIn();
        String username = in.getHeader("username", String.class);
        String password = in.getHeader("password", String.class);
    
        Authentication authenticationToken =
            new UsernamePasswordAuthenticationToken(username,
                password);
        SecurityContextHolder.getContext()
            .setAuthentication(authenticationToken);
      }
    }

    In this simple instance we expect the credentials to come down the route within the message headers. Credentials are stored in an implementation of org.springframework.security.core.Authentication.

    Note

    The org.springframework.security.core.context.SecurityContextHolder is a ThreadLocal-based mechanism that is accessed by Spring Security for authentication and authorization.

  3. Register the processor as a regular Spring bean:
    <bean id="securityContextLoader"
          class="org.camelcookbook.security.springsecurity.SecurityContextLoader"/>
  4. Define a user service that maps credentials to roles. Here, we use a very simple instance where these details are stored in the Spring configuration:
    <sec:user-service id="userService">
      <sec:user name="jakub"
                password="supersecretpassword1" 
                authorities="ROLE_USER, ROLE_ADMIN"/>
      <sec:user name="scott"
                password="supersecretpassword2" 
                authorities="ROLE_USER"/>
    </sec:user-service>

    A user service is one which, given a user name, fetches the corresponding details—it does not actually authenticate them. Spring Security provides out of the box implementations for accessing databases and LDAP servers, or you can build your own.

  5. Next, define an AuthenticationManager that will authenticate the credentials using this user service:
    <sec:authentication-manager alias="authenticationManager">
      <sec:authentication-provider user-service-ref="userService"/>
    </sec:authentication-manager>
  6. Define an access decision manager that will make a decision on whether to permit the given credentials access to the resource.

    Spring Security's authorization process works on the idea of voters (implementations of org.springframework.security.access.AccessDecisionVoter) that each get to decide in turn whether the Authentication object has access to the resource. Each voter votes to either grant or deny access, or abstain from the vote.

    The access decision manager pulls together the votes, and makes the final decision. Three decision managers are available, which require that either any one voter votes yes (AffirmativeBased), all voters vote yes (UnanimousBased), or the majority of voters vote yes (ConcensusBased).

    <bean id="accessDecisionManager"
          class="org.springframework.security.access.vote.AffirmativeBased">
      <constructor-arg>
        <list>
          <bean class="org.springframework.security.access.vote.RoleVoter"/>
        </list>
      </constructor-arg>
    </bean>

    The sole voter used here makes a decision based on roles.

  7. Wrap the Spring Security mechanisms in a Camel Policy, which provides the hook into a Camel route. Here, we specify that the authentication pulled from the exchange must be in the ROLE_ADMIN role:
    <camel-sec:authorizationPolicy
        id="adminAuthPolicy"
        access="ROLE_ADMIN"
        authenticationManager="authenticationManager"
        accessDecisionManager="accessDecisionManager"
        useThreadSecurityContext="true"/>
  8. In your route, trigger the processor that you have defined to extract the authentication from the exchange, and then use the policy around any sensitive code to which access must be protected:
    <from uri="direct:in"/>
    <process ref="securityContextLoader"/>
    <policy ref="adminAuthPolicy">
      <to uri="mock:secure"/>
    </policy>
    

How it works...

The process for granting access is to:

  1. Extract the credentials from the message.
  2. Authenticate the credentials using data from the user service.
  3. Determine whether the Principal has access to the specified resources.

If either of the preceding steps 2 or 3 fail, an org.apache.camel.CamelAuthorizationException will be thrown detailing the problem. This exception can be caught and handled as usual using the mechanism described in the Catching exceptions, and Fine-grained error handling using doTry…doCatch recipes in Chapter 7, Error Handling and Compensation.

There's more...

The SecurityContextHolder used to carry the user credentials relies on a ThreadLocal internally. This is a design decision stemming from Spring Security's original use within web applications. In the context of integrations with Camel, it occasionally causes headaches, as the Authentication object is lost when the exchange crosses a thread boundary, such as when being passed across a seda: endpoint as discussed in the Asynchronously connecting routes recipe in Chapter 1, Structuring Routes. In general, you should extract the credentials from the transport and verify that they have access to the operation being performed within the same thread.

If this is not possible, you can get around this constraint by wrapping the Authentication in a javax.security.auth.Subject and setting it in the CamelAuthentication header (defined as a constant in Exchange.AUTHENTICATION) of the exchange. Camel's Spring Security policy will extract it from the header and pass it into the authentication and access decision managers.

To do this, you need only to change your SecurityContextLoader implementation to perform the following steps instead of initializing the SecurityContextHolder:

Subject subject = new Subject();
subject.getPrincipals().add(authenticationToken);
in.setHeader(Exchange.AUTHENTICATION, subject);

Spring Security breaks down authentication and authorization into separate processes that are configured independently of each other. There is a substantial number of options available. For full details you should refer to the Spring Security documentation.

The Spring Security mechanisms are easy to use within Spring applications. They are much more difficult to use within OSGi Blueprints and plain Java applications, due to the dependency on Spring lifecycle interfaces and the amount of code that is hidden away from you within the custom Spring namespace handlers.

If you require authentication and authorization in one of these environments, you should consider the Apache Shiro security project as an alternative. This is accessible through the Camel Shiro component.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.144.248