Compare commits
14 Commits
8c5d7c6c3f
...
main
Author | SHA1 | Date | |
---|---|---|---|
499fbc6afb | |||
e78e98ad37 | |||
f10b028e04 | |||
cc2a34403d | |||
e8a785a20d | |||
afef6dfa80 | |||
a0ffeb236e | |||
fa1df19f64 | |||
b0398fccee | |||
92be6caf80 | |||
31aed4bda0 | |||
438bfc3bc5 | |||
![]() |
c07e7511d3 | ||
![]() |
0617347395 |
25
Dockerfile
25
Dockerfile
@@ -1,25 +0,0 @@
|
|||||||
# ./my-spring-app/Dockerfile
|
|
||||||
|
|
||||||
# 공식 OpenJDK 이미지를 기반으로 사용
|
|
||||||
FROM openjdk:17-jdk-slim
|
|
||||||
|
|
||||||
# 컨테이너의 작업 디렉터리를 설정
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Gradle Wrapper와 빌드 파일을 컨테이너에 복사
|
|
||||||
COPY gradlew .
|
|
||||||
COPY gradle ./gradle
|
|
||||||
|
|
||||||
# 프로젝트 설정 파일들을 복사
|
|
||||||
COPY build.gradle .
|
|
||||||
COPY settings.gradle .
|
|
||||||
|
|
||||||
# 의존성을 미리 다운로드
|
|
||||||
RUN ./gradlew dependencies
|
|
||||||
|
|
||||||
# 소스 파일들을 복사
|
|
||||||
COPY src ./src
|
|
||||||
|
|
||||||
# Gradle Wrapper를 사용하여 애플리케이션을 실행
|
|
||||||
# 이 명령어는 docker-compose.yml에서 개발용 명령으로 덮어쓸 것입니다.
|
|
||||||
CMD ["./gradlew", "bootRun"]
|
|
@@ -48,6 +48,8 @@ dependencies {
|
|||||||
// MyBatis
|
// MyBatis
|
||||||
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'
|
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'
|
||||||
|
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||||
|
|
||||||
// jwt
|
// jwt
|
||||||
implementation 'io.jsonwebtoken:jjwt-api:0.12.5'
|
implementation 'io.jsonwebtoken:jjwt-api:0.12.5'
|
||||||
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5'
|
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5'
|
||||||
|
@@ -69,7 +69,6 @@
|
|||||||
oid bigint not null,
|
oid bigint not null,
|
||||||
updated_at timestamp(6) not null,
|
updated_at timestamp(6) not null,
|
||||||
updated_oid bigint,
|
updated_oid bigint,
|
||||||
role varchar(40) not null check (role in ('MEMBER','ADMIN','SYSTEM_ADMIN')),
|
|
||||||
login_ip varchar(45),
|
login_ip varchar(45),
|
||||||
name varchar(100) not null,
|
name varchar(100) not null,
|
||||||
password varchar(100) not null,
|
password varchar(100) not null,
|
||||||
|
BIN
jpa-curd-0.0.1.vsix
Normal file
BIN
jpa-curd-0.0.1.vsix
Normal file
Binary file not shown.
@@ -46,7 +46,7 @@ public class CommonCodeServiceImpl implements CommonCodeService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public void updateGroupCode(String code, CommonGroupCodeDto groupCodeDto) {
|
public void updateGroupCode(String code, CommonGroupCodeDto groupCodeDto) {
|
||||||
CommonGroupCode existingGroupCode = commonGroupCodeRepository.findByCode(code)
|
CommonGroupCode existingGroupCode = commonGroupCodeRepository.findByCode(code)
|
||||||
.orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_NOT_FOUND, "그룹 코드를 찾을 수 없습니다: " + code));
|
.orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_TARGET_NOT_FOUND, "그룹 코드를 찾을 수 없습니다: " + code));
|
||||||
|
|
||||||
commonGroupCodeMapper.updateCommonGroupCodeFromDto(groupCodeDto, existingGroupCode);
|
commonGroupCodeMapper.updateCommonGroupCodeFromDto(groupCodeDto, existingGroupCode);
|
||||||
commonGroupCodeRepository.save(existingGroupCode);
|
commonGroupCodeRepository.save(existingGroupCode);
|
||||||
|
@@ -22,6 +22,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
import com.bio.bio_backend.global.constants.ApiResponseCode;
|
import com.bio.bio_backend.global.constants.ApiResponseCode;
|
||||||
import com.bio.bio_backend.global.annotation.LogExecution;
|
import com.bio.bio_backend.global.annotation.LogExecution;
|
||||||
import com.bio.bio_backend.global.utils.SecurityUtils;
|
import com.bio.bio_backend.global.utils.SecurityUtils;
|
||||||
|
import com.bio.bio_backend.global.utils.JwtUtils;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
|
||||||
@Tag(name = "Member", description = "회원 관련 API")
|
@Tag(name = "Member", description = "회원 관련 API")
|
||||||
@@ -33,6 +35,7 @@ public class MemberController {
|
|||||||
|
|
||||||
private final MemberService memberService;
|
private final MemberService memberService;
|
||||||
private final MemberMapper memberMapper;
|
private final MemberMapper memberMapper;
|
||||||
|
private final JwtUtils jwtUtils;
|
||||||
|
|
||||||
@LogExecution("회원 등록")
|
@LogExecution("회원 등록")
|
||||||
@Operation(summary = "회원 등록", description = "새로운 회원을 등록합니다.")
|
@Operation(summary = "회원 등록", description = "새로운 회원을 등록합니다.")
|
||||||
@@ -57,10 +60,14 @@ public class MemberController {
|
|||||||
@ApiResponse(responseCode = "401", description = "인증 실패", content = @Content(schema = @Schema(implementation = ApiResponseDto.class)))
|
@ApiResponse(responseCode = "401", description = "인증 실패", content = @Content(schema = @Schema(implementation = ApiResponseDto.class)))
|
||||||
})
|
})
|
||||||
@PostMapping("/logout")
|
@PostMapping("/logout")
|
||||||
public ResponseEntity<ApiResponseDto<Void>> logout() {
|
public ResponseEntity<ApiResponseDto<Void>> logout(HttpServletResponse response) {
|
||||||
try {
|
try {
|
||||||
String userId = SecurityUtils.getCurrentUserId();
|
String userId = SecurityUtils.getCurrentUserId();
|
||||||
memberService.deleteRefreshToken(userId);
|
memberService.deleteRefreshToken(userId);
|
||||||
|
|
||||||
|
// 모든 토큰 쿠키 삭제
|
||||||
|
jwtUtils.deleteAllTokenCookies(response);
|
||||||
|
|
||||||
log.info("사용자 로그아웃 완료: {}", userId);
|
log.info("사용자 로그아웃 완료: {}", userId);
|
||||||
|
|
||||||
return ResponseEntity.ok(ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS));
|
return ResponseEntity.ok(ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS));
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
package com.bio.bio_backend.domain.base.member.dto;
|
package com.bio.bio_backend.domain.base.member.dto;
|
||||||
|
|
||||||
import com.bio.bio_backend.domain.base.member.enums.MemberRole;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -16,7 +15,6 @@ public class CreateMemberResponseDto {
|
|||||||
|
|
||||||
private Long oid;
|
private Long oid;
|
||||||
private String userId;
|
private String userId;
|
||||||
private MemberRole role;
|
|
||||||
private Boolean useFlag;
|
private Boolean useFlag;
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
@@ -14,6 +14,6 @@ import java.time.LocalDateTime;
|
|||||||
public class LoginResponseDto {
|
public class LoginResponseDto {
|
||||||
|
|
||||||
private String userId;
|
private String userId;
|
||||||
private String role;
|
private String name;
|
||||||
private LocalDateTime lastLoginAt;
|
private LocalDateTime lastLoginAt;
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
package com.bio.bio_backend.domain.base.member.dto;
|
package com.bio.bio_backend.domain.base.member.dto;
|
||||||
|
|
||||||
import com.bio.bio_backend.domain.base.member.enums.MemberRole;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -24,7 +23,6 @@ public class MemberDto implements UserDetails {
|
|||||||
private String password;
|
private String password;
|
||||||
private String name;
|
private String name;
|
||||||
private String email;
|
private String email;
|
||||||
private MemberRole role;
|
|
||||||
private Boolean useFlag;
|
private Boolean useFlag;
|
||||||
private String refreshToken;
|
private String refreshToken;
|
||||||
private String loginIp;
|
private String loginIp;
|
||||||
@@ -34,7 +32,7 @@ public class MemberDto implements UserDetails {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
return Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + this.role.getValue()));
|
return Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
package com.bio.bio_backend.domain.base.member.entity;
|
package com.bio.bio_backend.domain.base.member.entity;
|
||||||
|
|
||||||
import com.bio.bio_backend.domain.base.member.enums.MemberRole;
|
|
||||||
import com.bio.bio_backend.global.constants.AppConstants;
|
import com.bio.bio_backend.global.constants.AppConstants;
|
||||||
import com.bio.bio_backend.global.entity.BaseEntity;
|
import com.bio.bio_backend.global.entity.BaseEntity;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
@@ -37,9 +36,7 @@ public class Member extends BaseEntity {
|
|||||||
@Column(name = "email", nullable = false, length = 255)
|
@Column(name = "email", nullable = false, length = 255)
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
@Enumerated(EnumType.STRING)
|
|
||||||
@Column(name = "role", nullable = false, length = 40)
|
|
||||||
private MemberRole role;
|
|
||||||
|
|
||||||
@Column(name = "use_flag", nullable = false)
|
@Column(name = "use_flag", nullable = false)
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
|
@@ -1,38 +0,0 @@
|
|||||||
package com.bio.bio_backend.domain.base.member.enums;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 회원 역할을 정의하는 Enum
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public enum MemberRole {
|
|
||||||
|
|
||||||
MEMBER("MEMBER", "일반 회원"),
|
|
||||||
ADMIN("ADMIN", "관리자"),
|
|
||||||
SYSTEM_ADMIN("SYSTEM_ADMIN", "시스템 관리자");
|
|
||||||
|
|
||||||
private final String value;
|
|
||||||
private final String description;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 문자열 값으로부터 MemberRole을 찾는 메서드
|
|
||||||
*/
|
|
||||||
public static MemberRole fromValue(String value) {
|
|
||||||
for (MemberRole role : values()) {
|
|
||||||
if (role.value.equals(value)) {
|
|
||||||
return role;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Unknown MemberRole value: " + value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 기본 역할 반환
|
|
||||||
*/
|
|
||||||
public static MemberRole getDefault() {
|
|
||||||
return MEMBER;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -2,13 +2,13 @@ package com.bio.bio_backend.domain.base.member.mapper;
|
|||||||
|
|
||||||
import com.bio.bio_backend.domain.base.member.dto.CreateMemberRequestDto;
|
import com.bio.bio_backend.domain.base.member.dto.CreateMemberRequestDto;
|
||||||
import com.bio.bio_backend.domain.base.member.dto.CreateMemberResponseDto;
|
import com.bio.bio_backend.domain.base.member.dto.CreateMemberResponseDto;
|
||||||
|
import com.bio.bio_backend.domain.base.member.dto.LoginResponseDto;
|
||||||
import com.bio.bio_backend.domain.base.member.dto.MemberDto;
|
import com.bio.bio_backend.domain.base.member.dto.MemberDto;
|
||||||
import com.bio.bio_backend.domain.base.member.entity.Member;
|
import com.bio.bio_backend.domain.base.member.entity.Member;
|
||||||
import com.bio.bio_backend.global.annotation.IgnoreBaseEntityMapping;
|
import com.bio.bio_backend.global.annotation.IgnoreBaseEntityMapping;
|
||||||
import com.bio.bio_backend.global.config.GlobalMapperConfig;
|
import com.bio.bio_backend.global.config.GlobalMapperConfig;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.factory.Mappers;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Mapper(config = GlobalMapperConfig.class)
|
@Mapper(config = GlobalMapperConfig.class)
|
||||||
@@ -16,10 +16,9 @@ public interface MemberMapper {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* CreateMemberRequestDto를 MemberDto로 변환
|
* CreateMemberRequestDto를 MemberDto로 변환
|
||||||
* 기본값 설정: role = MemberRole.MEMBER, useFlag = true
|
* 기본값 설정: useFlag = true
|
||||||
*/
|
*/
|
||||||
@Mapping(target = "oid", ignore = true)
|
@Mapping(target = "oid", ignore = true)
|
||||||
@Mapping(target = "role", expression = "java(com.bio.bio_backend.domain.base.member.enums.MemberRole.getDefault())")
|
|
||||||
@Mapping(target = "useFlag", constant = "true")
|
@Mapping(target = "useFlag", constant = "true")
|
||||||
@Mapping(target = "refreshToken", ignore = true)
|
@Mapping(target = "refreshToken", ignore = true)
|
||||||
@Mapping(target = "loginIp", ignore = true)
|
@Mapping(target = "loginIp", ignore = true)
|
||||||
@@ -53,4 +52,9 @@ public interface MemberMapper {
|
|||||||
*/
|
*/
|
||||||
@IgnoreBaseEntityMapping
|
@IgnoreBaseEntityMapping
|
||||||
void updateMemberFromDto(MemberDto memberDto, @org.mapstruct.MappingTarget Member member);
|
void updateMemberFromDto(MemberDto memberDto, @org.mapstruct.MappingTarget Member member);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MemberDto를 LoginResponseDto로 변환
|
||||||
|
*/
|
||||||
|
LoginResponseDto toLoginResponseDto(MemberDto memberDto);
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
package com.bio.bio_backend.domain.base.member.service;
|
package com.bio.bio_backend.domain.base.member.service;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
|
||||||
import com.bio.bio_backend.domain.base.member.dto.MemberDto;
|
import com.bio.bio_backend.domain.base.member.dto.MemberDto;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public interface MemberService extends UserDetailsService {
|
public interface MemberService extends UserDetailsService {
|
||||||
|
|
||||||
UserDetails loadUserByUsername(String id);
|
UserDetails loadUserByUsername(String id);
|
||||||
@@ -16,9 +16,9 @@ public interface MemberService extends UserDetailsService {
|
|||||||
|
|
||||||
String getRefreshToken(String id);
|
String getRefreshToken(String id);
|
||||||
|
|
||||||
int deleteRefreshToken(String id);
|
void deleteRefreshToken(String id);
|
||||||
|
|
||||||
|
void updateMember(MemberDto member);
|
||||||
|
|
||||||
List<MemberDto> selectMemberList(Map<String, String> params);
|
List<MemberDto> selectMemberList(Map<String, String> params);
|
||||||
|
|
||||||
int updateMember(MemberDto member);
|
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,6 @@ package com.bio.bio_backend.domain.base.member.service;
|
|||||||
|
|
||||||
import com.bio.bio_backend.domain.base.member.dto.MemberDto;
|
import com.bio.bio_backend.domain.base.member.dto.MemberDto;
|
||||||
import com.bio.bio_backend.domain.base.member.entity.Member;
|
import com.bio.bio_backend.domain.base.member.entity.Member;
|
||||||
import com.bio.bio_backend.domain.base.member.enums.MemberRole;
|
|
||||||
import com.bio.bio_backend.domain.base.member.mapper.MemberMapper;
|
import com.bio.bio_backend.domain.base.member.mapper.MemberMapper;
|
||||||
import com.bio.bio_backend.domain.base.member.repository.MemberRepository;
|
import com.bio.bio_backend.domain.base.member.repository.MemberRepository;
|
||||||
import com.bio.bio_backend.global.exception.ApiException;
|
import com.bio.bio_backend.global.exception.ApiException;
|
||||||
@@ -50,7 +49,6 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
.password(bCryptPasswordEncoder.encode(memberDto.getPassword()))
|
.password(bCryptPasswordEncoder.encode(memberDto.getPassword()))
|
||||||
.name(memberDto.getName())
|
.name(memberDto.getName())
|
||||||
.email(memberDto.getEmail())
|
.email(memberDto.getEmail())
|
||||||
.role(MemberRole.getDefault())
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
member.setCreatedOid(AppConstants.ADMIN_OID);
|
member.setCreatedOid(AppConstants.ADMIN_OID);
|
||||||
@@ -65,13 +63,12 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public int updateMember(MemberDto memberDto) {
|
public void updateMember(MemberDto memberDto) {
|
||||||
Member member = memberRepository.findActiveMemberByUserId(memberDto.getUserId())
|
Member member = memberRepository.findActiveMemberByUserId(memberDto.getUserId())
|
||||||
.orElseThrow(() -> new ApiException(ApiResponseCode.USER_NOT_FOUND));
|
.orElseThrow(() -> new ApiException(ApiResponseCode.USER_NOT_FOUND));
|
||||||
|
|
||||||
memberMapper.updateMemberFromDto(memberDto, member);
|
memberMapper.updateMemberFromDto(memberDto, member);
|
||||||
memberRepository.save(member);
|
memberRepository.save(member);
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -84,13 +81,12 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public int deleteRefreshToken(String id) {
|
public void deleteRefreshToken(String id) {
|
||||||
Member member = memberRepository.findActiveMemberByUserId(id)
|
Member member = memberRepository.findActiveMemberByUserId(id)
|
||||||
.orElseThrow(() -> new ApiException(ApiResponseCode.USER_NOT_FOUND));
|
.orElseThrow(() -> new ApiException(ApiResponseCode.USER_NOT_FOUND));
|
||||||
|
|
||||||
member.setRefreshToken(null);
|
member.setRefreshToken(null);
|
||||||
memberRepository.save(member);
|
memberRepository.save(member);
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -16,8 +16,6 @@ public class CorsConfig {
|
|||||||
config.addAllowedHeader("*");
|
config.addAllowedHeader("*");
|
||||||
config.addAllowedMethod("*");
|
config.addAllowedMethod("*");
|
||||||
config.setAllowCredentials(true);
|
config.setAllowCredentials(true);
|
||||||
|
|
||||||
config.addExposedHeader("Authorization");
|
|
||||||
source.registerCorsConfiguration("/**", config);
|
source.registerCorsConfiguration("/**", config);
|
||||||
|
|
||||||
return new CorsFilter(source);
|
return new CorsFilter(source);
|
||||||
|
@@ -33,6 +33,7 @@ public enum ApiResponseCode {
|
|||||||
|
|
||||||
// 404 Not Found
|
// 404 Not Found
|
||||||
COMMON_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "리소스를 찾을 수 없습니다"),
|
COMMON_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "리소스를 찾을 수 없습니다"),
|
||||||
|
COMMON_TARGET_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "대상을 찾을 수 없습니다"),
|
||||||
|
|
||||||
// 405 Method Not Allowed
|
// 405 Method Not Allowed
|
||||||
COMMON_METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED.value(), "허용되지 않는 메소드입니다"),
|
COMMON_METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED.value(), "허용되지 않는 메소드입니다"),
|
||||||
|
@@ -9,6 +9,7 @@ import com.bio.bio_backend.global.dto.ApiResponseDto;
|
|||||||
import com.bio.bio_backend.domain.base.member.dto.LoginRequestDto;
|
import com.bio.bio_backend.domain.base.member.dto.LoginRequestDto;
|
||||||
import com.bio.bio_backend.domain.base.member.dto.LoginResponseDto;
|
import com.bio.bio_backend.domain.base.member.dto.LoginResponseDto;
|
||||||
import com.bio.bio_backend.domain.base.member.dto.MemberDto;
|
import com.bio.bio_backend.domain.base.member.dto.MemberDto;
|
||||||
|
import com.bio.bio_backend.domain.base.member.mapper.MemberMapper;
|
||||||
import com.bio.bio_backend.domain.base.member.service.MemberService;
|
import com.bio.bio_backend.domain.base.member.service.MemberService;
|
||||||
import com.bio.bio_backend.global.constants.ApiResponseCode;
|
import com.bio.bio_backend.global.constants.ApiResponseCode;
|
||||||
import com.bio.bio_backend.global.utils.JwtUtils;
|
import com.bio.bio_backend.global.utils.JwtUtils;
|
||||||
@@ -40,6 +41,7 @@ public class JwtTokenIssuanceFilter extends UsernamePasswordAuthenticationFilter
|
|||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
private final MemberService memberService;
|
private final MemberService memberService;
|
||||||
private final HttpUtils httpUtils;
|
private final HttpUtils httpUtils;
|
||||||
|
private final MemberMapper memberMapper;
|
||||||
|
|
||||||
// 사용자 login 인증 처리
|
// 사용자 login 인증 처리
|
||||||
@Override
|
@Override
|
||||||
@@ -64,8 +66,8 @@ public class JwtTokenIssuanceFilter extends UsernamePasswordAuthenticationFilter
|
|||||||
MemberDto member = (MemberDto) userDetails;
|
MemberDto member = (MemberDto) userDetails;
|
||||||
|
|
||||||
// 토큰 생성
|
// 토큰 생성
|
||||||
String accessToken = jwtUtils.createAccessToken(member.getUserId(), member.getRole().getValue());
|
String accessToken = jwtUtils.createAccessToken(member.getUserId());
|
||||||
String refreshToken = jwtUtils.createRefreshToken(member.getUserId(), member.getRole().getValue());
|
String refreshToken = jwtUtils.createRefreshToken(member.getUserId(), httpUtils.getClientIp());
|
||||||
|
|
||||||
member.setRefreshToken(refreshToken);
|
member.setRefreshToken(refreshToken);
|
||||||
member.setLoginIp(httpUtils.getClientIp());
|
member.setLoginIp(httpUtils.getClientIp());
|
||||||
@@ -75,25 +77,22 @@ public class JwtTokenIssuanceFilter extends UsernamePasswordAuthenticationFilter
|
|||||||
// Refresh 토큰 쿠키 저장
|
// Refresh 토큰 쿠키 저장
|
||||||
jwtUtils.setRefreshTokenCookie(response, refreshToken);
|
jwtUtils.setRefreshTokenCookie(response, refreshToken);
|
||||||
|
|
||||||
// Access 토큰 전달
|
// Access 토큰 쿠키 저장
|
||||||
response.setHeader("Authorization", "Bearer " + accessToken);
|
jwtUtils.setAccessTokenCookie(response, accessToken);
|
||||||
|
|
||||||
SecurityContextHolderStrategy contextHolder = SecurityContextHolder.getContextHolderStrategy();
|
SecurityContextHolderStrategy contextHolder = SecurityContextHolder.getContextHolderStrategy();
|
||||||
SecurityContext context = contextHolder.createEmptyContext();
|
SecurityContext context = contextHolder.createEmptyContext();
|
||||||
context.setAuthentication(authResult);
|
context.setAuthentication(authResult);
|
||||||
contextHolder.setContext(context);
|
contextHolder.setContext(context);
|
||||||
|
|
||||||
LoginResponseDto memberData = new LoginResponseDto();
|
LoginResponseDto loginResponseDto = memberMapper.toLoginResponseDto(member);
|
||||||
memberData.setUserId(member.getUserId());
|
|
||||||
memberData.setRole(member.getRole().getValue());
|
|
||||||
memberData.setLastLoginAt(member.getLastLoginAt());
|
|
||||||
|
|
||||||
// login 성공 메시지 전송
|
// login 성공 메시지 전송
|
||||||
response.setStatus(HttpStatus.OK.value());
|
response.setStatus(HttpStatus.OK.value());
|
||||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8");
|
response.setContentType(MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8");
|
||||||
objectMapper.writeValue(
|
objectMapper.writeValue(
|
||||||
response.getWriter(),
|
response.getWriter(),
|
||||||
ApiResponseDto.success(ApiResponseCode.LOGIN_SUCCESSFUL, memberData)
|
ApiResponseDto.success(ApiResponseCode.LOGIN_SUCCESSFUL, loginResponseDto)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,11 @@
|
|||||||
package com.bio.bio_backend.global.filter;
|
package com.bio.bio_backend.global.filter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import com.bio.bio_backend.domain.base.member.dto.MemberDto;
|
||||||
|
import com.bio.bio_backend.global.utils.HttpUtils;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
@@ -30,6 +33,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
public class JwtTokenValidationFilter extends OncePerRequestFilter {
|
public class JwtTokenValidationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
private final JwtUtils jwtUtils;
|
private final JwtUtils jwtUtils;
|
||||||
|
private final HttpUtils httpUtils;
|
||||||
private final MemberService memberService;
|
private final MemberService memberService;
|
||||||
private final Environment env;
|
private final Environment env;
|
||||||
private final SecurityPathConfig securityPathConfig;
|
private final SecurityPathConfig securityPathConfig;
|
||||||
@@ -66,8 +70,7 @@ public class JwtTokenValidationFilter extends OncePerRequestFilter {
|
|||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
|
||||||
// Access Token이 없거나 만료된 경우, Refresh Token으로 갱신 시도
|
// Access Token이 없거나 만료된 경우, Refresh Token으로 갱신 시도
|
||||||
if (refreshToken != null) {
|
if (refreshToken != null) {
|
||||||
// 1. Refresh Token 유효성 검증
|
// 1. Refresh Token 유효성 검증
|
||||||
@@ -78,7 +81,7 @@ public class JwtTokenValidationFilter extends OncePerRequestFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. IP 주소 검증
|
// 2. IP 주소 검증
|
||||||
if (!jwtUtils.isValidClientIp(refreshToken, request.getRemoteAddr())) {
|
if (!jwtUtils.isValidClientIp(refreshToken, httpUtils.getClientIp())) {
|
||||||
log.warn("클라이언트 IP 주소가 일치하지 않습니다. URI: {}, IP: {}",
|
log.warn("클라이언트 IP 주소가 일치하지 않습니다. URI: {}, IP: {}",
|
||||||
request.getRequestURI(), request.getRemoteAddr());
|
request.getRequestURI(), request.getRemoteAddr());
|
||||||
sendJsonResponse(response, ApiResponseDto.fail(ApiResponseCode.INVALID_CLIENT_IP));
|
sendJsonResponse(response, ApiResponseDto.fail(ApiResponseCode.INVALID_CLIENT_IP));
|
||||||
@@ -87,33 +90,37 @@ public class JwtTokenValidationFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
// 모든 검증을 통과한 경우 토큰 갱신 진행
|
// 모든 검증을 통과한 경우 토큰 갱신 진행
|
||||||
String username = jwtUtils.extractUsername(refreshToken);
|
String username = jwtUtils.extractUsername(refreshToken);
|
||||||
String role = jwtUtils.extractRole(refreshToken);
|
|
||||||
|
UserDetails userDetails = memberService.loadUserByUsername(username);
|
||||||
|
|
||||||
// 새로운 Access Token 생성
|
// 새로운 Access Token 생성
|
||||||
String newAccessToken = jwtUtils.generateToken(username, role,
|
String newAccessToken = jwtUtils.createAccessToken(username);
|
||||||
Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_access"))));
|
|
||||||
|
|
||||||
// 새로운 Access Token을 응답 헤더에 설정
|
// 새로운 Access Token을 쿠키에 설정
|
||||||
response.setHeader("Authorization", "Bearer " + newAccessToken);
|
jwtUtils.setAccessTokenCookie(response, newAccessToken);
|
||||||
|
|
||||||
// Refresh Token 갱신
|
// Refresh Token 갱신
|
||||||
String newRefreshToken = jwtUtils.refreshTokens(username, role);
|
String newRefreshToken = jwtUtils.createRefreshToken(username, httpUtils.getClientIp());
|
||||||
jwtUtils.setRefreshTokenCookie(response, newRefreshToken);
|
jwtUtils.setRefreshTokenCookie(response, newRefreshToken);
|
||||||
|
|
||||||
|
MemberDto member = (MemberDto) userDetails;
|
||||||
|
member.setRefreshToken(newRefreshToken);
|
||||||
|
member.setLoginIp(httpUtils.getClientIp());
|
||||||
|
memberService.updateMember(member);
|
||||||
|
|
||||||
// 인증 정보 설정
|
// 인증 정보 설정
|
||||||
UserDetails userDetails = memberService.loadUserByUsername(username);
|
|
||||||
if (userDetails != null) {
|
|
||||||
UsernamePasswordAuthenticationToken authentication =
|
UsernamePasswordAuthenticationToken authentication =
|
||||||
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
|
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
|
||||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
}
|
|
||||||
|
|
||||||
log.info("토큰 자동 갱신 성공: {}", username);
|
log.info("토큰 자동 갱신 성공: {}", username);
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// 토큰이 없거나 모두 유효하지 않은 경우
|
// 토큰이 없거나 모두 유효하지 않은 경우
|
||||||
log.warn("유효한 JWT 토큰이 없습니다. URI: {}", request.getRequestURI());
|
log.warn("유효한 JWT 토큰이 없습니다. URI: {}", request.getRequestURI());
|
||||||
sendJsonResponse(response, ApiResponseDto.fail(ApiResponseCode.JWT_TOKEN_NULL));
|
sendJsonResponse(response, ApiResponseDto.fail(ApiResponseCode.JWT_TOKEN_NULL));
|
||||||
|
@@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
|
|
||||||
|
|
||||||
import com.bio.bio_backend.domain.base.member.service.MemberService;
|
import com.bio.bio_backend.domain.base.member.service.MemberService;
|
||||||
|
import com.bio.bio_backend.domain.base.member.mapper.MemberMapper;
|
||||||
import com.bio.bio_backend.global.exception.CustomAuthenticationFailureHandler;
|
import com.bio.bio_backend.global.exception.CustomAuthenticationFailureHandler;
|
||||||
import com.bio.bio_backend.global.utils.JwtUtils;
|
import com.bio.bio_backend.global.utils.JwtUtils;
|
||||||
import com.bio.bio_backend.global.utils.HttpUtils;
|
import com.bio.bio_backend.global.utils.HttpUtils;
|
||||||
@@ -37,16 +38,17 @@ public class WebSecurity {
|
|||||||
private final Environment env;
|
private final Environment env;
|
||||||
private final SecurityPathConfig securityPathConfig;
|
private final SecurityPathConfig securityPathConfig;
|
||||||
private final HttpUtils httpUtils;
|
private final HttpUtils httpUtils;
|
||||||
|
private final MemberMapper memberMapper;
|
||||||
|
|
||||||
private JwtTokenIssuanceFilter getJwtTokenIssuanceFilter(AuthenticationManager authenticationManager) throws Exception {
|
private JwtTokenIssuanceFilter getJwtTokenIssuanceFilter(AuthenticationManager authenticationManager) throws Exception {
|
||||||
JwtTokenIssuanceFilter filter = new JwtTokenIssuanceFilter(authenticationManager, jwtUtils, objectMapper, memberService, httpUtils);
|
JwtTokenIssuanceFilter filter = new JwtTokenIssuanceFilter(authenticationManager, jwtUtils, objectMapper, memberService, httpUtils, memberMapper);
|
||||||
filter.setFilterProcessesUrl("/login");
|
filter.setFilterProcessesUrl("/login");
|
||||||
filter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler(objectMapper));
|
filter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler(objectMapper));
|
||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JwtTokenValidationFilter getJwtTokenValidationFilter() {
|
private JwtTokenValidationFilter getJwtTokenValidationFilter() {
|
||||||
return new JwtTokenValidationFilter(jwtUtils, memberService, env, securityPathConfig);
|
return new JwtTokenValidationFilter(jwtUtils, httpUtils, memberService, env, securityPathConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -42,23 +42,4 @@ public class HttpUtils {
|
|||||||
|
|
||||||
return ip;
|
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 "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,6 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@@ -36,21 +35,42 @@ public class JwtUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Token 생성
|
// Token 생성
|
||||||
public String generateToken(String username, String role, long expirationTime) {
|
public String generateToken(String username, long expirationTime) {
|
||||||
|
|
||||||
return Jwts.builder()
|
return Jwts.builder()
|
||||||
.subject(username)
|
.subject(username)
|
||||||
.claim("role", role)
|
|
||||||
.issuedAt(new Date(System.currentTimeMillis()))
|
.issuedAt(new Date(System.currentTimeMillis()))
|
||||||
.expiration(new Date(System.currentTimeMillis() + expirationTime))
|
.expiration(new Date(System.currentTimeMillis() + expirationTime))
|
||||||
.signWith(getSigningKey())
|
.signWith(getSigningKey())
|
||||||
.compact();
|
.compact();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Token 생성(IP 정보 포함)
|
||||||
|
public String generateToken(String username, String clientIp, long expirationTime) {
|
||||||
|
return Jwts.builder()
|
||||||
|
.subject(username)
|
||||||
|
.claim("ip", clientIp)
|
||||||
|
.issuedAt(new Date(System.currentTimeMillis()))
|
||||||
|
.expiration(new Date(System.currentTimeMillis() + expirationTime))
|
||||||
|
.signWith(getSigningKey())
|
||||||
|
.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access Token 생성
|
||||||
|
public String createAccessToken(String username) {
|
||||||
|
long expirationTime = Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_access")));
|
||||||
|
return generateToken(username, expirationTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh Token 생성 시 IP 정보 포함
|
||||||
|
public String createRefreshToken(String username, String clientIp) {
|
||||||
|
long expirationTime = Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_refresh")));
|
||||||
|
return generateToken(username, clientIp, expirationTime);
|
||||||
|
}
|
||||||
|
|
||||||
// Token 검증
|
// Token 검증
|
||||||
public Boolean validateAccessToken(String token) {
|
public Boolean validateAccessToken(String token) {
|
||||||
try {
|
try {
|
||||||
return isTokenExpired(token);
|
return isValidTokenExpired(token);
|
||||||
} catch (io.jsonwebtoken.ExpiredJwtException e) {
|
} catch (io.jsonwebtoken.ExpiredJwtException e) {
|
||||||
log.debug("Access Token 만료: {}", e.getMessage());
|
log.debug("Access Token 만료: {}", e.getMessage());
|
||||||
return false;
|
return false;
|
||||||
@@ -60,50 +80,17 @@ public class JwtUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh Token 생성 시 IP 정보 포함
|
|
||||||
public String createRefreshToken(String username, String role, String clientIp) {
|
|
||||||
return generateToken(username, role, clientIp,
|
|
||||||
Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_refresh"))));
|
|
||||||
}
|
|
||||||
|
|
||||||
// IP 정보를 포함한 토큰 생성
|
|
||||||
public String generateToken(String username, String role, String clientIp, long expirationTime) {
|
|
||||||
return Jwts.builder()
|
|
||||||
.subject(username)
|
|
||||||
.claim("role", role)
|
|
||||||
.claim("ip", clientIp) // IP 정보 추가
|
|
||||||
.issuedAt(new Date(System.currentTimeMillis()))
|
|
||||||
.expiration(new Date(System.currentTimeMillis() + expirationTime))
|
|
||||||
.signWith(getSigningKey())
|
|
||||||
.compact();
|
|
||||||
}
|
|
||||||
|
|
||||||
// IP 정보 추출
|
// IP 정보 추출
|
||||||
public String extractClientIp(String token) {
|
public String extractClientIp(String token) {
|
||||||
Claims claims = extractAllClaims(token);
|
Claims claims = extractAllClaims(token);
|
||||||
return claims.get("ip", String.class);
|
return claims.get("ip", String.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh Token 검증 시 IP도 함께 검증
|
|
||||||
public Boolean validateRefreshToken(String token, String clientIp) {
|
|
||||||
// 1. 토큰 유효성 검증
|
|
||||||
if (!isValidRefreshToken(token)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. IP 주소 검증
|
|
||||||
if (!isValidClientIp(token, clientIp)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh Token 유효성 검증 (토큰 일치, 만료 여부)
|
// Refresh Token 유효성 검증 (토큰 일치, 만료 여부)
|
||||||
public Boolean isValidRefreshToken(String token) {
|
public Boolean isValidRefreshToken(String token) {
|
||||||
try {
|
try {
|
||||||
String savedToken = memberService.getRefreshToken(extractUsername(token));
|
String savedToken = memberService.getRefreshToken(extractUsername(token));
|
||||||
return savedToken.equals(token) && !isTokenExpired(token);
|
return savedToken.equals(token) && isValidTokenExpired(token);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.debug("Refresh Token 검증 실패: {}", e.getMessage());
|
log.debug("Refresh Token 검증 실패: {}", e.getMessage());
|
||||||
return false;
|
return false;
|
||||||
@@ -127,36 +114,35 @@ public class JwtUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isTokenExpired(String token) {
|
private boolean isValidTokenExpired(String token) {
|
||||||
return !extractAllClaims(token).getExpiration().before(new Date());
|
Date expiration = extractAllClaims(token).getExpiration();
|
||||||
|
return !expiration.before(new Date());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String extractUsername(String token) {
|
public String extractUsername(String token) {
|
||||||
return extractAllClaims(token).getSubject();
|
return extractAllClaims(token).getSubject();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Role 정보 추출
|
|
||||||
public String extractRole(String token) {
|
|
||||||
Claims claims = extractAllClaims(token);
|
|
||||||
return claims.get("role", String.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Claims extractAllClaims(String token) {
|
public Claims extractAllClaims(String token) {
|
||||||
|
|
||||||
return Jwts.parser()
|
return Jwts.parser()
|
||||||
.verifyWith(getSigningKey())
|
.verifyWith(getSigningKey())
|
||||||
.build()
|
.build()
|
||||||
.parseSignedClaims(token).getPayload();
|
.parseSignedClaims(token).getPayload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Access Token을 쿠키에서 추출
|
||||||
public String extractAccessJwtFromRequest(HttpServletRequest request) {
|
public String extractAccessJwtFromRequest(HttpServletRequest request) {
|
||||||
String bearerToken = request.getHeader("Authorization");
|
if (request.getCookies() != null) {
|
||||||
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
|
for (Cookie cookie : request.getCookies()) {
|
||||||
return bearerToken.substring(7); // "Bearer " 제거
|
if ("AccessToken".equals(cookie.getName())) {
|
||||||
|
return cookie.getValue();
|
||||||
}
|
}
|
||||||
return bearerToken;
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Refresh Token을 쿠키에서 추출
|
||||||
public String extractRefreshJwtFromCookie(HttpServletRequest request) {
|
public String extractRefreshJwtFromCookie(HttpServletRequest request) {
|
||||||
if (request.getCookies() != null) {
|
if (request.getCookies() != null) {
|
||||||
for (Cookie cookie : request.getCookies()) {
|
for (Cookie cookie : request.getCookies()) {
|
||||||
@@ -168,35 +154,41 @@ public class JwtUtils {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Access Token 생성
|
|
||||||
public String createAccessToken(String username, String role) {
|
|
||||||
return generateToken(username, role,
|
|
||||||
Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_access"))));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh Token 생성
|
|
||||||
public String createRefreshToken(String username, String role) {
|
|
||||||
return generateToken(username, role,
|
|
||||||
Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_refresh"))));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh Token 갱신 (Access Token 갱신 시 함께)
|
|
||||||
public String refreshTokens(String username, String role) {
|
|
||||||
// 새로운 Refresh Token 생성 및 DB 저장
|
|
||||||
String newRefreshToken = createRefreshToken(username, role);
|
|
||||||
|
|
||||||
log.info("Refresh Token 갱신 완료: {}", username);
|
|
||||||
return newRefreshToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh Token 쿠키 설정
|
// Refresh Token 쿠키 설정
|
||||||
public void setRefreshTokenCookie(HttpServletResponse response, String refreshToken) {
|
public void setRefreshTokenCookie(HttpServletResponse response, String refreshToken) {
|
||||||
Cookie refreshTokenCookie = new Cookie("RefreshToken", refreshToken);
|
setCookie(response, "RefreshToken", refreshToken,
|
||||||
refreshTokenCookie.setHttpOnly(true);
|
Integer.parseInt(env.getProperty("token.expiration_time_refresh")) / 1000);
|
||||||
refreshTokenCookie.setSecure(false);
|
}
|
||||||
refreshTokenCookie.setPath("/");
|
|
||||||
refreshTokenCookie.setMaxAge(Integer.parseInt(env.getProperty("token.expiration_time_refresh")));
|
|
||||||
|
|
||||||
response.addCookie(refreshTokenCookie);
|
// Access Token 쿠키 설정
|
||||||
|
public void setAccessTokenCookie(HttpServletResponse response, String accessToken) {
|
||||||
|
setCookie(response, "AccessToken", accessToken,
|
||||||
|
Integer.parseInt(env.getProperty("token.expiration_time_access")) / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access Token 쿠키 삭제
|
||||||
|
public void deleteAccessTokenCookie(HttpServletResponse response) {
|
||||||
|
setCookie(response, "AccessToken", "", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh Token 쿠키 삭제
|
||||||
|
public void deleteRefreshTokenCookie(HttpServletResponse response) {
|
||||||
|
setCookie(response, "RefreshToken", "", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 모든 토큰 쿠키 삭제
|
||||||
|
public void deleteAllTokenCookies(HttpServletResponse response) {
|
||||||
|
deleteAccessTokenCookie(response);
|
||||||
|
deleteRefreshTokenCookie(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 쿠키 설정 헬퍼 메서드
|
||||||
|
private void setCookie(HttpServletResponse response, String name, String value, int maxAge) {
|
||||||
|
Cookie cookie = new Cookie(name, value);
|
||||||
|
cookie.setHttpOnly(true);
|
||||||
|
cookie.setSecure(false);
|
||||||
|
cookie.setPath("/");
|
||||||
|
cookie.setMaxAge(maxAge);
|
||||||
|
response.addCookie(cookie);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -57,12 +57,5 @@ public class SecurityUtils {
|
|||||||
return authentication != null && authentication.isAuthenticated();
|
return authentication != null && authentication.isAuthenticated();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 현재 인증된 사용자의 역할을 반환합니다.
|
|
||||||
* @return 역할 문자열
|
|
||||||
*/
|
|
||||||
public static String getCurrentUserRole() {
|
|
||||||
MemberDto member = getCurrentMember();
|
|
||||||
return member != null ? member.getRole().name() : null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -113,8 +113,7 @@ springdoc.default-consumes-media-type=application/json
|
|||||||
|
|
||||||
# ========================================
|
# ========================================
|
||||||
# 보안 설정 - 허용할 경로
|
# 보안 설정 - 허용할 경로
|
||||||
# ========================================
|
security.permit-all-paths=/login,/logout,/members/register,/swagger-ui/**,/swagger-ui.html,/swagger-ui/index.html,/api-docs,/api-docs/**,/v3/api-docs,/v3/api-docs/**,/ws/**,/actuator/**,/actuator/health/**,/actuator/info
|
||||||
security.permit-all-paths=/login,/members/register,/swagger-ui/**,/swagger-ui.html,/swagger-ui/index.html,/api-docs,/api-docs/**,/v3/api-docs,/v3/api-docs/**,/ws/**,/actuator/**,/actuator/health/**,/actuator/info
|
|
||||||
|
|
||||||
# 파일 업로드 설정
|
# 파일 업로드 설정
|
||||||
# ========================================
|
# ========================================
|
||||||
|
Reference in New Issue
Block a user