Spring MVC 기반의 Java WAR 프로젝트입니다. MySQL 사용자 정보를 MyBatis로 조회하고, Spring Security와 JWT를 이용해 API 인증/인가를 처리합니다.
- Spring MVC 기반 웹 애플리케이션 구성
- HikariCP + MyBatis 기반 MySQL 연동
- Spring Security 기반 인증/인가
- JSON 로그인 API(
/api/auth/login)와 JWT 발급 Authorization: Bearer <token>헤더 기반 JWT 인증- 역할별 API 접근 제어
- JSP ViewResolver, 정적 리소스 매핑, 파일 업로드 설정
- JUnit 5 기반 설정/DB/JWT 테스트
| 구분 | 기술 |
|---|---|
| Language | Java 17 |
| Build | Gradle, WAR |
| Framework | Spring Framework 5.3.37, Spring Web MVC |
| Security | Spring Security 5.8.13 |
| Database | MySQL 8.x |
| Persistence | MyBatis 3.4.6, mybatis-spring 1.3.2 |
| Connection Pool | HikariCP 2.7.4 |
| JWT | jjwt 0.11.5 |
| View | JSP, JSTL |
| Logging | Log4j2, log4jdbc |
| Test | JUnit 5.9.2 |
| Utility | Lombok, Jackson |
apiserver/
├─ build.gradle
├─ settings.gradle
├─ gradlew.bat
├─ src/
│ ├─ main/
│ │ ├─ java/org/scoula/
│ │ │ ├─ config/
│ │ │ │ ├─ RootConfig.java
│ │ │ │ ├─ ServletConfig.java
│ │ │ │ └─ WebConfig.java
│ │ │ ├─ controller/
│ │ │ │ ├─ HomeController.java
│ │ │ │ └─ SecurityController.java
│ │ │ ├─ exception/
│ │ │ │ └─ CommonExceptionAdvice.java
│ │ │ └─ security/
│ │ │ ├─ account/
│ │ │ │ ├─ domain/
│ │ │ │ ├─ dto/
│ │ │ │ ├─ mapper/
│ │ │ │ └─ service/
│ │ │ ├─ config/
│ │ │ ├─ filter/
│ │ │ ├─ handler/
│ │ │ └─ util/
│ │ ├─ resources/
│ │ │ ├─ application.properties
│ │ │ ├─ mybatis-config.xml
│ │ │ └─ org/scoula/security/account/mapper/UserDetailsMapper.xml
│ │ └─ webapp/WEB-INF/views/
│ └─ test/java/org/scoula/
└─ README.md
최근 작업은 JWT 인증과 Spring Security 설정 쪽에 집중되어 있습니다.
| 파일 | 내용 |
|---|---|
JwtAuthenticationFilter.java |
Authorization 헤더에서 Bearer 토큰을 읽고 SecurityContext에 인증 객체 저장 |
SecurityConfig.java |
JWT 필터, 로그인 필터, 예외 처리 핸들러, Stateless 세션 정책, 권한별 API 접근 제어 설정 |
SecurityController.java |
/api/security/all, /api/security/member, /api/security/admin API 제공 |
CustomAuthenticationEntryPoint.java |
인증 실패 시 401 JSON 응답 처리 |
CustomAccessDeniedHandler.java |
권한 부족 시 403 JSON 응답 처리 |
AuthenticationErrorFilter.java |
인증 필터 단계의 예외 처리 |
JwtUsernamePasswordAuthenticationFilter.java |
/api/auth/login 로그인 요청 처리 |
LoginSuccessHandler.java |
로그인 성공 시 JWT와 사용자 정보를 JSON으로 반환 |
LoginFailureHandler.java |
로그인 실패 시 401 응답 반환 |
AuthResultDTO.java |
로그인 성공 응답 DTO |
sequenceDiagram
participant Client
participant LoginFilter as JwtUsernamePasswordAuthenticationFilter
participant AuthManager as AuthenticationManager
participant UserService as CustomUserDetailsService
participant Mapper as UserDetailsMapper
participant DB as MySQL
participant Success as LoginSuccessHandler
participant Jwt as JwtProcessor
Client->>LoginFilter: POST /api/auth/login
LoginFilter->>AuthManager: username/password 인증 요청
AuthManager->>UserService: 사용자 정보 조회
UserService->>Mapper: mapper.get(username)
Mapper->>DB: tbl_member, tbl_member_auth 조회
DB-->>Mapper: 회원 + 권한 정보
Mapper-->>UserService: MemberVO
UserService-->>AuthManager: CustomUser
AuthManager-->>Success: 인증 성공
Success->>Jwt: JWT 생성
Success-->>Client: token + user JSON 응답
JWT가 필요한 API 호출은 다음 헤더를 포함해야 합니다.
Authorization: Bearer <token>| Method | URL | 인증 | 권한 | 설명 |
|---|---|---|---|---|
POST |
/api/auth/login |
불필요 | - | username/password로 로그인하고 JWT 발급 |
GET |
/api/security/all |
불필요 | - | 전체 접근 가능 API |
GET |
/api/security/member |
필요 | ROLE_MEMBER |
로그인 사용자 이름 반환 |
GET |
/api/security/admin |
필요 | ROLE_ADMIN |
관리자 사용자 정보 반환 |
GET |
/ |
불필요 | - | JSP index 화면 |
로그인 요청 예시:
{
"username": "admin",
"password": "1234"
}로그인 성공 응답 형태:
{
"token": "jwt-token",
"user": {
"username": "admin",
"email": "admin@example.com",
"authList": []
}
}DB 접속 정보는 src/main/resources/application.properties에서 관리합니다.
jdbc.driver=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
jdbc.url=jdbc:log4jdbc:mysql://localhost:3306/scoula_db
jdbc.username=scoula
jdbc.password=1234MyBatis는 src/main/resources/mybatis-config.xml에서 다음 설정을 사용합니다.
mapUnderscoreToCamelCase=trueMemberVO,AuthVOtype alias 등록
사용자 인증 Mapper는 UserDetailsMapper.xml에서 tbl_member, tbl_member_auth를 조인해 회원 정보와 권한 목록을 조회합니다.
- JDK 17
- MySQL 8.x
- Tomcat 9.x 또는 Servlet 4.0 호환 WAS
- Gradle Wrapper 사용 가능 환경
gradlew.bat buildmacOS/Linux 환경에서는 다음 명령을 사용합니다.
./gradlew build빌드 결과물은 build/libs/apiserver-1.0-SNAPSHOT.war로 생성됩니다. 해당 WAR 파일을 Tomcat webapps에 배포하거나 IDE의 Tomcat 실행 설정에 연결해서 실행합니다.
전체 테스트 실행:
gradlew.bat test현재 확인한 테스트 상태:
- 총 7개 테스트 중 5개 통과, 2개 실패
- 실패 테스트:
JwtProcessorTest.getUsername,JwtProcessorTest.validateToken - 실패 원인: 테스트 코드에 하드코딩된 JWT가 만료됨
- 테스트 리포트:
build/reports/tests/test/index.html
JwtProcessorTest.generateToken에서 새 토큰을 생성한 뒤 만료되지 않은 토큰으로 테스트 값을 교체하면 JWT 파싱/검증 테스트를 다시 확인할 수 있습니다.
| 클래스 | 역할 |
|---|---|
WebConfig |
DispatcherServlet, UTF-8 필터, Multipart 설정 |
ServletConfig |
Controller scan, JSP ViewResolver, 정적 리소스 매핑 |
RootConfig |
DataSource, SqlSessionFactory, TransactionManager 설정 |
SecurityConfig |
Spring Security, JWT 필터, CORS, 권한 정책 설정 |
CustomUserDetailsService |
DB에서 회원 정보를 조회해 Spring Security UserDetails 생성 |
JwtUsernamePasswordAuthenticationFilter |
로그인 API 요청을 인증 토큰으로 변환 |
JwtAuthenticationFilter |
JWT를 검증하고 SecurityContext에 인증 저장 |
JwtProcessor |
JWT 생성, username 추출, 토큰 유효성 검증 |
JsonResponse |
인증 성공/실패 응답을 JSON으로 출력 |
- JWT 만료 시간은
JwtProcessor에서 5분으로 설정되어 있습니다. SecurityConfig는 세션을 사용하지 않는STATELESS방식으로 설정되어 있습니다./assets/**,/*,/api/member/**는web.ignoring()에 등록되어 Spring Security 필터를 거치지 않습니다.- DB 기반 로그인 테스트를 위해
tbl_member,tbl_member_auth테이블과 테스트 사용자 데이터가 필요합니다.