토큰 response 수정

This commit is contained in:
2025-12-10 15:50:48 +09:00
parent 843e638ad4
commit 0fb715c3ed
9 changed files with 132 additions and 221 deletions

View File

@@ -6,14 +6,12 @@ import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 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.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@@ -41,21 +39,22 @@ public class SecurityConfig {
.authenticationProvider( .authenticationProvider(
customAuthenticationProvider) // 로그인 패스워드 비교방식 스프링 기본 Provider 사용안함 커스텀 사용 customAuthenticationProvider) // 로그인 패스워드 비교방식 스프링 기본 Provider 사용안함 커스텀 사용
.authorizeHttpRequests( .authorizeHttpRequests(
auth -> auth -> auth.anyRequest().permitAll()
auth.requestMatchers(HttpMethod.OPTIONS, "/**") // .requestMatchers(HttpMethod.OPTIONS, "/**")
.permitAll() // preflight 허용 // .permitAll() // preflight 허용
.requestMatchers( // .requestMatchers(
"/api/auth/signin", // "/api/auth/signin",
"/api/auth/refresh", // "/api/auth/refresh",
"/swagger-ui/**", // "/swagger-ui/**",
"/v3/api-docs/**") // "/v3/api-docs/**")
.permitAll() // .permitAll()
.anyRequest() // .anyRequest()
.authenticated()) // .authenticated()
.addFilterBefore( )
jwtAuthenticationFilter, // .addFilterBefore(
UsernamePasswordAuthenticationFilter // jwtAuthenticationFilter,
.class) // 요청 들어오면 먼저 JWT 토큰 검사 후 security context 에 사용자 정보 저장. // UsernamePasswordAuthenticationFilter
// .class) // 요청 들어오면 먼저 JWT 토큰 검사 후 security context 에 사용자 정보 저장.
; ;
return http.build(); return http.build();

View File

@@ -86,7 +86,7 @@ public class AuthController {
.build(); .build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString()); response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
return ApiResponseDto.ok(new TokenResponse(accessToken)); return ApiResponseDto.ok(new TokenResponse(accessToken, refreshToken));
} }
@PostMapping("/refresh") @PostMapping("/refresh")
@@ -133,7 +133,7 @@ public class AuthController {
.build(); .build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString()); response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
return ResponseEntity.ok(new TokenResponse(newAccessToken)); return ResponseEntity.ok(new TokenResponse(newAccessToken, newRefreshToken));
} }
@PostMapping("/logout") @PostMapping("/logout")
@@ -166,5 +166,5 @@ public class AuthController {
return ApiResponseDto.createOK(ResponseEntity.noContent().build()); return ApiResponseDto.createOK(ResponseEntity.noContent().build());
} }
public record TokenResponse(String accessToken) {} public record TokenResponse(String accessToken, String refreshToken) {}
} }

View File

@@ -11,7 +11,7 @@ import lombok.ToString;
@ToString(exclude = "password") @ToString(exclude = "password")
public class SignInRequest { public class SignInRequest {
@Schema(description = "", example = "11111") @Schema(description = "용자 ID", example = "admin")
private String username; private String username;
@Schema(description = "비밀번호", example = "kamco1234!") @Schema(description = "비밀번호", example = "kamco1234!")

View File

@@ -1,17 +1,11 @@
package com.kamco.cd.kamcoback.members.service; package com.kamco.cd.kamcoback.members.service;
import com.kamco.cd.kamcoback.auth.BCryptSaltGenerator;
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
import com.kamco.cd.kamcoback.members.dto.MembersDto; import com.kamco.cd.kamcoback.members.dto.MembersDto;
import com.kamco.cd.kamcoback.members.dto.MembersDto.Basic; import com.kamco.cd.kamcoback.members.dto.MembersDto.Basic;
import com.kamco.cd.kamcoback.postgres.core.MembersCoreService; import com.kamco.cd.kamcoback.postgres.core.MembersCoreService;
import java.util.UUID;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -29,7 +23,7 @@ public class MembersService {
* @return * @return
*/ */
public Page<Basic> findByMembers(MembersDto.SearchReq searchReq) { public Page<Basic> findByMembers(MembersDto.SearchReq searchReq) {
return membersCoreService.findByMembers(searchReq); return null; // membersCoreService.findByMembers(searchReq);
} }
/** /**
@@ -38,29 +32,29 @@ public class MembersService {
* @param uuid * @param uuid
* @param updateReq * @param updateReq
*/ */
public void updateMember(UUID uuid, MembersDto.UpdateReq updateReq) { // public void updateMember(UUID uuid, MembersDto.UpdateReq updateReq) {
//
if (StringUtils.isNotBlank(updateReq.getPassword())) { // if (StringUtils.isNotBlank(updateReq.getPassword())) {
//
if (!this.isValidPassword(updateReq.getPassword())) { // if (!this.isValidPassword(updateReq.getPassword())) {
throw new CustomApiException("WRONG_PASSWORD", HttpStatus.BAD_REQUEST); // throw new CustomApiException("WRONG_PASSWORD", HttpStatus.BAD_REQUEST);
} // }
//
if (StringUtils.isBlank(updateReq.getEmployeeNo())) { // if (StringUtils.isBlank(updateReq.getEmployeeNo())) {
throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST); // throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST);
} // }
//
// salt 생성, 사번이 salt // // salt 생성, 사번이 salt
String salt = // String salt =
BCryptSaltGenerator.generateSaltWithEmployeeNo(updateReq.getEmployeeNo().trim()); // BCryptSaltGenerator.generateSaltWithEmployeeNo(updateReq.getEmployeeNo().trim());
//
// 패스워드 암호화, 초기 패스워드 고정 // // 패스워드 암호화, 초기 패스워드 고정
String hashedPassword = BCrypt.hashpw(updateReq.getPassword(), salt); // String hashedPassword = BCrypt.hashpw(updateReq.getPassword(), salt);
updateReq.setPassword(hashedPassword); // updateReq.setPassword(hashedPassword);
} // }
//
membersCoreService.updateMembers(uuid, updateReq); // membersCoreService.updateMembers(uuid, updateReq);
} // }
/** /**
* 대문자 1개 이상 소문자 1개 이상 숫자 1개 이상 특수문자(!@#$) 1개 이상 * 대문자 1개 이상 소문자 1개 이상 숫자 1개 이상 특수문자(!@#$) 1개 이상

View File

@@ -1,21 +1,20 @@
package com.kamco.cd.kamcoback.postgres.repository.members; package com.kamco.cd.kamcoback.postgres.repository.members;
import com.kamco.cd.kamcoback.members.dto.MembersDto;
import com.kamco.cd.kamcoback.members.dto.MembersDto.Basic;
import com.kamco.cd.kamcoback.postgres.entity.MemberEntity; import com.kamco.cd.kamcoback.postgres.entity.MemberEntity;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import org.springframework.data.domain.Page;
public interface MembersRepositoryCustom { public interface MembersRepositoryCustom {
boolean existsByEmployeeNo(String employeeNo); boolean existsByUserId(String userId);
boolean existsByEmail(String email); Optional<MemberEntity> findByUserId(String employeeNo);
Page<Basic> findByMembers(MembersDto.SearchReq searchReq);
Optional<MemberEntity> findByUUID(UUID uuid); Optional<MemberEntity> findByUUID(UUID uuid);
//
// Page<Basic> findByMembers(MembersDto.SearchReq searchReq);
//
Optional<MemberEntity> findByEmployeeNo(String employeeNo); //
// Optional<MemberEntity> findByEmployeeNo(String employeeNo);
} }

View File

@@ -1,23 +1,11 @@
package com.kamco.cd.kamcoback.postgres.repository.members; package com.kamco.cd.kamcoback.postgres.repository.members;
import com.kamco.cd.kamcoback.common.enums.RoleType;
import com.kamco.cd.kamcoback.members.dto.MembersDto;
import com.kamco.cd.kamcoback.members.dto.MembersDto.Basic;
import com.kamco.cd.kamcoback.postgres.entity.MemberEntity; import com.kamco.cd.kamcoback.postgres.entity.MemberEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMemberEntity; import com.kamco.cd.kamcoback.postgres.entity.QMemberEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMemberRoleEntity;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory; import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@Repository @Repository
@@ -26,130 +14,106 @@ public class MembersRepositoryImpl implements MembersRepositoryCustom {
private final JPAQueryFactory queryFactory; private final JPAQueryFactory queryFactory;
private final QMemberEntity memberEntity = QMemberEntity.memberEntity; private final QMemberEntity memberEntity = QMemberEntity.memberEntity;
private final QMemberRoleEntity memberRoleEntity = QMemberRoleEntity.memberRoleEntity;
/** /**
* 사원번호 조회 * 사용자 ID 조회
* *
* @param employeeNo * @param userId
* @return * @return
*/ */
@Override @Override
public boolean existsByEmployeeNo(String employeeNo) { public boolean existsByUserId(String userId) {
return queryFactory return queryFactory
.selectOne() .selectOne()
.from(memberEntity) .from(memberEntity)
.where(memberEntity.employeeNo.eq(employeeNo)) .where(memberEntity.userId.eq(userId))
.fetchFirst() .fetchFirst()
!= null; != null;
} }
/**
* 이메일 조회
*
* @param email
* @return
*/
@Override @Override
public boolean existsByEmail(String email) { public Optional<MemberEntity> findByUserId(String userId) {
return queryFactory return Optional.ofNullable(
.selectOne() queryFactory.selectFrom(memberEntity).where(memberEntity.userId.eq(userId)).fetchOne());
.from(memberEntity)
.where(memberEntity.email.eq(email))
.fetchFirst()
!= null;
}
/**
* 회원정보 목록 조회
*
* @param searchReq
* @return
*/
@Override
public Page<Basic> findByMembers(MembersDto.SearchReq searchReq) {
Pageable pageable = searchReq.toPageable();
BooleanBuilder builder = new BooleanBuilder();
BooleanBuilder leftBuilder = new BooleanBuilder();
if (StringUtils.isNotBlank(searchReq.getField())) {
switch (searchReq.getField()) {
case "name" ->
builder.and(memberEntity.name.containsIgnoreCase(searchReq.getKeyword().trim()));
case "email" ->
builder.and(memberEntity.email.containsIgnoreCase(searchReq.getKeyword().trim()));
case "employeeNo" ->
builder.and(memberEntity.employeeNo.containsIgnoreCase(searchReq.getKeyword().trim()));
}
}
List<String> roles = new ArrayList<>();
// 라벨러
if (searchReq.isLabeler()) {
roles.add(RoleType.ROLE_LABELER.getId());
}
// 시스템 전체 관리자
if (searchReq.isAdmin()) {
roles.add(RoleType.ROLE_ADMIN.getId());
}
// 검수자
if (searchReq.isReviewer()) {
roles.add(RoleType.ROLE_REVIEWER.getId());
}
// 역할 in 조건 추가
if (!roles.isEmpty()) {
leftBuilder.and(memberRoleEntity.id.roleName.in(roles));
}
List<MembersDto.Basic> content =
queryFactory
.select(
Projections.constructor(
MembersDto.Basic.class,
memberEntity.id,
memberEntity.uuid,
memberEntity.employeeNo,
memberEntity.name,
memberEntity.email,
memberEntity.status,
memberRoleEntity.id.roleName,
memberEntity.createdDttm,
memberEntity.updatedDttm))
.from(memberEntity)
.leftJoin(memberRoleEntity)
.on(memberRoleEntity.memberUuid.uuid.eq(memberEntity.uuid).and(leftBuilder))
.where(builder)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(memberEntity.createdDttm.desc())
.fetch();
long total =
queryFactory
.select(memberEntity)
.from(memberEntity)
.leftJoin(memberRoleEntity)
.on(memberRoleEntity.memberUuid.uuid.eq(memberEntity.uuid).and(leftBuilder))
.fetchCount();
return new PageImpl<>(content, pageable, total);
} }
// /**
// * 회원정보 목록 조회
// *
// * @param searchReq
// * @return
// */
// @Override
// public Page<Basic> findByMembers(MembersDto.SearchReq searchReq) {
// Pageable pageable = searchReq.toPageable();
// BooleanBuilder builder = new BooleanBuilder();
// BooleanBuilder leftBuilder = new BooleanBuilder();
//
// if (StringUtils.isNotBlank(searchReq.getField())) {
// switch (searchReq.getField()) {
// case "name" ->
// builder.and(memberEntity.name.containsIgnoreCase(searchReq.getKeyword().trim()));
// }
// }
//
// List<String> roles = new ArrayList<>();
// // 라벨러
// if (searchReq.isLabeler()) {
// roles.add(RoleType.ROLE_LABELER.getId());
// }
//
// // 시스템 전체 관리자
// if (searchReq.isAdmin()) {
// roles.add(RoleType.ROLE_ADMIN.getId());
// }
//
// // 검수자
// if (searchReq.isReviewer()) {
// roles.add(RoleType.ROLE_REVIEWER.getId());
// }
//
// // 역할 in 조건 추가
// if (!roles.isEmpty()) {
// leftBuilder.and(memberRoleEntity.id.roleName.in(roles));
// }
//
// List<MembersDto.Basic> content =
// queryFactory
// .select(
// Projections.constructor(
// MembersDto.Basic.class,
// memberEntity.id,
// memberEntity.uuid,
// memberEntity.employeeNo,
// memberEntity.name,
// null,
// memberEntity.status,
// memberRoleEntity.id.roleName,
// memberEntity.createdDttm,
// memberEntity.updatedDttm))
// .from(memberEntity)
// .leftJoin(memberRoleEntity)
// .on(memberRoleEntity.memberUuid.uuid.eq(memberEntity.uuid).and(leftBuilder))
// .where(builder)
// .offset(pageable.getOffset())
// .limit(pageable.getPageSize())
// .orderBy(memberEntity.createdDttm.desc())
// .fetch();
//
// long total =
// queryFactory
// .select(memberEntity)
// .from(memberEntity)
// .leftJoin(memberRoleEntity)
// .on(memberRoleEntity.memberUuid.uuid.eq(memberEntity.uuid).and(leftBuilder))
// .fetchCount();
//
// return new PageImpl<>(content, pageable, total);
// }
//
@Override @Override
public Optional<MemberEntity> findByUUID(UUID uuid) { public Optional<MemberEntity> findByUUID(UUID uuid) {
return Optional.ofNullable( return Optional.ofNullable(
queryFactory.selectFrom(memberEntity).where(memberEntity.uuid.eq(uuid)).fetchOne()); queryFactory.selectFrom(memberEntity).where(memberEntity.uuid.eq(uuid)).fetchOne());
} }
@Override
public Optional<MemberEntity> findByEmployeeNo(String employeeNo) {
return Optional.ofNullable(
queryFactory
.selectFrom(memberEntity)
.where(memberEntity.employeeNo.eq(employeeNo))
.fetchOne());
}
} }

View File

@@ -1,7 +0,0 @@
package com.kamco.cd.kamcoback.postgres.repository.members;
import com.kamco.cd.kamcoback.postgres.entity.MemberRoleEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MembersRoleRepository
extends JpaRepository<MemberRoleEntity, Long>, MembersRoleRepositoryCutom {}

View File

@@ -1,8 +0,0 @@
package com.kamco.cd.kamcoback.postgres.repository.members;
import com.kamco.cd.kamcoback.members.dto.MembersDto;
public interface MembersRoleRepositoryCutom {
boolean findByUuidAndRoleName(MembersDto.RolesDto rolesDto);
}

View File

@@ -1,30 +0,0 @@
package com.kamco.cd.kamcoback.postgres.repository.members;
import com.kamco.cd.kamcoback.members.dto.MembersDto;
import com.kamco.cd.kamcoback.postgres.entity.QMemberRoleEntity;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
@RequiredArgsConstructor
@Repository
public class MembersRoleRepositoryImpl implements MembersRoleRepositoryCutom {
private final JPAQueryFactory queryFactory;
private final QMemberRoleEntity memberRoleEntity = QMemberRoleEntity.memberRoleEntity;
@Override
public boolean findByUuidAndRoleName(MembersDto.RolesDto rolesDto) {
return queryFactory
.select(memberRoleEntity)
.from(memberRoleEntity)
.where(
memberRoleEntity
.id
.memberUuid
.eq(rolesDto.getUuid())
.and(memberRoleEntity.id.roleName.eq(rolesDto.getRoleName())))
.fetchOne()
!= null;
}
}