Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

10주자 과제 제출 #3

Merged
merged 2 commits into from
Nov 27, 2023
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package kr.megaptera.backendsurvivalweek10.application.auth;

import jakarta.transaction.Transactional;
import kr.megaptera.backendsurvivalweek10.dtos.auth.LoginResultDto;
import kr.megaptera.backendsurvivalweek10.infrastructure.UserDetailsDao;
import kr.megaptera.backendsurvivalweek10.util.AccessTokenGenerator;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.stream.Collectors;

@Service
@Transactional
public class LoginService {
private final AccessTokenGenerator accessTokenGenerator;

private final PasswordEncoder passwordEncoder;

private final UserDetailsDao userDetailsDao;

public LoginService(AccessTokenGenerator accessTokenGenerator,
PasswordEncoder passwordEncoder,
UserDetailsDao userDetailsDao) {
this.accessTokenGenerator = accessTokenGenerator;
this.passwordEncoder = passwordEncoder;
this.userDetailsDao = userDetailsDao;
}

public LoginResultDto login(String username, String password) {
return userDetailsDao.findByUsername(username)
.filter(userDetails -> passwordEncoder.matches(
password, userDetails.getPassword()))
.map(userDetails -> {
String id = userDetails.getUsername();
String accessToken = accessTokenGenerator.generate(id);
userDetailsDao.addAccessToken(id, accessToken);
return new LoginResultDto(
accessToken,
userDetails
.getAuthorities()
.stream().map(Object::toString)
.collect(Collectors.toList()));
})
.orElseThrow(() -> new BadCredentialsException("Login failed"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package kr.megaptera.backendsurvivalweek10.application.auth;

import jakarta.transaction.Transactional;
import kr.megaptera.backendsurvivalweek10.infrastructure.UserDetailsDao;
import org.springframework.stereotype.Service;

@Service
@Transactional
public class LogoutService {
private final UserDetailsDao userDetailsDao;


public LogoutService(UserDetailsDao userDetailsDao) {
this.userDetailsDao = userDetailsDao;
}

public void logout(String accessToken) {
userDetailsDao.removeAccessToken(accessToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package kr.megaptera.backendsurvivalweek10.application.auth;

import io.hypersistence.tsid.TSID;
import jakarta.transaction.Transactional;
import kr.megaptera.backendsurvivalweek10.dtos.auth.SignupResultDto;
import kr.megaptera.backendsurvivalweek10.exceptions.UserAlreadyExistsException;
import kr.megaptera.backendsurvivalweek10.infrastructure.UserDetailsDao;
import kr.megaptera.backendsurvivalweek10.util.AccessTokenGenerator;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
@Transactional
public class SignupService {
private final AccessTokenGenerator accessTokenGenerator;
private final PasswordEncoder passwordEncoder;
private final UserDetailsDao userDetailsDao;

public SignupService(AccessTokenGenerator accessTokenGenerator,
PasswordEncoder passwordEncoder,
UserDetailsDao userDetailsDao) {
this.accessTokenGenerator = accessTokenGenerator;
this.passwordEncoder = passwordEncoder;
this.userDetailsDao = userDetailsDao;
}

public SignupResultDto signup(String username, String password) {
if (userDetailsDao.existsByUsername(username)) {
throw new UserAlreadyExistsException();
}

String id = TSID.Factory.getTsid().toString();
String encodedPassword = passwordEncoder.encode(password);
String accessToken = accessTokenGenerator.generate(id);

userDetailsDao.addUser(id, username, encodedPassword);
userDetailsDao.addAccessToken(id, accessToken);

return new SignupResultDto(accessToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import kr.megaptera.backendsurvivalweek10.models.CartId;
import kr.megaptera.backendsurvivalweek10.models.Product;
import kr.megaptera.backendsurvivalweek10.models.ProductId;
import kr.megaptera.backendsurvivalweek10.models.UserId;
import kr.megaptera.backendsurvivalweek10.repositories.CartRepository;
import kr.megaptera.backendsurvivalweek10.repositories.ProductRepository;
import org.springframework.stereotype.Service;
Expand All @@ -21,12 +22,12 @@ public AddProductToCartService(CartRepository cartRepository,
this.productRepository = productRepository;
}

public Cart addProduct(ProductId productId, int quantity) {
public Cart addProduct(String userId, ProductId productId, int quantity) {
Product product = productRepository.findById(productId)
.orElseThrow();

Cart cart = cartRepository.findById(CartId.DEFAULT)
.orElse(new Cart(CartId.DEFAULT));
Cart cart = cartRepository.findByUserId(UserId.of(userId))
.orElse(new Cart(CartId.generate(), UserId.of(userId)));

cart.addProduct(product, quantity);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import kr.megaptera.backendsurvivalweek10.models.Cart;
import kr.megaptera.backendsurvivalweek10.models.CartId;
import kr.megaptera.backendsurvivalweek10.models.LineItemId;
import kr.megaptera.backendsurvivalweek10.models.UserId;
import kr.megaptera.backendsurvivalweek10.repositories.CartRepository;
import jakarta.transaction.Transactional;
import org.springframework.stereotype.Service;
Expand All @@ -16,8 +17,8 @@ public ChangeCartItemQuantityService(CartRepository cartRepository) {
this.cartRepository = cartRepository;
}

public void changeQuantity(LineItemId lineItemId, int quantity) {
Cart cart = cartRepository.findById(CartId.DEFAULT).get();
public void changeQuantity(String userId, LineItemId lineItemId, int quantity) {
Cart cart = cartRepository.findByUserId(UserId.of(userId)).get();

cart.changeLineItemQuantity(lineItemId, quantity);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package kr.megaptera.backendsurvivalweek10.application.cart;

import kr.megaptera.backendsurvivalweek10.dtos.CartDto;
import kr.megaptera.backendsurvivalweek10.dtos.cart.CartDto;
import kr.megaptera.backendsurvivalweek10.infrastructure.CartDtoFetcher;
import kr.megaptera.backendsurvivalweek10.models.CartId;
import kr.megaptera.backendsurvivalweek10.models.UserId;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -15,7 +16,7 @@ public GetCartService(CartDtoFetcher cartDtoFetcher) {
this.cartDtoFetcher = cartDtoFetcher;
}

public CartDto getCartDto() {
return cartDtoFetcher.fetchCartDto(CartId.DEFAULT);
public CartDto getCartDto(String userId) {
return cartDtoFetcher.fetchCartDto(UserId.of(userId));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package kr.megaptera.backendsurvivalweek10.application.product;

import kr.megaptera.backendsurvivalweek10.dtos.ProductListDto;
import kr.megaptera.backendsurvivalweek10.dtos.product.ProductListDto;
import kr.megaptera.backendsurvivalweek10.infrastructure.ProductDtoFetcher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
import kr.megaptera.backendsurvivalweek10.application.cart.AddProductToCartService;
import kr.megaptera.backendsurvivalweek10.application.cart.ChangeCartItemQuantityService;
import kr.megaptera.backendsurvivalweek10.application.cart.GetCartService;
import kr.megaptera.backendsurvivalweek10.dtos.AddCartLineItemDto;
import kr.megaptera.backendsurvivalweek10.dtos.CartDto;
import kr.megaptera.backendsurvivalweek10.dtos.ChangeCartLineItemDto;
import kr.megaptera.backendsurvivalweek10.dtos.cart.AddCartLineItemDto;
import kr.megaptera.backendsurvivalweek10.dtos.cart.CartDto;
import kr.megaptera.backendsurvivalweek10.dtos.cart.ChangeCartLineItemDto;
import kr.megaptera.backendsurvivalweek10.models.LineItemId;
import kr.megaptera.backendsurvivalweek10.models.ProductId;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.*;

import java.security.Principal;
import java.util.NoSuchElementException;

@RestController
Expand All @@ -30,28 +32,40 @@ public LineItemController(
}

@GetMapping
public CartDto list() {
return getCartService.getCartDto();
@Secured({"ROLE_USER", "ROLE_ADMIN"})
public CartDto list(Principal principal) {
String userId = principal.getName();

return getCartService.getCartDto(userId);
}

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public void create(@RequestBody AddCartLineItemDto dto) {
@Secured({"ROLE_USER", "ROLE_ADMIN"})
public void create(
Principal principal,
@RequestBody AddCartLineItemDto dto) {
String userId = principal.getName();

ProductId productId = new ProductId(dto.productId());
int quantity = dto.quantity();

addProductToCartService.addProduct(productId, quantity);
addProductToCartService.addProduct(userId, productId, quantity);
}

@PatchMapping("{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
@Secured({"ROLE_USER", "ROLE_ADMIN"})
public void update(
Principal principal,
@PathVariable("id") String id,
@RequestBody ChangeCartLineItemDto dto) {
String userId = principal.getName();

LineItemId lineItemId = new LineItemId(id);
int quantity = dto.quantity();

changeCartItemQuantityService.changeQuantity(lineItemId, quantity);
changeCartItemQuantityService.changeQuantity(userId, lineItemId, quantity);
}

@ExceptionHandler(NoSuchElementException.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

import kr.megaptera.backendsurvivalweek10.application.product.CreateProductService;
import kr.megaptera.backendsurvivalweek10.application.product.GetProductListService;
import kr.megaptera.backendsurvivalweek10.dtos.CreateProductDto;
import kr.megaptera.backendsurvivalweek10.dtos.ProductListDto;
import kr.megaptera.backendsurvivalweek10.dtos.product.CreateProductDto;
import kr.megaptera.backendsurvivalweek10.dtos.product.ProductListDto;
import kr.megaptera.backendsurvivalweek10.models.Money;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.*;

@RestController
Expand All @@ -27,6 +28,7 @@ public ProductListDto list() {

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
@Secured("ROLE_ADMIN")
public void create(@RequestBody CreateProductDto dto) {
String name = dto.name().strip();
Money price = new Money(dto.price());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package kr.megaptera.backendsurvivalweek10.controllers;

import jakarta.validation.Valid;
import kr.megaptera.backendsurvivalweek10.application.auth.LoginService;
import kr.megaptera.backendsurvivalweek10.application.auth.LogoutService;
import kr.megaptera.backendsurvivalweek10.dtos.auth.LoginRequestDto;
import kr.megaptera.backendsurvivalweek10.dtos.auth.LoginResultDto;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/session")
public class SessionController {

private final LoginService loginService;
private final LogoutService logoutService;

public SessionController(LoginService loginService, LogoutService logoutService) {
this.loginService = loginService;
this.logoutService = logoutService;
}

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public LoginResultDto login(
@Valid @RequestBody LoginRequestDto dto) {
return loginService.login(dto.username(), dto.password());
}

@DeleteMapping
@ResponseStatus(HttpStatus.NO_CONTENT)
public String logout(Authentication authentication) {
String accessToken = authentication.getCredentials().toString();

logoutService.logout(accessToken);

return "Logout";
}

@ExceptionHandler(BadCredentialsException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String loginFailed() {
return "Bad Request";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package kr.megaptera.backendsurvivalweek10.controllers;

import jakarta.validation.Valid;
import kr.megaptera.backendsurvivalweek10.application.auth.SignupService;
import kr.megaptera.backendsurvivalweek10.dtos.auth.SignupRequestDto;
import kr.megaptera.backendsurvivalweek10.dtos.auth.SignupResultDto;
import kr.megaptera.backendsurvivalweek10.exceptions.UserAlreadyExistsException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/users")
public class UserController {
private final SignupService signupService;

public UserController(SignupService signupService) {
this.signupService = signupService;
}

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public SignupResultDto signup(
@Valid @RequestBody SignupRequestDto signupRequestDto) {
return signupService.signup(
signupRequestDto.username().trim(),
signupRequestDto.password().trim());
}

@ExceptionHandler(UserAlreadyExistsException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String alreadyExists() {
return "User already exists.";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package kr.megaptera.backendsurvivalweek10.controllers;

import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class WelcomeController {
@GetMapping("/")
public String home() {
return "Hello, world!";
}

@GetMapping("/admin")
@Secured("ROLE_ADMIN")
public String admin() {
return "Hello, world!";
}

@GetMapping("/user")
@Secured({"ROLE_USER", "ROLE_ADMIN"})
public String user() {
return "Hello, world!";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package kr.megaptera.backendsurvivalweek10.dtos.auth;

public record LoginRequestDto(
String username,
String password
) {
}
Loading