Back

TechnologyNov 25, 2015

Simple AWS Credentials with Spring Framework

Joshua Casey

Frameworks generally make our development lives easier, and cloud services help our deployment lives to be a little more stress-free. What’s great is seeing them come together to give you exactly what you want: reducing boilerplate. With a great framework in Spring and a strong Java SDK for Amazon Web Services (AWS), there’s no reason these can’t be combined for extremely configurable and powerful applications with minimal boilerplate.

This article will show you how to set up an STS + Gradle project that will utilize some of the best AWS authentication practices that enable connecting to an AWS DynamoDB database. But first, let’s introduce the technologies, services, and products we’ll be covering.

Amazon Web Services (AWS)

Amazon Web Services is a collection of remote computing services that make up the cloud-computing platform offered by Amazon.com. Many AWS services are free for a period of time after signing up. Otherwise, you can use the pricing tool to get a grasp of cost over time. AWS is divided into regions, which are composed of availability zones and edge locations (for more information, see Global Infrastructure). All AWS components are created within a region and may not be accessible to components in other regions. Not all services may be available in all regions and availability zones.

AWS IAM

AWS Identity and Access Management (IAM) is AWS’ method for authorization and permissions (note: this does not mean firewalls). The IAM documentation has an excellent best practices article, which is summarized here:

  1. Create new policies with appropriate permissions if existing policies do not meet your needs.

  2. Create groups to contain buckets of capability (“policies”) for you, your users, and your applications.

  3. Create a privileged account for yourself for administrative purposes. Enable multi-factor authentication (MFA), and use this for all AWS administration other than billing. (In general, your “human account”—an account with an email address—is only for billing.) Use IAM users with appropriate permissions to perform AWS management.

  4. Create unique accounts for each human user who accesses this account. Follow least-permissions rules to determine what each user can do, and manage their permissions via groups rather than by attaching policies directly to users. It’s recommended that you enable MFA and strong password policies for all human users.

  5. Create roles for applications running on EC2. AWS can automatically configure the EC2 instance to have this role—without the need for any separate authentication mechanism such as a key store. This implies that applications running on EC2 instances do not need user accounts.

Spring Framework

Credera’s IDS practice is very familiar with the Spring framework-it’s part of what we do every day. If you search our Insights blog, you’ll see a variety of ways we use Spring; however, this is how we’ll use it in this post:

  • Package a runnable JAR file with a tomcat webserver using Spring Boot.

  • Use 

     to distinguish between development and production code.

  • Use 

     to autowire configuration parameters from different environments.

To get the application up and running, all you need is the following class to house the main function. Under src/main/resources, place the application-{env}.yml configuration property files that @EnableConfigurationProperties will set up.

To perform this development on your local machine, create an IAM user with appropriate permissions as well as a credentials file at ~/.aws/credentials. Or you can log in to the Eclipse AWS Explorer plugin, which will create the credentials file for you. Don’t use your “human” AWS account—use only a configured IAM user with least-permissions roles. So in dev mode, the application will use local permissions; in prod mode, the application will use the EC2 instance permissions. To use local permissions, the application will need to know the username you will use.

//Don't need to specify basePackages, since 'Application' is in the lowest package @SpringBootApplication @EnableConfigurationProperties public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}

You may desire different parameters between dev and prod environments. Make those adjustments as you see fit. Note the spring.profiles.active field—it will be used later on.

Application.yml

spring: profiles: active: dev   aws: dynamo: tableName: tableName keyName: keyName valueName: valueName

Application-dev.yml

aws: region: us-west-2

Application-prod.yml

aws: region: us-west-2 username: app-user1

To actually utilize the different authentication mechanisms between dev and prod environments, we set up a connection to a DynamoDB database that requires authentication.

@Configuration public class DynamoTableFacade {   @Value("${aws.dynamo.tableName") protected String tableName;   @Value("${aws.dynamo.keyName") protected String keyName;   @Value("${aws.dynamo.valueName") protected String valueName;   @Value("${aws.region}") protected String awsRegion;   @Autowired protected AWSCredentialsProvider credProvider;   // TODO: Implement connection pooling // TODO: Don't build the connection every time you try to get data! public String getHelloWorld() throws AmazonClientException { AmazonDynamoDBClient dbClient = new AmazonDynamoDBClient(credProvider); // default region is US_EAST_1 dbClient.configureRegion(Regions.fromName(awsRegion)); DynamoDB dynamoDB = new DynamoDB(dbClient);   Table table = dynamoDB.getTable(tableName); Item item = table.getItem(keyName, valueName);   return item.toJSON(); }}

As you might have guessed, the autowired “credProvider” is the authentication mechanism. Where does this come from? Here’s where the @Profile annotation comes into play. When spring.profiles.active includes the profile given, the class is loaded. In development, the application will use the profile provider with an IAM username. In production, the application will use instance provider.

@Configuration @Profile("dev")public class CredProviderDevelopment {   @Value("${aws.username}") protected String awsUsername;   @Bean AWSCredentialsProvider credProvider() { return new ProfileCredentialsProvider(awsUsername); }}

@Configuration @Profile("prod")public class CredProviderProduction {   @Bean AWSCredentialsProvider credProvider() { return new InstanceProfileCredentialsProvider(); }}

And that will provide the correct authentication mechanism for the environment. If it seems too easy, it should! Frameworks, libraries, and services can fit together in ways that clearly express intent with a minimum of boilerplate.

This program was built with Gradle 2.3, Spring Tool Suite 3.7.0.RELEASE, and JDK 8u40.