1. Giới thiệu về Spring Security và JWT
Trong bài viết này, chúng ta sẽ tiếp tục triển khai phần code liên quan đến bảo mật ứng dụng Spring Boot thông qua Spring Security 6 với sự hỗ trợ của JWT và OAuth2. Phần 4 này sẽ giúp bạn nắm rõ hơn về quy trình xác thực người dùng trong dự án của mình. Bạn có thể tham khảo lại nội dung của phần 3 tại đây: Phần 3 - Spring Security 6 với JWT và Oauth2.
2. Triển Khai Code Bảo Mật với Spring Security
Sau khi đã hoàn thành phần đăng ký trước đó, chúng ta cần cập nhật các thành phần liên quan đến bảo mật trong ứng dụng. Dưới đây là một số bước quan trọng.
2.1. Cập Nhật Entity User
Chúng ta sẽ cần cập nhật entity người dùng (User entity) để tích hợp với Spring Security. Điều này đòi hỏi chúng ta phải implement giao diện UserDetails
để cung cấp thông tin người dùng và quyền hạn của họ. Đầu tiên, khai báo enum Role như sau:
java
public enum Role {
USER,
ADMIN
}
Sau đó, cập nhật lại entity User:
java
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;
private String username;
private String password;
private String firstName;
private String lastName;
private String email;
@Enumerated(EnumType.STRING)
private Role role;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(new SimpleGrantedAuthority(role.name()));
}
@Override
public boolean isAccountNonExpired() {
return UserDetails.super.isAccountNonExpired();
}
@Override
public boolean isAccountNonLocked() {
return UserDetails.super.isAccountNonLocked();
}
@Override
public boolean isCredentialsNonExpired() {
return UserDetails.super.isCredentialsNonExpired();
}
@Override
public boolean isEnabled() {
return UserDetails.super.isEnabled();
}
}
2.2. Cấu Hình Application
Để làm việc với các interface trong Spring Security, chúng ta cần phải tạo ra các instance của chúng và đánh dấu bằng annotation @Bean
. Tạo lớp ApplicationConfiguration
trong package config:
java
@Configuration
@RequiredArgsConstructor
public class ApplicationConfiguration {
private final UserRepository userRepository;
@Bean
public UserDetailsService userDetailsService() {
return username -> userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
// Các bean khác sẽ được định nghĩa ở đây...
}
2.3. Triển Khai AuthenticationFilter
Chúng ta cần tạo lớp AuthenticationFilter
, được kế thừa từ OncePerRequestFilter
. Lớp này đảm bảo rằng phương thức doFilterInternal()
chỉ được gọi một lần cho mỗi yêu cầu.
java
@Component
@RequiredArgsConstructor
public class AuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
// Logic xử lý xác thực sẽ được thực hiện ở đây...
filterChain.doFilter(request, response);
}
}
2.4. Triển Khai JwtService và JwtServiceImpl
Tiếp theo là tạo interface JwtService
và triển khai chúng trong JwtServiceImpl
để trích xuất thông tin từ JWT:
java
public interface JwtService {
String extractUsername(String token);
String generateToken(User user);
}
2.5. Cập Nhật AuthenticationFilter
Cập nhật nội dung phương thức doFilterInternal()
trong lớp AuthenticationFilter
để xác thực người dùng với token:
java
@Override
protected void doFilterInternal(...){
// Logic xác thực user qua JWT
}
2.6. Cập Nhật SecurityConfiguration
Cuối cùng, cập nhật SecurityConfiguration
để thiết lập Spring Security cho ứng dụng:
java
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration {
private final AuthenticationFilter authenticationFilter;
private final AuthenticationProvider authenticationProvider;
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.cors(AbstractHttpConfigurer::disable)
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authenticationProvider(authenticationProvider)
.addFilterBefore(authenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
2.7. Cập Nhật AuthenticationService
Chúng ta cần cập nhật lớp AuthenticationService
để hỗ trợ logic đăng nhập và đăng ký với mã hóa mật khẩu:
java
public interface AuthenticationService {
AuthenticationResponse register(RegisterRequest request);
AuthenticationResponse login(AuthenticationRequest request);
}
2.8. Cập Nhật AuthenticationController
Cuối cùng, cập nhật các controller để xử lý yêu cầu từ client:
java
@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthenticationController {
private final AuthenticationService authenticationService;
@PostMapping("/register")
public ResponseEntity<AuthenticationResponse> register(@RequestBody RegisterRequest registerRequest) {
AuthenticationResponse response = authenticationService.register(registerRequest);
return ResponseEntity.status(HttpStatus.CREATED).body(response);
}
@PostMapping("/login")
public ResponseEntity<AuthenticationResponse> login(@RequestBody AuthenticationRequest request) {
AuthenticationResponse response = authenticationService.login(request);
return ResponseEntity.status(HttpStatus.OK).body(response);
}
}
3. Kết Luận
Chúng ta đã hoàn thành việc thiết lập Spring Security với JWT và Oauth2 trong ứng dụng của mình. Bây giờ bạn có thể sử dụng Postman để thử nghiệm các chức năng đăng nhập, kiểm tra xác thực và bảo mật cho API của mình. Đảm bảo rằng token được quản lý và kiểm tra đúng cách. Nếu bạn gặp bất kỳ vấn đề nào, hãy theo dõi log để tìm hiểu nguyên nhân và xử lý kịp thời.
source: viblo