Skip to content

Commit

Permalink
Merge pull request #45 from Nexters/feature/39
Browse files Browse the repository at this point in the history
Feature/39 : ๊ด€๋ฆฌ์ž ๋กœ๊ทธ์ธ API
  • Loading branch information
shincheeun authored Feb 3, 2024
2 parents 2ac54c1 + c935a81 commit d3e95d7
Show file tree
Hide file tree
Showing 21 changed files with 550 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
package com.nexters.dailyphrase.admin.business;

import java.util.stream.Collectors;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.nexters.dailyphrase.admin.domain.Admin;
import com.nexters.dailyphrase.admin.implement.AdminQueryService;
import com.nexters.dailyphrase.admin.presentation.dto.AdminRequestDTO;
import com.nexters.dailyphrase.admin.presentation.dto.AdminResponseDTO;
import com.nexters.dailyphrase.common.jwt.JwtTokenService;
import com.nexters.dailyphrase.phrase.domain.Phrase;
import com.nexters.dailyphrase.phrase.implement.PhraseCommandService;
import com.nexters.dailyphrase.phrase.implement.PhraseQueryService;
Expand All @@ -19,8 +28,38 @@
public class AdminFacade {
private final PhraseCommandService phraseCommandService;
private final PhraseQueryService phraseQueryService;
private final AdminQueryService adminQueryService;
private final PhraseImageCommandService phraseImageCommandService;
private final AdminMapper adminMapper;
private final JwtTokenService jwtTokenService;
private final AuthenticationManagerBuilder authenticationManagerBuilder;

@Transactional
public AdminResponseDTO.LoginAdmin loginAdmin(final AdminRequestDTO.LoginAdmin request) {

String username = request.getUserId();
String password = request.getPassword();

adminQueryService.findByLoginId(username); // UserId Notfound ์˜ˆ์™ธ์ฒ˜๋ฆฌ์šฉ

UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(username, password);

Authentication authentication =
authenticationManagerBuilder.getObject().authenticate(authenticationToken);

String role =
authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(",")); // role_admin
String authenticatedUserId = authentication.getName(); // UserId

Admin admin = adminQueryService.findByLoginId(authenticatedUserId); // id
String accessToken = jwtTokenService.generateAccessToken(admin.getId(), role);
String refreshToken = jwtTokenService.generateRefreshToken(admin.getId());

return adminMapper.toLogin(admin, accessToken, refreshToken);
}

@Transactional
public AdminResponseDTO.AddPhrase addPhrase(final AdminRequestDTO.AddPhrase request) {
Expand Down Expand Up @@ -59,6 +98,7 @@ public AdminResponseDTO.ModifyPhrase modifyPhrase(
return adminMapper.toModifyPhrase(updatedPhrase);
}


@Transactional(readOnly = true)
public AdminResponseDTO.AdminPhraseList getAdminPhraseList() {
return phraseQueryService.findAdminPhraseListDTO();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import org.springframework.stereotype.Component;

import com.nexters.dailyphrase.admin.domain.Admin;
import com.nexters.dailyphrase.admin.presentation.dto.AdminRequestDTO;
import com.nexters.dailyphrase.admin.presentation.dto.AdminResponseDTO;
import com.nexters.dailyphrase.phrase.domain.Phrase;
Expand Down Expand Up @@ -67,4 +68,14 @@ public AdminResponseDTO.ModifyPhrase toModifyPhrase(Phrase updatedPhrase) {
public AdminResponseDTO.DeletePhrase toDeletePhrase() {
return AdminResponseDTO.DeletePhrase.builder().deletedAt(LocalDateTime.now()).build();
}


public AdminResponseDTO.LoginAdmin toLogin(
Admin admin, String accessToken, String refreshToken) {
return AdminResponseDTO.LoginAdmin.builder()
.userId(admin.getUserId())
.accessToken(accessToken)
.refreshToken(refreshToken)
.build();
}
}
45 changes: 42 additions & 3 deletions src/main/java/com/nexters/dailyphrase/admin/domain/Admin.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package com.nexters.dailyphrase.admin.domain;

import java.util.Collection;
import java.util.Collections;

import jakarta.persistence.*;

import com.nexters.dailyphrase.common.domain.BaseDateTimeEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.nexters.dailyphrase.common.enums.MemberRole;

import lombok.*;
Expand All @@ -12,17 +18,50 @@
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Admin extends BaseDateTimeEntity {
public class Admin implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;

@Column(nullable = false)
private String userId;

@Column(nullable = false)
private String password;

@Builder.Default
@Enumerated(EnumType.STRING)
private MemberRole role;
private final MemberRole role = MemberRole.ADMIN;

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singleton(new SimpleGrantedAuthority(role.toString()));
}

@Override
public String getUsername() {
return String.valueOf(userId);
}

@Override
public boolean isAccountNonExpired() {
return true;
}

@Override
public boolean isAccountNonLocked() {
return true;
}

@Override
public boolean isCredentialsNonExpired() {
return true;
}

@Override
public boolean isEnabled() {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.nexters.dailyphrase.admin.domain.repository;

import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

import com.nexters.dailyphrase.admin.domain.Admin;

public interface AdminRepository extends JpaRepository<Admin, Long> {}
public interface AdminRepository extends JpaRepository<Admin, Long> {
Optional<Admin> findByUserId(String userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.nexters.dailyphrase.admin.exception;

import com.nexters.dailyphrase.common.exception.BaseCodeException;

public class AdminBadCredentialException extends BaseCodeException {

public static BaseCodeException EXCEPTION =
new com.nexters.dailyphrase.admin.exception.AdminBadCredentialException();

public AdminBadCredentialException() {
super(AdminErrorCode.ADMIN_BAD_CREDENTIALS_EXCEPTION);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.nexters.dailyphrase.admin.exception;

import static com.nexters.dailyphrase.common.consts.DailyPhraseStatic.NOT_FOUND;
import static com.nexters.dailyphrase.common.consts.DailyPhraseStatic.UNAUTHORIZED;

import java.lang.reflect.Field;
import java.util.Objects;

import com.nexters.dailyphrase.common.annotation.ExplainError;
import com.nexters.dailyphrase.common.exception.BaseErrorCode;
import com.nexters.dailyphrase.common.exception.ErrorReason;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum AdminErrorCode implements BaseErrorCode {
ADMIN_NOT_FOUND(NOT_FOUND, "ADMIN_404_1", "ํ•ด๋‹น ๊ด€๋ฆฌ์ž๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."),
ADMIN_BAD_CREDENTIALS_EXCEPTION(UNAUTHORIZED, "ADMIN_401_1", "์ž๊ฒฉ์ฆ๋ช…์ด ์—†์Šต๋‹ˆ๋‹ค. ์•„์ด๋”” ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ‹€๋ ธ์Šต๋‹ˆ๋‹ค.");

private final Integer status;
private final String code;
private final String reason;

@Override
public ErrorReason getErrorReason() {
return ErrorReason.builder().reason(reason).code(code).status(status).build();
}

@Override
public String getExplainError() throws NoSuchFieldException {
Field field = this.getClass().getField(this.name());
ExplainError annotation = field.getAnnotation(ExplainError.class);
return Objects.nonNull(annotation) ? annotation.value() : this.getReason();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.nexters.dailyphrase.admin.exception;

import com.nexters.dailyphrase.common.exception.BaseCodeException;

public class AdminNotFoundException extends BaseCodeException {
public static BaseCodeException EXCEPTION = new AdminNotFoundException();

public AdminNotFoundException() {
super(AdminErrorCode.ADMIN_NOT_FOUND);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.nexters.dailyphrase.admin.implement;

import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import com.nexters.dailyphrase.admin.domain.Admin;
import com.nexters.dailyphrase.admin.domain.repository.AdminRepository;
import com.nexters.dailyphrase.admin.exception.AdminNotFoundException;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class AdminDetailsServiceImpl implements UserDetailsService {

private final AdminRepository adminRepository;
private final PasswordEncoder passwordEncoder;

@Override
public UserDetails loadUserByUsername(String username) throws AdminNotFoundException {
return adminRepository
.findByUserId(username)
.map(this::createUserDetails)
.orElseThrow(() -> AdminNotFoundException.EXCEPTION);
}

private UserDetails createUserDetails(Admin admin) {
return User.builder()
.username(admin.getUserId())
.password(passwordEncoder.encode(admin.getPassword()))
.roles(String.valueOf(admin.getRole()))
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@

import org.springframework.stereotype.Service;

import com.nexters.dailyphrase.admin.domain.Admin;
import com.nexters.dailyphrase.admin.domain.repository.AdminRepository;
import com.nexters.dailyphrase.phrase.domain.repository.PhraseRepository;
import com.nexters.dailyphrase.admin.exception.AdminNotFoundException;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class AdminQueryService {
private final AdminRepository adminRepository;
private final PhraseRepository phraseRepository;

public Admin findByLoginId(String userId) {
return adminRepository
.findByUserId(userId)
.orElseThrow(() -> AdminNotFoundException.EXCEPTION);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,19 @@
public class AdminApi {
private final AdminFacade adminFacade;

@Operation(summary = "05-07 Admin๏ธ๐Ÿ‘ท๐Ÿป ๊ด€๋ฆฌ์ž ๋กœ๊ทธ์ธ Made By ์ฑ„์€", description = "๊ด€๋ฆฌ์ž ๋กœ๊ทธ์ธ API์ž…๋‹ˆ๋‹ค.")
@Operation(summary = "05-07 Admin๐Ÿ‘ท๐Ÿป ๊ด€๋ฆฌ์ž ๋กœ๊ทธ์ธ Made By ์ฑ„์€", description = "๊ด€๋ฆฌ์ž ๋กœ๊ทธ์ธ API์ž…๋‹ˆ๋‹ค.")
@PostMapping("/login")
public CommonResponse<AdminResponseDTO.LoginAdmin> loginAdmin() {
return null;
public CommonResponse<AdminResponseDTO.LoginAdmin> loginAdmin(
@RequestBody final AdminRequestDTO.LoginAdmin request) {
return CommonResponse.onSuccess(adminFacade.loginAdmin(request));
}

@Operation(
summary = "05-07 Admin๐Ÿ‘ท๐Ÿป ๊ด€๋ฆฌ์ž ๋กœ๊ทธ์ธ ํ† ํฐ ์žฌ๋ฐœํ–‰ Made By ์ฑ„์€",
description = "๊ด€๋ฆฌ์ž ๋กœ๊ทธ์ธ ํ† ํฐ ์žฌ๋ฐœํ–‰ API์ž…๋‹ˆ๋‹ค.")
@PostMapping("/reissue")
public void reissueToken() {}

@Operation(summary = "05-06 Admin๏ธ๐Ÿ‘ท๐Ÿป ๊ด€๋ฆฌ์ž ๋กœ๊ทธ์•„์›ƒ Made By ์ฑ„์€", description = "๊ด€๋ฆฌ์ž ๋กœ๊ทธ์•„์›ƒ API์ž…๋‹ˆ๋‹ค.")
@PostMapping("/logout")
public CommonResponse<AdminResponseDTO.LogoutAdmin> logoutAdmin() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,13 @@ public static class ModifyPhrase {
private String fileName;
private String imageRatio;
}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class LoginAdmin {
private String userId;
private String password;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ public static class LogoutAdmin {
@NoArgsConstructor
@AllArgsConstructor
public static class LoginAdmin {
private String field;
private String userId;
private String accessToken;
private String refreshToken;
}

@Builder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
@Getter
@AllArgsConstructor
public enum MemberRole {
ROLE_GUEST("๊ฒŒ์ŠคํŠธ"),
ROLE_USER("ํšŒ์›"),
ROLE_ADMIN("๊ด€๋ฆฌ์ž");
GUEST("๊ฒŒ์ŠคํŠธ"),
USER("ํšŒ์›"),
ADMIN("๊ด€๋ฆฌ์ž");

private final String description;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ public enum GlobalErrorCode implements BaseErrorCode {
ARGUMENT_NOT_VALID_ERROR(BAD_REQUEST, "GLOBAL_400_1", "์ž˜๋ชป๋œ ์š”์ฒญ"),
INTERNAL_SERVER_ERROR(INTERNAL_SERVER, "GLOBAL_500_1", "์„œ๋ฒ„ ์˜ค๋ฅ˜. ๊ด€๋ฆฌ์ž์—๊ฒŒ ๋ฌธ์˜ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค."),
TOO_MANY_REQUEST(TOO_MANY_REQUESTS, "GLOBAL_429_1", "๊ณผ๋„ํ•œ ์š”์ฒญ์„ ๋ณด๋‚ด์…จ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ๊ธฐ๋‹ค๋ ค ์ฃผ์„ธ์š”.");

private final Integer status;
private final String code;
private final String reason;
Expand Down
Loading

0 comments on commit d3e95d7

Please sign in to comment.