diff --git a/build.gradle b/build.gradle index e85e69fa..0359f278 100644 --- a/build.gradle +++ b/build.gradle @@ -41,6 +41,16 @@ dependencies { annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api" + + implementation 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'org.springframework.security:spring-security-test' + + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:3.1.1.RELEASE' + + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' } diff --git a/src/main/generated/umc/study/domain/QUser.java b/src/main/generated/umc/study/domain/QUser.java index 67813307..229964fe 100644 --- a/src/main/generated/umc/study/domain/QUser.java +++ b/src/main/generated/umc/study/domain/QUser.java @@ -43,12 +43,16 @@ public class QUser extends EntityPathBase { public final StringPath name = createString("name"); + public final StringPath password = createString("password"); + public final StringPath phoneNumber = createString("phoneNumber"); public final NumberPath point = createNumber("point", Integer.class); public final ListPath reviewList = this.createList("reviewList", Review.class, QReview.class, PathInits.DIRECT2); + public final EnumPath role = createEnum("role", umc.study.domain.enums.Role.class); + public final EnumPath status = createEnum("status", umc.study.domain.enums.UserStatus.class); //inherited diff --git a/src/main/generated/umc/study/domain/mapping/QUserPrefer.java b/src/main/generated/umc/study/domain/mapping/QUserPrefer.java index a119bdf5..9b311d8b 100644 --- a/src/main/generated/umc/study/domain/mapping/QUserPrefer.java +++ b/src/main/generated/umc/study/domain/mapping/QUserPrefer.java @@ -54,7 +54,7 @@ public QUserPrefer(PathMetadata metadata, PathInits inits) { public QUserPrefer(Class type, PathMetadata metadata, PathInits inits) { super(type, metadata, inits); - this.foodCategory = inits.isInitialized("foodCategory") ? new umc.study.domain.QFoodCategory(forProperty("foodCategory")) : null; + this.foodCategory = inits.isInitialized("foodCategory") ? new umc.study.domain.QFoodCategory(forProperty("foodCategory"), inits.get("foodCategory")) : null; this.user = inits.isInitialized("user") ? new umc.study.domain.QUser(forProperty("user")) : null; } diff --git a/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java b/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java index 4ad13880..d55c0569 100644 --- a/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java +++ b/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java @@ -21,6 +21,10 @@ public enum ErrorStatus implements BaseErrorCode { USER_NOT_FOUND(HttpStatus.BAD_REQUEST, "USER4001", "no user found."), NICKNAME_NOT_EXIST(HttpStatus.BAD_REQUEST, "USER4002", "nickname is required."), INVALID_GENDER(HttpStatus.BAD_REQUEST, "USER4003", "Invalid gender."), + INVALID_PASSWORD(HttpStatus.BAD_REQUEST, "USER4003", "password is incorrect."), + DUPLICATE_JOIN_REQUEST(HttpStatus.BAD_REQUEST, "USER4004", "already exists with the same email address."), + + INVALID_TOKEN(HttpStatus.BAD_REQUEST, "USER4005", "Invalid token."), // 게시글 에러 ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "ARTICLE4001", "no articles."), diff --git a/src/main/java/umc/study/apiPayload/exceptition/ExceptionAdvice.java b/src/main/java/umc/study/apiPayload/exception/ExceptionAdvice.java similarity index 99% rename from src/main/java/umc/study/apiPayload/exceptition/ExceptionAdvice.java rename to src/main/java/umc/study/apiPayload/exception/ExceptionAdvice.java index 2b67bb8d..c785d149 100644 --- a/src/main/java/umc/study/apiPayload/exceptition/ExceptionAdvice.java +++ b/src/main/java/umc/study/apiPayload/exception/ExceptionAdvice.java @@ -1,4 +1,4 @@ -package umc.study.apiPayload.exceptition; +package umc.study.apiPayload.exception; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.ConstraintViolationException; diff --git a/src/main/java/umc/study/apiPayload/exceptition/GeneralException.java b/src/main/java/umc/study/apiPayload/exception/GeneralException.java similarity index 91% rename from src/main/java/umc/study/apiPayload/exceptition/GeneralException.java rename to src/main/java/umc/study/apiPayload/exception/GeneralException.java index 59dc3d0e..e9a456e1 100644 --- a/src/main/java/umc/study/apiPayload/exceptition/GeneralException.java +++ b/src/main/java/umc/study/apiPayload/exception/GeneralException.java @@ -1,4 +1,4 @@ -package umc.study.apiPayload.exceptition; +package umc.study.apiPayload.exception; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/umc/study/apiPayload/exceptition/handler/FoodCategoryHandler.java b/src/main/java/umc/study/apiPayload/exception/handler/FoodCategoryHandler.java similarity index 65% rename from src/main/java/umc/study/apiPayload/exceptition/handler/FoodCategoryHandler.java rename to src/main/java/umc/study/apiPayload/exception/handler/FoodCategoryHandler.java index d7a379a2..234afa96 100644 --- a/src/main/java/umc/study/apiPayload/exceptition/handler/FoodCategoryHandler.java +++ b/src/main/java/umc/study/apiPayload/exception/handler/FoodCategoryHandler.java @@ -1,7 +1,7 @@ -package umc.study.apiPayload.exceptition.handler; +package umc.study.apiPayload.exception.handler; import umc.study.apiPayload.code.BaseErrorCode; -import umc.study.apiPayload.exceptition.GeneralException; +import umc.study.apiPayload.exception.GeneralException; public class FoodCategoryHandler extends GeneralException { public FoodCategoryHandler(BaseErrorCode errorCode) { diff --git a/src/main/java/umc/study/apiPayload/exceptition/handler/LocationHandler.java b/src/main/java/umc/study/apiPayload/exception/handler/LocationHandler.java similarity index 64% rename from src/main/java/umc/study/apiPayload/exceptition/handler/LocationHandler.java rename to src/main/java/umc/study/apiPayload/exception/handler/LocationHandler.java index f77e8d2d..847a534b 100644 --- a/src/main/java/umc/study/apiPayload/exceptition/handler/LocationHandler.java +++ b/src/main/java/umc/study/apiPayload/exception/handler/LocationHandler.java @@ -1,7 +1,7 @@ -package umc.study.apiPayload.exceptition.handler; +package umc.study.apiPayload.exception.handler; import umc.study.apiPayload.code.BaseErrorCode; -import umc.study.apiPayload.exceptition.GeneralException; +import umc.study.apiPayload.exception.GeneralException; public class LocationHandler extends GeneralException { public LocationHandler(BaseErrorCode errorCode) { diff --git a/src/main/java/umc/study/apiPayload/exceptition/handler/MissionHandler.java b/src/main/java/umc/study/apiPayload/exception/handler/MissionHandler.java similarity index 63% rename from src/main/java/umc/study/apiPayload/exceptition/handler/MissionHandler.java rename to src/main/java/umc/study/apiPayload/exception/handler/MissionHandler.java index 7992cf19..4ff020d2 100644 --- a/src/main/java/umc/study/apiPayload/exceptition/handler/MissionHandler.java +++ b/src/main/java/umc/study/apiPayload/exception/handler/MissionHandler.java @@ -1,7 +1,7 @@ -package umc.study.apiPayload.exceptition.handler; +package umc.study.apiPayload.exception.handler; import umc.study.apiPayload.code.BaseErrorCode; -import umc.study.apiPayload.exceptition.GeneralException; +import umc.study.apiPayload.exception.GeneralException; public class MissionHandler extends GeneralException { MissionHandler(BaseErrorCode errorCode) { diff --git a/src/main/java/umc/study/apiPayload/exceptition/handler/StoreHandler.java b/src/main/java/umc/study/apiPayload/exception/handler/StoreHandler.java similarity index 63% rename from src/main/java/umc/study/apiPayload/exceptition/handler/StoreHandler.java rename to src/main/java/umc/study/apiPayload/exception/handler/StoreHandler.java index e660bc35..88c39751 100644 --- a/src/main/java/umc/study/apiPayload/exceptition/handler/StoreHandler.java +++ b/src/main/java/umc/study/apiPayload/exception/handler/StoreHandler.java @@ -1,7 +1,7 @@ -package umc.study.apiPayload.exceptition.handler; +package umc.study.apiPayload.exception.handler; import umc.study.apiPayload.code.BaseErrorCode; -import umc.study.apiPayload.exceptition.GeneralException; +import umc.study.apiPayload.exception.GeneralException; public class StoreHandler extends GeneralException { public StoreHandler(BaseErrorCode errorCode) { diff --git a/src/main/java/umc/study/apiPayload/exceptition/handler/TempHandler.java b/src/main/java/umc/study/apiPayload/exception/handler/TempHandler.java similarity index 63% rename from src/main/java/umc/study/apiPayload/exceptition/handler/TempHandler.java rename to src/main/java/umc/study/apiPayload/exception/handler/TempHandler.java index dc726286..5ca4a8f6 100644 --- a/src/main/java/umc/study/apiPayload/exceptition/handler/TempHandler.java +++ b/src/main/java/umc/study/apiPayload/exception/handler/TempHandler.java @@ -1,7 +1,7 @@ -package umc.study.apiPayload.exceptition.handler; +package umc.study.apiPayload.exception.handler; import umc.study.apiPayload.code.BaseErrorCode; -import umc.study.apiPayload.exceptition.GeneralException; +import umc.study.apiPayload.exception.GeneralException; public class TempHandler extends GeneralException { diff --git a/src/main/java/umc/study/apiPayload/exceptition/handler/UserHandler.java b/src/main/java/umc/study/apiPayload/exception/handler/UserHandler.java similarity index 63% rename from src/main/java/umc/study/apiPayload/exceptition/handler/UserHandler.java rename to src/main/java/umc/study/apiPayload/exception/handler/UserHandler.java index d3d326e9..5c626c23 100644 --- a/src/main/java/umc/study/apiPayload/exceptition/handler/UserHandler.java +++ b/src/main/java/umc/study/apiPayload/exception/handler/UserHandler.java @@ -1,7 +1,7 @@ -package umc.study.apiPayload.exceptition.handler; +package umc.study.apiPayload.exception.handler; import umc.study.apiPayload.code.BaseErrorCode; -import umc.study.apiPayload.exceptition.GeneralException; +import umc.study.apiPayload.exception.GeneralException; public class UserHandler extends GeneralException { public UserHandler(BaseErrorCode errorCode) { diff --git a/src/main/java/umc/study/config/properties/Constants.java b/src/main/java/umc/study/config/properties/Constants.java new file mode 100644 index 00000000..047af2aa --- /dev/null +++ b/src/main/java/umc/study/config/properties/Constants.java @@ -0,0 +1,6 @@ +package umc.study.config.properties; + +public class Constants { + public static final String AUTH_HEADER = "Authorization"; + public static final String TOKEN_PREFIX = "Bearer "; +} diff --git a/src/main/java/umc/study/config/properties/JwtProperties.java b/src/main/java/umc/study/config/properties/JwtProperties.java new file mode 100644 index 00000000..c08c5a9b --- /dev/null +++ b/src/main/java/umc/study/config/properties/JwtProperties.java @@ -0,0 +1,22 @@ +package umc.study.config.properties; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@Getter +@Setter +@ConfigurationProperties("jwt.token") +public class JwtProperties { + private String secretKey=""; + private Expiration expiration; + + @Getter + @Setter + public static class Expiration{ + private Long access; + // TODO: refreshToken + } +} diff --git a/src/main/java/umc/study/config/security/CustomUserDetailService.java b/src/main/java/umc/study/config/security/CustomUserDetailService.java new file mode 100644 index 00000000..1b46d030 --- /dev/null +++ b/src/main/java/umc/study/config/security/CustomUserDetailService.java @@ -0,0 +1,34 @@ +package umc.study.config.security; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import umc.study.domain.User; +import umc.study.repository.UserRepository.UserRepository; + +@Slf4j +@Service +@RequiredArgsConstructor +public class CustomUserDetailService implements UserDetailsService { + private final UserRepository userRepository; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + log.info(">>> 사용자 정보 로딩: {}", username); + + User user = (User) userRepository.findByEmail(username) + .orElseThrow(() -> new UsernameNotFoundException("해당 이메일을 가진 유저가 존재하지 않습니다: " + username)); + + log.info(">>> 로드된 사용자 정보 - ID: {}, 이메일: {}, 역할: {}", + user.getId(), user.getEmail(), user.getRole()); + + return org.springframework.security.core.userdetails.User + .withUsername(user.getEmail()) + .password(user.getPassword()) + .roles(user.getRole().name()) + .build(); + } +} diff --git a/src/main/java/umc/study/config/security/SecurityConfig.java b/src/main/java/umc/study/config/security/SecurityConfig.java new file mode 100644 index 00000000..9997f2aa --- /dev/null +++ b/src/main/java/umc/study/config/security/SecurityConfig.java @@ -0,0 +1,46 @@ +package umc.study.config.security; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import umc.study.config.security.jwt.JwtAuthenticationFilter; +import umc.study.config.security.jwt.JwtTokenProvider; + +@EnableWebSecurity +@Configuration +@RequiredArgsConstructor +public class SecurityConfig { + + private final JwtTokenProvider jwtTokenProvider; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .sessionManagement(session -> + session.sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ) + .authorizeHttpRequests( + (requests) -> requests + .requestMatchers("/", "/users/join", "/users/login", "/swagger-ui/**", "/v3/api-docs/**").permitAll() + .requestMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ) + .csrf() + .disable() + .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} \ No newline at end of file diff --git a/src/main/java/umc/study/config/security/jwt/JwtAuthenticationFilter.java b/src/main/java/umc/study/config/security/jwt/JwtAuthenticationFilter.java new file mode 100644 index 00000000..76c5d687 --- /dev/null +++ b/src/main/java/umc/study/config/security/jwt/JwtAuthenticationFilter.java @@ -0,0 +1,43 @@ +package umc.study.config.security.jwt; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; +import umc.study.config.properties.Constants; + +import java.io.IOException; + +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtTokenProvider jwtTokenProvider; + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) + throws ServletException, IOException { + + String token = resolveToken(request); + + if(StringUtils.hasText(token) && jwtTokenProvider.validateToken(token)) { + Authentication authentication = jwtTokenProvider.getAuthentication(token); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + filterChain.doFilter(request, response); + } + + private String resolveToken(HttpServletRequest request) { + String bearerToken = request.getHeader(Constants.AUTH_HEADER); + if(StringUtils.hasText(bearerToken) && bearerToken.startsWith(Constants.TOKEN_PREFIX)) { + return bearerToken.substring(Constants.TOKEN_PREFIX.length()); + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/umc/study/config/security/jwt/JwtTokenProvider.java b/src/main/java/umc/study/config/security/jwt/JwtTokenProvider.java new file mode 100644 index 00000000..8bd9ceea --- /dev/null +++ b/src/main/java/umc/study/config/security/jwt/JwtTokenProvider.java @@ -0,0 +1,84 @@ +package umc.study.config.security.jwt; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.User; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import umc.study.apiPayload.code.status.ErrorStatus; +import umc.study.apiPayload.exception.handler.UserHandler; +import umc.study.config.properties.Constants; +import umc.study.config.properties.JwtProperties; + +import java.security.Key; +import java.util.Date; +import java.util.Collections; + +@Component +@RequiredArgsConstructor +public class JwtTokenProvider { + + private final JwtProperties jwtProperties; + + private Key getSigningKey() { + return Keys.hmacShaKeyFor(jwtProperties.getSecretKey().getBytes()); + } + + public String generateToken(Authentication authentication) { + String email = authentication.getName(); + + return Jwts.builder() + .setSubject(email) + .claim("role", authentication.getAuthorities().iterator().next().getAuthority()) + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + jwtProperties.getExpiration().getAccess())) + .signWith(getSigningKey(), SignatureAlgorithm.HS256) + .compact(); + } + + public boolean validateToken(String token) { + try { + Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build() + .parseClaimsJws(token); + return true; + } catch (JwtException | IllegalArgumentException e) { + return false; + } + } + + public Authentication getAuthentication(String token) { + Claims claims = Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build() + .parseClaimsJws(token) + .getBody(); + + String email = claims.getSubject(); + String role = claims.get("role", String.class); + + User principal = new User(email, "", Collections.singleton(() -> role)); + return new UsernamePasswordAuthenticationToken(principal, token, principal.getAuthorities()); + } + + public static String resolveToken(HttpServletRequest request) { + String bearerToken = request.getHeader(Constants.AUTH_HEADER); + if(StringUtils.hasText(bearerToken) && bearerToken.startsWith(Constants.TOKEN_PREFIX)) { + return bearerToken.substring(Constants.TOKEN_PREFIX.length()); + } + return null; + } + + public Authentication extractAuthentication(HttpServletRequest request){ + String accessToken = resolveToken(request); + if(accessToken == null || !validateToken(accessToken)) { + throw new UserHandler(ErrorStatus.INVALID_TOKEN); + } + return getAuthentication(accessToken); + } +} \ No newline at end of file diff --git a/src/main/java/umc/study/converter/StoreConverter.java b/src/main/java/umc/study/converter/StoreConverter.java index d29691ed..7f601c2e 100644 --- a/src/main/java/umc/study/converter/StoreConverter.java +++ b/src/main/java/umc/study/converter/StoreConverter.java @@ -37,17 +37,15 @@ public static StoreResponseDto.ReviewPreViewDTO reviewPreViewDTO(Review review){ .build(); } public static StoreResponseDto.ReviewPreViewListDTO reviewPreViewListDTO(Page reviewList){ - - List reviewPreViewDTOList = reviewList.stream() - .map(StoreConverter::reviewPreViewDTO).collect(Collectors.toList()); + Page reviewPreViewDTOPage = reviewList.map(StoreConverter::reviewPreViewDTO); return StoreResponseDto.ReviewPreViewListDTO.builder() - .isLast(reviewList.isLast()) - .isFirst(reviewList.isFirst()) - .totalPage(reviewList.getTotalPages()) - .totalElements(reviewList.getTotalElements()) - .listSize(reviewPreViewDTOList.size()) - .reviewList(reviewPreViewDTOList) + .isLast(reviewPreViewDTOPage.isLast()) + .isFirst(reviewPreViewDTOPage.isFirst()) + .totalPage(reviewPreViewDTOPage.getTotalPages()) + .totalElements(reviewPreViewDTOPage.getTotalElements()) + .listSize(reviewPreViewDTOPage.getContent().size()) + .reviewList(reviewPreViewDTOPage.getContent()) .build(); } diff --git a/src/main/java/umc/study/converter/UserConverter.java b/src/main/java/umc/study/converter/UserConverter.java index e98d0d50..33801299 100644 --- a/src/main/java/umc/study/converter/UserConverter.java +++ b/src/main/java/umc/study/converter/UserConverter.java @@ -1,16 +1,11 @@ package umc.study.converter; import org.springframework.data.domain.Page; -import umc.study.apiPayload.code.status.ErrorStatus; -import umc.study.apiPayload.exceptition.GeneralException; import umc.study.domain.User; -import umc.study.domain.enums.Gender; -import umc.study.domain.enums.UserStatus; import umc.study.domain.mapping.UserMission; import umc.study.web.dto.UserRequestDto; import umc.study.web.dto.UserResponseDto; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -21,23 +16,29 @@ public static UserResponseDto.JoinResultDTO toJoinResultDTO(User user){ .createdAt(user.getCreatedAt()) .build(); } + public static UserResponseDto.LoginResultDTO toLoginResultDTO(Long userId, String accessToken) { + return UserResponseDto.LoginResultDTO.builder() + .userId(userId) + .accessToken(accessToken) + .build(); + } + public static UserResponseDto.UserInfoDTO toUserInfoDTO(User user){ + return UserResponseDto.UserInfoDTO.builder() + .name(user.getName()) + .email(user.getEmail()) + .gender(user.getGender().name()) + .build(); + } public static User toUser(UserRequestDto.JoinDto request){ - Gender gender = null; - - switch (request.getGender()){ - case MALE -> gender = Gender.MALE; - case FEMALE -> gender = Gender.FEMALE; - case NONE -> gender = Gender.NONE; - default -> throw new GeneralException(ErrorStatus.INVALID_GENDER); - } - return User.builder() .address(request.getAddress()) - .gender(gender) + .gender(request.getGender()) .birth(request.getBirth()) .email(request.getEmail()) .name(request.getName()) + .password(request.getPassword()) + .role(request.getRole()) .phoneNumber(request.getPhoneNumber()) .userPreferList(new ArrayList<>()) .build(); diff --git a/src/main/java/umc/study/domain/User.java b/src/main/java/umc/study/domain/User.java index bafe6810..4cc84b31 100644 --- a/src/main/java/umc/study/domain/User.java +++ b/src/main/java/umc/study/domain/User.java @@ -8,6 +8,7 @@ import org.hibernate.annotations.DynamicUpdate; import umc.study.domain.common.BaseEntity; import umc.study.domain.enums.Gender; +import umc.study.domain.enums.Role; import umc.study.domain.enums.SocialType; import umc.study.domain.enums.UserStatus; import umc.study.domain.mapping.UserAgree; @@ -53,14 +54,20 @@ public class User extends BaseEntity { private LocalDate inactiveDate; - //@Column(nullable = false, unique = true) // 중복 방지 - private String email; - private Integer point; //@Column(nullable = false, length = 20) private String phoneNumber; + @Column(nullable = false, unique = true) + private String email; + + @Column(nullable = false) + private String password; + + @Enumerated(EnumType.STRING) + private Role role; + @Builder.Default @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) private List userAgreeList = new ArrayList<>(); @@ -84,4 +91,8 @@ public class User extends BaseEntity { @Builder.Default @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) private List memberAlarmList = new ArrayList<>(); + + public void encodePassword(String password) { + this.password = password; + } } \ No newline at end of file diff --git a/src/main/java/umc/study/domain/enums/Gender.java b/src/main/java/umc/study/domain/enums/Gender.java index fc825caf..db9c66e5 100644 --- a/src/main/java/umc/study/domain/enums/Gender.java +++ b/src/main/java/umc/study/domain/enums/Gender.java @@ -1,15 +1,56 @@ package umc.study.domain.enums; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; import lombok.Getter; import lombok.RequiredArgsConstructor; +import umc.study.apiPayload.code.status.ErrorStatus; +import umc.study.apiPayload.exception.GeneralException; @Getter @RequiredArgsConstructor public enum Gender { MALE("남자"), FEMALE("여자"), - NONE("선택 안함"); // 예시로 추가 + NONE("선택 안함"); private final String description; + + @JsonCreator + public static Gender from(String value) { + if (value == null) { + return null; + } + + // Enum 이름으로 매칭 (MALE, FEMALE, NONE) + for (Gender gender : Gender.values()) { + if (gender.name().equalsIgnoreCase(value)) { + return gender; + } + } + + // Description으로 매칭 (남자, 여자, 선택 안함) + for (Gender gender : Gender.values()) { + if (gender.description.equals(value)) { + return gender; + } + } + + throw new GeneralException(ErrorStatus.INVALID_GENDER); + } + + public static Gender fromDescription(String description) { + for (Gender gender : Gender.values()) { + if (gender.description.equals(description)) { + return gender; + } + } + throw new GeneralException(ErrorStatus.INVALID_GENDER); + } + + @JsonValue + public String getName() { + return name(); + } } diff --git a/src/main/java/umc/study/domain/enums/Role.java b/src/main/java/umc/study/domain/enums/Role.java new file mode 100644 index 00000000..778f22e7 --- /dev/null +++ b/src/main/java/umc/study/domain/enums/Role.java @@ -0,0 +1,28 @@ +package umc.study.domain.enums; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public enum Role { + ADMIN, USER; + + @JsonCreator + public static Role from(String value) { + if (value == null) { + return null; + } + + for (Role role : Role.values()) { + if (role.name().equalsIgnoreCase(value)) { + return role; + } + } + + throw new IllegalArgumentException("Invalid role: " + value); + } + + @JsonValue + public String getName() { + return name(); + } +} diff --git a/src/main/java/umc/study/repository/MissionRepository/MissionRepository.java b/src/main/java/umc/study/repository/MissionRepository/MissionRepository.java index d33da52a..85ec0d6d 100644 --- a/src/main/java/umc/study/repository/MissionRepository/MissionRepository.java +++ b/src/main/java/umc/study/repository/MissionRepository/MissionRepository.java @@ -4,13 +4,10 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import umc.study.domain.Mission; -import umc.study.domain.Store; -import umc.study.domain.User; -import umc.study.domain.enums.MissionStatus; public interface MissionRepository extends JpaRepository, MissionRepositoryCustom { // 미션이 이미 도전 중인지 확인 //boolean existsByStatusAndId(MissionStatus status, Long id); // 특정 가게의 미션 목록 조회 - Page findAllByStore(Store store, Pageable pageable); + Page findAllByStore(Long store, Pageable pageable); } diff --git a/src/main/java/umc/study/repository/UserRepository/UserRepository.java b/src/main/java/umc/study/repository/UserRepository/UserRepository.java index 9b5dcb1e..a7934a1c 100644 --- a/src/main/java/umc/study/repository/UserRepository/UserRepository.java +++ b/src/main/java/umc/study/repository/UserRepository/UserRepository.java @@ -4,6 +4,8 @@ import umc.study.domain.User; import umc.study.repository.UserMissionRepository.UserMissionRepositoryCustom; -public interface UserRepository extends JpaRepository , UserRepositoryCustom{ +import java.util.Optional; +public interface UserRepository extends JpaRepository , UserRepositoryCustom{ + Optional findByEmail(String email); } diff --git a/src/main/java/umc/study/repository/UserRepository/UserRepositoryCustom.java b/src/main/java/umc/study/repository/UserRepository/UserRepositoryCustom.java index 04b7aa80..937d514e 100644 --- a/src/main/java/umc/study/repository/UserRepository/UserRepositoryCustom.java +++ b/src/main/java/umc/study/repository/UserRepository/UserRepositoryCustom.java @@ -1,8 +1,9 @@ package umc.study.repository.UserRepository; -import umc.study.web.dto.UserRequestDto; +import umc.study.web.dto.UserResponseDto; + +import java.util.Optional; public interface UserRepositoryCustom { - //List dynamicQueryWithBooleanBuilder(String name, Float score); - UserRequestDto findUserInfoById(Long userId); + UserResponseDto findUserInfoById(Long userId); } diff --git a/src/main/java/umc/study/repository/UserRepository/UserRepositoryImpl.java b/src/main/java/umc/study/repository/UserRepository/UserRepositoryImpl.java index f16e0c6e..33394090 100644 --- a/src/main/java/umc/study/repository/UserRepository/UserRepositoryImpl.java +++ b/src/main/java/umc/study/repository/UserRepository/UserRepositoryImpl.java @@ -1,13 +1,15 @@ -/*package umc.study.repository.UserRepository; +package umc.study.repository.UserRepository; import com.querydsl.core.types.Projections; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import umc.study.domain.QUser; -import umc.study.web.dto.UserRequestDto; +import umc.study.domain.User; import umc.study.web.dto.UserResponseDto; +import java.util.Optional; + @Repository @RequiredArgsConstructor public class UserRepositoryImpl implements UserRepositoryCustom { @@ -16,7 +18,7 @@ public class UserRepositoryImpl implements UserRepositoryCustom { @Override - public UserRequestDto findUserInfoById(Long userId) { + public UserResponseDto findUserInfoById(Long userId) { return queryFactory .select(Projections.constructor(UserResponseDto.class, user.name, @@ -29,4 +31,4 @@ public UserRequestDto findUserInfoById(Long userId) { .where(user.id.eq(userId)) .fetchOne(); } -}*/ \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/java/umc/study/service/MissionService/MissionCommandServiceImpl.java b/src/main/java/umc/study/service/MissionService/MissionCommandServiceImpl.java index cbba57fa..f85d5008 100644 --- a/src/main/java/umc/study/service/MissionService/MissionCommandServiceImpl.java +++ b/src/main/java/umc/study/service/MissionService/MissionCommandServiceImpl.java @@ -5,10 +5,8 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; -import umc.study.apiPayload.code.status.ErrorStatus; -import umc.study.apiPayload.exceptition.GeneralException; +import umc.study.apiPayload.exception.GeneralException; import umc.study.domain.Mission; -import umc.study.domain.Review; import umc.study.domain.Store; import umc.study.domain.User; import umc.study.domain.enums.MissionStatus; @@ -87,7 +85,7 @@ public void startMission(Long userId, Long missionId) { public Page getMissionsByStore(Long storeId, Integer page) { Store store = storeRepository.findById(storeId).get(); - Page MissionPage = missionRepository.findAllByStore(store, PageRequest.of(page, 10)); + Page MissionPage = missionRepository.findAllByStore(storeId, PageRequest.of(page, 10)); return MissionPage; } } diff --git a/src/main/java/umc/study/service/ReviewService/ReviewCommandServiceImpl.java b/src/main/java/umc/study/service/ReviewService/ReviewCommandServiceImpl.java index da0131b4..8123cff7 100644 --- a/src/main/java/umc/study/service/ReviewService/ReviewCommandServiceImpl.java +++ b/src/main/java/umc/study/service/ReviewService/ReviewCommandServiceImpl.java @@ -4,7 +4,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import umc.study.apiPayload.code.status.ErrorStatus; -import umc.study.apiPayload.exceptition.GeneralException; +import umc.study.apiPayload.exception.GeneralException; import umc.study.converter.ReviewConverter; import umc.study.domain.Review; import umc.study.domain.Store; diff --git a/src/main/java/umc/study/service/ReviewService/ReviewQueryServiceImpl.java b/src/main/java/umc/study/service/ReviewService/ReviewQueryServiceImpl.java index 04c96dfd..205f78fa 100644 --- a/src/main/java/umc/study/service/ReviewService/ReviewQueryServiceImpl.java +++ b/src/main/java/umc/study/service/ReviewService/ReviewQueryServiceImpl.java @@ -1,23 +1,13 @@ package umc.study.service.ReviewService; -import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import umc.study.apiPayload.code.status.ErrorStatus; -import umc.study.apiPayload.exceptition.GeneralException; -import umc.study.converter.ReviewConverter; import umc.study.domain.Review; -import umc.study.domain.Store; -import umc.study.domain.User; import umc.study.repository.ReviewRepository.ReviewRepository; -import umc.study.repository.StoreRepository.StoreRepository; -import umc.study.repository.UserRepository.UserRepository; -import umc.study.web.dto.ReviewRequestDto; -import java.math.BigDecimal; import java.util.List; @Service diff --git a/src/main/java/umc/study/service/StoreService/StoreCommandServiceImpl.java b/src/main/java/umc/study/service/StoreService/StoreCommandServiceImpl.java index e2dd14d6..e62d26ee 100644 --- a/src/main/java/umc/study/service/StoreService/StoreCommandServiceImpl.java +++ b/src/main/java/umc/study/service/StoreService/StoreCommandServiceImpl.java @@ -7,8 +7,7 @@ import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import umc.study.apiPayload.code.status.ErrorStatus; -import umc.study.apiPayload.exceptition.GeneralException; -import umc.study.converter.StoreConverter; +import umc.study.apiPayload.exception.GeneralException; import umc.study.domain.FoodCategory; import umc.study.domain.Region; import umc.study.domain.Review; diff --git a/src/main/java/umc/study/service/TempService/TempQueryServiceImpl.java b/src/main/java/umc/study/service/TempService/TempQueryServiceImpl.java index 2b9f2371..0e3ee862 100644 --- a/src/main/java/umc/study/service/TempService/TempQueryServiceImpl.java +++ b/src/main/java/umc/study/service/TempService/TempQueryServiceImpl.java @@ -3,7 +3,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import umc.study.apiPayload.code.status.ErrorStatus; -import umc.study.apiPayload.exceptition.handler.TempHandler; +import umc.study.apiPayload.exception.handler.TempHandler; @Service @RequiredArgsConstructor diff --git a/src/main/java/umc/study/service/UserMissionService/UserMissionCommandServiceImpl.java b/src/main/java/umc/study/service/UserMissionService/UserMissionCommandServiceImpl.java index 705b51aa..086ad3aa 100644 --- a/src/main/java/umc/study/service/UserMissionService/UserMissionCommandServiceImpl.java +++ b/src/main/java/umc/study/service/UserMissionService/UserMissionCommandServiceImpl.java @@ -5,7 +5,7 @@ import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import umc.study.apiPayload.code.status.ErrorStatus; -import umc.study.apiPayload.exceptition.GeneralException; +import umc.study.apiPayload.exception.GeneralException; import umc.study.domain.Mission; import umc.study.domain.User; import umc.study.domain.enums.MissionStatus; diff --git a/src/main/java/umc/study/service/UserService/UserCommandService.java b/src/main/java/umc/study/service/UserService/UserCommandService.java index ab50967e..fc05154b 100644 --- a/src/main/java/umc/study/service/UserService/UserCommandService.java +++ b/src/main/java/umc/study/service/UserService/UserCommandService.java @@ -3,7 +3,10 @@ import jakarta.validation.Valid; import umc.study.domain.User; import umc.study.web.dto.UserRequestDto; +import umc.study.web.dto.UserResponseDto; public interface UserCommandService { User joinUser(UserRequestDto.@Valid JoinDto request); + UserResponseDto.LoginResultDTO loginUser(UserRequestDto.LoginRequestDTO request); + } \ No newline at end of file diff --git a/src/main/java/umc/study/service/UserService/UserCommandServiceImpl.java b/src/main/java/umc/study/service/UserService/UserCommandServiceImpl.java index 7549057b..81a71cfd 100644 --- a/src/main/java/umc/study/service/UserService/UserCommandServiceImpl.java +++ b/src/main/java/umc/study/service/UserService/UserCommandServiceImpl.java @@ -1,9 +1,16 @@ package umc.study.service.UserService; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import umc.study.apiPayload.code.status.ErrorStatus; -import umc.study.apiPayload.exceptition.handler.FoodCategoryHandler; +import umc.study.apiPayload.exception.handler.FoodCategoryHandler; +import umc.study.apiPayload.exception.handler.UserHandler; +import umc.study.config.security.jwt.JwtTokenProvider; import umc.study.converter.UserConverter; import umc.study.converter.UserPreferConverter; import umc.study.domain.FoodCategory; @@ -12,28 +19,67 @@ import umc.study.repository.FoodCategoryRepository.FoodCategoryRepository; import umc.study.repository.UserRepository.UserRepository; import umc.study.web.dto.UserRequestDto; +import umc.study.web.dto.UserResponseDto; +//import org.springframework.security.crypto.password.PasswordEncoder; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; +@Slf4j @Service @RequiredArgsConstructor public class UserCommandServiceImpl implements UserCommandService { private final UserRepository userRepository; private final FoodCategoryRepository foodCategoryRepository; + private final PasswordEncoder passwordEncoder; + private final JwtTokenProvider jwtTokenProvider; @Override + @Transactional public User joinUser(UserRequestDto.JoinDto request) { + if(userRepository.findByEmail(request.getEmail()).isPresent()) { + throw new UserHandler(ErrorStatus.DUPLICATE_JOIN_REQUEST); + } User newUser = UserConverter.toUser(request); - List foodCategoryList = request.getPreferCategory().stream() - .map(category -> { - return foodCategoryRepository.findById(category).orElseThrow(() -> new FoodCategoryHandler(ErrorStatus.FOOD_CATEGORY_NOT_FOUND)); - }).collect(Collectors.toList()); + String encodedPassword = passwordEncoder.encode(request.getPassword()); + newUser.encodePassword(encodedPassword); - List userPreferList = UserPreferConverter.toUserPreferList(foodCategoryList); + if (!request.getPreferCategory().isEmpty()) { + List foodCategoryList = request.getPreferCategory().stream() + .map(category -> { + return foodCategoryRepository.findById(category).orElseThrow(() -> new FoodCategoryHandler(ErrorStatus.FOOD_CATEGORY_NOT_FOUND)); + }).collect(Collectors.toList()); - userPreferList.forEach(userPrefer -> {userPrefer.setUser(newUser);}); + List userPreferList = UserPreferConverter.toUserPreferList(foodCategoryList); + userPreferList.forEach(userPrefer -> { + userPrefer.setUser(newUser); + }); + + } return userRepository.save(newUser); } + @Override + @Transactional + public UserResponseDto.LoginResultDTO loginUser(UserRequestDto.LoginRequestDTO request) { + User user = userRepository.findByEmail(request.getEmail()) + .orElseThrow(() -> new UserHandler(ErrorStatus.USER_NOT_FOUND)); + + if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) { + throw new UserHandler(ErrorStatus.INVALID_PASSWORD); + } + + Authentication authentication = new UsernamePasswordAuthenticationToken( + user.getEmail(), null, + Collections.singleton(() -> user.getRole().name()) + ); + + String accessToken = jwtTokenProvider.generateToken(authentication); + + return UserConverter.toLoginResultDTO( + user.getId(), + accessToken + ); + } } diff --git a/src/main/java/umc/study/service/UserService/UserQueryService.java b/src/main/java/umc/study/service/UserService/UserQueryService.java index ad41face..2dba3a26 100644 --- a/src/main/java/umc/study/service/UserService/UserQueryService.java +++ b/src/main/java/umc/study/service/UserService/UserQueryService.java @@ -1,4 +1,8 @@ package umc.study.service.UserService; +import jakarta.servlet.http.HttpServletRequest; +import umc.study.web.dto.UserResponseDto; + public interface UserQueryService { + UserResponseDto.UserInfoDTO getUserInfo(HttpServletRequest request); } diff --git a/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java b/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java index 3c238c1a..03901cb5 100644 --- a/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java +++ b/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java @@ -1,15 +1,34 @@ package umc.study.service.UserService; -import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.security.core.Authentication; +import umc.study.apiPayload.code.status.ErrorStatus; +import umc.study.apiPayload.exception.handler.UserHandler; +import umc.study.config.security.jwt.JwtTokenProvider; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import umc.study.domain.User; import umc.study.repository.UserRepository.UserRepository; +import umc.study.web.dto.UserResponseDto; +import umc.study.converter.UserConverter; @Service @RequiredArgsConstructor @Transactional(readOnly = true) public class UserQueryServiceImpl implements UserQueryService { private final UserRepository userRepository; + private final JwtTokenProvider jwtTokenProvider; + + @Override + @Transactional(readOnly = true) + public UserResponseDto.UserInfoDTO getUserInfo(HttpServletRequest request){ + Authentication authentication = jwtTokenProvider.extractAuthentication(request); + String email = authentication.getName(); + + User user = userRepository.findByEmail(email) + .orElseThrow(()-> new UserHandler(ErrorStatus.USER_NOT_FOUND)); + return UserConverter.toUserInfoDTO(user); + } } diff --git a/src/main/java/umc/study/validation/validator/MissionExistValidator.java b/src/main/java/umc/study/validation/validator/MissionExistValidator.java index 5a512ec8..1bc57e40 100644 --- a/src/main/java/umc/study/validation/validator/MissionExistValidator.java +++ b/src/main/java/umc/study/validation/validator/MissionExistValidator.java @@ -5,12 +5,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import umc.study.apiPayload.code.status.ErrorStatus; -import umc.study.apiPayload.exceptition.GeneralException; -import umc.study.domain.User; import umc.study.repository.UserMissionRepository.UserMissionRepository; -import umc.study.repository.UserRepository.UserRepository; import umc.study.validation.annotation.ExistMission; -import umc.study.web.dto.MissionRequestDto; import java.util.List; diff --git a/src/main/java/umc/study/web/controller/UserRestController.java b/src/main/java/umc/study/web/controller/UserRestController.java index 8c53ca02..c9282ddc 100644 --- a/src/main/java/umc/study/web/controller/UserRestController.java +++ b/src/main/java/umc/study/web/controller/UserRestController.java @@ -1,29 +1,48 @@ package umc.study.web.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; import umc.study.apiPayload.ApiResponse; import umc.study.converter.UserConverter; import umc.study.domain.User; import umc.study.service.MissionService.MissionCommandServiceImpl; import umc.study.service.UserService.UserCommandService; +import umc.study.service.UserService.UserQueryService; import umc.study.web.dto.UserRequestDto; import umc.study.web.dto.UserResponseDto; +@Slf4j @RestController @RequiredArgsConstructor @RequestMapping("/users") public class UserRestController { private final UserCommandService userCommandService; - private final MissionCommandServiceImpl missionCommandServiceImpl; + private final UserQueryService userQueryService; - @PostMapping("/") + @PostMapping("/join") + @Operation(summary = "유저 회원가입 API",description = "유저가 회원가입하는 API입니다.") public ApiResponse join(@RequestBody @Valid UserRequestDto.JoinDto request){ User user = userCommandService.joinUser(request); return ApiResponse.onSuccess(UserConverter.toJoinResultDTO(user)); } + + @PostMapping("/login") + @Operation(summary = "유저 로그인 API",description = "유저가 로그인하는 API입니다.") + public ApiResponse login(@RequestBody @Valid UserRequestDto.LoginRequestDTO request) { + return ApiResponse.onSuccess(userCommandService.loginUser(request)); + } + + @GetMapping("/info") + @Operation(summary = "유저 내 정보 조회 API - 인증 필요", + description = "유저가 내 정보를 조회하는 API입니다.", + security = { @SecurityRequirement(name = "JWT TOKEN") } + ) + public ApiResponse getMyInfo(HttpServletRequest request) { + return ApiResponse.onSuccess(userQueryService.getUserInfo(request)); + } } diff --git a/src/main/java/umc/study/web/dto/UserRequestDto.java b/src/main/java/umc/study/web/dto/UserRequestDto.java index f9783d71..9edb9228 100644 --- a/src/main/java/umc/study/web/dto/UserRequestDto.java +++ b/src/main/java/umc/study/web/dto/UserRequestDto.java @@ -1,18 +1,25 @@ package umc.study.web.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import umc.study.domain.enums.Gender; +import umc.study.domain.enums.Role; import umc.study.validation.annotation.ExistCategories; import java.util.List; -@AllArgsConstructor public class UserRequestDto { @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor public static class JoinDto{ @NotNull String name; @@ -23,12 +30,27 @@ public static class JoinDto{ @Size(min = 5, max = 12) @NotNull String address; - //@NotNull + @Email String email; + @NotBlank + String password; @NotNull String phoneNumber; @ExistCategories @NotNull List preferCategory; + @NotNull + Role role; + } + + @Getter + @Setter + public static class LoginRequestDTO{ + @NotBlank(message = "이메일은 필수입니다.") + @Email(message = "올바른 이메일 형식이어야 합니다.") + private String email; + + @NotBlank(message = "패스워드는 필수입니다.") + private String password; } } diff --git a/src/main/java/umc/study/web/dto/UserResponseDto.java b/src/main/java/umc/study/web/dto/UserResponseDto.java index 37b01b76..ad1137c6 100644 --- a/src/main/java/umc/study/web/dto/UserResponseDto.java +++ b/src/main/java/umc/study/web/dto/UserResponseDto.java @@ -18,6 +18,16 @@ public static class JoinResultDTO{ LocalDateTime createdAt; } + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class UserInfoDTO{ + String name; + String email; + String gender; + } + @Getter @Builder @NoArgsConstructor @@ -40,4 +50,13 @@ public static class OngoingMissionListDTO { private Boolean isFirst; private Boolean isLast; } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class LoginResultDTO { + Long userId; + String accessToken; + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0241ace2..d9f055e1 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -17,3 +17,8 @@ spring: hbm2ddl: auto: update default_batch_fetch_size: 1000 +jwt: + token: + secretKey: umceightfightingjwttokenauthentication + expiration: + access: 14400000 \ No newline at end of file diff --git a/src/main/resources/templates/admin.html b/src/main/resources/templates/admin.html new file mode 100644 index 00000000..55dbff1a --- /dev/null +++ b/src/main/resources/templates/admin.html @@ -0,0 +1,10 @@ + + + + Admin Page + + +

Admin Page

+

관리자만 접근할 수 있는 페이지입니다.

+ + \ No newline at end of file diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html new file mode 100644 index 00000000..8c10cb16 --- /dev/null +++ b/src/main/resources/templates/home.html @@ -0,0 +1,20 @@ + + + + Home + + +

Welcome to Home Page!

+ +

+ + + + + +
+ +
+ \ No newline at end of file diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 00000000..7804a3a2 --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,26 @@ + + + + Login + + +

Login

+
+
+ + +
+
+ + +
+ +
+ +

사용자 이름 또는 비밀번호가 잘못되었습니다.

+

로그아웃되었습니다.

+ + +

계정이 없나요? Sign up

+ + \ No newline at end of file diff --git a/src/main/resources/templates/signup.html b/src/main/resources/templates/signup.html new file mode 100644 index 00000000..01bdb624 --- /dev/null +++ b/src/main/resources/templates/signup.html @@ -0,0 +1,64 @@ + + + + 회원가입 + + + +

회원가입

+
+
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + + +
+
+
+ + +
+ +
+ + \ No newline at end of file