• Home
  • Java Spring Boot JWT example

Java Spring Boot JWT example

In one of my latest projects I need to build server-side application in Java ( SpringBoot framework ) with Spring Security along with JWT authentication that will be used for my frontend application built in React. Frontend part of this project in React will be shown to you in some of my other post.

For building this I was using Spring Tool Suite as my IDE but this is not relevant. You can use any IDE that suits you when you building Java apps. I will put all code to my Github so you can check how everything is done.

So steps to build this are:

  1. In dependencies I include Web, Jpa, MySql and Security. So why MySql here … in my case client already have old MySql database with users so another database was not an option at that moment… but I sure can implement any other database solution here
  2. Also for everything to work I include another two dependencies in my pom.xml file.
<!-- Must be for work with Json Web Tokens (JWT) -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

<!-- Included For Java 8 Date/Time Support -->
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

Now I insert some properties to application.properties file. In my development process I use localhost server port 5000, and my database is called boat_app. Also notice here that I use ddl-auto property update but this is only used when I develop that server side test example ( not in production because I don’t need tables to be created/updated there  I already have tables in production database )

You can spot here also that I want to use Date/Time values as ISO string so I’ve set WRITE_DATES_AS_TIMESTAMPS to false

## Server Properties
server.port= 5000

## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url= jdbc:mysql://localhost:3306/boat_app?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username= root
spring.datasource.password= nopassatthemoment

## Hibernate Properties

# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto = update

## Hibernate Logging
logging.level.org.hibernate.SQL= DEBUG

## Jackson Properties
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS= false
spring.jackson.time-zone= UTC

Main Class of App. In main Class I register JPA2.1 converters  so that all Java Date/Time fields get converted to SQL types when we persist them in database and here we also set application default timezone to UTC.

package com.chrisvz.boatapp;

import java.util.TimeZone;
import javax.annotation.PostConstruct;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters;

import com.chrisvz.boatapp.config.FileStorageProperties;


/**
 * @author kristijanklepac
 *
 */
@SpringBootApplication

@EntityScan(basePackageClasses = { 
    BoatappV1Application.class,
    Jsr310JpaConverters.class 
})
@EnableConfigurationProperties({
    FileStorageProperties.class
})
public class BoatappV1Application {
  
  
  
  @PostConstruct
  void init() {
    TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
    
  }

  public static void main(String[] args) {
    SpringApplication.run(BoatappV1Application.class, args);
  }
  
}

User model (entity). So in our model package there are all fields that we have in our users table, roles is field with Many-To-Many relationship to entity Role, and DateAudit class which User extends is class in which we have createdAt, and updatedAt fields that we also use in multiple other classes. User can have one or more Roles and this is used in final product to determine if user have access to some API endpoint.

@Entity
@Table(name = "users", uniqueConstraints = {
        @UniqueConstraint(columnNames = {
            "username"
        }),
        @UniqueConstraint(columnNames = {
            "email"
        })
})
public class User extends DateAudit {
  
  @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank
    @Size(max = 40)
    private String name;

    @NotBlank
    @Size(max = 15)
    private String username;

    @NaturalId
    @NotBlank
    @Size(max = 40)
    @Email
    private String email;

    @NotBlank
    @Size(max = 100)
    private String password;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "user_roles",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles = new HashSet<>();

...getters, setters

RoleName enum. In my case there are two roles  ROLE_USER and ROLE_ADMIN. I don’t show you Role class here because you can see it in my Github. Also this roles must be present in table roles (ROLE_USER and ROLE_ADMIN) in our database

package com.chrisvz.boatapp.model;

public enum RoleName {
  ROLE_USER,
  ROLE_ADMIN

}

User and Role must have their repositories. So in package called Repository you can find two interfaces UserRepository and RoleRepository. Inside this repositories you can see some methods

package com.chrisvz.boatapp.repository;

import java.util.List;
import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.chrisvz.boatapp.model.User;



@Repository
public interface UserRepository extends JpaRepository<User, Long> {
  
  Optional<User> findByEmail(String email);

    Optional<User> findByUsernameOrEmail(String username, String email);

    List<User> findByIdIn(List<Long> userIds);

    Optional<User> findById(Long userId);

    Optional<User> findByUsername(String username);

    Boolean existsByUsername(String username);

    Boolean existsByEmail(String email);

}
package com.chrisvz.boatapp.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.chrisvz.boatapp.model.Role;
import com.chrisvz.boatapp.model.RoleName;

@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
    Optional<Role> findByName(RoleName roleName);
}

Next steps explanation:

  • we need API that register new user
  • we need API to let users to login and after validating user API should generate JWT token which is send in response to user. Later user will be use this token in every request (as header request) to protected resources in our app
  • restrict some resources to certain roles that user have so that user with ROLE_USER can’t access to admin resources

So in order to do this things first I create config package and inside class SecurityConfig. In that class I use some of annotations and configurations for Security

@EnableWebSecurity – must have annotation to enable web security in project

@EnableGlobalMethodSecurity in which I enable

  • securedEnabled = true,  this allows me for example use of @Secured(“ROLE_ADMIN”) in my controllers or service (protect methods)
  • jsr250Enabled = true,  this allows me for example use of @RolesAllowed(“ROLE_ADMIN”)
  • prePostEnabled = true, with this I can use @PreAuthorize or @PostAuthorize annotations

My SecurityConfig class extends WebSecurityConfigurerAdapter and we override some methods to provide some custom security configuration (check in code on my Github and for more info check Spring Security Guides)

CustomUserDetailsService – Spring security will load user details here through loadByUsername method (will return UserPrincipal object)

JWTAutheticationEntryPoint – this class return 401 error if user try to access protected route. Class implements AutheticationEntryPoint interface from Spring Security

JWTAutheticationFilter – what this do:

  • read JWT token from Authorization header
  • validates the token
  • loads the user details
  • set user details in Spring’s Security  SecurityContext. When user details are stored in SecurityContext we can access to them from our controllers

AutheticationManagerBuilder and AutheticationManager – we use it to create AutheticationManager instance which is the main SpringSecurity interface for autheticating a user. I provide customUserDetailsService and passwordEncoder to build the AuthenticationManager

HttpSecurity configurations – here are configured protected routes, public API endpoints, csrf protection and similar stuff

JwtTokenProvider class – after user login sucessfuly this class generate JWT token and also validate token send in Authorization header of the request

JWT properties – this properties are added to application.properties and define secret key for JWT and also expiration time for token

JWTAutheticationFilter – get token from request, validate it, load user and pass all to Spring Security

CurrentUser class – this is simply wrapper arround @AuthenticationPrincipal annotation. If we wanna sometime remove Spring Security we can simply change CurrentUser (meta annotation)

This is basically it.  You can check all files for this example on my Github here

Thanks for reading.

 

 

Tags: ,

Copyright by Kristijan Klepač 2018