We need Java 11, Maven and MySQL installed
sudo apt install -y openjdk-11-jdk maven
sudo apt install -y mysql-server mysql-client
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'@'%';
~$ 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
curl -X POST -H 'Content-Type: application/json' \
-d '{"username":"john","password":"2simple","email":"johndoe@example.com","role":["admin"]}' \
http://localhost:8080/auth/signup
curl -X POST -H 'Content-Type: application/json' \
-d '{"username":"john","password":"2simple"}' \
http://localhost:8080/auth/signin
curl -X GET -H 'Content-Type: application/json' \
http://localhost:8080/api/getAuthors
You should get a response with 401 Unauthorized.
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
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.
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.
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.
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).
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:
- Cross-Site Request Forgery Prevention Cheat Sheet
- Cross-Site Scripting Prevention Cheat Sheet
- JWT Security Best Practices
- Spring Data JPA + MySQL is used but will not be discussed here
- Refresh Token will NOT be implemented here
- Signup page is NOT implemented
- Introduction to JSON Web Tokens (JWT)
- Java JWT
- JJWT
- HttpOnly Cookie
- Accessing data with MySQL
- Spring Initializr
- Dietmar Rabich / Wikimedia Commons / “Dülmen, Kirchspiel, Börnste, Felder und Bäume -- 2017 -- 3220-6” / CC BY-SA 4.0
- Dietmar Rabich / Wikimedia Commons / “Dülmen, Merfeld, Feldweg am Mühlenbach -- 2021 -- 4278-80” / CC BY-SA 4.0
- Dietmar Rabich / Wikimedia Commons / “Santorin (GR), Exomytis, Vlychada Beach -- 2017 -- 2999 (bw)” / CC BY-SA 4.0
- Report issues
- Open pull request with improvements
- Spread the word