- JWT
- Hibernate / Flyway / Data JPA
- Queries
- Indexing
- Caching
- Composite Key Structure
- Proper PostgresSQL configuration & syntax
- Testing
- Mocking
- Reflection (in testing)
- Unit testing
- Integration Testing (h2m)
- Generics (JWTService)
- Custom DTO parameter validation
- Custom PreAuthorize
- Image upload & processing
- Admin
- Moderator
- User
Username & password Authentication filter
- Responsible for user login (populating the authentication object which is then handled by spring magic)
- SecurityContextHolder.getContext().getAuthentication().getPrincipal() - Is our principle object in this case model.PrincipleUser (implements our UserDetails extension interface ExpandedUserDetails, so we can get email and id) which is extension of spring.security.User it is created in UserDetailsService it needed to be extended to include userId & email. UserDetailsService is called in DaoAuthenticationProvider which is called in AuthenticationManager all of this is true because it is configured as such.
- If there is a case where authentication.principle is not PrincipleUser (ExpandedUserDetails) we will get Runtime exception
Database Access
- We needed to add @Transactional to userDetailsService.loadUserByUsername since we are using lazy loading we could use eager loading which would allow us to remove the annotation
Input validation
- @Validated(ValidationSequence.class) @RequestBody AuthSignUpDTO signUpDTO we use this and example: @Annotation(... groups = First.class) to have an order in validation for example check if its not null then check if valid data format
- We defined our custom annotations to avoid code duplication and more control over our input fields
- Currently, UniqueEmail & UniqueUsername annotations check the database we also index those columns for faster search (UserService autowired)
Development
Starts pgAdmin & postgresql database use this compose file while developing start spring app with IDE preferably
docker-compose up
- Start with IDE
- Add data source (Postgres)
- Terminate process - stop server
docker-compose down- stop Postgres & pgAdmin
Quickstart
Build API docker image locally & start it with database & pg admin uses (dock profile not relevant for production). Uses default bridge network between 3 services.
Automatic (New)
docker-compose upmvn clean package -P docker-localdocker-compose downdocker-compose -f docker-compose-dock.yaml updocker-compose down --remove-orphans- stop all services
Manually (Old)
docker-compose up- so tests can run with databasemvn clean package compile com.google.cloud.tools:jib-maven-plugin:3.2.1:dockerBuild -Dimage=spring-jwt-v2docker-compose down- to shut down databasedocker-compose -f docker-compose-dock.yaml updocker-compose down --remove-orphans- stop all services
- Setup IAM user should belong to group or assigned policy directly
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["ecr:GetAuthorizationToken"],
"Resource": "*"
}
]
}
- Save IAM user Credentials
- Elastic Containe Registry -> Create Repository (private) -> name: jwt-v2-api (More Info)
- Make sure user has permissions for repository
Select -> Action -> Edit -> Permission -> Add Statement
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPushPull",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::USER_ID_PLACEHOLDER:user/USER_ID_PLACEHOLDER"
},
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:BatchGetImage",
"ecr:CompleteLayerUpload",
"ecr:GetDownloadUrlForLayer",
"ecr:InitiateLayerUpload",
"ecr:PutImage",
"ecr:UploadLayerPart"
]
}
]
}
- Install
aws-cli
brew install awscli
- Install
amazon-ecr-credential-helper(Google recommends to use credentail helper for Jib) More info
brew install docker-credential-helper-ecr
- Setup docker configuration
~/.docker/configuration.json(xxxxxxxxxxxx generated ECR id)
{
"credHelpers": {
"xxxxxxxxxxxx.dkr.ecr.eu-central-1.amazonaws.com": "ecr-login"
}
}
- Setup AWS configuration
~/.aws/config(no file extension)
[default] region = eu-central-1
- Setup AWS credentials
~/.aws/credentials
[] - matches a AWS profile will select values bellow it ([default]will be selected when not specifying profile)
[ecr-push-user] aws_access_key_id = IAM_ID_PLACEHOLDER aws_secret_access_key = IAM_SECRET_PLACEHOLDER
- Configure JIB (pom.xml)
docker-remote profile(xxxxxxxxxxxx generated ECR id)
<to>
<image>xxxxxxxxxxxx.dkr.ecr.eu-central-1.amazonaws.com/jwt-api-v2</image>
<credHelper>ecr-login</credHelper>
</to>
Or to avoid exposing registry id in public repository send a variable instead
<to>
<image>${container_registry}.dkr.ecr.eu-central-1.amazonaws.com/jwt-api-v2</image>
<credHelper>ecr-login</credHelper>
</to>
- Run maven command with maven profile set
AWS_PROFILEsystem env var if no default value after that add flag-Pdocker-remoteor-P docker-remoteso we use publishing profile
Automatic(New)
docker-compose upAWS_PROFILE=ecr-push-user mvn clean package -P docker-remote -Dcontainer_registry=xxxxxxxxxxxxdocker-compose down
Manually(Old)
AWS_PROFILE=ecr-push-user mvn --debug jib:build -Pdocker-remote AWS_PROFILE=ecr-push-user mvn clean package --debug jib:build -Pdocker-remoteOr if we are using a variable instead of setting xxxxxxxxxxxx directly we can add `-Dcontainer_registry=xxxxxxxxxxxx`
AWS_PROFILE=ecr-push-user mvn --debug jib:build -Pdocker-remote -Dcontainer_registry=xxxxxxxxxxxx AWS_PROFILE=ecr-push-user mvn clean package --debug jib:build -Pdocker-remote -Dcontainer_registry=xxxxxxxxxxxx
Caution!
If you get 403 or 404 check your <image>...</image> tag or container_registry variable
docker-remote
docker-local
manual-integration-test
native
Draft
brew install docker-credential-helper
{
"credsStore": "osxkeychain"
}
- Configure default credentials in
~/.aws/credentials - Run:
aws ecr get-login-password --region eu-central-1 | docker login --username AWS --password-stdin xxxxxxxxxxxx.dkr.ecr.eu-central-1.amazonaws.com
{
"auths": {
"xxxxxxxxxxxx.dkr.ecr.eu-central-1.amazonaws.com": {} -- will be generated
},
"credsStore": "osxkeychain"
}
- Pick RDS create database (Postgres).
- Set password username
- We use default VPC group which means we can't yet connect outside AWS network (only ec2 for example can connect)
if we wish to connect we will need to setPublic access: Yes& assign to a VPC which has:Inbound rule: Postgres allow from anywhere
Note!! Don't do this for prod environments or for instances running on AWS (never assign to default VPC or set public access) - To add inbound rule to default VPC go to security groups -> find default VPCs security group & click -> Edit Inbound rules
{
"newRules": [ { "Type": "PostgresSQL", "Source": "Anywhere-IPv4" }, { "Type": "PostgresSQL", "Source": "Anywhere-IPv4" } ]
}
And yet again don't do this in production 5. Add data source (Postgres) to IntelliJ (username & password) IAM also possible 6. Not supported in this spring version but we could add for better integration (read replicas, cluster support)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-aws</artifactId>
</dependency>
- Create a new RDS with new VPS no public access -> Add access ElasticBeans HTTP (or setup when creating elastic beans instance)
- Go to Roles -> Elastic Beans (default role for elastic bean instances) -> attach permission EC2ContainerRegistryReadOnly (so we can download our container)
- Create
Dockerrun.aws.json
Note version 3 is when you use compose
For accessing other private container registries (none ECS) Take a look at
Private registry authentication for container instances
Accessing private Docker images from AWS Elastic Beanstalk
Docker configuration
{
"AWSEBDockerrunVersion": "1",
"Image": {
"Name": "xxxxxxxxxxxx.dkr.ecr.eu-central-1.amazonaws.com/jwt-v2-api:latest"
},
"Ports": [
{
"ContainerPort": 8080,
"HostPort": 8080
}
]
}
- Elastic Beans -> New -> Select Docker -> Upload
Dockerrun.aws.json - Setup configuration ENV variables (values for all the ENV variables in application.yml(default one) - currently stored as plaintext presumably a better solution exists AWS secret manager to name one)
- Finish
- Repeat the steps just to confirm