Recently, just put all staff done to create a fundamental work to migrate the existing system to aws beanstalk. The main reason to use beanstalk is because that the current system used spring+java. Beanstalk will be make it easy to handle the serverless environment to save the company cost.
1. Write the basic spring boot rest application, I created 2 controller, one for authenticate to fetch te token and one DemoController to support general resource request using Bearer Token of Cognito.
DemoController:
2.Configure to set the public access to /auth and secured access to /demo
The SecurityConfig.java above also defined the JWTAuthenticateEntryPoint to handle the validation error case and JwtAuthenticationFilter to do the validation based on cognito Bearer token.
JWTAuthenticateEntryPoint:
JwtAuthenticationFilter:
The line 18 set the credential back to requestContext as the global context which is used by DemoController above.
3. Start the spring boot server, and run the test, assuming you had done for your CognitoServic implementation.
For life easy purpose, I use port 5000 since Elastic Beanstalks use 5000 as the default port for mapping.
4. follow up aws official doc to upload the jar you built with gradle, if you compile with JDK 11 and make sure to use corretto 11 Evn during the configuration(very important). Also to enable Load balancer to scalability purpose for real production usage.
5. After that we should be all set to run now.
1. Write the basic spring boot rest application, I created 2 controller, one for authenticate to fetch te token and one DemoController to support general resource request using Bearer Token of Cognito.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@RestController | |
public class AuthController { | |
@Autowired | |
private CognitoService cognitoService; | |
private static Logger logger = LoggerFactory.getLogger(AuthController.class); | |
@GetMapping("auth") | |
public ResponseEntity<?> fetchToken(@RequestHeader(value = "Authorization") String basicAndencodedUserPassword) throws AuthenticationException { | |
try { | |
StringTokenizer encodedUserPasswordTokenizer = new StringTokenizer(basicAndencodedUserPassword, " "); | |
encodedUserPasswordTokenizer.nextToken(); | |
final String encodedUserPassword = encodedUserPasswordTokenizer.nextToken(); | |
String usernameAndPassword = new String(Base64.getDecoder().decode(encodedUserPassword)); | |
final StringTokenizer tokenizer = new StringTokenizer(usernameAndPassword, ":"); | |
final String username = tokenizer.nextToken(); | |
final String password = tokenizer.nextToken(); | |
AuthenticationResultType authResult = cognitoService.login(username, password); | |
if(StringUtils.isEmpty(authResult.getAccessToken())) { | |
return Result.fail("failed to authenticate with " + basicAndencodedUserPassword, HttpStatus.UNAUTHORIZED); | |
} | |
return Result.ok(authResult); | |
} catch (Exception e) { | |
//return Result.fail(AUTH_ERROR.withMessage("failed to authenticate with " + basicAndencodedUserPassword)); | |
logger.error("failed to authenticate with " + basicAndencodedUserPassword, e); | |
return Result.fail("failed to authenticate with " + basicAndencodedUserPassword, HttpStatus.UNAUTHORIZED); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@RestController | |
public class DemoController { | |
private static Logger logger = LoggerFactory.getLogger(DemoController.class); | |
@GetMapping("demo") | |
public ResponseEntity<?> demo() { | |
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); | |
return Result.ok(authentication); | |
} | |
} |
2.Configure to set the public access to /auth and secured access to /demo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Configuration | |
@ComponentScan | |
@EnableWebSecurity | |
public class SecurityConfig extends WebSecurityConfigurerAdapter { | |
@Autowired | |
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; | |
@Autowired | |
private JwtAuthenticationFilter jwtRequestFilter; | |
@Override | |
protected void configure(HttpSecurity http) throws Exception { | |
http.cors(); | |
http.authorizeRequests() | |
.antMatchers("/auth").permitAll() | |
.antMatchers("/health").permitAll() | |
.anyRequest().authenticated(); | |
http.exceptionHandling() | |
.authenticationEntryPoint(jwtAuthenticationEntryPoint); | |
http.sessionManagement() | |
.sessionCreationPolicy(SessionCreationPolicy.STATELESS); | |
// Add a filter to validate the tokens with every request | |
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); | |
} | |
} |
JWTAuthenticateEntryPoint:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Component | |
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { | |
@Override | |
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { | |
// This is invoked when user tries to access a secured REST resource without supplying any credentials | |
// We should just send a 401 Unauthorized response because there is no 'login page' to redirect to | |
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Component | |
public class JwtAuthenticationFilter extends OncePerRequestFilter { | |
@Autowired | |
private CognitoService cognitoService; | |
@Override | |
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { | |
final String requestTokenHeader = request.getHeader("Authorization"); | |
// JWT Token is in the form "Bearer token". Remove Bearer word and get | |
// only the Token | |
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) { | |
String jwtToken = requestTokenHeader.substring(7); | |
try { | |
GetUserResult user = cognitoService.verifyUserByToken(jwtToken); | |
List<GrantedAuthority> granites = user.getUserAttributes().stream() | |
.map(attr -> new CognitoGrantedAuthority(attr.getName(), attr.getValue())).collect(Collectors.toList()); | |
SecurityContextHolder.getContext() | |
.setAuthentication(new UsernamePasswordAuthenticationToken(user.getUsername(), jwtToken, granites)); | |
} catch (Exception e) { | |
logger.error("failed to verfy user by token:", e); | |
} | |
} else { | |
logger.warn("JWT Token does not begin with Bearer String"); | |
} | |
filterChain.doFilter(request, response); | |
} | |
} |
3. Start the spring boot server, and run the test, assuming you had done for your CognitoServic implementation.
For life easy purpose, I use port 5000 since Elastic Beanstalks use 5000 as the default port for mapping.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@SpringBootApplication | |
public class RestServicesApplication { | |
public static void main(String[] args) { | |
SpringApplication app = new SpringApplication(RestServicesApplication.class); | |
app.setDefaultProperties(Collections | |
.singletonMap("server.port", "5000")); | |
app.run(args); | |
} | |
} |
5. After that we should be all set to run now.
Comments
Post a Comment