Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions contents/todoListAPI/seungseop/todolist/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ dependencies {
"jakarta.annotation:jakarta.annotation-api",
"com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta")

// spring security 설정
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt:0.12.3' // jwt token

testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;

@EnableScheduling
@SpringBootApplication
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,16 @@ public interface ExampleData {
}
""";

String BAD_CREDENTIALS_DATA = """
{
"timestamp": "2024-05-27T21:48:53.1796943",
"status": 401,
"error": "UNAUTHORIZED",
"code": "BAD_CREDENTIALS",
"message": [
"이메일 또는 비밀번호가 맞지 않습니다."
]
}
""";

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

Expand All @@ -11,8 +13,16 @@ public class SwaggerConfig {

@Bean
public OpenAPI openAPI() {
SecurityScheme apiKey = new SecurityScheme()
.type(SecurityScheme.Type.APIKEY)
.in(SecurityScheme.In.HEADER)
.name("Auth-Token");

SecurityRequirement securityRequirement = new SecurityRequirement()
.addList("Access Token");
return new OpenAPI()
.components(new Components())
.components(new Components().addSecuritySchemes("Access Token", apiKey))
.addSecurityItem(securityRequirement)
.info(apiInfo());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.serverstudy.todolist.dto.request.FolderReq.FolderPost;
import com.serverstudy.todolist.dto.response.FolderRes;
import com.serverstudy.todolist.exception.ErrorResponse;
import com.serverstudy.todolist.security.SecurityUser;
import com.serverstudy.todolist.service.FolderService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand All @@ -18,6 +19,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

Expand All @@ -26,16 +28,14 @@
@Tag(name = "Folder", description = "Folder API 입니다.")
@Validated
@RestController
@RequestMapping("/api/folder")
@RequestMapping("/api/folders")
@CrossOrigin(origins = "*")
@RequiredArgsConstructor
public class FolderController implements ExampleData {

private final FolderService folderService;

@Operation(summary = "폴더 생성", description = "새로운 폴더를 생성합니다.", parameters = {
@Parameter(name = "userId", description = "유저 id", example = "1")
}, responses = {
@Operation(summary = "폴더 생성", description = "새로운 폴더를 생성합니다.", responses = {
@ApiResponse(responseCode = "201", description = "폴더 생성 성공", useReturnTypeSchema = true),
@ApiResponse(responseCode = "400", description = "잘못된 파라미터 입력", content = @Content(schema = @Schema(implementation = ErrorResponse.class), examples = {
@ExampleObject(name = "INVALID_PARAMETER", value = INVALID_PARAMETER_DATA),
Expand All @@ -48,25 +48,23 @@ public class FolderController implements ExampleData {
}))
})
@PostMapping
public ResponseEntity<Long> postFolder(@Valid @RequestBody FolderPost folderPost, @NotNull Long userId) { // @RequestParam만 붙여도 null 값 입력 시 예외 발생
public ResponseEntity<Long> postFolder(@Valid @RequestBody FolderPost folderPost, @AuthenticationPrincipal SecurityUser user) { // @RequestParam만 붙여도 null 값 입력 시 예외 발생

Long folderId = folderService.create(folderPost, userId);
Long folderId = folderService.create(folderPost, user.getId());

return ResponseEntity.status(HttpStatus.CREATED).body(folderId);
}

@Operation(summary = "폴더 목록 조회", description = "폴더 목록을 가져옵니다.", parameters = {
@Parameter(name = "userId", description = "유저 id", example = "1")
}, responses = {
@Operation(summary = "폴더 목록 조회", description = "폴더 목록을 가져옵니다.", responses = {
@ApiResponse(responseCode = "200", description = "폴더 목록 조회 성공", useReturnTypeSchema = true),
@ApiResponse(responseCode = "400", description = "잘못된 파라미터 입력", content = @Content(schema = @Schema(implementation = ErrorResponse.class), examples = {
@ExampleObject(name = "INVALID_PARAMETER", value = INVALID_PARAMETER_DATA),
}))
})
@GetMapping
public ResponseEntity<List<FolderRes>> getFoldersByUser(@NotNull Long userId) {
public ResponseEntity<List<FolderRes>> getFoldersByUser(@AuthenticationPrincipal SecurityUser user) {

List<FolderRes> responseList = folderService.getAllWithTodoCount(userId);
List<FolderRes> responseList = folderService.getAllWithTodoCount(user.getId());

return ResponseEntity.ok(responseList);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.serverstudy.todolist.dto.request.TodoReq.TodoPost;
import com.serverstudy.todolist.dto.response.TodoRes;
import com.serverstudy.todolist.exception.ErrorResponse;
import com.serverstudy.todolist.security.SecurityUser;
import com.serverstudy.todolist.service.TodoService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand All @@ -18,6 +19,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

Expand All @@ -29,16 +31,14 @@
@Tag(name = "Todo", description = "Todo API 입니다.")
@Validated
@RestController
@RequestMapping("/api/todo")
@RequestMapping("/api/todos")
@CrossOrigin(origins = "*")
@RequiredArgsConstructor
public class TodoController implements ExampleData {

private final TodoService todoService;

@Operation(summary = "투두 생성", description = "새로운 투두를 생성합니다.", parameters = {
@Parameter(name = "userId", description = "유저 id", example = "1")
}, responses = {
@Operation(summary = "투두 생성", description = "새로운 투두를 생성합니다.", responses = {
@ApiResponse(responseCode = "201", description = "투두 생성 성공", useReturnTypeSchema = true),
@ApiResponse(responseCode = "400", description = "잘못된 파라미터 입력", content = @Content(schema = @Schema(implementation = ErrorResponse.class), examples = {
@ExampleObject(name = "INVALID_PARAMETER", value = INVALID_PARAMETER_DATA),
Expand All @@ -49,26 +49,24 @@ public class TodoController implements ExampleData {
}))
})
@PostMapping
public ResponseEntity<Long> postTodo(@Valid @RequestBody TodoPost todoPost, @NotNull Long userId) {
public ResponseEntity<Long> postTodo(@Valid @RequestBody TodoPost todoPost, @AuthenticationPrincipal SecurityUser user) {

Long todoId = todoService.create(todoPost, userId);
Long todoId = todoService.create(todoPost, user.getId());


return ResponseEntity.status(HttpStatus.CREATED).body(todoId);
}

@Operation(summary = "투두 목록 조회", description = "조건에 맞는 투두 목록을 가져옵니다.", parameters = {
@Parameter(name = "userId", description = "유저 id", example = "1")
}, responses = {
@Operation(summary = "투두 목록 조회", description = "조건에 맞는 투두 목록을 가져옵니다.", responses = {
@ApiResponse(responseCode = "200", description = "투두 목록 조회 성공", useReturnTypeSchema = true),
@ApiResponse(responseCode = "400", description = "잘못된 파라미터 입력", content = @Content(schema = @Schema(implementation = ErrorResponse.class), examples = {
@ExampleObject(name = "INVALID_PARAMETER", value = INVALID_PARAMETER_DATA),
}))
})
@GetMapping
public ResponseEntity<List<TodoRes>> getTodosByRequirements(@Valid @ModelAttribute TodoGet todoGet, @NotNull Long userId) {
public ResponseEntity<List<TodoRes>> getTodosByRequirements(@Valid @ModelAttribute TodoGet todoGet, @AuthenticationPrincipal SecurityUser user) {

List<TodoRes> responseList = todoService.findAllByConditions(todoGet, userId);
List<TodoRes> responseList = todoService.findAllByConditions(todoGet, user.getId());

return ResponseEntity.ok(responseList);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package com.serverstudy.todolist.controller;

import com.serverstudy.todolist.common.ExampleData;
import com.serverstudy.todolist.domain.User;
import com.serverstudy.todolist.domain.enums.Role;
import com.serverstudy.todolist.dto.request.UserReq;
import com.serverstudy.todolist.dto.request.UserReq.UserLoginPost;
import com.serverstudy.todolist.dto.request.UserReq.UserPatch;
import com.serverstudy.todolist.dto.request.UserReq.UserPost;
import com.serverstudy.todolist.dto.response.JwtRes;
import com.serverstudy.todolist.dto.response.UserRes;
import com.serverstudy.todolist.exception.ErrorResponse;
import com.serverstudy.todolist.repository.UserRepository;
import com.serverstudy.todolist.security.SecurityUser;
import com.serverstudy.todolist.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand All @@ -20,13 +27,18 @@
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

@Tag(name = "User", description = "User API 입니다.")
@Validated
@RestController
@RequestMapping("/api/user")
@RequestMapping("/api/users")
@CrossOrigin(origins = "*")
@RequiredArgsConstructor
public class UserController implements ExampleData {
Expand All @@ -43,11 +55,28 @@ public class UserController implements ExampleData {
}))
})
@PostMapping
public ResponseEntity<Long> postUser(@Valid @RequestBody UserPost userPost) {
public ResponseEntity<JwtRes> postUser(@Valid @RequestBody UserPost userPost) {

JwtRes token = userService.join(userPost);

return ResponseEntity.status(HttpStatus.CREATED).body(token);
}

@Operation(summary = "유저 로그인", description = "해당 유저로 로그인합니다.", responses = {
@ApiResponse(responseCode = "200", description = "유저 로그인 성공", useReturnTypeSchema = true),
@ApiResponse(responseCode = "400", description = "잘못된 파라미터 입력", content = @Content(schema = @Schema(implementation = ErrorResponse.class), examples = {
@ExampleObject(name = "INVALID_PARAMETER", value = INVALID_PARAMETER_DATA),
})),
@ApiResponse(responseCode = "401", description = "이메일 또는 비밀번호 불일치", content = @Content(schema = @Schema(implementation = ErrorResponse.class), examples = {
@ExampleObject(name = "BAD_CREDENTIALS", value = BAD_CREDENTIALS_DATA),
}))
})
@PostMapping("/login")
public ResponseEntity<JwtRes> login(@RequestBody UserLoginPost loginDto) {

Long userId = userService.join(userPost);
JwtRes token = userService.login(loginDto);

return ResponseEntity.status(HttpStatus.CREATED).body(userId);
return ResponseEntity.ok(token);
}

@Operation(summary = "이메일 중복 검사", description = "이메일의 중복 여부를 검사합니다.", parameters = {
Expand All @@ -73,9 +102,7 @@ public ResponseEntity<String> checkUserEmail(
return ResponseEntity.ok(email);
}

@Operation(summary = "유저 조회", description = "해당 유저의 정보를 조회합니다.", parameters = {
@Parameter(name = "userId", description = "유저 Id", example = "1")
}, responses = {
@Operation(summary = "유저 조회", description = "해당 유저의 정보를 조회합니다.", responses = {
@ApiResponse(responseCode = "200", description = "유저 조회 성공", useReturnTypeSchema = true),
@ApiResponse(responseCode = "400", description = "잘못된 파라미터 입력", content = @Content(schema = @Schema(implementation = ErrorResponse.class), examples = {
@ExampleObject(name = "INVALID_PARAMETER", value = INVALID_PARAMETER_DATA),
Expand All @@ -85,16 +112,14 @@ public ResponseEntity<String> checkUserEmail(
}))
})
@GetMapping
public ResponseEntity<UserRes> getUser(@NotNull Long userId) {
public ResponseEntity<UserRes> getUser(@AuthenticationPrincipal SecurityUser user) {

UserRes response = userService.get(userId);
UserRes response = userService.get(user.getId());

return ResponseEntity.ok(response);
}

@Operation(summary = "유저 정보 수정", description = "해당 유저의 정보를 수정합니다.", parameters = {
@Parameter(name = "userId", description = "유저 Id", example = "1")
}, responses = {
@Operation(summary = "유저 정보 수정", description = "해당 유저의 정보를 수정합니다.", responses = {
@ApiResponse(responseCode = "200", description = "유저 정보 수정 성공", useReturnTypeSchema = true),
@ApiResponse(responseCode = "400", description = "잘못된 파라미터 입력", content = @Content(schema = @Schema(implementation = ErrorResponse.class), examples = {
@ExampleObject(name = "INVALID_PARAMETER", value = INVALID_PARAMETER_DATA),
Expand All @@ -104,28 +129,49 @@ public ResponseEntity<UserRes> getUser(@NotNull Long userId) {
}))
})
@PatchMapping
public ResponseEntity<Long> patchUser(@Valid @RequestBody UserPatch UserPatch, @NotNull Long userId) {
public ResponseEntity<UserRes> patchUser(@Valid @RequestBody UserPatch UserPatch, @AuthenticationPrincipal SecurityUser user) {

Long modifiedUserId = userService.modify(UserPatch, userId);
UserRes modifiedUserRes = userService.modify(UserPatch, user.getId());

return ResponseEntity.ok(modifiedUserId);
return ResponseEntity.ok(modifiedUserRes);
}


@Operation(summary = "유저 삭제", description = "해당 유저를 삭제합니다.", parameters = {
@Parameter(name = "userId", description = "유저 Id", example = "1")
}, responses = {
@Operation(summary = "유저 삭제", description = "해당 유저를 삭제합니다.", responses = {
@ApiResponse(responseCode = "204", description = "유저 삭제 성공"),
@ApiResponse(responseCode = "400", description = "잘못된 파라미터 입력", content = @Content(schema = @Schema(implementation = ErrorResponse.class), examples = {
@ExampleObject(name = "INVALID_PARAMETER", value = INVALID_PARAMETER_DATA),
})),
})
@DeleteMapping
public ResponseEntity<Void> deleteUser(@NotNull Long userId) {
public ResponseEntity<Void> deleteUser(@AuthenticationPrincipal SecurityUser user) {

userService.delete(userId);
userService.delete(user.getId());

return ResponseEntity.noContent().build();
}

@Operation(summary = "관리자 로그인", description = "관리자 계정으로 로그인합니다.", responses = {
@ApiResponse(responseCode = "200", description = "관리자 로그인 성공", useReturnTypeSchema = true),
})
@PostMapping("/admin")
public ResponseEntity<JwtRes> getAdmin() {

JwtRes token = userService.getAdmin();

return ResponseEntity.ok(token);
}

@Operation(summary = "모든 유저 정보 조회", description = "모든 유저 정보를 조회합니다. 관리자 계정으로 로그인 되어 있어야 합니다.", responses = {
@ApiResponse(responseCode = "200", description = "조회 성공", useReturnTypeSchema = true),
})
@Secured("ROLE_ADMIN")
@GetMapping("/admin")
public ResponseEntity<List<UserRes>> getAllUsers() {

List<UserRes> userResList = userService.getAll();

return ResponseEntity.ok(userResList);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.HashSet;
import java.util.Set;

@Getter
@Entity
@Table(name = "user_tb")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class User {

@Id
Expand Down Expand Up @@ -54,5 +55,4 @@ public void modifyUser(UserPatch userPatch) {
public void addRole(Role role) {
this.roles.add(role);
}

}
Loading