[프로젝트 구조 세팅]
This commit is contained in:
		@@ -1,26 +1,17 @@
 | 
			
		||||
package com.bio.bio_backend.domain.user.member.controller;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import org.modelmapper.ModelMapper;
 | 
			
		||||
import org.springframework.http.HttpStatus;
 | 
			
		||||
import org.springframework.http.ResponseEntity;
 | 
			
		||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
 | 
			
		||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 | 
			
		||||
import org.springframework.web.bind.annotation.DeleteMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.GetMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.PathVariable;
 | 
			
		||||
import org.springframework.web.bind.annotation.PostMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.PutMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestBody;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
 | 
			
		||||
import jakarta.validation.Valid;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.dto.MemberDTO;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.dto.CreateMemberRequestDTO;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.dto.MemberDto;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.dto.CreateMemberRequestDto;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.dto.CreateMemberResponseDto;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.service.MemberService;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
@@ -42,20 +33,20 @@ public class MemberController {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @PostMapping("/join")
 | 
			
		||||
    public ResponseEntity<CreateMemberResponseDto> createMember(@RequestBody @Valid CreateMemberRequestDTO requestDto) {
 | 
			
		||||
    public ResponseEntity<Long> createMember(@RequestBody @Valid CreateMemberRequestDto requestDto) {
 | 
			
		||||
 | 
			
		||||
        // RequestMember를 MemberDTO로 변환
 | 
			
		||||
        MemberDTO member = new MemberDTO();
 | 
			
		||||
        MemberDto member = new MemberDto();
 | 
			
		||||
        member.setId(requestDto.getUserId());
 | 
			
		||||
        member.setPw(requestDto.getPassword());
 | 
			
		||||
 | 
			
		||||
        int oid = memberService.createMember(member);
 | 
			
		||||
        long oid = memberService.createMember(member);
 | 
			
		||||
        
 | 
			
		||||
        // 생성된 회원 정보를 조회하여 응답
 | 
			
		||||
        MemberDTO createdMember = memberService.selectMember(oid);
 | 
			
		||||
        CreateMemberResponseDto responseDto = mapper.map(createdMember, CreateMemberResponseDto.class);
 | 
			
		||||
        //MemberDto createdMember = memberService.selectMember(oid);
 | 
			
		||||
        //CreateMemberResponseDto responseDto = mapper.map(createdMember, CreateMemberResponseDto.class);
 | 
			
		||||
 | 
			
		||||
        return ResponseEntity.status(HttpStatus.CREATED).body(responseDto);
 | 
			
		||||
        return ResponseEntity.status(HttpStatus.CREATED).body(oid);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // @PostMapping("/member/list")
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ import lombok.Data;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@JsonInclude(JsonInclude.Include.NON_NULL)
 | 
			
		||||
public class CreateMemberRequestDTO {
 | 
			
		||||
public class CreateMemberRequestDto {
 | 
			
		||||
 | 
			
		||||
    @NotBlank(message = "사용자 ID는 필수입니다")
 | 
			
		||||
    private String userId;
 | 
			
		||||
@@ -0,0 +1,15 @@
 | 
			
		||||
package com.bio.bio_backend.domain.user.member.dto;
 | 
			
		||||
 | 
			
		||||
import jakarta.validation.constraints.NotNull;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
public class LoginRequestDto {
 | 
			
		||||
    @NotNull(message = "ID cannot be null")
 | 
			
		||||
    private String id;
 | 
			
		||||
 | 
			
		||||
    @NotNull(message = "Password cannot be null")
 | 
			
		||||
    private String pw;
 | 
			
		||||
 | 
			
		||||
    private int loginLogFlag;
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,24 @@
 | 
			
		||||
package com.bio.bio_backend.domain.user.member.dto;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonFormat;
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonInclude;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
import java.sql.Date;
 | 
			
		||||
import java.sql.Timestamp;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@JsonInclude(JsonInclude.Include.NON_NULL)
 | 
			
		||||
public class LoginResponseDto {
 | 
			
		||||
    private String id;
 | 
			
		||||
 | 
			
		||||
    private String role;
 | 
			
		||||
 | 
			
		||||
    private String status;
 | 
			
		||||
 | 
			
		||||
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul")
 | 
			
		||||
    private Date regAt;
 | 
			
		||||
 | 
			
		||||
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul")
 | 
			
		||||
    private Timestamp lastLoginAt;
 | 
			
		||||
}
 | 
			
		||||
@@ -16,7 +16,7 @@ import lombok.Data;
 | 
			
		||||
/**
 | 
			
		||||
 * 회원
 | 
			
		||||
 */
 | 
			
		||||
public class MemberDTO implements UserDetails {
 | 
			
		||||
public class MemberDto implements UserDetails {
 | 
			
		||||
	/**
 | 
			
		||||
	 * 시퀀스 (PK)
 | 
			
		||||
	 */
 | 
			
		||||
@@ -5,23 +5,23 @@ import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import org.apache.ibatis.annotations.Mapper;
 | 
			
		||||
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.dto.MemberDTO;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.dto.MemberDto;
 | 
			
		||||
 | 
			
		||||
@Mapper
 | 
			
		||||
public interface MemberMapper {
 | 
			
		||||
    int createMember(MemberDTO memberDTO);
 | 
			
		||||
    int createMember(MemberDto memberDTO);
 | 
			
		||||
 | 
			
		||||
    MemberDTO loadUserByUsername(String id);
 | 
			
		||||
    MemberDto loadUserByUsername(String id);
 | 
			
		||||
 | 
			
		||||
    void updateRefreshToken(MemberDTO memberDTO);
 | 
			
		||||
    void updateRefreshToken(MemberDto memberDTO);
 | 
			
		||||
 | 
			
		||||
    String getRefreshToken(String id);
 | 
			
		||||
 | 
			
		||||
    int deleteRefreshToken(String id);
 | 
			
		||||
 | 
			
		||||
    List<MemberDTO> selectMemberList(Map<String, String> params);
 | 
			
		||||
    List<MemberDto> selectMemberList(Map<String, String> params);
 | 
			
		||||
 | 
			
		||||
    MemberDTO selectMemberBySeq(int seq);
 | 
			
		||||
    MemberDto selectMemberBySeq(long seq);
 | 
			
		||||
 | 
			
		||||
    int updateMember(MemberDTO member);
 | 
			
		||||
    int updateMember(MemberDto member);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,25 +6,25 @@ import java.util.Map;
 | 
			
		||||
import org.springframework.security.core.userdetails.UserDetails;
 | 
			
		||||
import org.springframework.security.core.userdetails.UserDetailsService;
 | 
			
		||||
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.dto.MemberDTO;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.dto.MemberDto;
 | 
			
		||||
 | 
			
		||||
public interface MemberService extends UserDetailsService {
 | 
			
		||||
 | 
			
		||||
    UserDetails loadUserByUsername(String id);
 | 
			
		||||
 | 
			
		||||
    int createMember(MemberDTO memberDTO);
 | 
			
		||||
    long createMember(MemberDto memberDTO);
 | 
			
		||||
 | 
			
		||||
    void updateRefreshToken(MemberDTO memberDTO);
 | 
			
		||||
    void updateRefreshToken(MemberDto memberDTO);
 | 
			
		||||
 | 
			
		||||
    String getRefreshToken(String id);
 | 
			
		||||
 | 
			
		||||
    int deleteRefreshToken(String id);
 | 
			
		||||
 | 
			
		||||
    List<MemberDTO> selectMemberList(Map<String, String> params);
 | 
			
		||||
    List<MemberDto> selectMemberList(Map<String, String> params);
 | 
			
		||||
 | 
			
		||||
    MemberDTO selectMember(int seq);
 | 
			
		||||
    MemberDto selectMember(long seq);
 | 
			
		||||
 | 
			
		||||
    int updateMember(MemberDTO member);
 | 
			
		||||
    int updateMember(MemberDto member);
 | 
			
		||||
 | 
			
		||||
    int deleteMember(MemberDTO member);
 | 
			
		||||
    int deleteMember(MemberDto member);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
package com.bio.bio_backend.domain.user.member.service;
 | 
			
		||||
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.dto.MemberDTO;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.dto.MemberDto;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.entity.Member;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.mapper.MemberMapper;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.repository.MemberRepository;
 | 
			
		||||
@@ -27,7 +27,7 @@ public class MemberServiceImpl implements MemberService {
 | 
			
		||||
    @Override
 | 
			
		||||
    public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {
 | 
			
		||||
 | 
			
		||||
        MemberDTO member = memberMapper.loadUserByUsername(id);
 | 
			
		||||
        MemberDto member = memberMapper.loadUserByUsername(id);
 | 
			
		||||
 | 
			
		||||
        if (member == null) {
 | 
			
		||||
            throw new UsernameNotFoundException("User not found with id : " + id);
 | 
			
		||||
@@ -37,7 +37,7 @@ public class MemberServiceImpl implements MemberService {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int createMember(MemberDTO memberDTO) {
 | 
			
		||||
    public long createMember(MemberDto memberDTO) {
 | 
			
		||||
        // JPA Entity를 사용하여 회원 생성
 | 
			
		||||
        Member member = Member.builder()
 | 
			
		||||
                .userId(memberDTO.getId())
 | 
			
		||||
@@ -50,11 +50,11 @@ public class MemberServiceImpl implements MemberService {
 | 
			
		||||
        Member savedMember = memberRepository.save(member);
 | 
			
		||||
        
 | 
			
		||||
        // 저장된 회원의 oid를 반환
 | 
			
		||||
        return savedMember.getOid().intValue();
 | 
			
		||||
        return savedMember.getOid();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateRefreshToken(MemberDTO memberDTO) {
 | 
			
		||||
    public void updateRefreshToken(MemberDto memberDTO) {
 | 
			
		||||
        memberMapper.updateRefreshToken(memberDTO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -69,22 +69,40 @@ public class MemberServiceImpl implements MemberService {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<MemberDTO> selectMemberList(Map<String, String> params) {
 | 
			
		||||
    public List<MemberDto> selectMemberList(Map<String, String> params) {
 | 
			
		||||
        return memberMapper.selectMemberList(params);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MemberDTO selectMember(int seq) {
 | 
			
		||||
        return memberMapper.selectMemberBySeq(seq);
 | 
			
		||||
    public MemberDto selectMember(long seq) {
 | 
			
		||||
        // JPA 레파지토리를 사용하여 회원 조회
 | 
			
		||||
        Member member = memberRepository.findById(seq)
 | 
			
		||||
                .orElseThrow(() -> new RuntimeException("회원을 찾을 수 없습니다. seq: " + seq));
 | 
			
		||||
        
 | 
			
		||||
        // Member 엔티티를 MemberDto로 변환하여 반환
 | 
			
		||||
        MemberDto memberDto = new MemberDto();
 | 
			
		||||
        memberDto.setSeq(member.getOid().intValue());  // Long을 int로 안전하게 변환
 | 
			
		||||
        memberDto.setId(member.getUserId());
 | 
			
		||||
        memberDto.setPw(member.getPassword());
 | 
			
		||||
        memberDto.setRole(member.getRole());
 | 
			
		||||
        memberDto.setStatus(member.getStatus());
 | 
			
		||||
        memberDto.setRegAt(java.sql.Timestamp.valueOf(member.getCreatedAt()));  // LocalDateTime을 Timestamp로 변환
 | 
			
		||||
        memberDto.setUdtAt(java.sql.Timestamp.valueOf(member.getUpdatedAt()));
 | 
			
		||||
        if (member.getLastLoginAt() != null) {
 | 
			
		||||
            memberDto.setLastLoginAt(java.sql.Timestamp.valueOf(member.getLastLoginAt()));
 | 
			
		||||
        }
 | 
			
		||||
        memberDto.setRefreshToken(member.getRefreshToken());
 | 
			
		||||
        
 | 
			
		||||
        return memberDto;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int updateMember(MemberDTO member) {
 | 
			
		||||
    public int updateMember(MemberDto member) {
 | 
			
		||||
        return memberMapper.updateMember(member);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int deleteMember(MemberDTO member) {
 | 
			
		||||
    public int deleteMember(MemberDto member) {
 | 
			
		||||
 | 
			
		||||
        member.setStatus(MemberConstants.MEMBER_INACTIVE);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,26 @@
 | 
			
		||||
package com.bio.bio_backend.global.config;
 | 
			
		||||
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
import org.springframework.web.cors.CorsConfiguration;
 | 
			
		||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
 | 
			
		||||
import org.springframework.web.filter.CorsFilter;
 | 
			
		||||
 | 
			
		||||
@Configuration
 | 
			
		||||
public class CorsConfig {
 | 
			
		||||
    @Bean
 | 
			
		||||
    public CorsFilter corsFilter() {
 | 
			
		||||
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
 | 
			
		||||
        CorsConfiguration config = new CorsConfiguration();
 | 
			
		||||
        config.addAllowedOriginPattern("*");
 | 
			
		||||
        config.addAllowedHeader("*");
 | 
			
		||||
        config.addAllowedMethod("*");
 | 
			
		||||
        config.setAllowCredentials(true);
 | 
			
		||||
 | 
			
		||||
        config.addExposedHeader("Authorization");
 | 
			
		||||
        source.registerCorsConfiguration("/**", config);
 | 
			
		||||
 | 
			
		||||
        return new CorsFilter(source);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,39 @@
 | 
			
		||||
package com.bio.bio_backend.global.dto;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonInclude;
 | 
			
		||||
 | 
			
		||||
import com.bio.bio_backend.global.utils.ApiResponseCode;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
 | 
			
		||||
//공통 response Class
 | 
			
		||||
@Data
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
@JsonInclude(JsonInclude.Include.NON_NULL)
 | 
			
		||||
public class CustomApiResponse<T> {
 | 
			
		||||
 | 
			
		||||
    private int code;
 | 
			
		||||
    private String message;
 | 
			
		||||
    private String description;
 | 
			
		||||
    private T data;
 | 
			
		||||
 | 
			
		||||
    private static final int SUCCESS = 200;
 | 
			
		||||
 | 
			
		||||
    private CustomApiResponse(int code, String message, String description, T data){
 | 
			
		||||
        this.code = code;
 | 
			
		||||
        this.message = message;
 | 
			
		||||
        this.description = description;
 | 
			
		||||
        this.data = data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static <T> CustomApiResponse<T> success(ApiResponseCode responseCode, T data) {
 | 
			
		||||
        return new CustomApiResponse<T>(SUCCESS, responseCode.name(), responseCode.getDescription(), data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static <T> CustomApiResponse<T> fail(ApiResponseCode responseCode, T data) {
 | 
			
		||||
        return new CustomApiResponse<T>(responseCode.getStatusCode(), responseCode.name(), responseCode.getDescription(), data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -3,6 +3,7 @@ package com.bio.bio_backend.global.entity;
 | 
			
		||||
import jakarta.persistence.*;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import org.hibernate.annotations.GenericGenerator;
 | 
			
		||||
import org.springframework.data.annotation.CreatedDate;
 | 
			
		||||
import org.springframework.data.annotation.LastModifiedDate;
 | 
			
		||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 | 
			
		||||
@@ -24,7 +25,11 @@ public abstract class BaseEntity {
 | 
			
		||||
     * 자동 증가하는 Long 타입으로 설정
 | 
			
		||||
     */
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @GeneratedValue(generator = "customOidGenerator")
 | 
			
		||||
    @GenericGenerator(
 | 
			
		||||
        name = "customOidGenerator",
 | 
			
		||||
        type = com.bio.bio_backend.global.utils.CustomIdGenerator.class
 | 
			
		||||
    )
 | 
			
		||||
    @Column(name = "oid", nullable = false)
 | 
			
		||||
    private Long oid;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,53 @@
 | 
			
		||||
package com.bio.bio_backend.global.exception;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
import org.springframework.http.HttpStatus;
 | 
			
		||||
import org.springframework.http.MediaType;
 | 
			
		||||
import org.springframework.security.authentication.BadCredentialsException;
 | 
			
		||||
import org.springframework.security.core.AuthenticationException;
 | 
			
		||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
 | 
			
		||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
 | 
			
		||||
import jakarta.servlet.ServletException;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import jakarta.servlet.http.HttpServletResponse;
 | 
			
		||||
import com.bio.bio_backend.global.dto.CustomApiResponse;
 | 
			
		||||
import com.bio.bio_backend.global.utils.ApiResponseCode;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
 | 
			
		||||
 | 
			
		||||
    //Spring Security login -> filter 수행 -> ExceptionHandler 적용불가로 별도 FailureHandler 를 통하여 Error 응답 처리
 | 
			
		||||
 | 
			
		||||
    private final ObjectMapper objectMapper;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
 | 
			
		||||
 | 
			
		||||
        log.info("exception : " + exception.toString());
 | 
			
		||||
 | 
			
		||||
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
 | 
			
		||||
        response.setCharacterEncoding("UTF-8");
 | 
			
		||||
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
 | 
			
		||||
 | 
			
		||||
        CustomApiResponse<String> apiResponse;
 | 
			
		||||
        if (exception instanceof UsernameNotFoundException) {
 | 
			
		||||
            apiResponse = CustomApiResponse.fail(ApiResponseCode.USER_NOT_FOUND, null);
 | 
			
		||||
        } else if (exception instanceof BadCredentialsException) {
 | 
			
		||||
            apiResponse = CustomApiResponse.fail(ApiResponseCode.AUTHENTICATION_FAILED, null);
 | 
			
		||||
        } else {
 | 
			
		||||
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
 | 
			
		||||
            apiResponse = CustomApiResponse.fail(ApiResponseCode.INTERNAL_SERVER_ERROR, null);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        String jsonResponse = objectMapper.writeValueAsString(apiResponse);
 | 
			
		||||
        response.getWriter().write(jsonResponse);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,74 @@
 | 
			
		||||
package com.bio.bio_backend.global.exception;
 | 
			
		||||
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
import org.springframework.http.converter.HttpMessageNotReadableException;
 | 
			
		||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
 | 
			
		||||
import org.springframework.web.bind.annotation.ExceptionHandler;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
 | 
			
		||||
import org.springframework.web.servlet.resource.NoResourceFoundException;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.core.JsonProcessingException;
 | 
			
		||||
 | 
			
		||||
import io.jsonwebtoken.ExpiredJwtException;
 | 
			
		||||
import io.jsonwebtoken.JwtException;
 | 
			
		||||
import io.jsonwebtoken.MalformedJwtException;
 | 
			
		||||
import io.jsonwebtoken.security.SignatureException;
 | 
			
		||||
import com.bio.bio_backend.global.dto.CustomApiResponse;
 | 
			
		||||
import com.bio.bio_backend.global.utils.ApiResponseCode;
 | 
			
		||||
 | 
			
		||||
@RestControllerAdvice
 | 
			
		||||
public class GlobalExceptionHandler {
 | 
			
		||||
    @ExceptionHandler(HttpMessageNotReadableException.class)
 | 
			
		||||
    public CustomApiResponse<Void> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
 | 
			
		||||
        if (Objects.requireNonNull(e.getMessage()).contains("JSON parse error")) {
 | 
			
		||||
            return CustomApiResponse.fail(ApiResponseCode.COMMON_FORMAT_WRONG, null);
 | 
			
		||||
        }
 | 
			
		||||
        return CustomApiResponse.fail(ApiResponseCode.COMMON_BAD_REQUEST, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
 | 
			
		||||
    public CustomApiResponse<Void> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
 | 
			
		||||
        return CustomApiResponse.fail(ApiResponseCode.COMMON_METHOD_NOT_ALLOWED, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ExceptionHandler(NoResourceFoundException.class)
 | 
			
		||||
    public CustomApiResponse<Void> handleNoResourceFoundException(NoResourceFoundException e){
 | 
			
		||||
        return CustomApiResponse.fail(ApiResponseCode.COMMON_NOT_FOUND, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ExceptionHandler(IllegalArgumentException.class)
 | 
			
		||||
    public CustomApiResponse<Void> handleIllegalArgumentException(IllegalArgumentException e){
 | 
			
		||||
        return CustomApiResponse.fail(ApiResponseCode.ARGUMENT_NOT_VALID, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ExceptionHandler(IndexOutOfBoundsException.class)
 | 
			
		||||
    public CustomApiResponse<Void> handleIndexOutOfBoundsException(IndexOutOfBoundsException e){
 | 
			
		||||
        return CustomApiResponse.fail(ApiResponseCode.INDEX_OUT_OF_BOUND, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ExceptionHandler(SignatureException.class)
 | 
			
		||||
    public CustomApiResponse<Void> handleSignatureException(SignatureException e) {
 | 
			
		||||
        return CustomApiResponse.fail(ApiResponseCode.JWT_SIGNATURE_MISMATCH, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ExceptionHandler(MalformedJwtException.class)
 | 
			
		||||
    public CustomApiResponse<Void> handleMalformedJwtException(MalformedJwtException e) {
 | 
			
		||||
        return CustomApiResponse.fail(ApiResponseCode.JWT_SIGNATURE_MISMATCH, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ExceptionHandler(JwtException.class)
 | 
			
		||||
    public CustomApiResponse<Void> handleJwtExceptionException(JwtException e) {
 | 
			
		||||
        return CustomApiResponse.fail(ApiResponseCode.JWT_TOKEN_NULL, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ExceptionHandler(ExpiredJwtException.class)
 | 
			
		||||
    public CustomApiResponse<Void> handleExpiredJwtException(ExpiredJwtException e) {
 | 
			
		||||
        return CustomApiResponse.fail(ApiResponseCode.JWT_TOKEN_EXPIRED, null);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    @ExceptionHandler(JsonProcessingException.class)
 | 
			
		||||
    public CustomApiResponse<Void> handleExpiredJwtException(JsonProcessingException e) {
 | 
			
		||||
        return CustomApiResponse.fail(ApiResponseCode.JSON_PROCESSING_EXCEPTION, null);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,42 @@
 | 
			
		||||
package com.bio.bio_backend.global.exception;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
import org.springframework.beans.factory.annotation.Qualifier;
 | 
			
		||||
import org.springframework.http.MediaType;
 | 
			
		||||
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 com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
 | 
			
		||||
import jakarta.servlet.ServletException;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import jakarta.servlet.http.HttpServletResponse;
 | 
			
		||||
import com.bio.bio_backend.global.dto.CustomApiResponse;
 | 
			
		||||
import com.bio.bio_backend.global.utils.ApiResponseCode;
 | 
			
		||||
 | 
			
		||||
@Component
 | 
			
		||||
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
 | 
			
		||||
    private final HandlerExceptionResolver resolver;
 | 
			
		||||
 | 
			
		||||
    public JwtAccessDeniedHandler(@Qualifier("handlerExceptionResolver") HandlerExceptionResolver resolver) {
 | 
			
		||||
        this.resolver = resolver;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
 | 
			
		||||
 | 
			
		||||
        Exception exception = (Exception) request.getAttribute("exception");
 | 
			
		||||
 | 
			
		||||
        if (exception != null) {
 | 
			
		||||
            resolver.resolveException(request, response, null, exception);
 | 
			
		||||
        } else {
 | 
			
		||||
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
 | 
			
		||||
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
 | 
			
		||||
            new ObjectMapper().writeValue(response.getWriter(),
 | 
			
		||||
                    CustomApiResponse.fail(ApiResponseCode.COMMON_FORBIDDEN, null));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,35 @@
 | 
			
		||||
package com.bio.bio_backend.global.exception;
 | 
			
		||||
 | 
			
		||||
import jakarta.servlet.ServletException;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import jakarta.servlet.http.HttpServletResponse;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
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
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
 | 
			
		||||
    private final HandlerExceptionResolver resolver;
 | 
			
		||||
 | 
			
		||||
    public JwtAuthenticationEntryPoint(@Qualifier("handlerExceptionResolver") HandlerExceptionResolver resolver) {
 | 
			
		||||
        this.resolver = resolver;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
 | 
			
		||||
 | 
			
		||||
        Exception exception = (Exception) request.getAttribute("exception");
 | 
			
		||||
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
 | 
			
		||||
 | 
			
		||||
        if (exception == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        resolver.resolveException(request, response, null, exception);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,57 @@
 | 
			
		||||
//package com.bio.bio_backend.filter;
 | 
			
		||||
//
 | 
			
		||||
//import java.io.IOException;
 | 
			
		||||
//import java.sql.Timestamp;
 | 
			
		||||
//
 | 
			
		||||
//import org.springframework.security.core.Authentication;
 | 
			
		||||
//import org.springframework.security.core.context.SecurityContextHolder;
 | 
			
		||||
//import org.springframework.web.filter.OncePerRequestFilter;
 | 
			
		||||
//
 | 
			
		||||
//import jakarta.servlet.FilterChain;
 | 
			
		||||
//import jakarta.servlet.ServletException;
 | 
			
		||||
//import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
//import jakarta.servlet.http.HttpServletResponse;
 | 
			
		||||
//import com.bio.bio_backend.domain.common.dto.AccessLogDTO;
 | 
			
		||||
//import com.bio.bio_backend.domain.common.service.AccessLogService;
 | 
			
		||||
//import com.bio.bio_backend.domain.user.member.dto.MemberDTO;
 | 
			
		||||
//import com.bio.bio_backend.global.utils.HttpUtils;
 | 
			
		||||
//
 | 
			
		||||
//public class HttpLoggingFilter extends OncePerRequestFilter {
 | 
			
		||||
//
 | 
			
		||||
////	private AccessLogService accessLogService;
 | 
			
		||||
//	private HttpUtils httpUtils;
 | 
			
		||||
//
 | 
			
		||||
//	public HttpLoggingFilter(AccessLogService accessLogService, HttpUtils httpUtils) {
 | 
			
		||||
//		this.accessLogService = accessLogService;
 | 
			
		||||
//		this.httpUtils = httpUtils;
 | 
			
		||||
//	}
 | 
			
		||||
//
 | 
			
		||||
//	@Override
 | 
			
		||||
//	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
 | 
			
		||||
//			throws ServletException, IOException {
 | 
			
		||||
//		// Request 요청 시간
 | 
			
		||||
//		Long startedAt = System.currentTimeMillis();
 | 
			
		||||
//		filterChain.doFilter(request, response);
 | 
			
		||||
//		Long finishedAt = System.currentTimeMillis();
 | 
			
		||||
//
 | 
			
		||||
//		Authentication auth = SecurityContextHolder.getContext().getAuthentication();
 | 
			
		||||
//		MemberDTO member = null;
 | 
			
		||||
//		if(auth != null && auth.getPrincipal() instanceof MemberDTO) {
 | 
			
		||||
//			member = (MemberDTO) auth.getPrincipal();
 | 
			
		||||
//		}
 | 
			
		||||
//
 | 
			
		||||
//		AccessLogDTO log = new AccessLogDTO();
 | 
			
		||||
//		log.setMbrSeq(member == null ? -1 : member.getSeq());
 | 
			
		||||
//		log.setType(httpUtils.getResponseType(response.getContentType()));
 | 
			
		||||
//		log.setMethod(request.getMethod());
 | 
			
		||||
//		log.setIp(httpUtils.getClientIp());
 | 
			
		||||
//		log.setUri(request.getRequestURI());
 | 
			
		||||
//		log.setReqAt(new Timestamp(startedAt));
 | 
			
		||||
//		log.setResAt(new Timestamp(finishedAt));
 | 
			
		||||
//		log.setElapsedTime(finishedAt - startedAt);
 | 
			
		||||
//		log.setResStatus(String.valueOf(response.getStatus()));
 | 
			
		||||
//
 | 
			
		||||
//		accessLogService.createAccessLog(log);
 | 
			
		||||
//	}
 | 
			
		||||
//
 | 
			
		||||
//}
 | 
			
		||||
@@ -0,0 +1,120 @@
 | 
			
		||||
package com.bio.bio_backend.global.security;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
import jakarta.servlet.FilterChain;
 | 
			
		||||
import jakarta.servlet.ServletException;
 | 
			
		||||
import jakarta.servlet.http.Cookie;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import jakarta.servlet.http.HttpServletResponse;
 | 
			
		||||
import com.bio.bio_backend.global.dto.CustomApiResponse;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.dto.LoginRequestDto;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.dto.LoginResponseDto;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.dto.MemberDto;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.service.MemberService;
 | 
			
		||||
import com.bio.bio_backend.global.utils.ApiResponseCode;
 | 
			
		||||
import com.bio.bio_backend.global.utils.JwtUtils;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.SneakyThrows;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.modelmapper.ModelMapper;
 | 
			
		||||
import org.springframework.core.env.Environment;
 | 
			
		||||
import org.springframework.http.HttpStatus;
 | 
			
		||||
import org.springframework.http.MediaType;
 | 
			
		||||
import org.springframework.security.authentication.AuthenticationManager;
 | 
			
		||||
import org.springframework.security.authentication.AuthenticationServiceException;
 | 
			
		||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 | 
			
		||||
import org.springframework.security.core.Authentication;
 | 
			
		||||
import org.springframework.security.core.AuthenticationException;
 | 
			
		||||
import org.springframework.security.core.context.SecurityContext;
 | 
			
		||||
import org.springframework.security.core.context.SecurityContextHolder;
 | 
			
		||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
 | 
			
		||||
import org.springframework.security.core.userdetails.UserDetails;
 | 
			
		||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.sql.Timestamp;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
 | 
			
		||||
 | 
			
		||||
    private final AuthenticationManager authenticationManager;
 | 
			
		||||
    private final MemberService memberService;
 | 
			
		||||
    private final ModelMapper modelMapper;
 | 
			
		||||
    private final JwtUtils jwtUtils;
 | 
			
		||||
    private final Environment env;
 | 
			
		||||
 | 
			
		||||
    // 사용자 login 인증 처리
 | 
			
		||||
    @SneakyThrows
 | 
			
		||||
    @Override
 | 
			
		||||
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
 | 
			
		||||
            throws AuthenticationException {
 | 
			
		||||
 | 
			
		||||
        LoginRequestDto req = new ObjectMapper().readValue(request.getInputStream(), LoginRequestDto.class);
 | 
			
		||||
 | 
			
		||||
//        UsernamePasswordAuthenticationToken authToken;
 | 
			
		||||
        UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(req.getId(), req.getPw());
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        if (req.getLoginLogFlag() == 1) {
 | 
			
		||||
            authToken = new UsernamePasswordAuthenticationToken("admin2", "test123!"); // 비밀번호는 실제 비밀번호로 설정해야 함
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new AuthenticationServiceException("login fail");
 | 
			
		||||
        }
 | 
			
		||||
         */
 | 
			
		||||
        return authenticationManager.authenticate(authToken);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 사용자 인증 성공 후 token 발급
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
 | 
			
		||||
                                            FilterChain chain, Authentication authResult) throws IOException, ServletException {
 | 
			
		||||
 | 
			
		||||
        UserDetails userDetails = (UserDetails) authResult.getPrincipal();
 | 
			
		||||
 | 
			
		||||
        MemberDto member = (MemberDto) userDetails;
 | 
			
		||||
        
 | 
			
		||||
        String accessToken = jwtUtils.generateToken(userDetails.getUsername(), member.getRole(),
 | 
			
		||||
                Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_access"))));
 | 
			
		||||
 | 
			
		||||
        String refreshToken = jwtUtils.generateToken(userDetails.getUsername(), member.getRole(),
 | 
			
		||||
                Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_refresh"))));
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        member.setRefreshToken(refreshToken);
 | 
			
		||||
        member.setLastLoginAt(Timestamp.valueOf(LocalDateTime.now()));
 | 
			
		||||
 | 
			
		||||
        memberService.updateRefreshToken(member);
 | 
			
		||||
 | 
			
		||||
        // Refresh 토큰 쿠키 저장
 | 
			
		||||
        Cookie refreshTokenCookie = new Cookie("RefreshToken", refreshToken);
 | 
			
		||||
        refreshTokenCookie.setHttpOnly(true);
 | 
			
		||||
        refreshTokenCookie.setSecure(false);
 | 
			
		||||
        refreshTokenCookie.setPath("/");
 | 
			
		||||
        refreshTokenCookie.setMaxAge(Integer.parseInt(env.getProperty("token.expiration_time_refresh")));
 | 
			
		||||
 | 
			
		||||
        // JWT 토큰 전달
 | 
			
		||||
        response.setHeader("Authorization", "Bearer " + accessToken);
 | 
			
		||||
//        response.addCookie(refreshTokenCookie);
 | 
			
		||||
        response.addHeader("Set-Cookie", 
 | 
			
		||||
        	    String.format("%s=%s; HttpOnly; Secure; Path=/; Max-Age=%d; SameSite=None", 
 | 
			
		||||
        	    refreshTokenCookie.getName(), 
 | 
			
		||||
        	    refreshTokenCookie.getValue(), 
 | 
			
		||||
        	    refreshTokenCookie.getMaxAge()));
 | 
			
		||||
 | 
			
		||||
        SecurityContextHolderStrategy contextHolder = SecurityContextHolder.getContextHolderStrategy();
 | 
			
		||||
        SecurityContext context = contextHolder.createEmptyContext();
 | 
			
		||||
        context.setAuthentication(authResult);
 | 
			
		||||
        contextHolder.setContext(context);
 | 
			
		||||
 | 
			
		||||
        LoginResponseDto memberData = modelMapper.map(member, LoginResponseDto.class);
 | 
			
		||||
 | 
			
		||||
        // login 성공 메시지 전송
 | 
			
		||||
        response.setStatus(HttpStatus.OK.value());
 | 
			
		||||
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
 | 
			
		||||
        new ObjectMapper().writeValue(response.getWriter(),
 | 
			
		||||
                CustomApiResponse.success(ApiResponseCode.LOGIN_SUCCESSFUL, memberData));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,104 @@
 | 
			
		||||
package com.bio.bio_backend.global.security;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.core.env.Environment;
 | 
			
		||||
import org.springframework.http.MediaType;
 | 
			
		||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 | 
			
		||||
import org.springframework.security.core.context.SecurityContextHolder;
 | 
			
		||||
import org.springframework.security.core.userdetails.UserDetails;
 | 
			
		||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
 | 
			
		||||
import org.springframework.web.filter.OncePerRequestFilter;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
 | 
			
		||||
import io.jsonwebtoken.ExpiredJwtException;
 | 
			
		||||
import jakarta.servlet.FilterChain;
 | 
			
		||||
import jakarta.servlet.ServletException;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import jakarta.servlet.http.HttpServletResponse;
 | 
			
		||||
import com.bio.bio_backend.global.dto.CustomApiResponse;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.service.MemberService;
 | 
			
		||||
import com.bio.bio_backend.global.utils.ApiResponseCode;
 | 
			
		||||
import com.bio.bio_backend.global.utils.JwtUtils;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class JwtTokenFilter extends OncePerRequestFilter {
 | 
			
		||||
 | 
			
		||||
    private final JwtUtils jwtUtils;
 | 
			
		||||
    private final MemberService memberService;
 | 
			
		||||
    private final Environment env;
 | 
			
		||||
 | 
			
		||||
    private final UriAllowFilter uriAllowFilter;
 | 
			
		||||
    
 | 
			
		||||
    @Override
 | 
			
		||||
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
 | 
			
		||||
    	return uriAllowFilter.authExceptionAllow(request);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
 | 
			
		||||
                                    FilterChain filterChain) throws ServletException, IOException {
 | 
			
		||||
 | 
			
		||||
        String accessToken = jwtUtils.extractAccessJwtFromRequest(request);
 | 
			
		||||
        String refreshToken = jwtUtils.extractRefreshJwtFromCookie(request);
 | 
			
		||||
 | 
			
		||||
        if(accessToken == null){
 | 
			
		||||
            sendJsonResponse(response, CustomApiResponse.fail(ApiResponseCode.JWT_TOKEN_NULL, null));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // 토큰 유효성 검사
 | 
			
		||||
            try {
 | 
			
		||||
                if (jwtUtils.validateAccessToken(accessToken)) {
 | 
			
		||||
                    String username = jwtUtils.extractUsername(accessToken);
 | 
			
		||||
                    UserDetails userDetails = memberService.loadUserByUsername(username);
 | 
			
		||||
 | 
			
		||||
                    if (userDetails != null) {
 | 
			
		||||
                        UsernamePasswordAuthenticationToken authentication =
 | 
			
		||||
                                new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
 | 
			
		||||
                        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
 | 
			
		||||
                        SecurityContextHolder.getContext().setAuthentication(authentication);
 | 
			
		||||
                        filterChain.doFilter(request, response);
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } catch (ExpiredJwtException ignored) {
 | 
			
		||||
                // Access Token이 만료된 경우에만 ignored >> Refresh Token 검증 수행
 | 
			
		||||
            }
 | 
			
		||||
            // Refresh Token 유효성 검사
 | 
			
		||||
            if (refreshToken != null && jwtUtils.validateRefreshToken(refreshToken)) {
 | 
			
		||||
                String username = jwtUtils.extractUsername(refreshToken);
 | 
			
		||||
                String role = (String) jwtUtils.extractAllClaims(refreshToken).get("role");
 | 
			
		||||
                String newAccessToken = jwtUtils.generateToken(username, role,
 | 
			
		||||
                        Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_access"))));
 | 
			
		||||
                // 새로운 Access Token을 응답 헤더에 설정
 | 
			
		||||
                response.setHeader("Authorization", "Bearer " + newAccessToken);
 | 
			
		||||
                filterChain.doFilter(request, response);
 | 
			
		||||
            } else {
 | 
			
		||||
                sendJsonResponse(response, CustomApiResponse.fail(ApiResponseCode.All_TOKEN_INVALID, null));
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            request.setAttribute("exception", e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        filterChain.doFilter(request, response);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void sendJsonResponse(HttpServletResponse response, CustomApiResponse<?> apiResponse) throws IOException {
 | 
			
		||||
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
 | 
			
		||||
        response.setCharacterEncoding("UTF-8");
 | 
			
		||||
        response.setStatus(apiResponse.getCode());
 | 
			
		||||
 | 
			
		||||
        ObjectMapper objectMapper = new ObjectMapper();
 | 
			
		||||
        String jsonResponse = objectMapper.writeValueAsString(apiResponse);
 | 
			
		||||
        response.getWriter().write(jsonResponse);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
package com.bio.bio_backend.global.security;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
 | 
			
		||||
@Component
 | 
			
		||||
public class UriAllowFilter {
 | 
			
		||||
	
 | 
			
		||||
	@Autowired
 | 
			
		||||
//	ProgramService programService;
 | 
			
		||||
		
 | 
			
		||||
	public boolean authExceptionAllow(HttpServletRequest request)	{
 | 
			
		||||
		// 임시로 모든 요청 허용
 | 
			
		||||
		return true;
 | 
			
		||||
		
 | 
			
		||||
		// String[] allowUrl = programService.getAuthException();
 | 
			
		||||
		// boolean success = Arrays.stream(allowUrl).anyMatch(pattern -> matches(pattern, request.getRequestURI()));
 | 
			
		||||
        // return success;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
    boolean matches(String pattern, String uri) { 
 | 
			
		||||
        String regex = pattern.replace("/**", "(/.*)?").replace("*", ".*"); 
 | 
			
		||||
        return Pattern.matches(regex, uri);
 | 
			
		||||
    }    
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,104 @@
 | 
			
		||||
package com.bio.bio_backend.global.security;
 | 
			
		||||
 | 
			
		||||
import org.modelmapper.ModelMapper;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
import org.springframework.core.env.Environment;
 | 
			
		||||
import org.springframework.security.authentication.AuthenticationManager;
 | 
			
		||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 | 
			
		||||
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.web.SecurityFilterChain;
 | 
			
		||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 | 
			
		||||
import org.springframework.web.filter.CorsFilter;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
 | 
			
		||||
import jakarta.servlet.Filter;
 | 
			
		||||
//import com.bio.bio_backend.domain.common.service.AccessLogService;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.service.MemberService;
 | 
			
		||||
import com.bio.bio_backend.global.constants.MemberConstants;
 | 
			
		||||
import com.bio.bio_backend.global.exception.CustomAuthenticationFailureHandler;
 | 
			
		||||
import com.bio.bio_backend.global.exception.JwtAccessDeniedHandler;
 | 
			
		||||
import com.bio.bio_backend.global.exception.JwtAuthenticationEntryPoint;
 | 
			
		||||
//import com.bio.bio_backend.global.filter.HttpLoggingFilter;
 | 
			
		||||
import com.bio.bio_backend.global.utils.HttpUtils;
 | 
			
		||||
import com.bio.bio_backend.global.utils.JwtUtils;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
 | 
			
		||||
@Configuration
 | 
			
		||||
@EnableWebSecurity
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
public class WebSecurity {
 | 
			
		||||
 | 
			
		||||
    private final MemberService memberService;
 | 
			
		||||
    private final BCryptPasswordEncoder bCryptPasswordEncoder;
 | 
			
		||||
    private final JwtUtils jwtUtils;
 | 
			
		||||
//    private final AccessLogService accessLogService;
 | 
			
		||||
    private final CorsFilter corsFilter;
 | 
			
		||||
    private final ModelMapper mapper;
 | 
			
		||||
    private final ObjectMapper objectMapper;
 | 
			
		||||
    private final HttpUtils httpUtils;
 | 
			
		||||
    private final Environment env;
 | 
			
		||||
    private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
 | 
			
		||||
    private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
 | 
			
		||||
    
 | 
			
		||||
    private final UriAllowFilter uriAllowFilter;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    private JwtAuthenticationFilter getJwtAuthenticationFilter(AuthenticationManager authenticationManager) throws Exception {
 | 
			
		||||
        JwtAuthenticationFilter filter = new JwtAuthenticationFilter(authenticationManager, memberService, mapper, jwtUtils, env);
 | 
			
		||||
        filter.setFilterProcessesUrl("/login"); // 로그인 EndPoint
 | 
			
		||||
        filter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler(objectMapper));
 | 
			
		||||
        return filter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Filter getJwtTokenFilter() {
 | 
			
		||||
        return new JwtTokenFilter(jwtUtils, memberService, env, uriAllowFilter);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    @Bean
 | 
			
		||||
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
 | 
			
		||||
 | 
			
		||||
        // AuthenticationManager 설정
 | 
			
		||||
        AuthenticationManagerBuilder authenticationManagerBuilder =
 | 
			
		||||
                http.getSharedObject(AuthenticationManagerBuilder.class);
 | 
			
		||||
        authenticationManagerBuilder.userDetailsService(memberService).passwordEncoder(bCryptPasswordEncoder);
 | 
			
		||||
 | 
			
		||||
        AuthenticationManager authenticationManager = authenticationManagerBuilder.build();
 | 
			
		||||
 | 
			
		||||
        http.csrf(AbstractHttpConfigurer::disable)          //csrf 비활성화
 | 
			
		||||
                .authorizeHttpRequests(request ->               //request 허용 설정
 | 
			
		||||
                        request
 | 
			
		||||
                        	.anyRequest().permitAll()  // 모든 요청 허용
 | 
			
		||||
//                    		.requestMatchers("/ws/**").permitAll()
 | 
			
		||||
//                            .requestMatchers("/admin/**", "/join").hasAnyAuthority(MemberConstants.ROLE_ADMIN)
 | 
			
		||||
//                            .requestMatchers("/member/**").hasAnyAuthority(MemberConstants.ROLE_MEMBER)
 | 
			
		||||
//                            .anyRequest().authenticated()
 | 
			
		||||
                )
 | 
			
		||||
                .authenticationManager(authenticationManager)
 | 
			
		||||
                .logout(AbstractHttpConfigurer::disable);
 | 
			
		||||
 | 
			
		||||
        // 예외 처리 핸들링
 | 
			
		||||
        http.exceptionHandling((exceptionConfig) ->
 | 
			
		||||
                exceptionConfig
 | 
			
		||||
                        .authenticationEntryPoint(jwtAuthenticationEntryPoint)
 | 
			
		||||
                        .accessDeniedHandler(jwtAccessDeniedHandler)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
//        http
 | 
			
		||||
//        		.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
 | 
			
		||||
//                .addFilterBefore(new HttpLoggingFilter(accessLogService, httpUtils), UsernamePasswordAuthenticationFilter.class)
 | 
			
		||||
//                .addFilterBefore(getJwtAuthenticationFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class)
 | 
			
		||||
//                .addFilterBefore(getJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//                .sessionManagement(httpSecuritySessionManagementConfigurer ->       //Session 사용 X
 | 
			
		||||
//                        httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return http.build();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,71 @@
 | 
			
		||||
package com.bio.bio_backend.global.utils;
 | 
			
		||||
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import org.springframework.http.HttpStatus;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * API 관련 RESPONSE ENUM
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@Getter
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public enum ApiResponseCode {
 | 
			
		||||
 | 
			
		||||
    /*login & logout*/
 | 
			
		||||
 | 
			
		||||
    // 200 OK
 | 
			
		||||
    LOGIN_SUCCESSFUL(HttpStatus.OK.value(), "Login successful"),
 | 
			
		||||
 | 
			
		||||
    LOGOUT_SUCCESSFUL(HttpStatus.OK.value(), "Logout successful"),
 | 
			
		||||
 | 
			
		||||
    USER_INFO_CHANGE(HttpStatus.OK.value(), "User info update successful"),
 | 
			
		||||
 | 
			
		||||
    USER_DELETE_SUCCESSFUL(HttpStatus.OK.value(),  "User delete is successful"),
 | 
			
		||||
 | 
			
		||||
    // 401 Unauthorized
 | 
			
		||||
    USER_NOT_FOUND(HttpStatus.UNAUTHORIZED.value(), "User not found. Authentication failed"),
 | 
			
		||||
 | 
			
		||||
    AUTHENTICATION_FAILED(HttpStatus.UNAUTHORIZED.value(), "Password is invalid"),
 | 
			
		||||
 | 
			
		||||
    /*auth*/
 | 
			
		||||
    // 401 Unauthorized
 | 
			
		||||
    JWT_SIGNATURE_MISMATCH(HttpStatus.UNAUTHORIZED.value(), "JWT signature does not match. Authentication failed"),
 | 
			
		||||
 | 
			
		||||
    JWT_TOKEN_NULL(HttpStatus.UNAUTHORIZED.value(), "JWT token is null"),
 | 
			
		||||
 | 
			
		||||
    JWT_TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED.value(), "Token is Expired"),
 | 
			
		||||
 | 
			
		||||
    All_TOKEN_INVALID(HttpStatus.UNAUTHORIZED.value(), "Access and Refresh tokens are expired or invalid"),
 | 
			
		||||
 | 
			
		||||
    /*공통 Code*/
 | 
			
		||||
    // 400 Bad Request
 | 
			
		||||
    COMMON_BAD_REQUEST(HttpStatus.BAD_REQUEST.value(), "Required request body is missing or Error"),
 | 
			
		||||
 | 
			
		||||
    COMMON_FORMAT_WRONG(HttpStatus.BAD_REQUEST.value(), "Request format is incorrect"),
 | 
			
		||||
 | 
			
		||||
    // 401 Unauthorized
 | 
			
		||||
    COMMON_UNAUTHORIZED(HttpStatus.UNAUTHORIZED.value(), "Unauthorized"),
 | 
			
		||||
 | 
			
		||||
    // 403 Forbidden
 | 
			
		||||
    COMMON_FORBIDDEN(HttpStatus.FORBIDDEN.value(), "Access is denied"),
 | 
			
		||||
 | 
			
		||||
    // 404 Not Found
 | 
			
		||||
    COMMON_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "Resource is not found"),
 | 
			
		||||
 | 
			
		||||
    // 405 Method Not Allowed
 | 
			
		||||
    COMMON_METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED.value(), "Method not Allowed"),
 | 
			
		||||
 | 
			
		||||
    // 500 Internal Server Error
 | 
			
		||||
    INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR.value(), "An error occurred on the server"),
 | 
			
		||||
 | 
			
		||||
    ARGUMENT_NOT_VALID(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Argument is not valid"),
 | 
			
		||||
 | 
			
		||||
    INDEX_OUT_OF_BOUND(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Index out of bounds for length"),
 | 
			
		||||
	
 | 
			
		||||
	JSON_PROCESSING_EXCEPTION(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Check if it is a valid JSON format");
 | 
			
		||||
 | 
			
		||||
    private final int statusCode;
 | 
			
		||||
    private final String description;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,18 @@
 | 
			
		||||
package com.bio.bio_backend.global.utils;
 | 
			
		||||
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
 | 
			
		||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
 | 
			
		||||
import org.hibernate.id.IdentifierGenerator;
 | 
			
		||||
 | 
			
		||||
import com.bio.bio_backend.global.utils.OidUtil;
 | 
			
		||||
 | 
			
		||||
public class CustomIdGenerator implements IdentifierGenerator {
 | 
			
		||||
	
 | 
			
		||||
	private static final long serialVersionUID = 1L;
 | 
			
		||||
	
 | 
			
		||||
    @Override
 | 
			
		||||
    public Serializable generate(SharedSessionContractImplementor session, Object object) {
 | 
			
		||||
        return OidUtil.generateOid(); // 재사용
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,64 @@
 | 
			
		||||
package com.bio.bio_backend.global.utils;
 | 
			
		||||
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.springframework.web.context.request.RequestContextHolder;
 | 
			
		||||
import org.springframework.web.context.request.ServletRequestAttributes;
 | 
			
		||||
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
 | 
			
		||||
@Component
 | 
			
		||||
public class HttpUtils {
 | 
			
		||||
	public String getClientIp() {
 | 
			
		||||
        String ip = "";
 | 
			
		||||
        HttpServletRequest request = 
 | 
			
		||||
        ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
 | 
			
		||||
 | 
			
		||||
        ip = request.getHeader("X-Forwarded-For");
 | 
			
		||||
        
 | 
			
		||||
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
 | 
			
		||||
            ip = request.getHeader("Proxy-Client-IP"); 
 | 
			
		||||
        } 
 | 
			
		||||
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
 | 
			
		||||
            ip = request.getHeader("WL-Proxy-Client-IP"); 
 | 
			
		||||
        } 
 | 
			
		||||
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
 | 
			
		||||
            ip = request.getHeader("HTTP_CLIENT_IP"); 
 | 
			
		||||
        } 
 | 
			
		||||
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
 | 
			
		||||
            ip = request.getHeader("HTTP_X_FORWARDED_FOR"); 
 | 
			
		||||
        }
 | 
			
		||||
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
 | 
			
		||||
            ip = request.getHeader("X-Real-IP"); 
 | 
			
		||||
        }
 | 
			
		||||
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
 | 
			
		||||
            ip = request.getHeader("X-RealIP"); 
 | 
			
		||||
        }
 | 
			
		||||
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
 | 
			
		||||
            ip = request.getHeader("REMOTE_ADDR");
 | 
			
		||||
        }
 | 
			
		||||
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 
 | 
			
		||||
            ip = request.getRemoteAddr(); 
 | 
			
		||||
        }
 | 
			
		||||
		
 | 
			
		||||
		return ip;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	public String getUri() {
 | 
			
		||||
		HttpServletRequest request = 
 | 
			
		||||
		        ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
 | 
			
		||||
		
 | 
			
		||||
		 return request.getRequestURI();
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	public String getResponseType(String contentType) {
 | 
			
		||||
		if(contentType == null) {
 | 
			
		||||
			return "";
 | 
			
		||||
		} else if(contentType.contains("text/html")) {
 | 
			
		||||
			return "PAGE";
 | 
			
		||||
		} else if (contentType.contains("application/json")) {
 | 
			
		||||
			return "API";
 | 
			
		||||
		};
 | 
			
		||||
		
 | 
			
		||||
		return "";
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										90
									
								
								src/main/java/com/bio/bio_backend/global/utils/JwtUtils.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								src/main/java/com/bio/bio_backend/global/utils/JwtUtils.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
package com.bio.bio_backend.global.utils;
 | 
			
		||||
 | 
			
		||||
import io.jsonwebtoken.Claims;
 | 
			
		||||
import io.jsonwebtoken.Jwts;
 | 
			
		||||
import io.jsonwebtoken.io.Decoders;
 | 
			
		||||
import io.jsonwebtoken.security.Keys;
 | 
			
		||||
import jakarta.servlet.http.Cookie;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import com.bio.bio_backend.domain.user.member.service.MemberService;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Value;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.springframework.util.StringUtils;
 | 
			
		||||
 | 
			
		||||
import javax.crypto.SecretKey;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
@Component
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class JwtUtils {
 | 
			
		||||
 | 
			
		||||
    private final MemberService memberService;
 | 
			
		||||
 | 
			
		||||
    @Value("${token.secret_key}")
 | 
			
		||||
    private String SECRET_KEY;
 | 
			
		||||
 | 
			
		||||
    private SecretKey getSigningKey() {
 | 
			
		||||
        byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
 | 
			
		||||
        return Keys.hmacShaKeyFor(keyBytes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Token 생성
 | 
			
		||||
    public String generateToken(String username, String role, long expirationTime) {
 | 
			
		||||
 | 
			
		||||
        return Jwts.builder()
 | 
			
		||||
                .subject(username)
 | 
			
		||||
                .claim("role", role)
 | 
			
		||||
                .issuedAt(new Date(System.currentTimeMillis()))
 | 
			
		||||
                .expiration(new Date(System.currentTimeMillis() + expirationTime))
 | 
			
		||||
                .signWith(getSigningKey())
 | 
			
		||||
                .compact();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Token 검증
 | 
			
		||||
    public Boolean validateAccessToken(String token) {
 | 
			
		||||
        return isTokenExpired(token);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Boolean validateRefreshToken(String token) {
 | 
			
		||||
        String saveToken = memberService.getRefreshToken(extractUsername(token));
 | 
			
		||||
        return (saveToken.equals(token) && isTokenExpired(token));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean isTokenExpired(String token) {
 | 
			
		||||
        return !extractAllClaims(token).getExpiration().before(new Date());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String extractUsername(String token) {
 | 
			
		||||
        return extractAllClaims(token).getSubject();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Claims extractAllClaims(String token) {
 | 
			
		||||
 | 
			
		||||
        return Jwts.parser()
 | 
			
		||||
                .verifyWith(getSigningKey())
 | 
			
		||||
                .build()
 | 
			
		||||
                .parseSignedClaims(token).getPayload();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String extractAccessJwtFromRequest(HttpServletRequest request) {
 | 
			
		||||
        String bearerToken = request.getHeader("Authorization");
 | 
			
		||||
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
 | 
			
		||||
            return bearerToken.substring(7);    // "Bearer " 제거
 | 
			
		||||
        }
 | 
			
		||||
        return bearerToken;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String extractRefreshJwtFromCookie(HttpServletRequest request) {
 | 
			
		||||
        if (request.getCookies() != null) {
 | 
			
		||||
            for (Cookie cookie : request.getCookies()) {
 | 
			
		||||
                if ("RefreshToken".equals(cookie.getName())) {
 | 
			
		||||
                    return cookie.getValue();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								src/main/java/com/bio/bio_backend/global/utils/OidUtil.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/main/java/com/bio/bio_backend/global/utils/OidUtil.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
package com.bio.bio_backend.global.utils;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.atomic.AtomicInteger;
 | 
			
		||||
 | 
			
		||||
public class OidUtil {
 | 
			
		||||
    private static final int MAX_SEQUENCE = 999;
 | 
			
		||||
    private static volatile Long lastTimestamp = null;
 | 
			
		||||
    private static AtomicInteger sequence = new AtomicInteger(0);
 | 
			
		||||
    private static final int SERVER_ID = resolveServerId();
 | 
			
		||||
 | 
			
		||||
    public static synchronized long generateOid() {
 | 
			
		||||
        long now = System.currentTimeMillis();
 | 
			
		||||
 | 
			
		||||
        if (lastTimestamp == null || now != lastTimestamp) {
 | 
			
		||||
            lastTimestamp = now;
 | 
			
		||||
            sequence.set(SERVER_ID);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int index = sequence.getAndAdd(2);
 | 
			
		||||
 | 
			
		||||
        if (index > MAX_SEQUENCE) {
 | 
			
		||||
            now += index / 1000;
 | 
			
		||||
            index = index % 1000;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return now * 1000L + index;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static int resolveServerId() {
 | 
			
		||||
        try {
 | 
			
		||||
    		//	서버 이중화        	
 | 
			
		||||
//            String ip = InetAddress.getLocalHost().getHostAddress();
 | 
			
		||||
//            if (ip.contains(".162")) return 1;
 | 
			
		||||
//            if (ip.contains(".163")) return 0;
 | 
			
		||||
        } catch (Exception ignored) {}
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -45,3 +45,9 @@ spring.output.ansi.enabled=always
 | 
			
		||||
# HikariCP 연결 풀 크기 설정 (선택사항)
 | 
			
		||||
# spring.datasource.hikari.maximum-pool-size=10
 | 
			
		||||
 | 
			
		||||
##JWT 설정
 | 
			
		||||
## access : 30분 / refresh : 7일
 | 
			
		||||
token.expiration_time_access=180000
 | 
			
		||||
token.expiration_time_refresh=604800000
 | 
			
		||||
token.secret_key= c3RhbV9qd3Rfc2VjcmV0X3Rva2Vuc3RhbV9qd3Rfc2VjcmV0X3Rva2Vu
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user