java 파일들은 솔직히 위치는 상관없을 듯 하고 파일 구조도 개인 마다 다르니 참고로만,
이름들도 딱히 상관 없을 듯 하다. 나중에 클래스 선언 때 맞춰서 쓰기만 하면 되니
참고로 패키지 명에 .이 들어간 건 두 단계이다 ex)model.member -> model 패키지 안에 member 패키지
JwtAuthorizationFilter.java
: Jwt를 검증하고 SecurityContextHolder에 유저 인증 정보를 넣어줄 필터. 직접 만들어서 적용할 예정
JwtProvider.java (생성, 검증)
: Jwt를 생성한다.
: JwtAuthorizationFilter에서 이 객체의 verify 함수를 호출하여 검증한다.
JwtToken.java
: AccessToken과 RefreshToken을 함께 반환하기 위한 객체
JwtTokenType.java
: 토큰의 타입을 정해놓은 Enum.
CustomUserByIdxService.java
: Jwt에 담긴 유저의 Idx로 유저 정보를 불러와 CustomUserDetails 객체를 생성할 서비스.
CustomUserDetails.java
: 유저의 인증 정보가 담길 객체.
ResDTO.java
: 응답 형식을 구조화 해놓음.
CommonExceptionHandler.java
: 에러 핸들러.
BadRequestException, UnauthorziedException.java
: 각 에러를 편히 처리하기 위한 클래스
PasswordConfig.java
: PasswordEncoder를 등록함.
SecurityConfig.java
: 시큐리티 설정(내용이 너무 많다)
AuthController.java
: 로그인, 회원가입 화면을 매핑해 줄 컨트롤러
AuthControllerApiV1.java
: 로그인, 재로그인, 회원가입 요청을 매핑할 Rest 컨트롤러.
ReqLoginApiV1DTO, ReqReLoginApiV1DTO, ReqSignUpApiV1DTO.java
각각 로그인, 재로그인, 회원가입 요청 시 받을 데이터를 담은 DTO
AuthServiceApiV1DTO.java
: 실제 로그인, 재로그인, 회원가입 로직을 구현할 곳.
TempControllerApiV1.java
: Jwt을 이용하여 실제로 인증이 되는 지 테스트 해 볼 API를 매핑.
MemberEntity, MemberRepository.java
: 유저 정보 Entity, Repository
index.html
: 메인 페이지. 로그인 화면과 회원가입 화면으로의 이동과 jwt 요청 테스트를 실행할 곳.
login, sign-up.html
: 각 로그인, 회원가입을 진행할 화면.
응답 형식이나, Exception을 처리하기 위한 핸들러가 담겨있다.
package com.example.jwtvelog.common.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ResDTO<T> {
// 응답 코드
private Integer code;
// 응답 메시지
private String message;
// 응답 데이터
private T data;
}
package com.example.jwtvelog.common.exception;
public class BadRequestException extends RuntimeException {
public BadRequestException(String message) {
super(message);
}
}
package com.example.jwtvelog.common.exception;
public class UnauthorizedException extends RuntimeException{
public UnauthorizedException(String message) {
super(message);
}
}
package com.example.jwtvelog.common.exception.handler;
import com.example.jwtvelog.common.dto.ResDTO;
import com.example.jwtvelog.common.exception.BadRequestException;
import com.example.jwtvelog.common.exception.UnauthorizedException;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class CommonExceptionHandler {
@ExceptionHandler(BadRequestException.class)
public HttpEntity<?> handleBadRequestException(BadRequestException e) {
return new ResponseEntity<>(
ResDTO.builder()
.code(-1)
.message(e.getMessage())
.build(),
HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(UnauthorizedException.class)
public HttpEntity<?> handleUnauthorizedException(UnauthorizedException e) {
return new ResponseEntity<>(
ResDTO.builder()
.code(-2)
.message(e.getMessage())
.build(),
HttpStatus.UNAUTHORIZED);
}
}
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.6'
id 'io.spring.dependency-management' version '1.1.4'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
// jwt 추가
implementation group: 'com.auth0', name: 'java-jwt', version: '4.3.0'
// validation 추가
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('bootBuildImage') {
builder = 'paketobuildpacks/builder-jammy-base:latest'
}
tasks.named('test') {
useJUnitPlatform()
}
spring:
thymeleaf:
cache: false
datasource:
url: jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1
driverClassName: org.h2.Driver
username: sa
password:
h2:
console:
enabled: true
path: /h2
sql:
init:
encoding: UTF-8
# schema-locations: classpath:h2/schema.sql
# dataLocations: classpath:h2/data.sql
mode: always
jpa:
defer-datasource-initialization: true
open-in-view: false # 트랜잭션 범위 밖에서 영속성 컨텍스트를 유지할지 여부
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: create # create-drop, update, validate, none
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
show-sql: true
properties:
hibernate:
format-sql: true
use-sql-comments: true
default-batch-fetch-size: 500
servlet:
multipart:
max-request-size: 300MB
max-file-size: 300MB
secret key를 yml로 빼야될 것 같기도 하다.
현재 서명에 쓰이는 secert key를 JwtProvider에 직접 정의하고 있다.