6-keem
Gallery
About
© Powered by 6-keem

Spring Security in Spring Boot 3

Backend
2025년 06월 11일
5분

Spring Framework

Series bookmark
  1. Spring 개요
  2. Spring 의존성 주입
  3. Spring MVC 패턴
  4. Spring MVC Web Form
  5. Spring JPA (Java Persistence API)
  6. Spring Entity Mapping
  7. Logging with SLF4J and Logback
  8. RESTful Web Service
  9. Restful Web Service with Spring
  10. Spring Boot 개요
  11. 첫 번째 Spring Boot 애플리케이션
  12. Spring Data JPA 사용하기
  13. Spring Security in Spring Boot 3
On this page
  • 1. 소개
  • 2. Spring Security 설정
  • 2.1 의존성 추가
  • 2.2 기본 사용자 설정
  • 3. Spring MVC 설정
  • 4. Thymeleaf에서 보안 기능 사용
  • 5. 데이터베이스 기반 사용자·역할 관리
  • 5.1 엔티티 정의
  • 5.2 레포지토리
  • 6. 사용자 인증 서비스 구현
  • 7. SecurityFilterChain 구성
  • 8. 비밀번호 인코딩 (BCrypt)
  • 9. CSRF 보호
  • 10. 회원가입(SignUp) 페이지

이전 포스트
Spring Data JPA 사용하기
다음 글이 없습니다
thumbnail.png
교과목 내용을 정리하기 위한 글입니다. 틀린 내용이 있을 수 있으니 참고하세요

1. 소개

Spring Security는 Java 애플리케이션을 위한 보안 프레임워크로, 인증(authentication)과 인가(authorization)를 유연하고 확장 가능하게 지원합니다. Spring Boot에서는 spring-boot-starter-security 스타터를 통해 관련 의존성을 한 번에 관리하고 자동 구성을 제공합니다.

2. Spring Security 설정

2.1 의존성 추가

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  • spring-boot-starter-security는 Spring Security 관련 의존성을 묶어 제공하며, 기본적인 보안 필터 체인을 자동 구성합니다.
  • /css/**, /js/**, /images/** 등 정적 리소스 경로는 인증 없이 접근할 수 있도록 기본 제외됩니다.

2.2 기본 사용자 설정

application.properties에서 기본 사용자 계정과 역할을 변경할 수 있습니다:

spring.security.user.name=alice
spring.security.user.password=alicepw
spring.security.user.roles=USER,ADMIN

3. Spring MVC 설정

템플릿을 렌더링하기 위한 컨트롤러 없이도 뷰 연결을 할 수 있습니다.

@Configuration
public class WebConfig implements WebMvcConfigurer {
  @Override
  public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("/").setViewName("home");
  }
  @Bean
  public SpringSecurityDialect securityDialect() {
    return new SpringSecurityDialect();
  }
}

필요에 따라 명시적 컨트롤러를 정의할 수도 있습니다:

@Controller
public class HomeController {
  @GetMapping("/")
  public ModelAndView home() {
    return new ModelAndView("home");
  }
}

4. Thymeleaf에서 보안 기능 사용

thymeleaf-extras-springsecurity6 의존성을 추가하고, 뷰에서 sec:authorize 등의 속성을 사용할 수 있습니다.

<dependency>
  <groupId>org.thymeleaf.extras</groupId>
  <artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="https://www.thymeleaf.org/extras/spring-security">
<head>
  <title>Spring Security 튜토리얼</title>
</head>
<body>
  <h2>Welcome</h2>
  <div sec:authorize="hasRole('USER')">User에게만 보이는 텍스트</div>
  <div sec:authorize="hasRole('ADMIN')">Admin에게만 보이는 텍스트</div>
  <div sec:authorize="isAuthenticated()">인증된 사용자만 보이는 텍스트</div>
  <p>인증된 사용자: <span sec:authentication="name"></span></p>
</body>
</html>

5. 데이터베이스 기반 사용자·역할 관리

5.1 엔티티 정의

@Entity
@Table(name = "users")
public class User {
  @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;
  @Column(nullable = false, unique = true)
  private String email;
  @Column(nullable = false)
  private String password;
  @ManyToMany
  @JoinTable(
    name = "user_role",
    joinColumns = @JoinColumn(name = "USER_ID"),
    inverseJoinColumns = @JoinColumn(name = "ROLE_ID")
  )
  private List<Role> roles;
}
@Entity
@Table(name = "roles")
public class Role {
  @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;
  @Column(nullable = false, unique = true)
  private String rolename;
  @ManyToMany(mappedBy = "roles")
  private List<User> users;
}

5.2 레포지토리

public interface UserRepository extends JpaRepository<User, Integer> {
  Optional<User> findByEmail(String email);
}
public interface RoleRepository extends JpaRepository<Role, Integer> {
  Optional<Role> findByRolename(String rolename);
}

6. 사용자 인증 서비스 구현

UserDetailsService를 구현하여 DB에서 사용자 정보를 로드합니다.

@Service
@Transactional
public class CustomUserDetailsService implements UserDetailsService {
  @Autowired
  private UserRepository userRepository;
 
  @Override
  public UserDetails loadUserByUsername(String username)
      throws UsernameNotFoundException {
    User user = userRepository.findByEmail(username)
      .orElseThrow(() -> new UsernameNotFoundException(
        "Email not found: " + username));
    return new org.springframework.security.core.userdetails.User(
      user.getEmail(), user.getPassword(), getAuthorities(user));
  }
}

7. SecurityFilterChain 구성

@Configuration
public class WebSecurityConfig {
  @Autowired
  private UserDetailsService customUserDetailsService;
 
  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }
  @Bean
  public AuthenticationManager authenticationManager(
        AuthenticationConfiguration config) throws Exception {
    return config.getAuthenticationManager();
  }
 
  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http)
        throws Exception {
    http.authorizeHttpRequests(authz -> authz
        .requestMatchers("/css/**", "/js/**", "/images/**").permitAll()
        .requestMatchers("/", "/home", "/signup").permitAll()
        .requestMatchers("/admin/**").hasRole("ADMIN")
        .anyRequest().authenticated()
      )
      .formLogin(form -> form
        .loginPage("/login")
        .defaultSuccessUrl("/home")
        .failureUrl("/login?error")
        .permitAll()
      )
      .logout(logout -> logout
        .logoutUrl("/logout")
        .logoutSuccessUrl("/login?logout")
        .permitAll()
      )
      .userDetailsService(customUserDetailsService)
      .csrf(csrf -> csrf.disable());
    return http.build();
  }
}

8. 비밀번호 인코딩 (BCrypt)

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

BCrypt는 자동으로 랜덤 솔트를 생성하여 같은 평문이라도 매번 다른 해시를 생성합니다.

9. CSRF 보호

  • Spring Security는 기본적으로 CSRF 보호를 활성화합니다.
  • Thymeleaf 폼에서 th:action과 POST/PUT/DELETE 메서드를 사용하면 CSRF 토큰이 자동 삽입됩니다.
  • 비활성화 시:
http.csrf(csrf -> csrf.disable());

10. 회원가입(SignUp) 페이지

<form th:action="@{/signup}" th:object="${user}" method="post">
  <div th:if="${emailExists}">
    <span style="color:red">Email already exists</span>
  </div>
  <label>Email:</label>
  <input type="text" th:field="*{email}" />
  <label>Password:</label>
  <input type="password" th:field="*{password}" />
  <button type="submit">SignUp</button>
</form>