Skip to content

Commit

Permalink
Merge pull request EnjoyTripKorea#5 from EnjoyTripKorea/feature/login
Browse files Browse the repository at this point in the history
1. 로그인 기능 구현입니다
  • Loading branch information
DebbieIsFree authored May 13, 2024
2 parents b7c955a + 9e8f68e commit 6c4595d
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package com.example.EnjoyTripBackend.controller;

import com.example.EnjoyTripBackend.dto.member.LoginRequestDto;
import com.example.EnjoyTripBackend.dto.member.SignUpRequestDto;
import com.example.EnjoyTripBackend.service.MemberService;
import com.example.EnjoyTripBackend.util.SessionConst;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
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 org.springframework.web.bind.annotation.*;

import static org.springframework.http.HttpStatus.CREATED;

@RestController
@RequestMapping("/api")
Expand All @@ -19,8 +22,25 @@ public class MemberController {
private final MemberService memberService;

@PostMapping("/signUp")
public ResponseEntity<?> signUp(@Valid @RequestBody SignUpRequestDto signUpRequestDto, BindingResult bindingResult){
public ResponseEntity<String> signUp(@Valid @RequestBody SignUpRequestDto signUpRequestDto, BindingResult bindingResult){
memberService.signUp(signUpRequestDto);
return ResponseEntity.ok().body("회원가입 완료");
return ResponseEntity.status(CREATED).body("회원가입 완료");
}

@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody LoginRequestDto loginRequestDto, HttpServletRequest request, BindingResult bindingResult){
memberService.login(loginRequestDto);
HttpSession session = request.getSession();
session.setAttribute(SessionConst.LOGIN_MEMBER, loginRequestDto.getEmail());
return ResponseEntity.ok().body("로그인 완료");
}

@PostMapping("/logout")
public ResponseEntity<String> logout(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
return ResponseEntity.ok().body("로그아웃 완료");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.EnjoyTripBackend.dto.member;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class LoginRequestDto {
private String email;
private String password;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
public enum ErrorCode {

INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 에러입니다."),
DUPLICATE_EMAIL(HttpStatus.CONFLICT, "이미 존재하는 이메일 계정입니다.");
DUPLICATE_EMAIL(HttpStatus.CONFLICT, "이미 존재하는 이메일 계정입니다."),
MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 회원입니다."),
LOGIN_FAIL(HttpStatus.UNAUTHORIZED,"아이디 또는 비밀번호가 잘못 되었습니다."),
ANONYMOUS_USER(HttpStatus.UNAUTHORIZED,"인증되지 않는 사용자입니다. 로그인을 진행해 주세요"),
REQUEST_METHOD_NOT_ALLOW(HttpStatus.METHOD_NOT_ALLOWED,"올바르지 않는 http method 입니다.");

private final HttpStatus httpstatus;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import static org.springframework.http.HttpStatus.METHOD_NOT_ALLOWED;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
Expand All @@ -17,6 +20,12 @@ public ResponseEntity<String> EnjoyTripExceptionHandler(EnjoyTripException e) {
return error(e);
}

@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseEntity<String> HttpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException e) {
log.error(e.getMessage());
return ResponseEntity.status(METHOD_NOT_ALLOWED.value()).body(ErrorCode.REQUEST_METHOD_NOT_ALLOW.getMessage());
}

@ExceptionHandler(RuntimeException.class)
public ResponseEntity<String> EnjoyTripExceptionHandler(RuntimeException e) {
log.error(e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.example.EnjoyTripBackend.filter;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;

@Slf4j
public class CorsFilter implements Filter {

public void init(FilterConfig filterConfig) throws ServletException {}

public void destroy() {}

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;

httpResponse.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:5500");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS, GET, DELETE, PUT");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
httpResponse.setHeader("Access-Control-Allow-Headers", "Authorization, x-requested-with, origin, content-type, accept");
chain.doFilter(servletRequest, servletResponse);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.example.EnjoyTripBackend.filter;

import com.example.EnjoyTripBackend.exception.EnjoyTripException;
import com.example.EnjoyTripBackend.exception.ErrorCode;
import com.example.EnjoyTripBackend.util.SessionConst;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.PatternMatchUtils;

import java.io.IOException;

import static org.springframework.http.MediaType.*;

@Slf4j
@RequiredArgsConstructor
public class LoginCheckFilter implements Filter {

private final ObjectMapper objectMapper;

private static final String[] whitelist = {"/api/signup", "/api/login", "/api/logout"};

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
String requestURI = httpRequest.getRequestURI();
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;

try {
log.info("인증 체크 필터 시작 {}", requestURI);
if (isLoginCheckPath(requestURI)) {
log.info("인증 체크 로직 실행 {}", requestURI);

HttpSession session = httpRequest.getSession(false);
if ((session == null) || (session.getAttribute(SessionConst.LOGIN_MEMBER) == null)) {
log.info("미인증 사용자 요청 {}", requestURI);
AnonymousUserHandler(httpResponse, ErrorCode.ANONYMOUS_USER.getMessage());
}
}
filterChain.doFilter(servletRequest, servletResponse);
} catch (Exception e) {
throw new EnjoyTripException(ErrorCode.ANONYMOUS_USER, e.getMessage());
} finally {
log.info("인증 체크 필터 종료 {}", requestURI);
}
}

private void AnonymousUserHandler(HttpServletResponse response, String message) throws IOException{
response.setStatus(HttpStatus.FORBIDDEN.value());
response.setCharacterEncoding("UTF-8");
response.setContentType(APPLICATION_JSON_VALUE);
objectMapper.writeValue(response.getWriter(), new ResponseEntity(message, null, HttpStatus.FORBIDDEN.value()));
}

private boolean isLoginCheckPath(String requestURI) {
return !PatternMatchUtils.simpleMatch(whitelist, requestURI);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.example.EnjoyTripBackend.filter;

import com.example.EnjoyTripBackend.util.SessionUserArgumentResolver;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.Filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new SessionUserArgumentResolver());
}

@Bean
public FilterRegistrationBean LoginCheckFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LoginCheckFilter(new ObjectMapper()));
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}

@Bean
public FilterRegistrationBean CorsFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new CorsFilter());
filterRegistrationBean.setOrder(0);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.example.EnjoyTripBackend.domain.Member;
import com.example.EnjoyTripBackend.domain.MemberRole;
import com.example.EnjoyTripBackend.dto.member.LoginRequestDto;
import com.example.EnjoyTripBackend.dto.member.SignUpRequestDto;
import com.example.EnjoyTripBackend.exception.EnjoyTripException;
import com.example.EnjoyTripBackend.exception.ErrorCode;
Expand Down Expand Up @@ -45,4 +46,14 @@ public Long signUp(SignUpRequestDto signUpRequestDto) {

return memberRepository.save(member);
}

public Long login(LoginRequestDto loginRequestDto) {

Member member = memberRepository.findByEmail(loginRequestDto.getEmail()).orElseThrow(() -> new EnjoyTripException(ErrorCode.MEMBER_NOT_FOUND));

if (!member.getPassword().equals(encrypt.encode(loginRequestDto.getPassword(), member.getSalt()))){
throw new EnjoyTripException(ErrorCode.LOGIN_FAIL);
}
return member.getId();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.EnjoyTripBackend.util;

public class SessionConst {
public static final String LOGIN_MEMBER = "loginMember";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.EnjoyTripBackend.util;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface SessionUser {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.example.EnjoyTripBackend.util;

import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import static com.example.EnjoyTripBackend.util.SessionConst.LOGIN_MEMBER;

@Slf4j
public class SessionUserArgumentResolver implements HandlerMethodArgumentResolver {

@Override
public boolean supportsParameter(MethodParameter parameter) {
boolean hasSessionUserAnnotation = parameter.hasParameterAnnotation(SessionUser.class);
boolean hasStringType = parameter.getParameterType().equals(String.class);
return hasSessionUserAnnotation && hasStringType;
}

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest httpRequest = (HttpServletRequest) webRequest.getNativeRequest();
return (String) httpRequest.getSession().getAttribute(LOGIN_MEMBER);
}
}

0 comments on commit 6c4595d

Please sign in to comment.