Skip to content

Spring Boot with Spring Security, JWT (auth0/java-jwt or jjwt), HttpOnly Cookie and Vue.js

License

Notifications You must be signed in to change notification settings

stupid-2020/spring-boot-with-jwt

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Spring Boot with JWT (auth0/java-jwt or jjwt)


Installation

Install System Packages Required

We need Java 11, Maven and MySQL installed

sudo apt install -y openjdk-11-jdk maven
sudo apt install -y mysql-server mysql-client

Create Database

Login as root:

sudo mysql -u root -p

Create database user and assign the privileges to the user created (Update YOUR_DB_USER, YOUR_DB_NAME and YOUR_DB_PASSWORD accordingly):

mysql> CREATE USER 'YOUR_DB_USER'@'%' IDENTIFIED BY 'YOUR_DB_PASSWORD';
mysql> CREATE DATABASE YOUR_DB_NAME
    -> DEFAULT CHARACTER SET utf8
    -> COLLATE utf8_general_ci;
mysql> GRANT ALL PRIVILEGES ON YOUR_DB_NAME.* to 'YOUR_DB_USER'@'%';

Run the Code

~$ cd /path/to/spring-boot-auto0-jwt
spring-boot-auto0-jwt$ mvn clean install
spring-boot-auto0-jwt$ mvn spring-boot:run

You have to populate the records for user role once:

~$ mysql -u YOUR_DB_USER -p YOUR_DB_NAME < /path/to/roles.sql 

Test the Code

Signup

curl -X POST -H 'Content-Type: application/json' \
  -d '{"username":"john","password":"2simple","email":"johndoe@example.com","role":["admin"]}' \
  http://localhost:8080/auth/signup

Signin

curl -X POST -H 'Content-Type: application/json' \
  -d '{"username":"john","password":"2simple"}' \
  http://localhost:8080/auth/signin

Test API without JWT

curl -X GET -H 'Content-Type: application/json' \
  http://localhost:8080/api/getAuthors

You should get a response with 401 Unauthorized.

Test API with JWT

You could use the JWT_TOKEN given from the output of Signin:

curl -X GET -H 'Content-Type: application/json' \
  -H "Authorization: Bearer JWT_TOKEN" \
  http://localhost:8080/api/getAuthors

Browser

Once you have signup an account, you could access the website http://localhost:8080/. It will redirect you to login page if valid JWT is not found. To logout the website, please go to http://localhost:8080/logout.


Security Consideration

If you have run the code and tried the JWT, you are authorized to use the API and get the data. You can also get the data using the curl command with the JWT at ANOTHER machine. Or you can replace the JWT stored in Browser with the one from curl command (or vice versa), you still get the data. As you may be aware, if one gets your JWT, one would have full access to your account and could do any operation as you even you have logout the application (before JWT is expired).

To prevent your JWT from being stolen, never use JWT without HTTPS. Otherwise, your application is vulnerable to Man-in-the-middle (MITM) attacks.

Store JWT in localStorage and Authorization header

In general, JWT can be sent in Authorization header using Bearer schema:

Authorization: Bearer <JWT>

A lot of examples use localStorage. It is not recommended but it is ok if you have good Cross-Site Scripting (XSS) prevention.

Store JWT in Cookie

This is the way this demonstration use.

    public String generateJwtCookie(String token) {
        return ResponseCookie
            .from("token", token)
            .maxAge(jwtExpiration)
            .httpOnly(true)
            .path("/")
            .build()
            .toString();
    }

In this way, the client side (Browser) does NOT need to handle the JWT nor sent the Authorization header (no headers property in our Axios requests). However, general cookies can be read by JavaScript (say, document.cookie) and is vulnerable to XSS attack. To avoid this, the server side need to set the HttpOnly flag on the cookie it creates. It means that the cookie can NOT be read using JavaScript but can still be sent back to the server in HTTP requests. (And that's why you need a safe browser!)

Note: this spring boot application supports both ways (Using Authorization header or sending JWT as cookie).

Token Lifetime

Using HttpOnly is not completely bulletproof. A long-lived JWT is definitely a bad idea. The better way is to protect your application against both XSS and CSRF:

  1. Cross-Site Request Forgery Prevention Cheat Sheet
  2. Cross-Site Scripting Prevention Cheat Sheet
  3. JWT Security Best Practices

Something Missed

  1. Spring Data JPA + MySQL is used but will not be discussed here
  2. Refresh Token will NOT be implemented here
  3. Signup page is NOT implemented

References

  1. Introduction to JSON Web Tokens (JWT)
  2. Java JWT
  3. JJWT
  4. HttpOnly Cookie
  5. Accessing data with MySQL
  6. Spring Initializr

Acknowledge

Background Image

Card Images


Contribution

  • Report issues
  • Open pull request with improvements
  • Spread the word