From b04a21edb0718416b7b5a308dd7d21745db61878 Mon Sep 17 00:00:00 2001 From: issuejong Date: Tue, 19 May 2026 17:00:06 +0900 Subject: [PATCH 1/3] practice/#08 --- build.gradle | 4 ++ .../global/config/SecurityConfig.java | 60 +++++++++++++++++++ .../handler/CustomAccessDeniedHandler.java | 33 ++++++++++ .../CustomAuthenticationEntryPoint.java | 33 ++++++++++ 4 files changed, 130 insertions(+) create mode 100644 src/main/java/com/example/Spring_Boot/global/config/SecurityConfig.java create mode 100644 src/main/java/com/example/Spring_Boot/global/security/handler/CustomAccessDeniedHandler.java create mode 100644 src/main/java/com/example/Spring_Boot/global/security/handler/CustomAuthenticationEntryPoint.java diff --git a/build.gradle b/build.gradle index c56ead3..08c46ce 100644 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,10 @@ dependencies { // Swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:3.0.1' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-api:3.0.1' + + // Security + implementation 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'org.springframework.security:spring-security-test' } tasks.named('test') { diff --git a/src/main/java/com/example/Spring_Boot/global/config/SecurityConfig.java b/src/main/java/com/example/Spring_Boot/global/config/SecurityConfig.java new file mode 100644 index 0000000..31b6853 --- /dev/null +++ b/src/main/java/com/example/Spring_Boot/global/config/SecurityConfig.java @@ -0,0 +1,60 @@ +package com.example.Spring_Boot.global.config; + +import com.example.Spring_Boot.global.security.handler.CustomAccessDeniedHandler; +import com.example.Spring_Boot.global.security.handler.CustomAuthenticationEntryPoint; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; + +@EnableWebSecurity +@Configuration +@RequiredArgsConstructor +public class SecurityConfig { + + private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint; + private final CustomAccessDeniedHandler customAccessDeniedHandler; + + private final String[] allowUris = { + // Swagger 허용 + "/swagger-ui/**", + "/swagger-resources/**", + "/v3/api-docs/**", + "/auth/**" + }; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(requests -> requests + .requestMatchers(allowUris).permitAll() + .anyRequest().authenticated() + ) + .formLogin(form -> form + .defaultSuccessUrl("/swagger-ui/index.html", true) + .permitAll() + ) + .logout(logout -> logout + .logoutUrl("/logout") + .logoutSuccessUrl("/login?logout") + .permitAll() + ) + .exceptionHandling(exception -> exception + .authenticationEntryPoint(customAuthenticationEntryPoint) + .accessDeniedHandler(customAccessDeniedHandler) + ); + + return http.build(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/src/main/java/com/example/Spring_Boot/global/security/handler/CustomAccessDeniedHandler.java b/src/main/java/com/example/Spring_Boot/global/security/handler/CustomAccessDeniedHandler.java new file mode 100644 index 0000000..f010c50 --- /dev/null +++ b/src/main/java/com/example/Spring_Boot/global/security/handler/CustomAccessDeniedHandler.java @@ -0,0 +1,33 @@ +package com.example.Spring_Boot.global.security.handler; + +import com.example.Spring_Boot.global.apiPayload.ApiResponse; +import com.example.Spring_Boot.global.apiPayload.code.BaseErrorCode; +import com.example.Spring_Boot.global.apiPayload.code.GeneralErrorCode; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class CustomAccessDeniedHandler implements AccessDeniedHandler { + + @Override + public void handle( + HttpServletRequest request, + HttpServletResponse response, + AccessDeniedException accessDeniedException + ) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + BaseErrorCode code = GeneralErrorCode.FORBIDDEN; + + response.setContentType("application/json;charset=UTF-8"); + response.setStatus(code.getStatus().value()); + + ApiResponse errorResponse = ApiResponse.onFailure(code, null); + objectMapper.writeValue(response.getOutputStream(), errorResponse); + } +} diff --git a/src/main/java/com/example/Spring_Boot/global/security/handler/CustomAuthenticationEntryPoint.java b/src/main/java/com/example/Spring_Boot/global/security/handler/CustomAuthenticationEntryPoint.java new file mode 100644 index 0000000..bfd9f9c --- /dev/null +++ b/src/main/java/com/example/Spring_Boot/global/security/handler/CustomAuthenticationEntryPoint.java @@ -0,0 +1,33 @@ +package com.example.Spring_Boot.global.security.handler; + +import com.example.Spring_Boot.global.apiPayload.ApiResponse; +import com.example.Spring_Boot.global.apiPayload.code.BaseErrorCode; +import com.example.Spring_Boot.global.apiPayload.code.GeneralErrorCode; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { + + @Override + public void commence( + HttpServletRequest request, + HttpServletResponse response, + AuthenticationException authException + ) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + BaseErrorCode code = GeneralErrorCode.UNAUTHORIZED; + + response.setContentType("application/json;charset=UTF-8"); + response.setStatus(code.getStatus().value()); + + ApiResponse errorResponse = ApiResponse.onFailure(code, null); + objectMapper.writeValue(response.getOutputStream(), errorResponse); + } +} From bdbe01f056b1c905903d911a0fdd9930b01ada0b Mon Sep 17 00:00:00 2001 From: issuejong Date: Tue, 19 May 2026 22:00:07 +0900 Subject: [PATCH 2/3] mission/#08 --- .../member/controller/MemberController.java | 5 +-- .../member/converter/MemberConverter.java | 35 ++++++++++++++++++- .../domain/member/dto/MemberReqDTO.java | 28 +++++++++++++++ .../domain/member/dto/MemberResDTO.java | 3 ++ .../domain/member/entity/Member.java | 5 +++ .../exception/code/MemberErrorCode.java | 5 +++ .../member/repository/MemberRepository.java | 6 ++++ .../domain/member/service/MemberService.java | 13 ++++++- .../global/config/SecurityConfig.java | 4 +++ .../global/security/auth/AuthMember.java | 33 +++++++++++++++++ .../auth/CustomUserDetailsService.java | 22 ++++++++++++ 11 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/example/Spring_Boot/global/security/auth/AuthMember.java create mode 100644 src/main/java/com/example/Spring_Boot/global/security/auth/CustomUserDetailsService.java diff --git a/src/main/java/com/example/Spring_Boot/domain/member/controller/MemberController.java b/src/main/java/com/example/Spring_Boot/domain/member/controller/MemberController.java index a276f46..bf8c7f7 100644 --- a/src/main/java/com/example/Spring_Boot/domain/member/controller/MemberController.java +++ b/src/main/java/com/example/Spring_Boot/domain/member/controller/MemberController.java @@ -5,6 +5,7 @@ import com.example.Spring_Boot.domain.member.exception.code.MemberSuccessCode; import com.example.Spring_Boot.domain.member.service.MemberService; import com.example.Spring_Boot.global.apiPayload.ApiResponse; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -34,8 +35,8 @@ public ApiResponse getMyPage( @PostMapping public ApiResponse createMember( - @RequestBody MemberReqDTO.CreateMemberRequest request - ) { + @Valid @RequestBody MemberReqDTO.CreateMemberRequest request + ) { MemberResDTO.CreateMemberResponse response = memberService.createMember(request); return ApiResponse.onSuccess( diff --git a/src/main/java/com/example/Spring_Boot/domain/member/converter/MemberConverter.java b/src/main/java/com/example/Spring_Boot/domain/member/converter/MemberConverter.java index 5faf4ba..7a3971b 100644 --- a/src/main/java/com/example/Spring_Boot/domain/member/converter/MemberConverter.java +++ b/src/main/java/com/example/Spring_Boot/domain/member/converter/MemberConverter.java @@ -1,8 +1,11 @@ package com.example.Spring_Boot.domain.member.converter; +import com.example.Spring_Boot.domain.member.dto.MemberReqDTO; import com.example.Spring_Boot.domain.member.dto.MemberResDTO; import com.example.Spring_Boot.domain.member.entity.Member; +import java.util.List; + public class MemberConverter { private MemberConverter() { @@ -13,10 +16,40 @@ public static MemberResDTO.MyPageResponse toMyPageResponse(Member member) { .userId(member.getUserId()) .name(member.getName()) .nickname(member.getNickname()) - .email(null) + .email(member.getEmail()) .phoneNumber(member.getPhoneNumber()) .point(member.getPoint()) .socialProvider(member.getSocialProvider().name()) .build(); } + + public static Member toMember(MemberReqDTO.CreateMemberRequest request, String encodedPassword) { + return Member.builder() + .email(request.email()) + .password(encodedPassword) + .name(request.name()) + .nickname(request.nickname()) + .phoneNumber(request.phoneNumber()) + .gender(request.gender()) + .birth(request.birth()) + .address(request.address()) + .build(); + } + + public static MemberResDTO.CreateMemberResponse toCreateMemberResponse( + Member member, + List categoryIds + ) { + return MemberResDTO.CreateMemberResponse.builder() + .userId(member.getUserId()) + .email(member.getEmail()) + .name(member.getName()) + .nickname(member.getNickname()) + .phoneNumber(member.getPhoneNumber()) + .gender(member.getGender()) + .birth(member.getBirth()) + .address(member.getAddress()) + .categoryIds(categoryIds) + .build(); + } } diff --git a/src/main/java/com/example/Spring_Boot/domain/member/dto/MemberReqDTO.java b/src/main/java/com/example/Spring_Boot/domain/member/dto/MemberReqDTO.java index afd81c7..521e106 100644 --- a/src/main/java/com/example/Spring_Boot/domain/member/dto/MemberReqDTO.java +++ b/src/main/java/com/example/Spring_Boot/domain/member/dto/MemberReqDTO.java @@ -1,6 +1,10 @@ package com.example.Spring_Boot.domain.member.dto; import com.example.Spring_Boot.domain.member.enums.Gender; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import lombok.Builder; import java.time.LocalDate; @@ -10,9 +14,33 @@ public class MemberReqDTO { @Builder public record CreateMemberRequest( + @NotBlank(message = "이메일은 필수입니다.") + @Email(message = "이메일 형식이 올바르지 않습니다.") + String email, + + @NotBlank(message = "비밀번호는 필수입니다.") + @Size(min = 8, max = 30, message = "비밀번호는 8자 이상 30자 이하로 입력해야 합니다.") + String password, + + @NotBlank(message = "이름은 필수입니다.") + @Size(max = 5, message = "이름은 5자 이하로 입력해야 합니다.") String name, + + @NotBlank(message = "닉네임은 필수입니다.") + @Size(max = 15, message = "닉네임은 15자 이하로 입력해야 합니다.") + String nickname, + + @NotBlank(message = "전화번호는 필수입니다.") + @Size(max = 15, message = "전화번호는 15자 이하로 입력해야 합니다.") + String phoneNumber, + + @NotNull(message = "성별은 필수입니다.") Gender gender, + + @NotNull(message = "생년월일은 필수입니다.") LocalDate birth, + + @NotBlank(message = "주소는 필수입니다.") String address, List categoryIds ) { diff --git a/src/main/java/com/example/Spring_Boot/domain/member/dto/MemberResDTO.java b/src/main/java/com/example/Spring_Boot/domain/member/dto/MemberResDTO.java index 6c08c9f..c9424f1 100644 --- a/src/main/java/com/example/Spring_Boot/domain/member/dto/MemberResDTO.java +++ b/src/main/java/com/example/Spring_Boot/domain/member/dto/MemberResDTO.java @@ -11,7 +11,10 @@ public class MemberResDTO { @Builder public record CreateMemberResponse( Long userId, + String email, String name, + String nickname, + String phoneNumber, Gender gender, LocalDate birth, String address, diff --git a/src/main/java/com/example/Spring_Boot/domain/member/entity/Member.java b/src/main/java/com/example/Spring_Boot/domain/member/entity/Member.java index 4d5be53..109dfcb 100644 --- a/src/main/java/com/example/Spring_Boot/domain/member/entity/Member.java +++ b/src/main/java/com/example/Spring_Boot/domain/member/entity/Member.java @@ -40,6 +40,11 @@ public class Member { @Column(nullable = false, length = 15) private String nickname; + @Column(unique = true) + private String email; + + private String password; + @Builder.Default @Enumerated(EnumType.STRING) @Column(nullable = false) diff --git a/src/main/java/com/example/Spring_Boot/domain/member/exception/code/MemberErrorCode.java b/src/main/java/com/example/Spring_Boot/domain/member/exception/code/MemberErrorCode.java index a141f55..3c93635 100644 --- a/src/main/java/com/example/Spring_Boot/domain/member/exception/code/MemberErrorCode.java +++ b/src/main/java/com/example/Spring_Boot/domain/member/exception/code/MemberErrorCode.java @@ -18,6 +18,11 @@ public enum MemberErrorCode implements BaseErrorCode { HttpStatus.NOT_FOUND, "MEMBER404_1", "유저를 찾을 수 없습니다." + ), + DUPLICATE_EMAIL( + HttpStatus.CONFLICT, + "MEMBER409_1", + "이미 사용 중인 이메일입니다." ); private final HttpStatus status; diff --git a/src/main/java/com/example/Spring_Boot/domain/member/repository/MemberRepository.java b/src/main/java/com/example/Spring_Boot/domain/member/repository/MemberRepository.java index 411e2c5..45ab3b7 100644 --- a/src/main/java/com/example/Spring_Boot/domain/member/repository/MemberRepository.java +++ b/src/main/java/com/example/Spring_Boot/domain/member/repository/MemberRepository.java @@ -3,5 +3,11 @@ import com.example.Spring_Boot.domain.member.entity.Member; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface MemberRepository extends JpaRepository { + + Optional findByEmail(String email); + + boolean existsByEmail(String email); } diff --git a/src/main/java/com/example/Spring_Boot/domain/member/service/MemberService.java b/src/main/java/com/example/Spring_Boot/domain/member/service/MemberService.java index fa2fc8b..2a62c16 100644 --- a/src/main/java/com/example/Spring_Boot/domain/member/service/MemberService.java +++ b/src/main/java/com/example/Spring_Boot/domain/member/service/MemberService.java @@ -8,6 +8,7 @@ import com.example.Spring_Boot.domain.member.exception.code.MemberErrorCode; import com.example.Spring_Boot.domain.member.repository.MemberRepository; import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -16,6 +17,7 @@ public class MemberService { private final MemberRepository memberRepository; + private final PasswordEncoder passwordEncoder; @Transactional(readOnly = true) public MemberResDTO.MyPageResponse getMyPage(String authorization) { @@ -25,8 +27,17 @@ public MemberResDTO.MyPageResponse getMyPage(String authorization) { return MemberConverter.toMyPageResponse(member); } + @Transactional public MemberResDTO.CreateMemberResponse createMember(MemberReqDTO.CreateMemberRequest request) { - throw new UnsupportedOperationException("Member creation is not implemented yet."); + if (memberRepository.existsByEmail(request.email())) { + throw new MemberException(MemberErrorCode.DUPLICATE_EMAIL); + } + + String encodedPassword = passwordEncoder.encode(request.password()); + Member member = MemberConverter.toMember(request, encodedPassword); + Member savedMember = memberRepository.save(member); + + return MemberConverter.toCreateMemberResponse(savedMember, request.categoryIds()); } private Long extractMemberId(String authorization) { diff --git a/src/main/java/com/example/Spring_Boot/global/config/SecurityConfig.java b/src/main/java/com/example/Spring_Boot/global/config/SecurityConfig.java index 31b6853..8499027 100644 --- a/src/main/java/com/example/Spring_Boot/global/config/SecurityConfig.java +++ b/src/main/java/com/example/Spring_Boot/global/config/SecurityConfig.java @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; @@ -34,9 +35,12 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(requests -> requests .requestMatchers(allowUris).permitAll() + .requestMatchers(HttpMethod.POST, "/api/members").permitAll() .anyRequest().authenticated() ) .formLogin(form -> form + .usernameParameter("email") + .passwordParameter("password") .defaultSuccessUrl("/swagger-ui/index.html", true) .permitAll() ) diff --git a/src/main/java/com/example/Spring_Boot/global/security/auth/AuthMember.java b/src/main/java/com/example/Spring_Boot/global/security/auth/AuthMember.java new file mode 100644 index 0000000..cdef655 --- /dev/null +++ b/src/main/java/com/example/Spring_Boot/global/security/auth/AuthMember.java @@ -0,0 +1,33 @@ +package com.example.Spring_Boot.global.security.auth; + +import com.example.Spring_Boot.domain.member.entity.Member; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.List; + +@Getter +@RequiredArgsConstructor +public class AuthMember implements UserDetails { + + private final Member member; + + @Override + public Collection getAuthorities() { + return List.of(new SimpleGrantedAuthority("ROLE_USER")); + } + + @Override + public String getPassword() { + return member.getPassword(); + } + + @Override + public String getUsername() { + return member.getEmail(); + } +} diff --git a/src/main/java/com/example/Spring_Boot/global/security/auth/CustomUserDetailsService.java b/src/main/java/com/example/Spring_Boot/global/security/auth/CustomUserDetailsService.java new file mode 100644 index 0000000..8be1f2e --- /dev/null +++ b/src/main/java/com/example/Spring_Boot/global/security/auth/CustomUserDetailsService.java @@ -0,0 +1,22 @@ +package com.example.Spring_Boot.global.security.auth; + +import com.example.Spring_Boot.domain.member.repository.MemberRepository; +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; + +@Service +@RequiredArgsConstructor +public class CustomUserDetailsService implements UserDetailsService { + + private final MemberRepository memberRepository; + + @Override + public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { + return memberRepository.findByEmail(email) + .map(AuthMember::new) + .orElseThrow(() -> new UsernameNotFoundException("회원을 찾을 수 없습니다.")); + } +} From 47e1ec4484218ca09f707a383e14ad918560f534 Mon Sep 17 00:00:00 2001 From: issuejong Date: Tue, 26 May 2026 15:58:10 +0900 Subject: [PATCH 3/3] =?UTF-8?q?mission/#08:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handler/GeneralExceptionAdvice.java | 22 ++++++++++++++++++ .../handler/CustomAccessDeniedHandler.java | 23 +++++++++---------- .../CustomAuthenticationEntryPoint.java | 23 +++++++++---------- 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/example/Spring_Boot/global/apiPayload/handler/GeneralExceptionAdvice.java b/src/main/java/com/example/Spring_Boot/global/apiPayload/handler/GeneralExceptionAdvice.java index 5303c31..985ac0a 100644 --- a/src/main/java/com/example/Spring_Boot/global/apiPayload/handler/GeneralExceptionAdvice.java +++ b/src/main/java/com/example/Spring_Boot/global/apiPayload/handler/GeneralExceptionAdvice.java @@ -5,6 +5,8 @@ import com.example.Spring_Boot.global.apiPayload.code.GeneralErrorCode; import com.example.Spring_Boot.global.apiPayload.exception.ProjectException; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.AuthenticationException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -15,6 +17,26 @@ @RestControllerAdvice public class GeneralExceptionAdvice { + // Spring Security 인증 실패 예외 처리 + @ExceptionHandler(AuthenticationException.class) + public ResponseEntity> handleAuthenticationException( + AuthenticationException e + ) { + BaseErrorCode code = GeneralErrorCode.UNAUTHORIZED; + return ResponseEntity.status(code.getStatus()) + .body(ApiResponse.onFailure(code, null)); + } + + // Spring Security 인가 실패 예외 처리 + @ExceptionHandler(AccessDeniedException.class) + public ResponseEntity> handleAccessDeniedException( + AccessDeniedException e + ) { + BaseErrorCode code = GeneralErrorCode.FORBIDDEN; + return ResponseEntity.status(code.getStatus()) + .body(ApiResponse.onFailure(code, null)); + } + // @Valid 어노테이션 검증 실패 예외 처리 @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity>> handleMethodArgumentNotValidException( diff --git a/src/main/java/com/example/Spring_Boot/global/security/handler/CustomAccessDeniedHandler.java b/src/main/java/com/example/Spring_Boot/global/security/handler/CustomAccessDeniedHandler.java index f010c50..fb0b2ac 100644 --- a/src/main/java/com/example/Spring_Boot/global/security/handler/CustomAccessDeniedHandler.java +++ b/src/main/java/com/example/Spring_Boot/global/security/handler/CustomAccessDeniedHandler.java @@ -1,33 +1,32 @@ package com.example.Spring_Boot.global.security.handler; -import com.example.Spring_Boot.global.apiPayload.ApiResponse; -import com.example.Spring_Boot.global.apiPayload.code.BaseErrorCode; -import com.example.Spring_Boot.global.apiPayload.code.GeneralErrorCode; -import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerExceptionResolver; import java.io.IOException; @Component public class CustomAccessDeniedHandler implements AccessDeniedHandler { + private final HandlerExceptionResolver handlerExceptionResolver; + + public CustomAccessDeniedHandler( + @Qualifier("handlerExceptionResolver") HandlerExceptionResolver handlerExceptionResolver + ) { + this.handlerExceptionResolver = handlerExceptionResolver; + } + @Override public void handle( HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException ) throws IOException { - ObjectMapper objectMapper = new ObjectMapper(); - BaseErrorCode code = GeneralErrorCode.FORBIDDEN; - - response.setContentType("application/json;charset=UTF-8"); - response.setStatus(code.getStatus().value()); - - ApiResponse errorResponse = ApiResponse.onFailure(code, null); - objectMapper.writeValue(response.getOutputStream(), errorResponse); + handlerExceptionResolver.resolveException(request, response, null, accessDeniedException); } } diff --git a/src/main/java/com/example/Spring_Boot/global/security/handler/CustomAuthenticationEntryPoint.java b/src/main/java/com/example/Spring_Boot/global/security/handler/CustomAuthenticationEntryPoint.java index bfd9f9c..546e538 100644 --- a/src/main/java/com/example/Spring_Boot/global/security/handler/CustomAuthenticationEntryPoint.java +++ b/src/main/java/com/example/Spring_Boot/global/security/handler/CustomAuthenticationEntryPoint.java @@ -1,33 +1,32 @@ package com.example.Spring_Boot.global.security.handler; -import com.example.Spring_Boot.global.apiPayload.ApiResponse; -import com.example.Spring_Boot.global.apiPayload.code.BaseErrorCode; -import com.example.Spring_Boot.global.apiPayload.code.GeneralErrorCode; -import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerExceptionResolver; import java.io.IOException; @Component public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { + private final HandlerExceptionResolver handlerExceptionResolver; + + public CustomAuthenticationEntryPoint( + @Qualifier("handlerExceptionResolver") HandlerExceptionResolver handlerExceptionResolver + ) { + this.handlerExceptionResolver = handlerExceptionResolver; + } + @Override public void commence( HttpServletRequest request, HttpServletResponse response, AuthenticationException authException ) throws IOException { - ObjectMapper objectMapper = new ObjectMapper(); - BaseErrorCode code = GeneralErrorCode.UNAUTHORIZED; - - response.setContentType("application/json;charset=UTF-8"); - response.setStatus(code.getStatus().value()); - - ApiResponse errorResponse = ApiResponse.onFailure(code, null); - objectMapper.writeValue(response.getOutputStream(), errorResponse); + handlerExceptionResolver.resolveException(request, response, null, authException); } }