728x90

 

개요

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 를 사용할 것이다.

728x90

+ Recent posts