CORS

Implementation on the Backend

  1. MvcConfig.java
package com.nighthawk.spring_portfolio;
import org.springframework.context.annotation.*;
import org.springframework.web.servlet.config.annotation.*;

@Configuration
public class MvcConfig implements WebMvcConfigurer {

    // This method sets up a custom index page for the "/login" path
    // Defines how the login page is accessed within app
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
    }

    // Configures the location for uploaded files outside the app's resources
    // CRUCIAL for file upload functionality -> make sure files stored and can be accessed properly by frontend
    @Override
    public void addResourceHandlers(final ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/volumes/uploads/**").addResourceLocations("file:volumes/uploads/");
    }

    // Sets up CORS settings --> allows requests from specified origins 
    // This case is GitHub Pages site & local dev site
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOrigins("https://nighthawkcoders.github.io", "http://localhost:4000");
    }
}
  1. Securityconfig.java
// Configure security settings for Spring app 

@Configuration
@EnableWebSecurity  // Enable basic Web security features
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {

    @Autowired
	private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

	@Autowired
	private JwtRequestFilter jwtRequestFilter;

	@Autowired
	private PersonDetailsService personDetailsService;

    // @Bean  // Sets up password encoding style
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

	// Configures the authentication manager to load user details 
	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(personDetailsService).passwordEncoder(passwordEncoder());
	}

	@Bean
	public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
		return authenticationConfiguration.getAuthenticationManager();
	}
	
    // Configure security settings, including CORS 
		@Bean
		public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
			http
				.csrf(csrf -> csrf
					.disable()
				)
				// List the requests/endpoints that need to be authenticated
				.authorizeHttpRequests(auth -> auth
					.requestMatchers("/authenticate").permitAll()
					.requestMatchers("/mvc/person/update/**", "/mvc/person/delete/**").authenticated()
					.requestMatchers("/api/person/post/**", "/api/person/delete/**").authenticated()
					.requestMatchers("/**").permitAll()
				)
				// CORS support is enabled within the security configuration
				.cors(Customizer.withDefaults())
				// Set up specific headers related to CORS
				// Ensure that the necessary headers are included in the HTTP responses
				.headers(headers -> headers
					.addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Credentials", "true"))
					.addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-ExposedHeaders", "*", "Authorization"))
					.addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Headers", "Content-Type", "Authorization", "x-csrf-token"))
					.addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-MaxAge", "600"))
					.addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Methods", "POST", "GET", "OPTIONS", "HEAD"))
					//.addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Origin", "https://nighthawkcoders.github.io", "http://localhost:4000"))
				)
				.formLogin(form -> form 
					.loginPage("/login")
				)
				.logout(logout -> logout
					.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
					.logoutSuccessUrl("/")
				)
				.exceptionHandling(exceptions -> exceptions
					.authenticationEntryPoint(jwtAuthenticationEntryPoint)
				)
				// Configures the session management to use a stateless approach--> Server does not store session information on the server-side between requests --> all the necessary information for authentication is contained within each request
				// Pro: more scalable, servers can handle requests independently 
				.sessionManagement(session -> session
					.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
				)
				// Add a filter to validate the tokens with every request
				.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
			return http.build();
	}
}

DotEnv

DotEnv in relation to JWT

Implementation

  1. Navigate to your project’s root directory using the command line: example - cd /home/aliyatang/vscode/aliyaBlog
  2. Initialize a new package.json file for your project: npm init -y
  3. Install dotenv npm install dotenv
  4. Create .env file in root of project, in this file set JWT secret key: example - JWT_SECRET=your_secret_key
  5. Make a .js file, require and configure dotenv so you can load variables from .env file into process.env: require('dotenv').config()
  6. Whenever you need to sign or verify JWT, use the secret from the environment variables, keep key secure and easily configureable: const jwtSecret = process.env.JWT_SECRET;

Good Practices

Instance Directory

  1. Purpose

Database

  1. Purpose

Mini-Guide: Deploying Your Site with AWS

Important Requirements: Must have a backend that runs locally and have a domain name pointing to the Public IP of your deployment server using AWS Route 53

AWS EC2 Access

  1. Login to AWS Console:
  2. Instance Selection:

Server Setup

  1. AWS EC2 Terminal:

Application Setup

  1. Finding Port:
  2. Docker Setup:

What your Dockerfile should look like

# syntax=docker/dockerfile:1
FROM openjdk:18-alpine3.13
WORKDIR /app
RUN apk update && apk upgrade && \
    apk add --no-cache git 
COPY . /app
RUN ./mvnw package
CMD ["java", "-jar", "target/spring-0.0.1-SNAPSHOT.jar"]
EXPOSE 8---

What your docker-compose.yml should look like

version: '3'
services:
  web:
    image: your_image_name
    build: .
    ports:
      - "8---:8085"
    volumes:
       - ./volumes:/volumes
    restart: unless-stopped

DNS & NGINX Setup

  1. Route 53 DNS:
  2. NGINX Configuration:

What your NGINX config file should look like

server {
    listen 80;
     listen [::]:80;
     server_name -----.example.nighthawkcodingsociety.com ; # change server name to your domain
     location / {
         proxy_pass http://localhost:8000; # change port to yours
         if ($request_method ~* "(GET|POST|PUT|DELETE)") {
                 add_header "Access-Control-Allow-Origin"  *;
         }
         if ($request_method = OPTIONS ) {
                 add_header "Access-Control-Allow-Origin"  *;
                 add_header "Access-Control-Allow-Methods" "GET, POST, PUT, DELETE, OPTIONS, HEAD"; # request methods above match here
                 add_header "Access-Control-Allow-Headers" "Authorization, Origin, X-Requested-With, Content-Type, Accept";
                 return 200;
         }
     }
 }
  1. Validation and Restart:

Certbot Configuration

  1. Run Certbot:
  2. Verify HTTPS:

Changing Code and Deployment Updates

  1. VSCode Changes:
  2. Deployment Update:

Pulling Changes into AWS EC2

  1. AWS EC2 Terminal:

Optional Troubleshooting Checks on AWS EC2

  1. Check Server Status:

DNS

Here’s a diagram that can help you visualize the DNS process:

Breakdown of the process:

Nginx

What is Nginx?

Why is it important?

Certbot

What is Certbot?

Here is a diagram to help visualize how Certbot works:

Why is Certbot important?