Skip to content

Commit

Permalink
Merge pull request #59 from Step3-kakao-tech-campus/weekly-refactor
Browse files Browse the repository at this point in the history
유저, 포트폴리오 리팩토링 작업
  • Loading branch information
Rizingblare authored Oct 8, 2023
2 parents da94ef8 + b9e06a5 commit d8f9c5f
Show file tree
Hide file tree
Showing 52 changed files with 583 additions and 509 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ public enum BaseException {
PORTFOLIO_NOT_FOUND("포트폴리오를 찾을 수 없습니다.", 404),
PORTFOLIO_IMAGE_NOT_FOUND("포트폴리오 이미지를 불러올 수 없습니다.", 404),
PERMISSION_DENIED_METHOD_ACCESS("사용할 수 없는 기능입니다.", 403),
DATABASE_ERROR("데이터베이스 에러입니다", 500),
QUOTATIONS_NOT_ALL_CONFIRMED("확정되지 않은 견적서가 있습니다.",400),
NO_QUOTATION_TO_CONFIRM("확정할 견적서가 없습니다",400),
MATCHING_NOT_FOUND("매칭 내역을 찾을 수 없습니다.", 400);


@Getter
private final String message;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

import com.kakao.sunsuwedding._core.errors.exception.*;
import com.kakao.sunsuwedding._core.utils.ApiUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import java.sql.SQLException;
import java.util.List;

@ControllerAdvice
Expand All @@ -21,6 +23,14 @@ public ResponseEntity<?> validationException(MethodArgumentNotValidException e)
return new ResponseEntity<>(ApiUtils.error(errors.get(0).getDefaultMessage(), HttpStatus.BAD_REQUEST), HttpStatus.BAD_REQUEST);
}

// database에 잘못된 값이 들어온 경우
// 예: unique 값인데 같은 게 또 들어온 경우
@ExceptionHandler({SQLException.class, DataAccessException.class})
public ResponseEntity<?> databaseException(){
BaseException e = BaseException.DATABASE_ERROR;
return new ResponseEntity<>(ApiUtils.error(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR), HttpStatus.INTERNAL_SERVER_ERROR);
}

@ExceptionHandler(Exception400.class)
public ResponseEntity<?> badRequest(Exception400 e){
return new ResponseEntity<>(e.body(), e.status());
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.kakao.sunsuwedding._core.security;

import com.kakao.sunsuwedding.user.constant.Role;
import com.kakao.sunsuwedding.user.couple.Couple;
import com.kakao.sunsuwedding.user.planner.Planner;
import com.kakao.sunsuwedding.user.base_user.User;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.data.util.Pair;
Expand All @@ -17,29 +15,25 @@
@Getter
public class CustomUserDetails implements UserDetails {

private final Planner planner;

private final Couple couple;
private final User user;

// security 에 사용하기 위한 권한 설정(플래너 -> planner, 커플 -> couple)
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singleton(new SimpleGrantedAuthority(getInfo().getFirst().getRoleName()));
return Collections.singleton(new SimpleGrantedAuthority(user.getDtype()));
}

// role(planner or couple), userId
public Pair<Role, Integer> getInfo(){
return (couple == null) ? Pair.of(Role.PLANNER, planner.getId()) : Pair.of(Role.COUPLE, couple.getId());
public Pair<String, Long> getInfo() {
return Pair.of(user.getDtype(), user.getId());
}

@Override
public String getPassword() {
return (couple == null) ? planner.getPassword() : couple.getPassword();
return user.getPassword();
}

@Override
public String getUsername() {
return (couple == null) ? planner.getEmail() : couple.getEmail();
return user.getEmail();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,26 @@
package com.kakao.sunsuwedding._core.security;

import com.kakao.sunsuwedding._core.errors.BaseException;
import com.kakao.sunsuwedding._core.errors.exception.Exception400;
import com.kakao.sunsuwedding.user.couple.Couple;
import com.kakao.sunsuwedding.user.couple.CoupleJPARepository;
import com.kakao.sunsuwedding.user.planner.Planner;
import com.kakao.sunsuwedding.user.planner.PlannerJPARepository;
import com.kakao.sunsuwedding.user.base_user.User;
import com.kakao.sunsuwedding.user.base_user.UserJPARepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Optional;

@RequiredArgsConstructor
@Service
public class CustomUserDetailsService implements UserDetailsService {

private final PlannerJPARepository plannerJPARepository;
private final CoupleJPARepository coupleJPARepository;
private final UserJPARepository userJPARepository;

@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Optional<Planner> plannerOP = plannerJPARepository.findByEmail(email);
Optional<Couple> coupleOP = coupleJPARepository.findByEmail(email);

Planner plannerPS = plannerOP.orElse(null);
Couple couplePS = coupleOP.orElse(null);

// 잘못된 email (플래너도 아니고, 예비 부부도 아님)
if (plannerPS == null && couplePS == null)
throw new Exception400(BaseException.USER_NOT_FOUND.getMessage());
User user = userJPARepository.findByEmailNative(email).orElseThrow(
() -> new UsernameNotFoundException(BaseException.USER_EMAIL_NOT_FOUND.getMessage())
);

return new CustomUserDetails(plannerPS, couplePS);
return new CustomUserDetails(user);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.kakao.sunsuwedding.user.constant.Role;
import com.kakao.sunsuwedding.user.couple.Couple;
import com.kakao.sunsuwedding.user.planner.Planner;
import com.kakao.sunsuwedding.user.base_user.User;
import org.springframework.stereotype.Component;

import java.util.Date;
Expand All @@ -21,21 +19,12 @@ public class JWTProvider {
public static final String HEADER = "Authorization";
public static final String SECRET = "MySecretKey";

public static String create(Couple couple) {
public static String create(User user) {
String jwt = JWT.create()
.withSubject(couple.getEmail())
.withSubject(user.getEmail())
.withExpiresAt(new Date(System.currentTimeMillis() + EXP))
.withClaim("id", couple.getId())
.withClaim("role", Role.COUPLE.getRoleName())
.sign(Algorithm.HMAC512(SECRET));
return TOKEN_PREFIX + jwt;
}
public static String create(Planner planner) {
String jwt = JWT.create()
.withSubject(planner.getEmail())
.withExpiresAt(new Date(System.currentTimeMillis() + EXP))
.withClaim("id", planner.getId())
.withClaim("role", Role.PLANNER.getRoleName())
.withClaim("id", user.getId())
.withClaim("role", user.getDtype())
.sign(Algorithm.HMAC512(SECRET));
return TOKEN_PREFIX + jwt;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.kakao.sunsuwedding.user.base_user.User;
import com.kakao.sunsuwedding.user.constant.Role;
import com.kakao.sunsuwedding.user.couple.Couple;
import com.kakao.sunsuwedding.user.planner.Planner;
Expand Down Expand Up @@ -36,22 +37,14 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
chain.doFilter(request, response);
return;
}
if (!jwt.startsWith("Bearer ")) {
throw new JWTDecodeException("토큰 형식이 잘못되었습니다.");
}
try {
DecodedJWT decodedJWT = JWTProvider.verify(jwt);
int id = decodedJWT.getClaim("id").asInt();
String roleName = decodedJWT.getClaim("role").asString();
System.out.println("role : "+ roleName);
Long userId = decodedJWT.getClaim("id").asLong();

String roleName = decodedJWT.getClaim("role").asString();
Role role = Role.valueOfRole(roleName);
if (role == null)
throw new JWTDecodeException("role 잘못됨");

Planner planner = (role == Role.PLANNER) ? Planner.builder().id(id).build() : null;
Couple couple = (role == Role.COUPLE) ? Couple.builder().id(id).build() : null;
CustomUserDetails myUserDetails = new CustomUserDetails(planner, couple);
User user = (role == Role.PLANNER) ? Planner.builder().id(userId).build() : Couple.builder().id(userId).build();
CustomUserDetails myUserDetails = new CustomUserDetails(user);

Authentication authentication =
new UsernamePasswordAuthenticationToken(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

Expand All @@ -16,7 +17,12 @@
@Component
public class JwtExceptionFilter extends OncePerRequestFilter {

private final ObjectMapper om = new ObjectMapper();
private final ObjectMapper om;

@Autowired
public JwtExceptionFilter(ObjectMapper om) {
this.om = om;
}

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.kakao.sunsuwedding._core.errors.exception.Exception401;
import com.kakao.sunsuwedding._core.errors.exception.Exception403;
import com.kakao.sunsuwedding._core.utils.FilterResponseUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
Expand All @@ -17,21 +18,24 @@
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;


@RequiredArgsConstructor
@Configuration
public class SecurityConfig {

private final JwtExceptionFilter jwtExceptionFilter;
private final FilterResponseUtils filterResponseUtils;

@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

public static class CustomSecurityFilterManager extends AbstractHttpConfigurer<CustomSecurityFilterManager, HttpSecurity> {
public class CustomSecurityFilterManager extends AbstractHttpConfigurer<CustomSecurityFilterManager, HttpSecurity> {
@Override
public void configure(HttpSecurity builder) throws Exception {
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
builder.addFilter(new JwtAuthenticationFilter(authenticationManager));
builder.addFilterBefore(new JwtExceptionFilter(), JwtAuthenticationFilter.class);
builder.addFilterBefore(jwtExceptionFilter, JwtAuthenticationFilter.class);
super.configure(builder);
}
}
Expand Down Expand Up @@ -62,14 +66,14 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
// 8. 인증 실패 처리
http.exceptionHandling((exceptionHandling) ->
exceptionHandling.authenticationEntryPoint((request, response, authException) -> {
FilterResponseUtils.unAuthorized(response, new Exception401("인증되지 않았습니다"));
filterResponseUtils.unAuthorized(response, new Exception401("인증되지 않았습니다"));
})
);

// 9. 권한 실패 처리
http.exceptionHandling((exceptionHandling) ->
exceptionHandling.accessDeniedHandler((request, response, accessDeniedException) -> {
FilterResponseUtils.forbidden(response, new Exception403("권한이 없습니다"));
filterResponseUtils.forbidden(response, new Exception403("권한이 없습니다"));
})
);

Expand All @@ -90,20 +94,21 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
new AntPathRequestMatcher("/user/**"),
new AntPathRequestMatcher("/portfolios/**"),
new AntPathRequestMatcher("/chat/**"),
new AntPathRequestMatcher("/quotations/**")
).authenticated()
new AntPathRequestMatcher("/quotations/**"),
new AntPathRequestMatcher("/payments/**")
).authenticated()
// 검증 필요
.requestMatchers(
new AntPathRequestMatcher("/chat", "POST"),
new AntPathRequestMatcher("/quotations/confirmAll/**", "POST")
).hasRole("COUPLE")
).hasRole("couple")
.requestMatchers(
new AntPathRequestMatcher("/portfolios", "POST"),
new AntPathRequestMatcher("/portfolios", "PUT"),
new AntPathRequestMatcher("/portfolios", "DELETE"),
new AntPathRequestMatcher("/quotations/**", "PUT"),
new AntPathRequestMatcher("/quotations/**", "POST")
).hasRole("PLANNER")
).hasRole("planner")
.anyRequest().permitAll()
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.springframework.http.HttpStatus;

public class ApiUtils {
Expand All @@ -15,14 +14,14 @@ public static ApiResult<?> error(String message, HttpStatus status) {
return new ApiResult<>(false, null, new ApiError(message, status.value()));
}

@Getter @Setter @AllArgsConstructor
@Getter @AllArgsConstructor
public static class ApiResult<T> {
private final boolean success;
private final T response;
private final ApiError error;
}

@Getter @Setter @AllArgsConstructor
@Getter @AllArgsConstructor
public static class ApiError {
private final String message;
private final int status;
Expand Down
Loading

0 comments on commit d8f9c5f

Please sign in to comment.