개요
1편에서 기본적인 세팅은 끝이 났다. 이제 본격적으로 Security와 JWT를 사용하기 위한 코드를 짜볼 것이다.
2편에서는 SecurityConfig에 대해서 알아보자.
<- 대략적인 파일 구조
SecurityConfig.java
/**
* 웹 애플리케이션의 보안을 설정
*/
@Configuration // 이 클래스가 Spring의 설정 클래스를 나타낸다는 것을 의미
@EnableWebSecurity // Spring Security를 활성화
@RequiredArgsConstructor // final로 선언된 필드를 위한 생성자 자동 생성
public class SecurityConfig {
private final CustomUserDetailsService customUserDetailsService; // 사용자 세부 정보 로드 서비스
private final JwtTokenizer jwtTokenizer; // JWT 토큰을 생성하고 검증하는 도구
private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint; // 인증 실패 시 처리할 커스텀 엔트리 포인트
private final JwtBlacklistService jwtBlacklistService; // 블랙리스트에 등록된 JWT를 관리하는 서비스
private final RefreshTokenService refreshTokenService; // 리프레시 토큰을 관리하는 서비스
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// 특정 URL 패턴에 대한 접근 권한 설정
http
.authorizeRequests(authorize -> authorize
.requestMatchers("/userregform", "/loginform", "/login", "/css/**", "/js/**", "/files/**", "/", "/api/login", "/api/**").permitAll() // 이 주소로 시작되면 인증 필요 없음
.requestMatchers("/posts/**", "/{username}", "/about/{username}", "/ws","/chat").permitAll() // 이 주소로 시작되면 인증 필요 없음
.requestMatchers("/admin/**", "/admin").hasRole("ADMIN") // 관리자 역할을 가진 사용자만 접근
.requestMatchers("/comments/post/**", "/likes/post/**").authenticated() // 인증된 사용자만 접근
.anyRequest().authenticated() // 그 외의 모든 요청은 인증된 사용자만 접근
)
// UsernamePasswordAuthenticationFilter 앞에 JWT 인증 필터를 추가하여 JWT를 통한 인증을 처리
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenizer, jwtBlacklistService, refreshTokenService), UsernamePasswordAuthenticationFilter.class)
.formLogin(form -> form.disable()) // 기본 폼 로그인 비활성화
.sessionManagement(sessionManagement -> sessionManagement
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 세션을 사용하지 않음
// CSRF 보호 비활성화
.csrf(csrf -> csrf.disable())
// http 기본 인증(헤더) 비활성화
.httpBasic(httpBasic -> httpBasic.disable())
// CORS (Cross-Origin Resource Sharing)를 구성 및 허용
.cors(cors -> cors.configurationSource(configurationSource()))
// 사용자 정의 인증 진입 지점을 처리하기 위한 예외 처리를 구성
.exceptionHandling(exception -> exception
.authenticationEntryPoint(customAuthenticationEntryPoint)
);
return http.build(); // SecurityFilterChain을 빌드 후 반환
}
/**
* 모든 origin, header, method를 허용하는 CORS 구성 소스 정의
*/
@Bean
public CorsConfigurationSource configurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*"); //모든 도메인 허용
config.addAllowedHeader("*"); //모든 HTTP 메서드 허용
config.addAllowedMethod("*");
config.setAllowedMethods(List.of("GET", "POST", "DELETE")); // 명시적으로 GET, POST, DELETE 메서드 허용
source.registerCorsConfiguration("/**", config);
return source;
}
/**
* BCryptPasswordEncoder 빈 정의
* 비밀번호를 암호화하는 데 사용
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
어노테이션에 대한 설명이나 기본적인 건 주석을 안할려고 했지만... 그래도 공부되고 좋으니깐^-^
Q. 여기서 세션을 사용하지 않은 이유는 ??
기본적으로 JWT의 정의를 살펴보면,
- 서버가 클라이언트의 상태를 저장하지 않고, 클라이언트가 요청할 때마다 JWT를 서버로 전달하여 인증을 수행한다.
- 클라이언트의 모든 요청은 JWT에 의해 독립적으로 인증되며, 서버는 JWT를 검증하여 요청의 유효성을 확인한다. 이로 인해 서버는 클라이언트 상태를 유지할 필요가 없다.
즉,
1. JWT 는 세션 기반 인증 방식의 한계를 극복하기 위해 설계된 것이다.
2. JWT 는 페이로드에 사용자 정보와 권한을 포함하고 서버는 JWT 를 검증하여 인증을 처리한다.
즉, 서버는 추가적인 상태 정보를 유지할 필요가 없다.
3. 그렇기에 filterChain에서 세션을 비활성화 시킨 것이다.
User, Role, Enum 엔티티 설정
- User.java
@Entity
@Getter@Setter
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;
@Column(name = "user_name", nullable = false, unique = true)
private String userName;
@Column(name = "user_nick", nullable = false, unique = true)
private String userNick;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
private String password;
}
유저 엔티티이다.
- Role.java
@Entity
@Table(name = "roles")
@Getter@Setter
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long roleId;
@Enumerated(EnumType.STRING)
// @Column(nullable = false, unique = true, length = 50)
private RoleName roleName;
}
역할 엔티티이다. 이때 역할 이름은 enum 타입으로 지정했다.
- RoleName.enum
public enum RoleName {
ROLE_ADMIN,
ROLE_USER
}
3편에서... jwt를 사용하기에 앞서 필요한 엔티티와 레포지토리, 서비스를 작성하고 4편부터 본격적으로 jwt 를 사용할 것이다.
'Spring > Security' 카테고리의 다른 글
Spring Security + JWT (RefreshToken, AccessToken)를 사용한 로그인, 로그아웃 구현 - 4편 (0) | 2024.07.23 |
---|---|
Spring Security + JWT (RefreshToken, AccessToken)를 사용한 로그인, 로그아웃 구현 - 3편 (1) | 2024.07.22 |
Spring Security + JWT (RefreshToken, AccessToken)를 사용한 로그인, 로그아웃 구현 - 1편 (1) | 2024.07.22 |
Spring Security : Spring Security 개념 및 필터들 (0) | 2024.07.10 |
[Spring Security] - 사용자 인증 처리 UserDetailsService 인터페이스 (0) | 2024.06.27 |