Spring Method Level Security with Amazon Cognito and JWT Token
Learn how to authenticate your user with AWS Cognito and secure your Spring REST endpoints with JWT token at the method level using Spring Security.
1. Introduction
The goal of this tutorial is to authenticate and authorize a user in a Spring REST service using the JWT token.
1.1 Prerequisite
- Create a user pool in Amazon Cognito.
- Create an application in Google Console.
- Add a user/email that we can authenticate later for testing.
1.2 Goal - Create a Java Library with the following Features
- Decoding an AWS Cognito JWT token.
- Verifying the JWT token issuer.
- Creating a custom SimpleCtAccount using the information contained in the JWT token.
- Convert the associated Cognito groups into a custom CtRole.
- A single interface to enable security.
2. The Spring Security Module
3. Class Diagram
3.1 Method Security
4. How to use our Library?
4.1 Service Integration
<dependency> <groupId>com.czetsuyatech</groupId> <artifactId>ct-iam-spring-security</artifactId> <version>LATEST-SNAPSHOT</version> </dependency>2. Extend the class DefaultMethodSecurityExpressionRoot, and add more authorization checks.
public class CtAppMethodSecurityExpressionExtension extends DefaultMethodSecurityExpressionRoot { public CtAppMethodSecurityExpressionExtension(Authentication authentication) { super(authentication); } public boolean isAuthorized() { return true; } public boolean isUnAuthorized() { return false; } }
@Configuration @RequiredArgsConstructor @EnableCtSecurity public class CtAppSecurityConfiguration { @Bean public CtHttpSecurityConfigurer httpSecurityConfig() { return http -> http .authorizeHttpRequests(authz -> authz .antMatchers(HttpMethod.GET, "/actuator/**").permitAll() .antMatchers("/api/**").authenticated() .anyRequest().permitAll() ); } @Bean public CtMethodSecurityExpressionHandlerFactory methodSecurityFactory() { return CtAppMethodSecurityExpressionRoot::new; } }
Don't forget to annotate this class with @EnableCtSecurity.
4.2 Secured Endpoints
Create a new controller with the following endpoints and use the @PreAuthorize annotation.@RestController @Validated @Slf4j @RequiredArgsConstructor public class ApiTestController { @GetMapping("/hello") @ResponseStatus(HttpStatus.OK) public String hello(@CurrentSecurityContext(expression = "authentication") Authentication auth) { log.debug("" + auth.getPrincipal()); log.debug("" + auth.getCredentials()); log.debug("" + auth.getDetails()); return "Hello " + auth.getPrincipal(); } @GetMapping("/api/testing/authenticated") @PreAuthorize("isAuthenticated()") @ResponseStatus(HttpStatus.OK) public String authenticated(@CurrentSecurityContext(expression = "authentication") Authentication auth) { log.debug("" + auth.getPrincipal()); log.debug("" + auth.getCredentials()); log.debug("" + auth.getDetails()); return "Hello " + auth.getPrincipal(); } @GetMapping("/api/testing/authorized") @PreAuthorize("isAuthorized()") @ResponseStatus(HttpStatus.OK) public String authorized() { return "authorized"; } @GetMapping("/api/testing/unauthorized") @PreAuthorize("isUnAuthorized()") @ResponseStatus(HttpStatus.FORBIDDEN) public String unAuthorized() { return "unauthorized"; } }
Post a Comment