SpringBoot Token Based Authentication Example - MySQL + JWT+ Spring JPA + RestAPIs
springboot
138
JWT
11
authentication
20
Male avatar

loveprogramming viết ngày 05/03/2021

https://loizenai.com/spring-boot-security-jwt-token-bsed-authentication-example-mysql-spring-jpa-restapis/

https://grokonez.com/spring-framework/spring-boot/spring-security-jwt-authentication-postgresql-restapis-springboot-spring-mvc-spring-jpa

"How to build SpringBoot Token Based Authentication Example?" is one of the most common questions for Java development world. So in the tutorial, I will introduce how to build it with clearly architecture and coding examples.

Related posts:

Video Guide

Spring Security JWT Architecture – Springboot Token Based Authentication Example

This is diagram for Spring Security/JWT (Springboot Token Based Authentication Example) classes that are separated into 3 layers:
– HTTP
– Spring Security
– REST API

[caption id="attachment_712" align="alignnone" width="750"]Spring Security Jwt Authentication Architecture Diagram Spring Security Jwt Authentication Architecture Diagram[/caption]

Look at the diagram above, we can easily associate these components with Spring Security Authentication process: receive HTTP request, filter, authenticate, store Authentication data, generate token, get User details, authorize, handle exception…

At a glance:
SecurityContextHolder provides access to the SecurityContext.
SecurityContext holds the Authentication and possibly request-specific security information.
Authentication represents the principal which includes GrantedAuthority that reflects the application-wide permissions granted to a principal.
UserDetails contains necessary information to build an Authentication object from DAOs or other source of security data.
UserDetailsService helps to create a UserDetails from a String-based username and is usually used by AuthenticationProvider.
JwtAuthTokenFilter (extends OncePerRequestFilter) pre-processes HTTP request, from Token, create Authentication and populate it to SecurityContext.
JwtProvider validates, parses token String or generates token String from UserDetails.
UsernamePasswordAuthenticationToken gets username/password from login Request and combines into an instance of Authentication interface.
AuthenticationManager uses DaoAuthenticationProvider (with help of UserDetailsService & PasswordEncoder) to validate instance of UsernamePasswordAuthenticationToken, then returns a fully populated Authentication instance on successful authentication.
SecurityContext is established by calling SecurityContextHolder.getContext().setAuthentication(…​) with returned authentication object above.
AuthenticationEntryPoint handles AuthenticationException.
– Access to Restful API is protected by HTTPSecurity and authorized with Method Security Expressions.

Receive HTTP Request

When a HTTP request comes (from a browser, a web service client, an HttpInvoker or an AJAX application – Spring doesn’t care), it will go through a chain of filters for authentication and authorization purposes.

So, it is also true for a User Authentication request, that filter chain will be applied until relevant Authentication Filter is found.

Filter the Request

In this architecture, we add our JwtAuthTokenFilter (that extends Spring OncePerRequestFilter abstract class) to the chain of filters.

class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ...
        http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

JwtAuthTokenFilter validates the Token using JwtProvider:

class JwtAuthTokenFilter extends OncePerRequestFilter {
    @Autowired
    private JwtProvider tokenProvider;
 
    @Override
    protected void doFilterInternal(...) {
        String jwt = getJwt(request);
        if (jwt!=null && tokenProvider.validateJwtToken(jwt)) {
            ...
        }
        filterChain.doFilter(request, response);
    }
}

Now we have 2 cases for Springboot Token Based Authentication Example:
– Login/SignUp: RestAPI with non-protected APIs -> authenticate Login Request with AuthenticationManager, if error occurs, handle AuthenticationException with AuthenticationEntryPoint.
– With protected Resources:

  • jwt token is null/invalid -> if Authenticated Error occurs, handle AuthenticationException with AuthenticationEntryPoint.
  • jwt token is valid -> from token, get User information, then create AuthenticationToken.

    Create AuthenticationToken from Token

    JwtAuthTokenFilter extracts username/password from the received token using JwtProvider, then based on the extracted data, JwtAuthTokenFilter: – creates a AuthenticationToken (that implements Authentication) – uses the AuthenticationToken as Authentication object and stores it in the SecurityContext for future filter uses (e.g: Authorization filters).

In this tutorial, we use UsernamePasswordAuthenticationToken:

// extract user information
String username = tokenProvider.getUserNameFromJwtToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
 
// create AuthenticationToken
UsernamePasswordAuthenticationToken authentication
        = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

Store Authentication object in SecurityContext

SecurityContextHolder.getContext().setAuthentication(authentication);

SecurityContextHolder is the most fundamental object where we store details of the present security context of the application (includes details of the principal). Spring Security uses an Authentication object to represent this information and we can query this Authentication object from anywhere in our application:

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// currently authenticated user
Object principal = authentication.getPrincipal();

getContext() returns an instance of SecurityContext interface that holds the Authentication and possibly request-specific security information.

Delegate AuthenticationToken for AuthenticationManagager

After AuthenticationToken object was created, it will be used as input parameter for authenticate() method of the AuthenticationManager:

public interface AuthenticationManager {
    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;
}

We can see that AuthenticationManager is just an interface, the default implementation in Spring Security is ProviderManager:

public class ProviderManager implements AuthenticationManager, ... {
    private List providers;
}

Authenticate with AuthenticationProvider

AuthenticationProviders

ProviderManager delegates to a list of configured AuthenticationProviders, each of them will try to authenticate the User, then either throw an exception or return a fully populated Authentication object:

public class ProviderManager implements AuthenticationManager, ... {
    private List providers;
 
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        for (AuthenticationProvider provider : getProviders()) {
            ...
            try {
                ...
                result = provider.authenticate(authentication);
                if (result != null) {
                    copyDetails(authentication, result);
                    break;
                }
            }
            catch (Exception...) {}
            ...
            return result;
        }
    }
}

These are some authentication providers that Spring Framework provides:

  • DaoAuthenticationProvider
  • PreAuthenticatedAuthenticationProvider
  • LdapAuthenticationProvider
  • ActiveDirectoryLdapAuthenticationProvider
  • JaasAuthenticationProvider
  • CasAuthenticationProvider
  • RememberMeAuthenticationProvider
  • AnonymousAuthenticationProvider
  • RunAsImplAuthenticationProvider
  • OpenIDAuthenticationProvider

DaoAuthenticationProvider

DaoAuthenticationProvider works well with form-based logins or HTTP Basic authentication which submits a simple username/password authentication request.
It authenticates the User simply by comparing the password submitted in a UsernamePasswordAuthenticationToken against the one loaded by the UserDetailsService (as a DAO):

@Autowired
AuthenticationManager authenticationManager;
...
Authentication authentication = 
        authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(loginRequest.username, loginRequest.password)
        );

Configuring this provider is simple with AuthenticationManagerBuilder:

class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    UserDetailsServiceImpl userDetailsService;
 
    @Override
    public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder
                .userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }
 
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

Retrieve User details with UserDetailsService

We can obtain a principal from the Authentication object. This principal can be cast into a UserDetails object to lookup the username, password and GrantedAuthoritys.

Therefore, after authenticating is successful, we can simply get UserDetails from Authentication object:

UserDetails userDetails = (UserDetails) authentication.getPrincipal();
// userDetails.getUsername()
// userDetails.getPassword()
// userDetails.getAuthorities()

DaoAuthenticationProvider also uses UserDetailsService for getting UserDetails object. This is the common approach in which we only pass a String-based ‘username’ argument and returns a UserDetails:

public interface UserDetailsService {
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

It is simple to implement UserDetailsService and easy for us to retrieve authentication information using a persistence strategy:

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    UserRepository userRepository;
 
    @Override
    @Transactional
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
 
      User user = userRepository.findByUsername(username).orElseThrow(
          () -> new UsernameNotFoundException("User Not Found with -> username or email : " + username));
 
      return UserPrinciple.build(user); // UserPrinciple implements UserDetails
    }
}

Get GrantedAuthority

Another important method provided by Authentication is getAuthorities() that provides an collection of GrantedAuthority objects:

public interface Authentication extends Principal, Serializable {
    Collection extends GrantedAuthority> getAuthorities();
}

A GrantedAuthority is an authority that is granted to the principal. Such authorities are usually ‘roles’, such as ROLE_ADMIN, ROLE_PM, ROLE_USER

Protect Resources with HTTPSecurity & Method Security Expressions

Configure HTTPSecurity

To help Spring Security know when we want to require all users to be authenticated, which Exception Handler to be chosen, which filter and when we want it to work. We implement WebSecurityConfigurerAdapter and provide a configuration in the configure(HttpSecurity http) method:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable().
                authorizeRequests()
                .antMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                ...;
        
        http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

Method Security Expressions

Spring Security provides some annotations for pre and post-invocation authorization checks, filtering of submitted collection arguments or return values: @PreAuthorize, @PreFilter, @PostAuthorize and @PostFilter.

To enable Method Security Expressions, we use @EnableGlobalMethodSecurity annotation:

@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    ...
}

In the code below, we use the most useful annotation @PreAuthorize to decide whether a method can actually be invoked or not:

@RestController
public class TestRestAPIs {
  
    @GetMapping("/api/test/user")
    @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
    public String userAccess() {
      return ">>> User Contents!";
    }
 
    @GetMapping("/api/test/pm")
    @PreAuthorize("hasRole('PM') or hasRole('ADMIN')")
    public String projectManagementAccess() {
      return ">>> Project Management Board";
    }
  
    @GetMapping("/api/test/admin")
    @PreAuthorize("hasRole('ADMIN')")
    public String adminAccess() {
        return ">>> Admin Contents";
    }
}

Handle AuthenticationException – AuthenticationEntryPoint

If the user requests a secure HTTP resource without being authenticated, AuthenticationEntryPoint will be called. At this time, an AuthenticationException is thrown, commence() method on the entry point is triggered:

@Component
public class JwtAuthEntryPoint implements AuthenticationEntryPoint {
   
    @Override
    public void commence(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException e) 
                            throws IOException, ServletException {
      
        logger.error("Unauthorized error. Message - {}", e.getMessage());
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error -> Unauthorized");
    }
}

Build Springboot Token Based Authentication Example

Technologies

For Springboot Token Based Authentication Example, we use below stack of tech:
– Spring Boot
– jjwt – 0.9.0
– Spring Security
– Spring JPA
– MySQL

Introduce JSON Web Token

JSON Web Token (JWT) defines a compact and self-contained way for securely transmitting information between parties as a JSON object.

Scenarios where JSON Web Tokens are useful:

  • Authorization: the most common scenario for using JWT. Single Sign On is a feature that widely uses JWT
  • Information Exchange: Because JWTs can be signed, JSON Web Tokens are a good way of securely transmitting information between parties.

JSON Web Tokens consist of 3 parts:

  • Header
  • Payload
  • Signature

-> JWT looks like Header-Base64-String.Payload-Base64-String.Signature-Base64-String

Header consists of two parts:

  • token type.
  • hashing algorithm.

-> Example:

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload contains the claims. Claims are statements about an entity and additional information.
There are 3 types of claims ->

  • Registered claims -> These are a set of predefined claims: iss (issuer), exp (expiration time), sub (subject)
  • Public claims
  • Private claims

Example:

{
  "sub": "thomas-loizenai.com",
  "iat": 1537603195,
  "exp": 1537689595
}

Signature: To create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.

Example:

HMACSHA512(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  your-256-bit-secret
)

Combine all together, we get 3 Base64-URL strings separated by dots, example:

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0aG9tYXNna3oiLCJpYXQiOjE1Mzc2MDMxOTUsImV4cCI6MTUzNzY4OTU5NX0.m2YMjTYmOnfR7nnVNxqCzWbQ2FhKRe1eiizxnC2TF4eAoEzKlwo7PheVkKcxj08ST3vB-ZOIhiORvYVfSgzcog

When accessing a protected route or resource, the user agent should send the JWT, typically in the Authorization header using the Bearer schema.

Example:

Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0aG9tYXNna3oiLCJpYXQiOjE1Mzc2MDMxOTUsImV4cCI6MTUzNzY4OTU5NX0.m2YMjTYmOnfR7nnVNxqCzWbQ2FhKRe1eiizxnC2TF4eAoEzKlwo7PheVkKcxj08ST3vB-ZOIhiORvYVfSgzcog

Overview

Project Structure for Springboot Token Based Authentication Example

We create a SpringBoot project as below:

[caption id="attachment_715" align="alignnone" width="536"]Springboot Jwt Authentication - Project Structure Springboot Jwt Authentication - Project Structure[/caption]

model package defines 2 entities User & Role that have many-to-many relationship:

[caption id="attachment_716" align="alignnone" width="571"]Springboot Restapi Jwt Json Web Token Authentication Many To Many User-Role Springboot Restapi Jwt Json Web Token Authentication Many To Many User-Role[/caption]

repository package contains interfaces that use Hibernate JPA to store/retrieve data from MySQL database.
controller package defines RestAPIs for user signup/signin and testing protected resources that is secured with JWT.
message package defines payload data transferred from user agents (Browser/RestClient…) to RestAPIs and message back.
security package is the main part of the project that implements JWT security.

Goal

In the tutorial "Springboot Token Based Authentication Example", we expose 2 RestAPIs to signup and signin:

Sign up: /api/auth/signup

[caption id="attachment_717" align="alignnone" width="622"]Springboot Jwt Authentication - Register User Phrase Springboot Jwt Authentication - Register User Phrase[/caption]

  • Sign In /api/auth/signin:

[caption id="attachment_718" align="alignnone" width="884"]springboot jwt authentication - register user phrase - Jack signin springboot jwt authentication - register user phrase - Jack signin[/caption]

– In the tutorial Springboot Token Based Authentication Example, We expose 3 RestAPIs to test protected resources:

@GetMapping("/api/test/user")
@PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
public String userAccess() {
  return ">>> User Contents!";
}
 
@GetMapping("/api/test/pm")
@PreAuthorize("hasRole('PM') or hasRole('ADMIN')")
public String projectManagementAccess() {
  return ">>> Board Management Project";
}
 
@GetMapping("/api/test/admin")
@PreAuthorize("hasRole('ADMIN')")
public String adminAccess() {
  return ">>> Admin Contents";
}
  • Example Access Successfully:

[caption id="attachment_719" align="alignnone" width="683"]springboot jwt authentication - register user phrase - jack access user content -successfully springboot jwt authentication - register user phrase - jack access user content - successfully[/caption]

  • Example Unauthorized:

[caption id="attachment_720" align="alignnone" width="795"]Springboot Jwt Authentication - Jack Access PM Content - Fail Springboot Jwt Authentication - Jack Access PM Content - Fail[/caption]

PRACTICE - SpringBoot Token Based Authentication Example

Create SpringBoot project

In the tutorial Springboot Token Based Authentication Example, We create a SpringBoot project with below dependencies:


<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.9.0</version>
</dependency>

If you use MySQL database, add more the below dependency:

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
</dependency>

If you use PostgreSQL database, add more the below dependency:

<dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <scope>runtime</scope>
</dependency>

Create Models

User.java model contains 5 attributes:

  • id
  • name
  • username
  • email
  • password
package com.loizenai.jwtauthentication.model;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

import org.hibernate.annotations.NaturalId;

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

    @NotBlank
    @Size(min=3, max = 50)
    private String name;

    @NotBlank
    @Size(min=3, max = 50)
    private String username;

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

    @NotBlank
    @Size(min=6, 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 roles = new HashSet();

    public User() {}

    public User(String name, String username, String email, String password) {
        this.name = name;
        this.username = username;
        this.email = email;
        this.password = password;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Set getRoles() {
        return roles;
    }

    public void setRoles(Set roles) {
        this.roles = roles;
    }
}

Role.java model contains 2 attributes:

  • id
  • rolename
package com.loizenai.jwtauthentication.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.NaturalId;

@Entity
@Table(name = "roles")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Enumerated(EnumType.STRING)
    @NaturalId
    @Column(length = 60)
    private RoleName name;

    public Role() {}

    public Role(RoleName name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public RoleName getName() {
        return name;
    }

    public void setName(RoleName name) {
        this.name = name;
    }
}

RoleName.java:

package com.loizenai.jwtauthentication.model;

public enum  RoleName {
    ROLE_USER,
    ROLE_PM,
    ROLE_ADMIN
}

More at: https://loizenai.com/spring-boot-security-jwt-token-bsed-authentication-example-mysql-spring-jpa-restapis/

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

Male avatar

loveprogramming

451 bài viết.
79 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
Male avatar
1 0
Tutorial Link: (Link) (Ảnh) Django is a Pythonbased free and opensource web framework that follows the modeltemplateview architectural pattern. A...
loveprogramming viết 6 tháng trước
1 0
Male avatar
1 0
https://loizenai.com/angular11nodejspostgresqlcrudexample/ Angular 11 Node.js PostgreSQL Crud Example (Ảnh) Tutorial: “Angular 11 Node.js Postg...
loveprogramming viết 5 tháng trước
1 0
Male avatar
1 0
Angular Spring Boot jwt Authentication Example Github https://loizenai.com/angularspringbootjwt/ (Ảnh) Tutorial: ” Angular Spring Boot jwt Authe...
loveprogramming viết 5 tháng trước
1 0
Bài viết liên quan
Male avatar
0 0
https://grokonez.com/frontend/angular/angular6/kotlinspringbootangular6crudhttpclientmysqlexamplespringdatajparestapisexample Kotlin Spring Boot +...
loveprogramming viết 1 tháng trước
0 0
Male avatar
0 0
https://grokonez.com/springframework/springmvc/angular4springbootcassandracrudexample no_toc]In this tutorial, grokonez shows you Angular Http Cli...
loveprogramming viết 2 tháng trước
0 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


Male avatar
{{userFollowed ? 'Following' : 'Follow'}}
451 bài viết.
79 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!