Spring Security 프로젝트 설정 1 - DB연결과 JPA 설정

✒️ 2025-05-28 14:19 내용 수정



흐름

  1. 클라이언트로부터 요청이 들어오면 SecurityFilterChain에서 요청을 받는다.
  2. Filter 중에 JwtAuthenticationFilter는 JWT에 관련된 토큰의 유효성을 검증하는 역할을 하며, 요청에서 같이 온 JWT를 확인하여 사용자 정보나 데이터를 추출한다.
    • 만약 JWT가 없으면 403 응답을 전송한다.
  3. 추출한 정보를 가지고 JwtAuthenticationFilterUserDetailsService를 호출하여 DB로부터 사용자 정보를 가져온다.
  4. DB로부터 온 정보에서 사용자 정보가 존재하지 않으면 403 응답을 전송하고, 존재한다면 JWT 유효성 검사를 진행한다.
    • JWT가 유효하지 않으면(만료되거나 대상 불일치 등) 403 응답을 전송한다.
  5. JWT가 유효하다면 SecurityContextHolder에 정보를 업데이트하고, DispatcherServlet을 호출하여 Controller로 요청을 전달하고, Controller는 사용자가 요청한 정보를 반환한다.

spring_security_authentication_flow.png


프로젝트 기본 설정

  1. https://start.spring.io/ 에서 프로젝트의 설정 및 Dependencies를 설정한다.
항목 설정
Project Gradle-Groovy
Language Java
Spring boot 3.3.5
Package Jar
Java 17
Dependencies Spring Security 6.3.4
Spring Data JPA 3.3.5
Spring Web 3.3.5
MySQL Driver 6.1.14(DB 연결)
Spring Boot DevTools
Lombok 1.18.34

spring_security_tutorial 1.png

  1. JWT 생성 및 조작에 관한 메소드를 사용하기 위해 build.gradle에 JWT 의존성을 추가한다.
    • 참고 자료 : https://www.devyummi.com/page?id=668d013f958b03acd4c248e5
    • 최신 버전은 0.12.3 버전이나, 가이드 영상 기준으론 0.11.5 버전을 사용하였고, 버전에 따라 메소드가 많이 상이하다는 참고 자료 내용이 있어 강의 영상과 같은 버전을 사용하였다.
dependencies {
    implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
    implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
    implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'
}

DB 연결과 JPA 설정(MySQL)

# 애플리케이션 이름
spring.application.name=security  

# port 설정
server.port=9000 

# MySQL 드라이버 설정
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver  

# 연결할 DB 주소. 마지막은 db 이름이나 schema 이름을 입력한다.
spring.datasource.url=jdbc:mysql://localhost:3306/userservice  

# DB 사용자 이름 및 비밀번호
spring.datasource.username=root  
spring.datasource.password=password

# hibernate ddl 설정 - create면 새로 생성
spring.jpa.hibernate.ddl-auto=create  

# sql을 콘솔에 표시할지 여부
spring.jpa.show-sql=true  

# Hibernate가 DB에 맞게 SQL을 생성하고 최적화(방언 설정)
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect 

# Hibernate가 실행하는 SQL 쿼리를 보기 쉽도록 포맷팅
spring.jpa.properties.hibernate.format_sql=true

# Spring Security에 사용자와 비밀번호 추가
spring.security.user.name=user  
spring.security.user.password=1234
# port  
server:  
  port: 9000  
  
# MySQL  
spring:  
  datasource:  
    driver-class-name: com.mysql.cj.jdbc.Driver  
    # DB 주소  
    url: jdbc:mysql://localhost:3306/userservice  
    # DB 사용자 정보  
    username: root  
    password: password  
  
  # hibernate ddl 설정 - create면 새로 생성  
  jpa:  
    hibernate:  
      ddl-auto: create-drop  
      # sql을 콘솔에 표시할지 여부  
    show-sql: true  
    properties:  
      hibernate:  
        # Hibernate가 DB에 맞게 SQL을 생성하고 최적화(방언 설정)  
        dialect: org.hibernate.dialect.MySQLDialect  
        # Hibernate가 실행하는 SQL 쿼리를 보기 쉽도록 포맷팅  
        format_sql: true  
  
# Spring Security에 사용자와 비밀번호 추가 
  security:  
    user:  
      name: user  
      password: 1234

Entity와 Repository 설정

  1. src/main/java/packageuser 패키지를 만들고, User 클래스를 생성한다.
package com.example.security.user;  
  
import jakarta.persistence.*;  
import lombok.AllArgsConstructor;  
import lombok.Builder;  
import lombok.Data;  
import lombok.NoArgsConstructor;  
import org.springframework.security.core.GrantedAuthority;  
import org.springframework.security.core.authority.SimpleGrantedAuthority;  
import org.springframework.security.core.userdetails.UserDetails;  
  
import java.util.Collection;  
import java.util.List;  
  
@Data  
@Entity // Entity임을 명시  
@Builder // for Object building  
@NoArgsConstructor  
@AllArgsConstructor  
@Table(name = "user") // DB에 테이블 이름 지정  
public class User implements UserDetails {  
    // Spring Security의 UserDetails  
    @Id // id로 지정  
    @GeneratedValue(strategy = GenerationType.AUTO)  
    private Long id;  
    private String firstname;  
    private String lastname;  
    private String email;  
    private String password;  
  
    @Enumerated(EnumType.STRING) // Role이 Enum임을 명시  
    // EnumType.STRING은 String value 순으로 정렬  
    private Role role;  
  
    @Override  
    public Collection<? extends GrantedAuthority> getAuthorities() {  
        // 역할 List를 반환  
        return List.of(new SimpleGrantedAuthority(role.name()));  
    }  
  
    @Override  
    public String getUsername() {  
        return email;  
    }  
  
    // 계정 만료 및 잠김에 대해선 테스트를 위해 true로 설정  
    @Override  
    public boolean isAccountNonExpired() {  
        return true;  
    }  
  
    @Override  
    public boolean isAccountNonLocked() {  
        return true;  
    }  
  
    @Override  
    public boolean isCredentialsNonExpired() {  
        return true;  
    }  
  
    @Override  
    public boolean isEnabled() {  
        return true;  
    }  
  
    // UserDetails에 있는 getPassword()를 오버라이드  
    // 현재 클래스의 password를 반환하도록 설정  
    @Override  
    public String getPassword() {  
        return password;  
    }  
}
package com.example.security.user;  
  
public enum Role {  
    USER, ADMIN  
}
  1. 같은 패키지에 UserRepository 인터페이스를 생성하고, JpaRepository를 상속 받는다.
    • JPA(Java Persistence API) 참고.
    • Optional : 값이 있을수도 없을 수도 있는 null로 인한 NullPointerException을 방지할 수 있는 Java 8 클래스이다.
package com.example.security.user;  
  
import org.springframework.data.jpa.repository.JpaRepository;  
  
import java.util.Optional;  
  
public interface UserRepository extends JpaRepository<User, Long> {  
  
    // 이메일로 사용자 조회  
    // Optional : 값이 있을수도 없을 수도 있는 null로 인한  
    // NullPointerException을 방지할 수 있는 Java 8 클래스  
    Optional<User> findByEmail(String email);  
}