Hướng dẫn sử dụng JWT với Spring Boot
Trong bài hướng dẫn sử dụng JWT với Spring Boot, chúng tôi sẽ hướng dẫn các bạn cách xác thực (Authentication) và phân quyền (Authorization) sử dụng JSON Web Token (JWT). Ở bài hướng dẫn sử dụng Thymeleaf trong Spring Boot, chúng tôi đã hướng dẫn các bạn cách phát triển ứng dụng web với 4 tính năng cơ bản là xem, thêm, xóa và cập nhật dữ liệu.
Người dùng sau khi đăng ký tài khoản thành công, sẽ sử dụng tài khoản này để đăng nhập vào hệ thống. Đăng nhập thành công, hệ thống sẽ tạo một token tương ứng cho người dùng đó.
Token này sẽ được sử dụng để nhận diện người dùng này với người dùng khác. Để hiểu rõ hơn cách JWT hoạt động, các bạn xem hình bên dưới
Đây là API chúng ta cần tạo trong bài hướng dẫn này
Phương thức | URL | Diễn giải |
---|---|---|
POST | /api/auth/signup | Tạo mới tài khoản |
POST | /api/auth/signin | Đăng nhập |
GET | /api/test/all | Get dữ liệu |
GET | /api/test/user | Get dữ liệu với quyền User |
GET | /api/test/mod | Get dữu liệu với quyền Moderator |
GET | /api/test/admin | Get dữ liệu với quyền Admin |
Hướng dẫn sử dụng JWT với Spring Boot – Chuẩn bị
Công nghệ sử dụng:
- Java 8+
- Spring Boot 2.3.4+ (có Spring Security, Spring Web và Spring Data JPA)
- JJWT 0.9.1+
- MySQL 8
- Maven 3.6.1+
Công cụ sử dụng:
- IDE sử dụng Eclipse phiên bản 2020-06 hoặc cao hơn
- Postman phiên bản mới nhất
Hướng dẫn sử dụng JWT với Spring Boot – Các bước thực hiện
1/ Tạo dự án Spring Boot sử dụng Spirng Initializer
- Các bạn truy cập https://start.spring.io/
- Nhập thông tin Group, Artifact và chọn dependencies
- Chọn Generate để tải file dự án về máy
- Giải nén file vừa tải về và import vào eclipse
Tại eclipse -> chọn File -> chọn Open Projects from File System… -> chọn Directory… -> chỉ định thư mục chứa file đã giải nén -> chọn project -> chọn Select Folder
2/ Bổ sung dependency
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
3/ Khai báo application.properties
spring.datasource.url= jdbc:mysql://localhost:3306/jwt?useSSL=false
spring.datasource.username= root
spring.datasource.password= root
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto= update
# App Properties
bezkoder.app.jwtSecret= bezKoderSecretKey
bezkoder.app.jwtExpirationMs= 86400000
Lưu ý:
- Password đăng nhập vào MySQL trên máy chúng tôi là root
- Tên cơ sở dữ liệu trên MySQL là jwt
4/ Tạo Entity
Mỗi entity là một table trong MySQL. Chúng ta cần tạo 3 tables trong database gồm users, roles và user_roles. Trong đó users và roles là quan hệ nhiều nhiều. Trước khi tạo những entity này, chúng ta sẽ tạo một package tên entities
Ngoài ra khi tạo entity tên Role chúng ta cần một Enum khai báo 3 roles gồm User, Moderator và Admin.
a/ Chúng tôi sẽ tạo Enum tên ERole này tại vn.giasutinhoc.demo.jwt.common
- Chuột phải lên vn.giasutinhoc.demo.jwt.common -> chọn New -> chọn Enum
- Nhập tên là ERole
- Nhập code
b/ Tạo các entity thuộc vn.giasutinhoc.demo.jwt.entities
Tạo entity tên Role
Tạo entity tên User
5/ Tạo Repository
Mỗi một entity cần một repository để thao tác với dữ liệu. Chúng ta cần tạo 2 repositories tại vn.giasutinhoc.demo.jwt.repositories
a/ Tạo UserRepository
- Tạo một interface
- Nhập code
b/ Tạo RoleRepository
6/ Tạo Java class tên UserDetailsImpl trong vn.giasutinhoc.demo.jwt.services
7/ Tạo Service tên UserDetailsServiceImpl trong vn.giasutinhoc.demo.jwt.services
8/ Tạo JWT Utility
Tạo Java class tên JwtUtils thuộc vn.giasutinhoc.demo.jwt.common và có 3 chức năng gồm
- Tạo JWT từ username, date, expiration và secret
- Lấy username từ JWT
- Xác nhận JWT
9/ Xử lý ngoại lệ về xác thực (Authencation)
Chúng ta tạo một Java class tên AuthEntryPointJwt tại vn.giasutinhoc.demo.jwt.config thực thi (implements) AuthenticationEntryPoint
HttpServletResponse.SC_UNAUTHORIZED chính là 401, cho biết yêu cầu cần xác thực
10/ Lọc những yêu cầu
Chúng ta tạo một Java class tên AuthTokenFilter tại vn.giasutinhoc.demo.jwt.config kế thừa OncePerRequestFilter
11/ Cấu hình Spring Security
Tại vn.giasutinhoc.demo.jwt.config, tạo một Java class tên WebSecurityConfig kế thừa WebSecurityConfigurerAdapter
12/ Tạo các DTO (Data Transfer Object)
a/ LoginRequest gồm username, password
package vn.giasutinhoc.demo.jwt.dto;
public class LoginRequest {
private String username;
private String password;
/**
* Create an empty LoginRequest object
*/
public LoginRequest() {
super();
}
/**
* Create a LoginRequest object with full attributes
*
* @param username user's user name
* @param password
*/
public LoginRequest(String username, String password) {
super();
this.username = username;
this.password = password;
}
/**
* @return the username
*/
public String getUsername() {
return username;
}
/**
* @param username the username to set
*/
public void setUsername(String username) {
this.username = username;
}
/**
* @return the password
*/
public String getPassword() {
return password;
}
/**
* @param password the password to set
*/
public void setPassword(String password) {
this.password = password;
}
}
b/ SignupRequest gồm username, email, password và role
package vn.giasutinhoc.demo.jwt.dto;
import java.util.Set;
public class SignupRequest {
private String username;
private String email;
private String password;
private Set<String> role;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
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<String> getRole() {
return this.role;
}
public void setRole(Set<String> role) {
this.role = role;
}
}
c/ JwtResponse gồm token, type, id, username, email và roles
package vn.giasutinhoc.demo.jwt.dto;
import java.util.List;
public class JwtResponse {
private String token;
private String type = "Bearer";
private Long id;
private String username;
private String email;
private List<String> roles;
public JwtResponse(String accessToken, Long id, String username, String email, List<String> roles) {
this.token = accessToken;
this.id = id;
this.username = username;
this.email = email;
this.roles = roles;
}
public String getAccessToken() {
return token;
}
public void setAccessToken(String accessToken) {
this.token = accessToken;
}
public String getTokenType() {
return type;
}
public void setTokenType(String tokenType) {
this.type = tokenType;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public List<String> getRoles() {
return roles;
}
}
d/ MessageResponse gồm message
package vn.giasutinhoc.demo.jwt.dto;
public class MessageResponse {
private String message;
public MessageResponse(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Kết quả sau khi tạo xong các DTO
13/ Tạo Spring Rest API Controller
Controller này hỗ trợ APIs cho việc đăng ký và đăng nhập, trong đó
- Tính năng đăng ký sẽ có URL là /api/auth/signup
- Tính năng đăng nhập sẽ có URL là /api/auth/signin
a/ Tạo Java class tên AuthController tại vn.giasutinhoc.demo.jwt.controllers
b/ Tạo thêm một Controller dùng để test tên TestController trong vn.giasutinhoc.demo.jwt.controllers
package vn.giasutinhoc.demo.jwt.controllers;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/test")
public class TestController {
@GetMapping("/all")
public String allAccess() {
return "Public Content.";
}
@GetMapping("/user")
@PreAuthorize("hasRole('USER') or hasRole('MODERATOR') or hasRole('ADMIN')")
public String userAccess() {
return "User Content.";
}
@GetMapping("/mod")
@PreAuthorize("hasRole('MODERATOR')")
public String moderatorAccess() {
return "Moderator Board.";
}
@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')")
public String adminAccess() {
return "Admin Board.";
}
}
Cấu trúc project sau khi hoàn tất các bước trên
Hướng dẫn sử dụng JWT với Spring Boot – Build, run và demo
1/ Build và run
Chuột phải tại Application.java -> chọn Run as -> chọn Java Application
Quan sát tại Console
Quan sát tại MySQL Workbench
Mở cửa số nhập lệnh và thực thi các câu lệnh sau để tạo các role như USER, MOD và ADMIN
use jwt;
INSERT INTO roles(name) VALUES('ROLE_USER');
INSERT INTO roles(name) VALUES('ROLE_MODERATOR');
INSERT INTO roles(name) VALUES('ROLE_ADMIN');
Dữ liệu tại bảng roles sau khi tạo
2/ Demo
Mở Postman và thực hiện tạo tài khoản, đăng nhập và kiểm tra authentication, authorization
a/ Tạo tại khoản loại mod
- URL: http://localhost:8080/api/auth/signup
- Method: POST
- Body
{
"username": "mod",
"email": "giasutinhoc.vn@gmail.com",
"password": "Abc12345",
"role": ["mod", "user"]
}
b/ Tạo tài khoản loại user
{
"username": "kylh84",
"email": "giasutinthoc.vn@gmail.com",
"password": "Abc12345",
"role": ["user"]
}
c/ Tạo tài khoản loại admin
{
"username": "admin",
"email": "kylh84@gmail.com",
"password": "Abc12345",
"role": ["admin"]
}
Dữ liệu tại các bảng users và user_roles
- Bảng users
- Bảng user_roles
c/ Test xác thực và phân quyền
- Truy cập tài nguyên không yêu cầu xác thực
- Truy cập tài nguyên cần xác thực, trong trường hợp chưa xác thực hệ thống sẽ thông báo Unauthorized
- Truy cập tài nguyên cần xác thực, trong trường hợp đã xác thực (Đã đăng nhập và có access token)
- Xác thực với role là user (Sử dụng access token sau khi đã đăng nhập thành công)
Kết luận
Chúng ta đã hoàn thành bài hướng dẫn về cách sử dụng Spring Security và JSON Web Tooken với Spring Boot. Thông qua bài hướng dẫn này, các bạn đã biết cách cầu hình cũng như cách tạo access tooken sau khi người dùng đã login.
Các bạn tự mình test các role khác như admin, mod để hiểu hơn về phân quyền (Authorization). Chẳng hạn các bạn đang là role mode nhưng thao tác như role admin thì hệ thống sẽ báo Forbidden
- Cơ sở dữ liệu
- Excel
- Hướng dẫn Android
- Hướng dẫn CSharp
- Hướng dẫn iOS
- Hướng dẫn Java
- Lab Android cơ bản
- Lab lập trình C
- Lab lập trình C#
- Lab lập trình java
- Lab SQL Server
- Lập trình Android cơ bản
- Lập trình Android với Kotlin
- Lập trình Android với Xamarin
- Lập trình C
- Lập trình C#
- Lập trình cơ sở dữ liệu
- Lập trình đa nền tảng với Xamarin
- Lập trình giao diện
- Lập trình iOS
- Lập trình iOS với Xamarin
- Lập trình Java cơ bản
- Lập trình Kotlin
- Lập trình PHP cơ bản
- Lập trình swift
- libGDX
- Phát triển app android
- Quản trị SQL Server
- Spring Framework
- SQLServer 2014
- Thiết kế web
- Tin nỗi bật
- Video hướng dẫn
- Word
- XML và JSON
android android project android studio android với xamarin app android C# cơ bản cơ sở dữ liệu database function gui if ios với xamarin java java cơ bản java web jdbc json kotlin listview lập trình android lập trình android cơ bản lập trình c lập trình giao diện mysql object oop php select spring framework Spring MVC sqlite sql server swift swing switch table thiết kế web vòng lặp vòng lặp do while vòng lặp for vòng lặp while web service xamarin xml đa nền tảng với xamarin