CÔNG NGHỆ THÔNG TIN >> SINH VIÊN BKAP

Bảo mật Rest API trong SpringBoot sử dụng JWT

Đăng lúc: 10:35 AM - 13/04/2025 bởi Charles Chung - 40

Trong bài này tôi sẽ hướng dẫn các bạn sinh JWT để xác thực các request tới rest api trong spring boot.

1. Giới thiệu về bảo mật REST API sử dụng JWT trong Spring Boot 3

Trong các ứng dụng web hiện đại, việc bảo mật các API là một yêu cầu bắt buộc nhằm đảm bảo rằng chỉ những người dùng đã được xác thực và có quyền mới có thể truy cập vào các tài nguyên hệ thống. Một trong những phương pháp phổ biến và hiệu quả để bảo vệ REST API là sử dụng JWT (JSON Web Token) – một chuẩn mở để truyền thông tin xác thực giữa client và server dưới dạng token an toàn và gọn nhẹ.

Với sự ra đời của Spring Boot 3Spring Security 6, cách cấu hình bảo mật cho REST API đã có nhiều thay đổi nhằm đơn giản hóa và chuẩn hóa các quy trình bảo mật, đồng thời hỗ trợ tốt hơn cho các kiến trúc không trạng thái (stateless), đặc biệt là khi kết hợp với JWT.

JWT hoạt động dựa trên nguyên tắc mã hóa thông tin người dùng vào một token sau khi đăng nhập thành công. Token này sẽ được client đính kèm trong các request tiếp theo dưới dạng Authorization header. Server sẽ giải mã và xác thực token trước khi cho phép truy cập vào tài nguyên.

Khi kết hợp JWT với Spring Boot 3, quá trình bảo mật REST API sẽ bao gồm các bước chính như:

  • Xây dựng hệ thống xác thực và tạo JWT sau khi người dùng đăng nhập thành công.

  • Cấu hình bộ lọc (filter) để kiểm tra token trong mỗi request.

  • Tùy chỉnh Spring Security để kiểm soát truy cập dựa trên quyền (roles).

  • Triển khai các biện pháp bảo mật bổ sung như cấu hình CORS, CSRF, hoặc giới hạn truy cập theo IP nếu cần.

Việc sử dụng JWT giúp ứng dụng đạt được kiến trúc stateless, tăng tính mở rộng và phù hợp với các hệ thống phân tán như microservices.

2. Ví dụ

Trong ví dụ này chúng ta sẽ thực hiện các công việc sau:

  • Đăng nhập: trước khi vào bất kỳ tài nguyên nào của rest api đều phải đăng nhập
  • Sinh JWT: khi đăng nhập thành công thì trả về JWT cho người dùng, kèm cả thông tin của người dùng.
  • Phân quyền: người dùng chia ra hai role (user, admin), mỗi role sẽ được truy cập các rest api chỉ định
  • Lấy thông tin người dùng trong JWT
  • Refresh token: mỗi khi token hết hạn, người dùng có thể gia hạn token với hành động refresh token.

Bước 1: Tạo project springboot với tên session12example1 và chọn các dependency như hình dưới

Cấu trúc project như sau:

Bước 2: Bổ sung thêm dependency jwt vào file pom.xml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
        <dependency>
		    <groupId>io.jsonwebtoken</groupId>
		    <artifactId>jjwt-api</artifactId>
		    <version>0.11.5</version>
		</dependency>
		<dependency>
		    <groupId>io.jsonwebtoken</groupId>
		    <artifactId>jjwt-impl</artifactId>
		    <version>0.11.5</version>
		    <scope>runtime</scope>
		</dependency>
		<dependency>
		    <groupId>io.jsonwebtoken</groupId>
		    <artifactId>jjwt-jackson</artifactId> <!-- hoặc jjwt-gson -->
		    <version>0.11.5</version>
		    <scope>runtime</scope>
		</dependency>

Bước 3: Cấu hình kết nối trong file application.properties

1
2
3
4
5
6
7
spring.datasource.url= jdbc:sqlserver://localhost:1433;encrypt=true;trustServerCertificate=true;databaseName=session12example1
spring.datasource.username= sa
spring.datasource.password= 123465
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.SQLServerDialect
spring.jpa.hibernate.ddl-auto= update
spring.jpa.properties.hibernate.use_nationalized_character_data =true

Bước 4: Code cho các lớp trong gói entities

Lớp Role (biểu diễn role của người dùng)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.bkap.entities;

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

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.Table;

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

    @Column(name="rolename")
    private String roleName; 

    @ManyToMany(mappedBy = "roles")
    @JsonIgnoreProperties("roles")
    private Set<User> users = new HashSet<>();

	public Long getRoleId() {
		return roleId;
	}

	public void setRoleId(Long roleId) {
		this.roleId = roleId;
	}


	public String getRoleName() {
		return roleName;
	}

	public void setRoleName(String roleName) {
		this.roleName = roleName;
	}

	public Set<User> getUsers() {
		return users;
	}

	public void setUsers(Set<User> users) {
		this.users = users;
	}
    
    
}

Lớp User (biểu diễn người dùng)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
package com.bkap.entities;

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

import com.bkap.models.UserModel;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinTable;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.Table;

@Table(name="users")
@Entity
public class User {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "userid")
	private Long userId;
	@Column(name = "username",unique =true)
	private String username;
	private String password;
	@Column(name = "fullname")
	private String fullName;
	private String email;
	private String address;

	@ManyToMany(fetch = FetchType.EAGER)
	@JoinTable(name = "user_roles", joinColumns = @JoinColumn(name = "userid"), inverseJoinColumns = @JoinColumn(name = "roleid"))
	@JsonIgnoreProperties("users")
	private Set<Role> roles = new HashSet<>();

	public User() {
		// TODO Auto-generated constructor stub
	}
	public User(UserModel user, Role role) {
		this.username=user.getUsername();
		this.fullName=user.getFullName();
		this.password=user.getPassword();
		this.address=user.getAddress();
		this.email=user.getEmail();
		this.roles.add(role);
	}
	public Long getUserId() {
		return userId;
	}

	public void setUserId(Long userId) {
		this.userId = userId;
	}

	public String getUsername() {
		return username;
	}

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

	public String getPassword() {
		return password;
	}

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

	public String getFullName() {
		return fullName;
	}

	public void setFullName(String fullName) {
		this.fullName = fullName;
	}

	public String getEmail() {
		return email;
	}

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

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public Set<Role> getRoles() {
		return roles;
	}

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

Bước 5: Code cho các lớp trong gói models

Lớp UserModel (biểu diễn người dùng khi register)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.bkap.models;

public class UserModel {
	
	private String username;
	private String password;
	private String fullName;
	private String email;
	private String address;
	private String roleName;
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getFullName() {
		return fullName;
	}
	public void setFullName(String fullName) {
		this.fullName = fullName;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public String getRoleName() {
		return roleName;
	}
	public void setRoleName(String roleName) {
		this.roleName = roleName;
	}
}

Lớp AuthRequest (biểu diễn username, password)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.bkap.models;

public class AuthRequest {
	private String username;
	private String password;

	public String getUsername() {
		return username;
	}

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

	public String getPassword() {
		return password;
	}

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

}

Lớp AuthResponse (biểu diễn token trả về, có thể bổ sung thêm các trường nếu cần)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.bkap.models;

public class AuthResponse {
	private String token;

	public AuthResponse(String token) {
		this.token = token;
	}

	public String getToken() {
		return token;
	}
}

Bước 6: Code cho các interface trong repositories

Interface RoleRepository

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.bkap.repositories;

import java.util.Optional;

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

import com.bkap.entities.Role;

public interface RoleRepository extends JpaRepository<Role, Long> {
    Optional<Role> findByRoleName(String name);
}

Interface UserRepository

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.bkap.repositories;

import java.util.Optional;

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

import com.bkap.entities.User;

public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);
}

Bước 7: Code cho các lớp trong Component

Lớp JwtFilter

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.bkap.components;

import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import com.bkap.services.MyUserDetailsService;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@Component
public class JwtFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
    	final String authHeader = request.getHeader("Authorization");
        String jwt = null;
        String username = null;

        // Lấy JWT từ header
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            jwt = authHeader.substring(7); // Bỏ "Bearer "
            try {
                username = jwtUtil.extractUsername(jwt);
            } catch (Exception e) {
                System.out.println("JWT không hợp lệ: " + e.getMessage());
            }
        }

        // Nếu có username và chưa xác thực
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);

            if (jwtUtil.validateToken(jwt, userDetails)) {
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities()
                );
                authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                // Gắn Authentication vào SecurityContext
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }

        chain.doFilter(request, response);
    }
}

Lớp JwtUtil

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package com.bkap.components;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

@Component
public class JwtUtil {
	private final String SECRET = "welcome_to_bkap_lai_duc_chung_teacher";

	public String generateToken(UserDetails userDetails, String fullName, String email) {
	    Map<String, Object> claims = new HashMap<>();
	    claims.put("fullName", fullName);
	    claims.put("email", email);

	    return Jwts.builder()
	        .setClaims(claims)
	        .setSubject(userDetails.getUsername())
	        .setIssuedAt(new Date())
	        .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60)) // 1 giờ
	        .signWith(Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8)), SignatureAlgorithm.HS256)
	        .compact();
	}

	
	private Claims extractAllClaims(String token) {
	    return Jwts.parserBuilder()
	            .setSigningKey(Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8)))
	            .build()
	            .parseClaimsJws(token)
	            .getBody();
	}
	public String extractFullName(String token) {
	    return extractAllClaims(token).get("fullName", String.class);
	}

	public String extractEmail(String token) {
	    return extractAllClaims(token).get("email", String.class);
	}
	public String extractUsername(String token) {
		return extractAllClaims(token).getSubject();
	}
	public boolean isTokenExpired(String token) {
        Date expiration = extractAllClaims(token).getExpiration();
        return expiration.before(new Date());
    }
	public boolean validateToken(String token, UserDetails userDetails) {
		try {
            final String username = extractUsername(token);
            return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
        } catch (Exception e) {
            System.out.println("Token validation error: " + e.getMessage());
            return false;
        }
	}
}

Bước 8: Code cho lớp MyUserDetailsService

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.bkap.services;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.bkap.entities.User;
import com.bkap.repositories.UserRepository;

@Service
public class MyUserDetailsService implements UserDetailsService {
	@Autowired
	private UserRepository userRepository;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		User user = userRepository.findByUsername(username)
				.orElseThrow(() -> new UsernameNotFoundException("User not found"));

		List<GrantedAuthority> authorities = user.getRoles().stream()
				.map(role -> new SimpleGrantedAuthority(role.getRoleName())).collect(Collectors.toList());

		return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
				authorities);
	}
	
	public Optional<User> getUser(String username) {
		return userRepository.findByUsername(username);
	}
	
	public User insert(User user){
		return userRepository.save(user);
	}
}

Bước 9: Code cho lớp SecurityConfig

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.bkap.config;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.bkap.components.JwtFilter;
import com.bkap.services.MyUserDetailsService;

@Configuration
@EnableMethodSecurity(prePostEnabled = true) 
public class SecurityConfig {

    @Autowired
    private JwtFilter jwtFilter;

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/login", "/register").permitAll()
                    .anyRequest().authenticated()
                )
                .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }
}

Bước 10: Code cho lớp HomeController

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package com.bkap.controllers;


import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

import com.bkap.components.JwtUtil;
import com.bkap.entities.Role;
import com.bkap.entities.User;
import com.bkap.models.AuthRequest;
import com.bkap.models.AuthResponse;
import com.bkap.models.UserModel;
import com.bkap.repositories.RoleRepository;
import com.bkap.services.MyUserDetailsService;

import jakarta.servlet.http.HttpServletRequest;

@RestController
public class HomeController {

	@Autowired
	private AuthenticationManager authManager;

	@Autowired
	private JwtUtil jwtUtil;

	@Autowired
	private MyUserDetailsService userDetailsService;

	@Autowired
	RoleRepository repositoryRoleRepository;
	
	@PostMapping("/login")
	public ResponseEntity<?> login(@RequestBody AuthRequest request) {
		try {
			Authentication authentication = authManager.authenticate(
					new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));

			UserDetails userDetails = (UserDetails) authentication.getPrincipal();
			User user = userDetailsService.getUser(userDetails.getUsername())
					.orElseThrow(() -> new RuntimeException("User not found"));

			String token = jwtUtil.generateToken(userDetails, user.getFullName(), user.getEmail());

			return ResponseEntity.ok(new AuthResponse(token));

		} catch (AuthenticationException e) {
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
		}
	}

	@PostMapping("/register")
	public ResponseEntity<?> register(@RequestBody UserModel userModel) {
		Role role = repositoryRoleRepository.findByRoleName(userModel.getRoleName()).get();
		var passencrypt=new BCryptPasswordEncoder().encode(userModel.getPassword());
		userModel.setPassword(passencrypt);
		User user=new User(userModel, role);
		User u = userDetailsService.insert(user);
		return ResponseEntity.ok(u);
	}

	@GetMapping("/admin")
	@PreAuthorize("hasRole('ADMIN')")
	public String admin() {
		return "Chào Admin";
	}

	@GetMapping("/user")
	@PreAuthorize("hasAnyRole('USER', 'ADMIN')")
	public String user() {
		return "Chào User";
	}

	@GetMapping("/getprofile")
	@PreAuthorize("hasAnyRole('USER', 'ADMIN')")
	public ResponseEntity<?> getMyProfile(@RequestHeader("Authorization") String authHeader) {
		String token = authHeader.replace("Bearer ", "");
		String username = jwtUtil.extractUsername(token);
		String fullName = jwtUtil.extractFullName(token);
		String email = jwtUtil.extractEmail(token);

		return ResponseEntity.ok(Map.of("username", username, "fullName", fullName, "email", email));
	}

	@GetMapping("/refresh")
	@PreAuthorize("hasAnyRole('USER', 'ADMIN')")
	public ResponseEntity<AuthResponse> refreshToken(HttpServletRequest request) {
		final String authHeader = request.getHeader("Authorization");
		if (authHeader == null || !authHeader.startsWith("Bearer ")) {
			return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
		}

		String oldToken = authHeader.substring(7);
		String username = jwtUtil.extractUsername(oldToken);

		UserDetails userDetails = userDetailsService.loadUserByUsername(username);

		if (!jwtUtil.validateToken(oldToken, userDetails)) {
			return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
		}

		User user = userDetailsService.getUser(userDetails.getUsername())
				.orElseThrow(() -> new RuntimeException("User not found"));

		String newToken = jwtUtil.generateToken(userDetails, user.getFullName(), user.getEmail());
		return ResponseEntity.ok(new AuthResponse(newToken));
	}
}

Bước 11: Chạy và kiểm tra kết quả

Đăng ký admin thành công

Đăng ký user thành công

Lấy token user thành công

Gửi request tới vùng user

Gửi request tới vùng admin -> sẽ báo lỗi ko có quyền truy cập

Lấy thông tin người dùng đã login

Gọi Refresh token khi cần thiết

Source code tải tại đây

3. Video quay trong buổi dạy C2308G

thay lời cảm ơn!

QUẢNG CÁO - TIẾP THỊ