이번에는 JWT를 위해 엔티티, 시큐리티 설정 등을 해보려고 한다.

프로젝트 생성

우선 의존성은 다음과 같다.
- Spring Web
- Data JPA
- Lombok
- Spring Security
- MySQL Driver
MySQL 연동을 위해 다음과 같이 설정해주자.
spring.application.name=jwt_practice
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/{db이름}?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQLDialect
# DB username
spring.datasource.username={username}
# DB password
spring.datasource.password={password}
spring.jpa.show-sql=true
# DDL(create, alter, drop
spring.jpa.hibernate.ddl-auto=update
SecurityConfig
package com.example.jwt_practice.security;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig{
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception
{
http
.csrf(csrf -> csrf.disable());
http
.formLogin(formLogin -> formLogin.disable());
http
.sessionManagement( session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http
.authorizeHttpRequests( authorizeRequests -> authorizeRequests.anyRequest().permitAll());
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception
{
return authenticationConfiguration.getAuthenticationManager();
}
}
- csrf.disable(): csrf는 인증정보를 쿠키에 저장하는 경우 발생하는 문제로, 헤더에 토큰을 직접 명시하는 JWT 인증에서는 크게 신경쓸 필요는 없으며, 개발환경이기에 disable
- formLogin.disable():
- ssession.sessionCreationPolicy(SessionCreationPolicy.STATELESS): 세션을 서버에서 관리하지 않으므로 STATELESS로 설정
UserDetailsService
유저정보를 DB에서 가져올 것이므로 CustomUserDetailsService를 직접 구현해야한다.
package com.example.jwt_practice.security;
import com.example.jwt_practice.user.domain.Role;
import com.example.jwt_practice.user.domain.User;
import com.example.jwt_practice.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
Optional<User> _user = userRepository.findByUsername(username);
if (_user.isEmpty())
{
throw new UsernameNotFoundException(username);
}
User user = _user.get();
List<GrantedAuthority> authorities = new ArrayList<>();
if (username.equals("admin"))
{
authorities.add(new SimpleGrantedAuthority(Role.ADMIN.getValue()));
}
else
{
authorities.add(new SimpleGrantedAuthority(Role.USER.getValue()));
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
}
}
username이 “admin”인 경우 관리자 권한을 주도록 구현했다.
User
package com.example.jwt_practice.user.domain;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@NoArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String username;
@Column(nullable = false)
private String password;
public User(String username, String password)
{
this.username = username;
this.password = password;
}
}
간단하게 구현할 것이므로 username과 password만 속성으로 추가했다.
UserRegisterDto
package com.example.jwt_practice.user.domain;
import lombok.Data;
@Data
public class UserRegisterDto {
private String username;
private String password;
}
회원가입 요청 시 사용할 DTO
Role
package com.example.jwt_practice.user.domain;
import lombok.Getter;
@Getter
public enum Role {
USER("ROLE_USER"),
ADMIN("ROLE_ADMIN");
private String value;
Role(String value)
{
this.value = value;
}
}
추후에 인증이 잘 이뤄지는지 확인하기 위해 두 가지 Role을 만들었다.
UserService
package com.example.jwt_practice.user.service;
import com.example.jwt_practice.user.domain.User;
import com.example.jwt_practice.user.domain.UserRegisterDto;
import com.example.jwt_practice.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
@Transactional
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public User register(UserRegisterDto userRegisterDto)
{
User user = new User(userRegisterDto.getUsername(), passwordEncoder.encode(userRegisterDto.getPassword()));
return userRepository.save(user);
}
}
회원가입을 처리한다. 예외처리는 생략.
UserRepository
package com.example.jwt_practice.user.repository;
import com.example.jwt_practice.user.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
}
UserController
package com.example.jwt_practice.user.controller;
import com.example.jwt_practice.user.domain.UserRegisterDto;
import com.example.jwt_practice.user.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping("/api/register")
public ResponseEntity<String> register(@RequestBody UserRegisterDto userRegisterDto)
{
userService.register(userRegisterDto);
return ResponseEntity.ok("회원가입 성공!");
}
}
여기까지 진행 후 회원가입 api를 호출해 정상적으로 동작하는지 확인해보자.

제대로 동작하고, DB에도 저장되는 것을 확인이 가능하다.
다음으로
다음에는 본격적으로 JWT인증을 다룰 예정이다.
'스프링' 카테고리의 다른 글
| [Spring Security] @PreAuthorize (0) | 2026.03.28 |
|---|---|
| AWS S3: Post Presign Url이 만료되지 않는 문제 (0) | 2026.01.20 |
| 스프링 JWT(3): JWTFilter (3) | 2025.01.09 |
| 스프링 JWT(2): JWTUtil & LoginFilter (0) | 2025.01.07 |