diff --git a/.github/workflows/create-jira-issue.yml b/.github/workflows/create-jira-issue.yml index 7e80c31..604c3d2 100644 --- a/.github/workflows/create-jira-issue.yml +++ b/.github/workflows/create-jira-issue.yml @@ -1,27 +1,27 @@ -name: Create Jira issue # 1 -on: # 2 - issues: - types: [opened] - -jobs: # 3 - create-issue: # 4 - name: Create Jira issue # 5 - runs-on: ubuntu-latest # 6 - steps: # 7 - - name: Login - uses: atlassian/gajira-login@v3 # 8 - env: # 9 - JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} - JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} - JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} - - - name: Create Issue - uses: atlassian/gajira-create@v3 - with: - project: KAN - issuetype: Task - summary: '${{ github.event.issue.title }}' - description: '${{ github.event.issue.html_url }}' +#name: Create Jira issue # 1 +#on: # 2 +# issues: +# types: [opened] +# +#jobs: # 3 +# create-issue: # 4 +# name: Create Jira issue # 5 +# runs-on: ubuntu-latest # 6 +# steps: # 7 +# - name: Login +# uses: atlassian/gajira-login@v3 # 8 +# env: # 9 +# JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} +# JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} +# JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} +# +# - name: Create Issue +# uses: atlassian/gajira-create@v3 +# with: +# project: KAN +# issuetype: Task +# summary: '${{ github.event.issue.title }}' +# description: '${{ github.event.issue.html_url }}' # - name: Checkout develop code # uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore index 7d20223..f878417 100644 --- a/.gitignore +++ b/.gitignore @@ -5,9 +5,7 @@ build/ !**/src/main/**/build/ !**/**/build/ !**/src/test/**/build/ -*/Api/build -*/Common/build -*/Search/build +**/build/ ### STS ### .apt_generated diff --git a/Api/build.gradle b/Api/build.gradle index ac2b9ac..3122563 100644 --- a/Api/build.gradle +++ b/Api/build.gradle @@ -16,6 +16,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation project(':Common') implementation project(':Domain') + implementation 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'org.springframework.security:spring-security-test' runtimeOnly 'com.mysql:mysql-connector-j' testImplementation platform('org.junit:junit-bom:5.9.1') testImplementation 'org.junit.jupiter:junit-jupiter' diff --git a/Api/build/classes/java/main/org/momo/MomoApiApplication.class b/Api/build/classes/java/main/org/momo/MomoApiApplication.class deleted file mode 100644 index 52ef0e8..0000000 Binary files a/Api/build/classes/java/main/org/momo/MomoApiApplication.class and /dev/null differ diff --git a/Api/build/tmp/compileJava/previous-compilation-data.bin b/Api/build/tmp/compileJava/previous-compilation-data.bin deleted file mode 100644 index 8cc0d98..0000000 Binary files a/Api/build/tmp/compileJava/previous-compilation-data.bin and /dev/null differ diff --git a/Api/src/main/java/org/momo/security/SecurityConfig.java b/Api/src/main/java/org/momo/security/SecurityConfig.java new file mode 100644 index 0000000..b316f22 --- /dev/null +++ b/Api/src/main/java/org/momo/security/SecurityConfig.java @@ -0,0 +1,72 @@ +package org.momo.security; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.momo.security.filter.JwtFilter; +import org.momo.security.handler.JwtAccessDeniedHandler; +import org.momo.security.handler.JwtAuthenticationEntryPoint; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +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.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; + +import java.util.Collections; + +@Configuration +@EnableWebSecurity //스프링 시큐리티 필터(Security Config)가 스프링 필터 체인에 등록이 된다. +@RequiredArgsConstructor +public class SecurityConfig { + private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; + private final JwtAccessDeniedHandler jwtAccessDeniedHandler; + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder(){ + return new BCryptPasswordEncoder(); + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception{ + return configuration.getAuthenticationManager(); + } + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{ + http + .cors(cors -> cors.configurationSource(new CorsConfigurationSource() { + @Override + public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Collections.singletonList("*")); + configuration.setAllowedMethods(Collections.singletonList("*")); + configuration.setAllowCredentials(true); + configuration.setAllowedHeaders(Collections.singletonList("*")); + configuration.setMaxAge(3600L); + configuration.setExposedHeaders(Collections.singletonList("Authorization")); + return configuration; + } + })) + .csrf(AbstractHttpConfigurer::disable) + .formLogin(AbstractHttpConfigurer::disable) + .httpBasic(AbstractHttpConfigurer::disable) + .sessionManagement((session) -> session + .sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + + .exceptionHandling((exception) -> exception + .authenticationEntryPoint(jwtAuthenticationEntryPoint) + .accessDeniedHandler(jwtAccessDeniedHandler)) + + .authorizeHttpRequests((request) -> request + .requestMatchers("/login").permitAll() + .anyRequest().authenticated()) + .addFilterBefore(new JwtFilter(), UsernamePasswordAuthenticationFilter.class); + return http.build(); + } +} diff --git a/Api/src/main/java/org/momo/security/filter/JwtFilter.java b/Api/src/main/java/org/momo/security/filter/JwtFilter.java new file mode 100644 index 0000000..c4764d8 --- /dev/null +++ b/Api/src/main/java/org/momo/security/filter/JwtFilter.java @@ -0,0 +1,67 @@ +package org.momo.security.filter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.momo.Member.Entity.Member; +import org.momo.common.BaseResponseDto; +import org.momo.common.status.ErrorStatus; +import org.momo.exception.handler.JwtExpiredHandler; +import org.momo.exception.handler.JwtInvalidHandler; +import org.momo.security.principal.PrincipalDetails; +import org.momo.util.JwtUtil; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.io.PrintWriter; + +@RequiredArgsConstructor +@Slf4j +public class JwtFilter extends OncePerRequestFilter { + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String accessToken = request.getHeader("Authorization"); + if (accessToken == null) { + filterChain.doFilter(request,response); + return; + } + try{ + JwtUtil.validateAccessToken(accessToken); + String email = JwtUtil.getEmail(accessToken); + + Member member = Member.builder() + .email(email) + .build(); + PrincipalDetails principalDetails = PrincipalDetails.createPrincipalDetails(member); + + Authentication authentication = new UsernamePasswordAuthenticationToken(principalDetails, null, principalDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authentication); + }catch (JwtExpiredHandler e){ + BaseResponseDto baseResponseDto = BaseResponseDto.onFailure( + ErrorStatus.JWT_ACCESS_TOKEN_EXPIRED.getCode(), + ErrorStatus.JWT_ACCESS_TOKEN_EXPIRED.getMessage(), + null + ); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.writeValue(response.getOutputStream(),baseResponseDto); + return; + }catch (JwtInvalidHandler e){ + BaseResponseDto baseResponseDto = BaseResponseDto.onFailure( + ErrorStatus.JWT_TOKEN_INVALID.getCode(), + ErrorStatus.JWT_TOKEN_INVALID.getMessage(), + null + ); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.writeValue(response.getOutputStream(),baseResponseDto); + return; + } + filterChain.doFilter(request,response); + } +} diff --git a/Api/src/main/java/org/momo/security/handler/JwtAccessDeniedHandler.java b/Api/src/main/java/org/momo/security/handler/JwtAccessDeniedHandler.java new file mode 100644 index 0000000..dda3f36 --- /dev/null +++ b/Api/src/main/java/org/momo/security/handler/JwtAccessDeniedHandler.java @@ -0,0 +1,28 @@ +package org.momo.security.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.momo.common.BaseResponseDto; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Slf4j +@Component +public class JwtAccessDeniedHandler implements AccessDeniedHandler { + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { + log.error("JwtAccessDeniedHandler 실행"); + response.setContentType("application/json"); + response.setStatus(HttpServletResponse.SC_FORBIDDEN); + BaseResponseDto baseResponseDto = BaseResponseDto.onFailure(403, "권한이 없습니다.", null); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.writeValue(response.getOutputStream(), baseResponseDto); + } +} diff --git a/Api/src/main/java/org/momo/security/handler/JwtAuthenticationEntryPoint.java b/Api/src/main/java/org/momo/security/handler/JwtAuthenticationEntryPoint.java new file mode 100644 index 0000000..71848e4 --- /dev/null +++ b/Api/src/main/java/org/momo/security/handler/JwtAuthenticationEntryPoint.java @@ -0,0 +1,31 @@ +package org.momo.security.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.momo.common.BaseResponseDto; +import org.momo.common.status.ErrorStatus; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Slf4j +@Component +public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + log.error("JwtAuthenticationEntryPoint 실행"); + response.setContentType("application/json"); + BaseResponseDto baseResponseDto = + BaseResponseDto.onFailure( + ErrorStatus.JWT_TOKEN_NOT_FOUND.getCode(), + ErrorStatus.JWT_TOKEN_NOT_FOUND.getMessage(), + null); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.writeValue(response.getOutputStream(), baseResponseDto); + } +} diff --git a/Api/src/main/java/org/momo/security/principal/PrincipalDetails.java b/Api/src/main/java/org/momo/security/principal/PrincipalDetails.java new file mode 100644 index 0000000..82d1045 --- /dev/null +++ b/Api/src/main/java/org/momo/security/principal/PrincipalDetails.java @@ -0,0 +1,59 @@ +package org.momo.security.principal; + +import lombok.RequiredArgsConstructor; +import org.momo.Member.Entity.Member; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.ArrayList; +import java.util.Collection; +@RequiredArgsConstructor +public class PrincipalDetails implements UserDetails { + + private final Member member; + + public static PrincipalDetails createPrincipalDetails(Member member) { + return new PrincipalDetails(member); + } + @Override + public Collection getAuthorities() { + Collection collect = new ArrayList<>(); + collect.add(new GrantedAuthority() { + @Override + public String getAuthority() { + return null; + } + }); + return collect; + } + + @Override + public String getPassword() { + return member.getPassword(); + } + + @Override + public String getUsername() { + return member.getName(); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } +} diff --git a/Api/src/main/resources/application.yml b/Api/src/main/resources/application.yml index 7d91f09..570905d 100644 --- a/Api/src/main/resources/application.yml +++ b/Api/src/main/resources/application.yml @@ -11,4 +11,4 @@ spring: hibernate: format_sql: true use_sql_comments: true -# show_sql: true \ No newline at end of file +# show_sql: true diff --git a/Auth/build.gradle b/Auth/build.gradle index 0a72543..8bd42aa 100644 --- a/Auth/build.gradle +++ b/Auth/build.gradle @@ -1,5 +1,6 @@ plugins { id 'java' + id 'java-library' } group = 'org.momo' @@ -13,13 +14,15 @@ bootJar { enabled = false } jar { enabled = true } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-security' + api project(':Common') + implementation project(':Domain') + //implementation 'org.springframework.boot:spring-boot-starter-security' testImplementation platform('org.junit:junit-bom:5.9.1') testImplementation 'org.junit.jupiter:junit-jupiter' - testImplementation 'org.springframework.security:spring-security-test' + //testImplementation 'org.springframework.security:spring-security-test' } test { useJUnitPlatform() -} \ No newline at end of file +} diff --git a/Auth/build/classes/java/main/org/momo/MomoAuthApplication.class b/Auth/build/classes/java/main/org/momo/MomoAuthApplication.class deleted file mode 100644 index 4799128..0000000 Binary files a/Auth/build/classes/java/main/org/momo/MomoAuthApplication.class and /dev/null differ diff --git a/Auth/build/tmp/compileJava/previous-compilation-data.bin b/Auth/build/tmp/compileJava/previous-compilation-data.bin deleted file mode 100644 index 846ad74..0000000 Binary files a/Auth/build/tmp/compileJava/previous-compilation-data.bin and /dev/null differ diff --git a/Auth/src/main/java/org/momo/controller/AuthController.java b/Auth/src/main/java/org/momo/controller/AuthController.java new file mode 100644 index 0000000..b3c10e8 --- /dev/null +++ b/Auth/src/main/java/org/momo/controller/AuthController.java @@ -0,0 +1,22 @@ +package org.momo.controller; + +import lombok.RequiredArgsConstructor; +import org.momo.Base.Entity.Base; +import org.momo.common.BaseResponseDto; +import org.momo.common.status.SuccessStatus; +import org.momo.dto.AuthRequest; +import org.momo.dto.AuthResponse; +import org.momo.service.AuthService; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class AuthController { + private final AuthService authService; + @PostMapping("/login") + public BaseResponseDto login(@RequestBody AuthRequest.LoginDto loginDto) { + return BaseResponseDto.of(SuccessStatus.LOGIN_SUCCESS.getCode(),SuccessStatus.LOGIN_SUCCESS.getMessage(), authService.login(loginDto)); + } +} diff --git a/Auth/src/main/java/org/momo/dto/AuthRequest.java b/Auth/src/main/java/org/momo/dto/AuthRequest.java new file mode 100644 index 0000000..b72d561 --- /dev/null +++ b/Auth/src/main/java/org/momo/dto/AuthRequest.java @@ -0,0 +1,15 @@ +package org.momo.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +public class AuthRequest { + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class LoginDto { + private String email; + private String password; + } +} diff --git a/Auth/src/main/java/org/momo/dto/AuthResponse.java b/Auth/src/main/java/org/momo/dto/AuthResponse.java new file mode 100644 index 0000000..f179415 --- /dev/null +++ b/Auth/src/main/java/org/momo/dto/AuthResponse.java @@ -0,0 +1,20 @@ +package org.momo.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +public class AuthResponse { + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class LoginResponseDto { + public String accessToken; + LocalDateTime accessTokenExpiresAt; + } +} diff --git a/Auth/src/main/java/org/momo/exception/MemberHandler.java b/Auth/src/main/java/org/momo/exception/MemberHandler.java new file mode 100644 index 0000000..55cfdcf --- /dev/null +++ b/Auth/src/main/java/org/momo/exception/MemberHandler.java @@ -0,0 +1,9 @@ +package org.momo.exception; + +import org.momo.common.BaseErrorCode; + +public class MemberHandler extends GeneralException { + public MemberHandler(BaseErrorCode baseErrorCode) { + super(baseErrorCode); + } +} diff --git a/Auth/src/main/java/org/momo/security/SecurityConfig.java b/Auth/src/main/java/org/momo/security/SecurityConfig.java new file mode 100644 index 0000000..a368a7a --- /dev/null +++ b/Auth/src/main/java/org/momo/security/SecurityConfig.java @@ -0,0 +1,73 @@ +package org.momo.security; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.momo.security.filter.JwtFilter; +import org.momo.security.handler.JwtAccessDeniedHandler; +import org.momo.security.handler.JwtAuthenticationEntryPoint; +import org.momo.security.principal.PrincipalDetailsService; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +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.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; + +import java.util.Collections; + +@Configuration +@EnableWebSecurity //스프링 시큐리티 필터(Security Config)가 스프링 필터 체인에 등록이 된다. +@RequiredArgsConstructor +public class SecurityConfig { + private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; + private final JwtAccessDeniedHandler jwtAccessDeniedHandler; + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder(){ + return new BCryptPasswordEncoder(); + } + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception{ + return configuration.getAuthenticationManager(); + } + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{ + http + .cors(cors -> cors.configurationSource(new CorsConfigurationSource() { + @Override + public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Collections.singletonList("*")); + configuration.setAllowedMethods(Collections.singletonList("*")); + configuration.setAllowCredentials(true); + configuration.setAllowedHeaders(Collections.singletonList("*")); + configuration.setMaxAge(3600L); + configuration.setExposedHeaders(Collections.singletonList("Authorization")); + return configuration; + } + })) + .csrf(AbstractHttpConfigurer::disable) + .formLogin(AbstractHttpConfigurer::disable) + .httpBasic(AbstractHttpConfigurer::disable) + .sessionManagement((session) -> session + .sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + + .exceptionHandling((exception) -> exception + .authenticationEntryPoint(jwtAuthenticationEntryPoint) + .accessDeniedHandler(jwtAccessDeniedHandler)) + + .authorizeHttpRequests((request) -> request + .requestMatchers("/login").permitAll() + .anyRequest().authenticated()) + .addFilterBefore(new JwtFilter(), UsernamePasswordAuthenticationFilter.class); + return http.build(); + } +} diff --git a/Auth/src/main/java/org/momo/security/filter/JwtFilter.java b/Auth/src/main/java/org/momo/security/filter/JwtFilter.java new file mode 100644 index 0000000..30296d7 --- /dev/null +++ b/Auth/src/main/java/org/momo/security/filter/JwtFilter.java @@ -0,0 +1,69 @@ +package org.momo.security.filter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.JwtException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.momo.Member.Entity.Member; +import org.momo.common.BaseResponseDto; +import org.momo.common.status.ErrorStatus; +import org.momo.exception.handler.JwtExpiredHandler; +import org.momo.exception.handler.JwtInvalidHandler; +import org.momo.security.principal.PrincipalDetails; +import org.momo.util.JwtUtil; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; +import java.io.IOException; + +@RequiredArgsConstructor +@Slf4j +public class JwtFilter extends OncePerRequestFilter { + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String accessToken = request.getHeader("Authorization"); + if (accessToken == null) { + filterChain.doFilter(request,response); + return; + } + try{ + JwtUtil.validateAccessToken(accessToken); + String email = JwtUtil.getEmail(accessToken); + + Member member = Member.builder() + .email(email) + .build(); + PrincipalDetails principalDetails = PrincipalDetails.createPrincipalDetails(member); + + Authentication authentication = new UsernamePasswordAuthenticationToken(principalDetails, null, principalDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authentication); + }catch (JwtExpiredHandler e){ + response.setContentType("application/json"); + BaseResponseDto baseResponseDto = BaseResponseDto.onFailure( + ErrorStatus.JWT_ACCESS_TOKEN_EXPIRED.getCode(), + ErrorStatus.JWT_ACCESS_TOKEN_EXPIRED.getMessage(), + null + ); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.writeValue(response.getOutputStream(),baseResponseDto); + return; + }catch (JwtInvalidHandler e){ + response.setContentType("application/json"); + BaseResponseDto baseResponseDto = BaseResponseDto.onFailure( + ErrorStatus.JWT_TOKEN_INVALID.getCode(), + ErrorStatus.JWT_TOKEN_INVALID.getMessage(), + null + ); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.writeValue(response.getOutputStream(),baseResponseDto); + return; + } + filterChain.doFilter(request,response); + } +} diff --git a/Auth/src/main/java/org/momo/security/handler/JwtAccessDeniedHandler.java b/Auth/src/main/java/org/momo/security/handler/JwtAccessDeniedHandler.java new file mode 100644 index 0000000..0e5bff3 --- /dev/null +++ b/Auth/src/main/java/org/momo/security/handler/JwtAccessDeniedHandler.java @@ -0,0 +1,27 @@ +package org.momo.security.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.momo.common.BaseResponseDto; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Slf4j +@Component +public class JwtAccessDeniedHandler implements AccessDeniedHandler { + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { + log.error("JwtAccessDeniedHandler 실행"); + response.setContentType("application/json"); + response.setStatus(HttpServletResponse.SC_FORBIDDEN); + BaseResponseDto baseResponseDto = BaseResponseDto.onFailure(403, "권한이 없습니다.", null); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.writeValue(response.getOutputStream(), baseResponseDto); + } +} diff --git a/Auth/src/main/java/org/momo/security/handler/JwtAuthenticationEntryPoint.java b/Auth/src/main/java/org/momo/security/handler/JwtAuthenticationEntryPoint.java new file mode 100644 index 0000000..71848e4 --- /dev/null +++ b/Auth/src/main/java/org/momo/security/handler/JwtAuthenticationEntryPoint.java @@ -0,0 +1,31 @@ +package org.momo.security.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.momo.common.BaseResponseDto; +import org.momo.common.status.ErrorStatus; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Slf4j +@Component +public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + log.error("JwtAuthenticationEntryPoint 실행"); + response.setContentType("application/json"); + BaseResponseDto baseResponseDto = + BaseResponseDto.onFailure( + ErrorStatus.JWT_TOKEN_NOT_FOUND.getCode(), + ErrorStatus.JWT_TOKEN_NOT_FOUND.getMessage(), + null); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.writeValue(response.getOutputStream(), baseResponseDto); + } +} diff --git a/Auth/src/main/java/org/momo/security/principal/PrincipalDetails.java b/Auth/src/main/java/org/momo/security/principal/PrincipalDetails.java new file mode 100644 index 0000000..82d1045 --- /dev/null +++ b/Auth/src/main/java/org/momo/security/principal/PrincipalDetails.java @@ -0,0 +1,59 @@ +package org.momo.security.principal; + +import lombok.RequiredArgsConstructor; +import org.momo.Member.Entity.Member; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.ArrayList; +import java.util.Collection; +@RequiredArgsConstructor +public class PrincipalDetails implements UserDetails { + + private final Member member; + + public static PrincipalDetails createPrincipalDetails(Member member) { + return new PrincipalDetails(member); + } + @Override + public Collection getAuthorities() { + Collection collect = new ArrayList<>(); + collect.add(new GrantedAuthority() { + @Override + public String getAuthority() { + return null; + } + }); + return collect; + } + + @Override + public String getPassword() { + return member.getPassword(); + } + + @Override + public String getUsername() { + return member.getName(); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } +} diff --git a/Auth/src/main/java/org/momo/security/principal/PrincipalDetailsService.java b/Auth/src/main/java/org/momo/security/principal/PrincipalDetailsService.java new file mode 100644 index 0000000..7821edc --- /dev/null +++ b/Auth/src/main/java/org/momo/security/principal/PrincipalDetailsService.java @@ -0,0 +1,31 @@ +package org.momo.security.principal; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.momo.Member.Entity.Member; +import org.momo.Member.Repository.MemberRepository; +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 java.util.Optional; + +/** + * security 설정에서 loginProcessingUrl("/login") + * /login으로 요청이 오면 자동으로 UserDetailsService 타입으로 IoC되어 있는 loadUserByUsername 함수가 실행 + */ +@RequiredArgsConstructor +@Slf4j +@Service +public class PrincipalDetailsService implements UserDetailsService { + + private final MemberRepository memberRepository; + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + log.info("loadUserByUsername 함수 실행"); + Optional member = memberRepository.findMemberByEmail(username); + if(member.isEmpty()) throw new UsernameNotFoundException("해당 유저를 찾을 수 없습니다."); + return PrincipalDetails.createPrincipalDetails(member.get()); + } +} diff --git a/Auth/src/main/java/org/momo/service/AuthService.java b/Auth/src/main/java/org/momo/service/AuthService.java new file mode 100644 index 0000000..6566e5a --- /dev/null +++ b/Auth/src/main/java/org/momo/service/AuthService.java @@ -0,0 +1,48 @@ +package org.momo.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.momo.Member.Entity.Member; +import org.momo.Member.Repository.MemberRepository; +import org.momo.common.status.ErrorStatus; +import org.momo.dto.AuthRequest; +import org.momo.dto.AuthResponse; +import org.momo.exception.MemberHandler; +import org.momo.util.JwtUtil; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class AuthService { + private final MemberRepository memberRepository; + private final BCryptPasswordEncoder bCryptPasswordEncoder; + /** + * 로그인 + */ + public AuthResponse.LoginResponseDto login(AuthRequest.LoginDto loginDto) { + String email = loginDto.getEmail(); + System.out.println(bCryptPasswordEncoder.encode(loginDto.getPassword())); + + Member member = memberRepository.findMemberByEmail(email) + .orElseThrow(() -> new MemberHandler(ErrorStatus.MEMBER_NOT_FOUND)); + + if(!bCryptPasswordEncoder.matches(loginDto.getPassword(), member.getPassword())) + throw new MemberHandler(ErrorStatus.PASSWORD_NOT_MATCH); + + String accessToken = JwtUtil.createJwt(member.getMemberId(), member.getEmail(), null); + + LocalDateTime expiredAt = LocalDateTime.now().plusMinutes(30); + + return AuthResponse.LoginResponseDto.builder() + .accessToken(accessToken) + .accessTokenExpiresAt(expiredAt) + .build(); + + } +} diff --git a/Auth/src/test/java/org/momo/controller/AuthControllerTest.java b/Auth/src/test/java/org/momo/controller/AuthControllerTest.java new file mode 100644 index 0000000..b9eed2a --- /dev/null +++ b/Auth/src/test/java/org/momo/controller/AuthControllerTest.java @@ -0,0 +1,48 @@ +package org.momo.controller; + + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.momo.common.BaseResponseDto; +import org.momo.common.status.SuccessStatus; +import org.momo.dto.AuthRequest; +import org.momo.dto.AuthResponse; +import org.momo.service.AuthService; +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class AuthControllerTest { + @Mock + private AuthService authService; + + @InjectMocks + private AuthController authController; + + @Test + @DisplayName("로그인 성공") + public void 로그인_성공(){ + AuthRequest.LoginDto loginDto = new AuthRequest.LoginDto("test@gmail.com","test"); + AuthResponse.LoginResponseDto loginResponseDto = AuthResponse.LoginResponseDto.builder() + .accessToken("accessToken") + .accessTokenExpiresAt(LocalDateTime.now().plusMinutes(30)) + .build(); + + when(authService.login(loginDto)).thenReturn(loginResponseDto); + + BaseResponseDto result = authController.login(loginDto); + + assertEquals(SuccessStatus.LOGIN_SUCCESS.getCode(),result.getCode()); + assertEquals(SuccessStatus.LOGIN_SUCCESS.getMessage(),result.getMessage()); + assertEquals("accessToken",result.getResult().getAccessToken()); + assertEquals(LocalDateTime.now().plusMinutes(30).getMinute(), result.getResult().getAccessTokenExpiresAt().getMinute(), 1); + } + + +} diff --git a/Auth/src/test/java/org/momo/service/AuthServiceTest.java b/Auth/src/test/java/org/momo/service/AuthServiceTest.java new file mode 100644 index 0000000..81e8647 --- /dev/null +++ b/Auth/src/test/java/org/momo/service/AuthServiceTest.java @@ -0,0 +1,89 @@ +package org.momo.service; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; +import org.momo.Member.Entity.Member; +import org.momo.Member.Repository.MemberRepository; +import org.momo.common.status.ErrorStatus; +import org.momo.dto.AuthRequest; +import org.momo.dto.AuthResponse; +import org.momo.exception.MemberHandler; +import org.momo.util.JwtUtil; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import java.time.LocalDateTime; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + + +@ExtendWith(MockitoExtension.class) +class AuthServiceTest { + @Mock + private MemberRepository memberRepository; + @Mock + private BCryptPasswordEncoder bCryptPasswordEncoder; + + @InjectMocks + private AuthService authService; + + @Test + @DisplayName("로그인 성공") + public void 로그인_성공(){ + try(MockedStatic mockedJwtUtil = mockStatic(JwtUtil.class)){ + Member member = Member.builder() + .email("test@gmail.com") + .password(bCryptPasswordEncoder.encode("test")) + .build(); + AuthRequest.LoginDto loginDto = new AuthRequest.LoginDto("test@gmail.com","test"); + when(memberRepository.findMemberByEmail(member.getEmail())).thenReturn(Optional.of(member)); + when(bCryptPasswordEncoder.matches(loginDto.getPassword(),member.getPassword())).thenReturn(true); + mockedJwtUtil.when(()-> JwtUtil.createJwt(member.getMemberId(), member.getEmail(), null)).thenReturn("mockedJwtToken"); + + AuthResponse.LoginResponseDto result = authService.login(loginDto); + + assertEquals("mockedJwtToken", result.getAccessToken()); + assertEquals(LocalDateTime.now().plusMinutes(30).getMinute(), result.getAccessTokenExpiresAt().getMinute(), 1); + } + } + + @Test + @DisplayName("로그인 이메일 불일치") + public void 이메일불일치(){ + AuthRequest.LoginDto loginDto = new AuthRequest.LoginDto("test@gmail.com","test"); + when(memberRepository.findMemberByEmail(loginDto.getEmail())).thenReturn(Optional.empty()); + + MemberHandler exception = assertThrows(MemberHandler.class, () -> { + authService.login(loginDto); + }); + + assertEquals(ErrorStatus.MEMBER_NOT_FOUND.getMessage(),exception.getReasonHttpStatus().getMessage()); + } + + @Test + @DisplayName("로그인 비밀번호 불일치") + public void 비밀번호불일치(){ + Member member = Member.builder() + .email("test@gmail.com") + .password(bCryptPasswordEncoder.encode("password")) + .build(); + AuthRequest.LoginDto loginDto = new AuthRequest.LoginDto("test@gmail.com", "test"); + when(memberRepository.findMemberByEmail(loginDto.getEmail())).thenReturn(Optional.of(member)); + when(bCryptPasswordEncoder.matches(loginDto.getPassword(), member.getPassword())).thenReturn(false); + + MemberHandler exception = assertThrows(MemberHandler.class, () -> { + authService.login(loginDto); + }); + + assertEquals(ErrorStatus.PASSWORD_NOT_MATCH.getMessage(), exception.getReasonHttpStatus().getMessage()); + } + +} diff --git a/Common/build.gradle b/Common/build.gradle index 7ff849d..bcd2130 100644 --- a/Common/build.gradle +++ b/Common/build.gradle @@ -1,21 +1,26 @@ plugins { id 'java' + id 'java-library' } -group = 'org.momo' -version = '0.0.1-SNAPSHOT' +bootJar { enabled = false } +jar { enabled = true } repositories { mavenCentral() } dependencies { - // Swagger + api group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.12.3' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' + api 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'org.springframework.security:spring-security-test' + runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.12.3' + runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.12.3' testImplementation platform('org.junit:junit-bom:5.9.1') testImplementation 'org.junit.jupiter:junit-jupiter' } test { useJUnitPlatform() -} \ No newline at end of file +} diff --git a/Common/build/classes/java/main/org/momo/MomoCommonApplication.class b/Common/build/classes/java/main/org/momo/MomoCommonApplication.class deleted file mode 100644 index ba08959..0000000 Binary files a/Common/build/classes/java/main/org/momo/MomoCommonApplication.class and /dev/null differ diff --git a/Common/build/libs/Common-0.0.1-SNAPSHOT-plain.jar b/Common/build/libs/Common-0.0.1-SNAPSHOT-plain.jar deleted file mode 100644 index 594d6c8..0000000 Binary files a/Common/build/libs/Common-0.0.1-SNAPSHOT-plain.jar and /dev/null differ diff --git a/Common/build/tmp/compileJava/previous-compilation-data.bin b/Common/build/tmp/compileJava/previous-compilation-data.bin deleted file mode 100644 index cd306d7..0000000 Binary files a/Common/build/tmp/compileJava/previous-compilation-data.bin and /dev/null differ diff --git a/Common/build/tmp/jar/MANIFEST.MF b/Common/build/tmp/jar/MANIFEST.MF deleted file mode 100644 index 58630c0..0000000 --- a/Common/build/tmp/jar/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/Common/src/main/java/org/momo/MomoCommonApplication.java b/Common/src/main/java/org/momo/MomoCommonApplication.java new file mode 100644 index 0000000..416a500 --- /dev/null +++ b/Common/src/main/java/org/momo/MomoCommonApplication.java @@ -0,0 +1,11 @@ +package org.momo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MomoCommonApplication { + public static void main(String[] args) { + SpringApplication.run(MomoCommonApplication.class,args); + } +} diff --git a/Common/src/main/java/org/momo/common/status/ErrorStatus.java b/Common/src/main/java/org/momo/common/status/ErrorStatus.java index e51b565..ed76f48 100644 --- a/Common/src/main/java/org/momo/common/status/ErrorStatus.java +++ b/Common/src/main/java/org/momo/common/status/ErrorStatus.java @@ -18,6 +18,7 @@ public enum ErrorStatus implements BaseErrorCode { // 멤버 관련 에러 MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, 401, "사용자가 없습니다."), + PASSWORD_NOT_MATCH(HttpStatus.UNAUTHORIZED, 401, "비밀번호가 일치하지 않습니다."), USER_NOT_EXISTS(HttpStatus.NOT_FOUND, 404, "존재하지 않는 사용자입니다."), NICKNAME_EXISTS(HttpStatus.CONFLICT, 409, "이미 존재하는 닉네임입니다."), diff --git a/Common/src/main/java/org/momo/exception/ExceptionAdvice.java b/Common/src/main/java/org/momo/exception/ExceptionAdvice.java index cc0e892..b126ea7 100644 --- a/Common/src/main/java/org/momo/exception/ExceptionAdvice.java +++ b/Common/src/main/java/org/momo/exception/ExceptionAdvice.java @@ -12,6 +12,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.WebRequest; @@ -22,7 +23,7 @@ import java.util.Optional; @Slf4j -@RestControllerAdvice(annotations = {RestControllerAdvice.class}) +@RestControllerAdvice(annotations = {RestController.class}) public class ExceptionAdvice extends ResponseEntityExceptionHandler { @ExceptionHandler public ResponseEntity validation(ConstraintViolationException e, WebRequest request) { @@ -60,6 +61,7 @@ public ResponseEntity exception(Exception e, WebRequest request) { @ExceptionHandler(value = GeneralException.class) public ResponseEntity onThrowException(GeneralException generalException, HttpServletRequest request) { + log.error("GeneralException 발생 : {}", generalException.getMessage()); ErrorReasonDto errorReasonHttpStatus = generalException.getReasonHttpStatus(); return handleExceptionInternal(generalException,errorReasonHttpStatus,null,request); } diff --git a/Common/src/main/java/org/momo/exception/handler/JwtExpiredHandler.java b/Common/src/main/java/org/momo/exception/handler/JwtExpiredHandler.java new file mode 100644 index 0000000..3a8be37 --- /dev/null +++ b/Common/src/main/java/org/momo/exception/handler/JwtExpiredHandler.java @@ -0,0 +1,13 @@ +package org.momo.exception.handler; + +import io.jsonwebtoken.JwtException; + +public class JwtExpiredHandler extends JwtException { + public JwtExpiredHandler(String message) { + super(message); + } + + public JwtExpiredHandler(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/Common/src/main/java/org/momo/exception/handler/JwtInvalidHandler.java b/Common/src/main/java/org/momo/exception/handler/JwtInvalidHandler.java new file mode 100644 index 0000000..9a62c3e --- /dev/null +++ b/Common/src/main/java/org/momo/exception/handler/JwtInvalidHandler.java @@ -0,0 +1,13 @@ +package org.momo.exception.handler; + +import io.jsonwebtoken.JwtException; + +public class JwtInvalidHandler extends JwtException { + public JwtInvalidHandler(String message) { + super(message); + } + + public JwtInvalidHandler(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/Common/src/main/java/org/momo/util/JwtUtil.java b/Common/src/main/java/org/momo/util/JwtUtil.java new file mode 100644 index 0000000..530ae2e --- /dev/null +++ b/Common/src/main/java/org/momo/util/JwtUtil.java @@ -0,0 +1,104 @@ +package org.momo.util; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import org.momo.exception.handler.JwtExpiredHandler; +import org.momo.exception.handler.JwtInvalidHandler; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import java.util.Date; + +@Component +public class JwtUtil implements InitializingBean { + @Value("${JWT_SECRET}") + private String secret; + private static SecretKey secretKey; + private static final Long expireMs = 1000L * 60; //30분 + @Override + public void afterPropertiesSet() throws Exception { + byte[] keyBytes = Decoders.BASE64.decode(secret); + secretKey = Keys.hmacShaKeyFor(keyBytes); + } + + public static String getEmail(String token){ + return Jwts.parser() + .verifyWith(secretKey) + .build() + .parseSignedClaims(token) + .getPayload() + .get("email", String.class); + } + + public static String getRole(String token){ + return Jwts.parser() + .verifyWith(secretKey) + .build() + .parseSignedClaims(token) + .getPayload() + .get("role", String.class); + } + + public static Boolean isExpired(String token){ + return Jwts.parser() + .verifyWith(secretKey) + .build() + .parseSignedClaims(token) + .getPayload() + .getExpiration() + .before(new Date()); + } + + public static String getPassword(String token){ + return Jwts.parser() + .verifyWith(secretKey) + .build() + .parseSignedClaims(token) + .getPayload() + .get("password", String.class); + } + + public static String createJwt(Long memberId, String email, String role){ + return Jwts.builder() + .claim("memberId", memberId) + .claim("email", email) + .claim("role",role) + .issuedAt(new Date(System.currentTimeMillis())) + .expiration(new Date(System.currentTimeMillis()+expireMs)) + .signWith(secretKey) + .compact(); + } + + public static String createJwt(String email, String password, String role){ + return Jwts.builder() + .claim("password",password) + .claim("email", email) + .claim("role", role) + .issuedAt(new Date(System.currentTimeMillis())) + .expiration(new Date(System.currentTimeMillis()+expireMs)) + .signWith(secretKey) + .compact(); + } + + public static boolean validateAccessToken(String accessToken) { + try { + return Jwts.parser() + .verifyWith(secretKey) + .build() + .parseSignedClaims(accessToken) + .getPayload() + .getExpiration() + .before(new Date()); + } catch (ExpiredJwtException e) { + throw new JwtExpiredHandler("Expired Token Exception"); + } catch (UnsupportedJwtException | SecurityException | MalformedJwtException | NullPointerException e) { + throw new JwtInvalidHandler("Invalid Token Exception"); + } catch (Exception e) { + return false; + } + } + +} diff --git a/Domain/build.gradle b/Domain/build.gradle index abd9c58..3759baf 100644 --- a/Domain/build.gradle +++ b/Domain/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java' + id 'java-library' } group = 'org.momo' @@ -11,11 +11,12 @@ repositories { dependencies { implementation project(':Common') - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + api 'org.springframework.boot:spring-boot-starter-data-jpa' testImplementation platform('org.junit:junit-bom:5.9.1') testImplementation 'org.junit.jupiter:junit-jupiter' + runtimeOnly 'com.mysql:mysql-connector-j' } test { useJUnitPlatform() -} \ No newline at end of file +} diff --git a/Domain/build/classes/java/main/org/momo/Member/Member.class b/Domain/build/classes/java/main/org/momo/Member/Member.class deleted file mode 100644 index 146b9be..0000000 Binary files a/Domain/build/classes/java/main/org/momo/Member/Member.class and /dev/null differ diff --git a/Domain/build/classes/java/main/org/momo/MomoDomainApplication.class b/Domain/build/classes/java/main/org/momo/MomoDomainApplication.class deleted file mode 100644 index e7eec2d..0000000 Binary files a/Domain/build/classes/java/main/org/momo/MomoDomainApplication.class and /dev/null differ diff --git a/Domain/build/libs/Domain-0.0.1-SNAPSHOT-plain.jar b/Domain/build/libs/Domain-0.0.1-SNAPSHOT-plain.jar deleted file mode 100644 index bf9a72d..0000000 Binary files a/Domain/build/libs/Domain-0.0.1-SNAPSHOT-plain.jar and /dev/null differ diff --git a/Domain/build/tmp/compileJava/previous-compilation-data.bin b/Domain/build/tmp/compileJava/previous-compilation-data.bin deleted file mode 100644 index 7921eda..0000000 Binary files a/Domain/build/tmp/compileJava/previous-compilation-data.bin and /dev/null differ diff --git a/Domain/build/tmp/jar/MANIFEST.MF b/Domain/build/tmp/jar/MANIFEST.MF deleted file mode 100644 index 58630c0..0000000 --- a/Domain/build/tmp/jar/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/Domain/src/main/java/org/momo/Member/Entity/Member.java b/Domain/src/main/java/org/momo/Member/Entity/Member.java index 9b4a051..beea888 100644 --- a/Domain/src/main/java/org/momo/Member/Entity/Member.java +++ b/Domain/src/main/java/org/momo/Member/Entity/Member.java @@ -32,7 +32,7 @@ public class Member extends Base { @Column(length = 50) private String email; - @Column(length = 15) + @Column(length = 100) private String password; private LocalDate birth; diff --git a/Domain/src/main/java/org/momo/Member/Repository/MemberRepository.java b/Domain/src/main/java/org/momo/Member/Repository/MemberRepository.java new file mode 100644 index 0000000..d56bebc --- /dev/null +++ b/Domain/src/main/java/org/momo/Member/Repository/MemberRepository.java @@ -0,0 +1,10 @@ +package org.momo.Member.Repository; + +import org.momo.Member.Entity.Member; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface MemberRepository extends JpaRepository{ + Optional findMemberByEmail(String email); +} diff --git a/Domain/src/main/java/org/momo/MomoDomainApplication.java b/Domain/src/main/java/org/momo/MomoDomainApplication.java index 072b697..73771e2 100644 --- a/Domain/src/main/java/org/momo/MomoDomainApplication.java +++ b/Domain/src/main/java/org/momo/MomoDomainApplication.java @@ -1,7 +1,15 @@ package org.momo; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@SpringBootApplication +@EnableJpaAuditing public class MomoDomainApplication { public static void main(String[] args) { - System.out.println("Hello world!"); + SpringApplication.run(MomoDomainApplication.class, args); } -} \ No newline at end of file +} + + diff --git a/Api/build/resources/main/application.yml b/Domain/src/main/resources/application.yml similarity index 93% rename from Api/build/resources/main/application.yml rename to Domain/src/main/resources/application.yml index 7d91f09..570905d 100644 --- a/Api/build/resources/main/application.yml +++ b/Domain/src/main/resources/application.yml @@ -11,4 +11,4 @@ spring: hibernate: format_sql: true use_sql_comments: true -# show_sql: true \ No newline at end of file +# show_sql: true diff --git a/build.gradle b/build.gradle index 19657fd..133f647 100644 --- a/build.gradle +++ b/build.gradle @@ -26,14 +26,11 @@ subprojects { apply plugin: 'java' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' - //implementation 'org.springframework.boot:spring-boot-starter-batch' - //implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' - //runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' - //testImplementation 'org.springframework.boot:spring-boot-starter-test' - //testImplementation 'org.springframework.batch:spring-batch-test' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.batch:spring-batch-test' } tasks.named('test') {