Skip to the content.

Deployment using Login and Authorization Code • 22 min read

Description

Documentation for Mort's Challenge Requirement on CORS security based on Python Guide

Full Stack and Deployment Introduction

This blog outlines a project that will be developed locally and then deployed to a publicly accessible web server. Here are some key elements that need solutions and consideration:

  • Java, REST API Endpoints: The project must handle switching between development and production URI access for frontend requests. This can be achieved by configuring the endpoint URLs in a properties file (config.properties) within the teacher_portfolio project, which should be referenced whenever a URL endpoint fetch is performed.

  • Java, Fetch, Credentials, and Cookies: The project’s APIs must consistently manage authorization options. This involves configuring HTTP clients to set the fetch mode to ‘cors’ and including credentials in the fetch options. This setup enables cookies to be passed between the frontend and backend during requests, facilitating user authentication and session management. These options should be defined and handled in the config.properties file as well.

  • Nginx, Credentials, and Cookies: For deployment and cookie handling, the project’s authorization credentials need to be defined in the Nginx configuration. This is done by setting the Access-Control-Allow-Credentials directive in the /etc/nginx/sites-available application configuration file.

  • Nginx and CORS: For deployment, CORS policies need to be configured in Nginx. This involves setting the Access-Control-Allow-Origin directive in the /etc/nginx/sites-available application configuration file.

  • Nginx and HTTP Methods: For deployment, the HTTP methods (GET, POST, PUT, DELETE) that the application uses need to be allowed in the Nginx configuration. This is done by setting the Access-Control-Allow-Methods directive in the /etc/nginx/sites-available application configuration file.

  • Java, CORS: For both local development and deployment, the project needs to consider Cross-Origin Resource Sharing (CORS). The CORS policies should be built into the Java code, specifically in your Spring Boot configuration class. This setup allows the necessary access between your frontend and backend, supporting both development and deployment environments.

  • Java, Instance Data: In your Spring Boot configuration, you need to configure the settings for your database and file uploads. These settings should ensure that data and files persist outside of the Docker container. If not correctly configured, any data stored in the database or any files uploaded will be lost when the Docker container is stopped.

  • Java, Authentication and Authorization: In this implementation, Spring Security is used to guard HTTP endpoints. This uses the allowed credentials which send the cookie to the application. The application unwraps the cookie and uses the user id to query and return the user object from the database. The user object can be used in the application logic of the API to identify the request’s owner.

  • Java, CSRF: When deploying your server, it’s crucial to manage secret keys for CSRF (Cross-Site Request Forgery) protection in your Spring Boot configuration. This secret key is used to generate unique tokens that help protect your application against CSRF attacks.

  • Certbot, HTTPS: For deployment security, using HTTPS is crucial to prevent sensitive information from being intercepted over the internet. Certbot is the tool of choice used in this project to automate the process of obtaining and installing SSL/TLS certificates. Running sudo certbot --nginx will modify the Nginx configuration to redirect HTTP requests to HTTPS, enhancing the security of your application. It’s important to note that this step is vital for maintaining the integrity and confidentiality of data in transit. Always verify the configuration change after running the command.

Login and Security in a Java / Spring Boot Project

A significant aspect of any deployed frontend-to-backend project is enabling user login and security. This project uses JSON Web Tokens (JWT) to enable and guard endpoints according to authentication and authorization.

  • Login Request: The frontend receives the user’s ID and password as input and performs an HTTP POST request to log in and authenticate the user.

  • Login Response: The backend validates the credentials against the stored database and prepares a JSON response with the authentication status, e.g., ‘Login success’ with a 200 status code, or ‘Bad request’ with a 400 status code.

  • JWT Cookie: If the login is successful, the backend returns a JWT that is stored in the web browser as a cookie. Subsequent requests to the server will include this JWT in the credentials.

  • Security Configuration: Backend endpoints in the Java application that require login credentials will be protected with Spring Security configuration, which ensures that only authenticated users can access certain endpoints.

  • User Credentials: The backend endpoint code can use the return values from the security context to control logic based on the user’s ID and their authorization level.

User Login System

Basic Authentication

  • Explanation: Overview of basic authentication mechanisms.
  • Code Example:
      import java.util.HashMap;
      import java.util.Map;
    
      public class BasicAuth {
          private static Map<String, String> users = new HashMap<>();
    
          static {
              users.put("user1", "password1");
              users.put("user2", "password2");
          }
    
          public static boolean authenticate(String username, String password) {
              return users.containsKey(username) && users.get(username).equals(password);
          }
    
          public static void main(String[] args) {
              System.out.println(authenticate("user1", "password1")); // true
              System.out.println(authenticate("user2", "wrongpassword")); // false
          }
      }
    
      BasicAuth.main(new String[0]);
    

Form-Based Authentication

  • Explanation: How form-based authentication works.
  • Code Example:
      import java.util.Scanner;
    
      public class FormAuth {
          private static Map<String, String> users = new HashMap<>();
    
          static {
              users.put("user1", "password1");
              users.put("user2", "password2");
          }
    
          public static boolean authenticate(String username, String password) {
              return users.containsKey(username) && users.get(username).equals(password);
          }
    
          public static void main(String[] args) {
              Scanner scanner = new Scanner(System.in);
    
              System.out.print("Enter username: ");
              String username = scanner.nextLine();
    
              System.out.print("Enter password: ");
              String password = scanner.nextLine();
    
              if (authenticate(username, password)) {
                  System.out.println("Login successful");
              } else {
                  System.out.println("Invalid credentials");
              }
          }
      }
    
      FormAuth.main(new String[0]);
    

Security

Password Hashing

  • Explanation: Importance of hashing passwords and common algorithms (e.g., BCrypt).
  • Code Example:
      import org.mindrot.jbcrypt.BCrypt;
    
      public class PasswordHashing {
          public static String hashPassword(String password) {
              return BCrypt.hashpw(password, BCrypt.gensalt());
          }
    
          public static boolean checkPassword(String password, String hashed) {
              return BCrypt.checkpw(password, hashed);
          }
    
          public static void main(String[] args) {
              String password = "securePassword";
              String hashed = hashPassword(password);
    
              System.out.println("Hashed password: " + hashed);
              System.out.println("Password matches: " + checkPassword(password, hashed));
          }
      }
    
      PasswordHashing.main(new String[0]);
    

Secure Token Authentication (JWT)

  • Explanation: JWT basics and usage.
  • Code Example:
      import io.jsonwebtoken.Jwts;
      import io.jsonwebtoken.SignatureAlgorithm;
    
      import java.util.Date;
    
      public class JWTAuth {
          private static final String SECRET_KEY = "mySecretKey";
    
          public static String createToken(String username) {
              return Jwts.builder()
                      .setSubject(username)
                      .setIssuedAt(new Date())
                      .setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1 hour
                      .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                      .compact();
          }
    
          public static void main(String[] args) {
              String token = createToken("user1");
              System.out.println("Generated Token: " + token);
          }
      }
    
      JWTAuth.main(new String[0]);
    

4. CORS Support

4.1 Basic CORS Configuration

  • Explanation: What is CORS and why it is needed.
  • Code Example:
      import spark.Filter;
      import spark.Request;
      import spark.Response;
      import static spark.Spark.*;
    
      public class CorsConfig {
          public static void enableCORS(final String origin, final String methods, final String headers) {
              before((request, response) -> {
                  response.header("Access-Control-Allow-Origin", origin);
                  response.header("Access-Control-Allow-Methods", methods);
                  response.header("Access-Control-Allow-Headers", headers);
                  // Note: this may or may not be necessary in your particular case
                  response.type("application/json");
              });
    
              options("/*", (request, response) -> {
                  String accessControlRequestHeaders = request.headers("Access-Control-Request-Headers");
                  if (accessControlRequestHeaders != null) {
                      response.header("Access-Control-Allow-Headers", accessControlRequestHeaders);
                  }
    
                  String accessControlRequestMethod = request.headers("Access-Control-Request-Method");
                  if (accessControlRequestMethod != null) {
                      response.header("Access-Control-Allow-Methods", accessControlRequestMethod);
                  }
    
                  return "OK";
              });
          }
    
          public static void main(String[] args) {
              enableCORS("*", "GET,POST,PUT,DELETE,OPTIONS", "Content-Type,Authorization");
    
              get("/hello", (req, res) -> "Hello World");
          }
      }
    
      CorsConfig.main(new String[0]);
    

Example Jupyter Notebook (Markdown and Code Cells)

Below is an example of how the notebook cells might look:

Markdown Cell

# Java Login, Security, and CORS Support Guide

This notebook provides an overview and examples of implementing user login, security measures, and CORS support in Java.

Code Cell

// Setup instructions
// To run Java in Jupyter Notebooks, use IJava kernel or other suitable kernel.

Markdown Cell

## 1. User Login System
### 1.1 Basic Authentication

Code Cell

import java.util.HashMap;
import java.util.Map;

public class BasicAuth {
    private static Map<String, String> users = new HashMap<>();

    static {
        users.put("user1", "password1");
        users.put("user2", "password2");
    }

    public static boolean authenticate(String username, String password) {
        return users.containsKey(username) && users.get(username).equals(password);
    }

    public static void main(String[] args) {
        System.out.println(authenticate("user1", "password1")); // true
        System.out.println(authenticate("user2", "wrongpassword")); // false
    }
}

BasicAuth.main(new String[0]);

Markdown Cell

### 1.2 Form-Based Authentication

Code Cell

import java.util.Scanner;

public class FormAuth {
    private static Map<String, String> users = new HashMap<>();

    static {
        users.put("user1", "password1");
        users.put("user2", "password2");
    }

    public static boolean authenticate(String username, String password) {
        return users.containsKey(username) && users.get(username).equals(password);
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("Enter username: ");
        String username = scanner.nextLine();

        System.out.print("Enter password: ");
        String password = scanner.nextLine();

        if (authenticate(username, password)) {
            System.out.println("Login successful");
        } else {
            System.out.println("Invalid credentials");
        }
    }
}

FormAuth.main(new String[0]);