diff --git a/README.md b/README.md index 6fb23e96..79ef5b2a 100644 --- a/README.md +++ b/README.md @@ -36,3 +36,17 @@ Java Web Server Project for CodeSquad Members 2022 - [x] POST 로 회원가입 기능이 정상적으로 동작하도록 구현 - [x] 중복아이디를 처리하기 위해서 `Map` 로 회원목록을 관리 - [x] 가입 후 페이지 이동을 위해 redirection 기능을 구현 + +## 4단계. 쿠키를 이용한 로그인 구현 +### 기능요구사항 +- [x] 회원가입한 사용자로 로그인을 할 수 있어야 한다. +- [x] “로그인” 메뉴를 클릭하면 `http://localhost:8080/user/login.html` 으로 이동해 로그인할 수 있다. +- [x] 로그인이 성공하면 index.html로 이동하고, 로그인이 실패하면 /user/login_failed.html로 이동해야 한다. + +### 프로그래밍 요구사항 +- [x] 정상적으로 로그인 되었는지 확인하려면 앞 단계에서 회원가입한 데이터를 유지해야 한다. +- [x] 앞 단계에서 회원가입할 때 생성한 User 객체를 DataBase.addUser() 메서드를 활용해 메모리에 저장한다. +- [x] 필요에 따라 Database 클래스의 메소드나 멤버변수를 수정해서 사용한다. +- [x] 아이디와 비밀번호가 같은지를 확인해서 로그인이 성공하면 응답 header의 Set-Cookie 값을 sessionId=적당한값으로 설정한다. +- [x] Set-Cookie 설정시 모든 요청에 대해 Cookie 처리가 가능하도록 Path 설정 값을 /(Path=/)로 설정한다. +- [x] 응답 header에 Set-Cookie값을 설정한 후 요청 header에 Cookie이 전달되는지 확인한다. diff --git a/src/main/java/db/SessionDataBase.java b/src/main/java/db/SessionDataBase.java new file mode 100644 index 00000000..bb8c645e --- /dev/null +++ b/src/main/java/db/SessionDataBase.java @@ -0,0 +1,22 @@ +package db; + +import com.google.common.collect.Maps; +import java.util.Map; + +public class SessionDataBase { + + private static Map sessions = Maps.newHashMap(); + + public static void add(String sessionId, String userId) { + sessions.put(sessionId, userId); + } + + public static String findBySessionId(String sessionId) { + return sessions.get(sessionId); + } + + public static void remove(String sessionId) { + sessions.remove(sessionId); + } + +} diff --git a/src/main/java/webserver/Request.java b/src/main/java/webserver/Request.java index 9fa2c055..8fe093ca 100644 --- a/src/main/java/webserver/Request.java +++ b/src/main/java/webserver/Request.java @@ -4,32 +4,39 @@ public class Request { - private String methodType; - private String requestLine; + private RequestLine requestLine; + private Map headers; private String messageBody; - private URL url; - public Request(String requestLine, String messageBody, String methodType, URL url) { + public Request(RequestLine requestLine, Map headers, String messageBody) { this.requestLine = requestLine; + this.headers = headers; this.messageBody = messageBody; - this.methodType = methodType; - this.url = url; } public String getMessageBody() { return messageBody; } - public String getRequestLine() { + public RequestLine getRequestLine() { return requestLine; } - public URL getURL() { - return url; + public Map getHeaders() { + return headers; } - public String getMethodType() { - return methodType; + public boolean isGetMethodType() { + return requestLine.isGetMethodType(); } + + public boolean isPostMethodType() { + return requestLine.isPostMethodType(); + } + + public URL getUrl() { + return requestLine.getUrl(); + } + } diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 03eb5fc0..56e4a6a8 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -1,6 +1,5 @@ package webserver; -import db.DataBase; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; @@ -9,17 +8,8 @@ import java.io.OutputStream; import java.net.Socket; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import util.HttpRequestUtils; -import util.IOUtils; -import util.RequestLineUtil; public class RequestHandler extends Thread { @@ -38,39 +28,21 @@ public void run() { try (InputStream in = connection.getInputStream(); OutputStream out = connection.getOutputStream()) { BufferedReader br = new BufferedReader(new InputStreamReader(in)); - Request request = makeRequest(br); + RequestReader requestReader = new RequestReader(br); + Request request = requestReader.getRequest(); - // URL init - URL url = request.getURL(); - - // user save - if (request.getMethodType().equals("POST") && url.comparePath("/user/create")) { - String messageBody = request.getMessageBody(); - userSave(messageBody, url); - } + UserManager userManager = new UserManager(request); + String sessionId = userManager.action(); // Response Message DataOutputStream dos = new DataOutputStream(out); - Response response = new Response(request.getRequestLine(), url); + Response response = new Response(request.getRequestLine(), sessionId); sendResponse(dos, response); } catch (IOException e) { log.error(e.getMessage()); } } - private void userSave(String messageBody, URL url) { - Map userInfo = HttpRequestUtils.parseQueryString(messageBody); - User user = new User(userInfo.get("userId"), userInfo.get("password"), userInfo.get("name"), - userInfo.get("email")); - - if (DataBase.findUserById(userInfo.get("userId")) == null) { - DataBase.addUser(user); - url.setRedirectHomePage(); - } else { - url.setRedirectSignUpPage(); - } - } - private void sendResponse(DataOutputStream dos, Response response) throws IOException { response.action(); @@ -82,61 +54,4 @@ private void sendResponse(DataOutputStream dos, Response response) throws IOExce dos.write(body, 0, body.length); } } - - - private Request makeRequest(BufferedReader bufferedReader) throws IOException { - String requestLine = initRequestLine(bufferedReader); - Map headers = new HashMap<>(); - - initRequestHeaders(bufferedReader, headers); - outputLog(headers, requestLine); - - URL url = initURL(requestLine, headers); - - String messageBody = ""; - String methodType = RequestLineUtil.getMethodType(requestLine); - - if (methodType.equals("POST")) { - messageBody = IOUtils.readData(bufferedReader, - Integer.parseInt(headers.get("Content-Length:"))); - log.debug(messageBody); - } - - return new Request(requestLine, messageBody, methodType, url); - } - - private void outputLog(Map headers, String requestLine) { - // Request Line Log - log.debug(requestLine); - // Request Headers Log - for (Entry entry : headers.entrySet()) { - log.debug("Request: {} {}", entry.getKey(), entry.getValue()); - } - } - - private String initRequestLine(BufferedReader bufferedReader) throws IOException { - String line; - if ("".equals(line = bufferedReader.readLine()) || line == null) { - throw new IllegalArgumentException("Request.bufferedReader.readLine == null 입니다."); - } - return line; - } - - private void initRequestHeaders(BufferedReader bufferedReader, Map headers) - throws IOException { - String line; - while (!"".equals(line = bufferedReader.readLine())) { - if (line == null) { - throw new IllegalArgumentException("Request.bufferedReader.readLine == null 입니다."); - } - String[] splitLine = line.split(" "); - headers.put(splitLine[0], splitLine[1]); - } - } - - private URL initURL(String requestLine, Map headers) { - String decodedUrl = URLDecoder.decode(requestLine, StandardCharsets.UTF_8); - String host = headers.get("Host:"); - return new URL(RequestLineUtil.getURL(decodedUrl), host); - } } diff --git a/src/main/java/webserver/RequestLine.java b/src/main/java/webserver/RequestLine.java new file mode 100644 index 00000000..b0ddc2da --- /dev/null +++ b/src/main/java/webserver/RequestLine.java @@ -0,0 +1,32 @@ +package webserver; + +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; + +public class RequestLine { + + private String methodType; + private URL url; + + public RequestLine(String methodType, String url) { + this.methodType = methodType; + this.url = makeURL(url); + } + + private URL makeURL(String url) { + String decodedUrl = URLDecoder.decode(url, StandardCharsets.UTF_8); + return new URL(decodedUrl); + } + + public boolean isGetMethodType() { + return "GET".equals(methodType); + } + + public boolean isPostMethodType() { + return "POST".equals(methodType); + } + + public URL getUrl() { + return url; + } +} diff --git a/src/main/java/webserver/RequestReader.java b/src/main/java/webserver/RequestReader.java new file mode 100644 index 00000000..91a23956 --- /dev/null +++ b/src/main/java/webserver/RequestReader.java @@ -0,0 +1,72 @@ +package webserver; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import util.IOUtils; +import util.RequestLineUtil; + +public class RequestReader { + + private static final Logger log = LoggerFactory.getLogger(RequestReader.class); + + private final BufferedReader bufferedReader; + + public RequestReader(BufferedReader bufferedReader) { + this.bufferedReader = bufferedReader; + } + + public Request getRequest() throws IOException { + RequestLine requestLine = makeRequestLine(bufferedReader); + + Map headers = makeRequestHeaders(bufferedReader); + outputLog(headers); + + String messageBody = ""; + if (requestLine.isPostMethodType()) { + messageBody = IOUtils.readData(bufferedReader, + Integer.parseInt(headers.get("Content-Length:"))); + log.debug(messageBody); + } + + return new Request(requestLine, headers, messageBody); + } + + private void outputLog(Map headers) { + // Request Headers Log + for (Entry entry : headers.entrySet()) { + log.debug("Request: {} {}", entry.getKey(), entry.getValue()); + } + } + + private RequestLine makeRequestLine(BufferedReader bufferedReader) throws IOException { + String line; + if ("".equals(line = bufferedReader.readLine()) || line == null) { + throw new IllegalArgumentException("Request.bufferedReader.readLine == null 입니다."); + } + // Request Line Log + log.debug(line); + + String url = RequestLineUtil.getURL(line); + String methodType = RequestLineUtil.getMethodType(line); + return new RequestLine(methodType, url); + } + + private Map makeRequestHeaders(BufferedReader bufferedReader) + throws IOException { + Map headers = new HashMap<>(); + String line; + while (!"".equals(line = bufferedReader.readLine())) { + if (line == null) { + throw new IllegalArgumentException("Request.bufferedReader.readLine == null 입니다."); + } + String[] splitLine = line.split(" "); + headers.put(splitLine[0], splitLine[1]); + } + return headers; + } +} diff --git a/src/main/java/webserver/Response.java b/src/main/java/webserver/Response.java index 502bd5af..84b9fbac 100644 --- a/src/main/java/webserver/Response.java +++ b/src/main/java/webserver/Response.java @@ -8,23 +8,36 @@ public class Response { private byte[] body; private String headers; - private String requestLine; + private String sessionId; + private RequestLine requestLine; private URL url; - public Response(String requestLine, URL url) { + public Response(RequestLine requestLine, String sessionId) { this.requestLine = requestLine; - this.url = url; + this.url = requestLine.getUrl(); + this.sessionId = sessionId; } public void action() throws IOException { - if (requestLine.contains("POST") && requestLine.contains("/user/create")) { - headers = response302Header(url); - } else { - body = Files.readAllBytes(new File("./webapp" + url.getPath()).toPath()); - headers = response200Header(body.length); + if (requestLine.isPostMethodType()) { + if (url.comparePath("/user/create")) { + headers = response302Header(url); + } + if (url.comparePath("/user/login")) { + headers = response302Header(url, sessionId, true); + } + } + if (requestLine.isGetMethodType()) { + if (url.comparePath("/user/logout")) { + headers = response302Header(url, sessionId, false); + } else { + body = Files.readAllBytes(new File("./webapp" + url.getPath()).toPath()); + headers = response200Header(body.length); + } } } + private String response200Header(int lengthOfBodyContent) { StringBuilder sb = new StringBuilder(); sb.append("HTTP/1.1 200 OK \r\n"); @@ -39,7 +52,23 @@ private String response302Header(URL url) { StringBuilder sb = new StringBuilder(); sb.append("HTTP/1.1 302 Found \r\n"); sb.append("Content-Type: text/html;charset=utf-8\r\n"); - sb.append("Location: " + url.getPath() + "\r\n"); + sb.append("Location: " + url.getRedirectPath() + "\r\n"); + sb.append("\r\n"); + + return sb.toString(); + } + + private String response302Header(URL url, String sessionId, boolean isLogin) { + StringBuilder sb = new StringBuilder(); + sb.append("HTTP/1.1 302 Found \r\n"); + sb.append("Content-Type: text/html;charset=utf-8\r\n"); + sb.append("Location: " + url.getRedirectPath() + "\r\n"); + + if (isLogin) { + sb.append("Set-Cookie: sessionId = " + sessionId + "; Path=/\r\n"); + } else { + sb.append("Set-Cookie: sessionId = " + sessionId + "; max-age=0; Path=/\r\n"); + } sb.append("\r\n"); return sb.toString(); diff --git a/src/main/java/webserver/URL.java b/src/main/java/webserver/URL.java index c1592232..0ce967ca 100644 --- a/src/main/java/webserver/URL.java +++ b/src/main/java/webserver/URL.java @@ -2,31 +2,36 @@ public class URL { - private static final String SCHEME = "http://"; + private static final String BASE_PATH = "http://localhost:8080"; private static final String HOME_PAGE_PATH = "/index.html"; private static final String SINE_UP_PAGE_PATH = "/user/form.html"; - - private final String homePage; - private final String signUpPage; + private static final String LOGIN_FAILED_PAGE_PATH = "/user/login_failed.html"; private String path; + private String redirectPath; - public URL(String path, String host) { + public URL(String path) { this.path = path; - homePage = SCHEME + host + HOME_PAGE_PATH; - signUpPage = SCHEME + host + SINE_UP_PAGE_PATH; } public String getPath() { return path; } - public void setRedirectHomePage() { - path = homePage; + public String getRedirectPath() { + return redirectPath; + } + + public void changePathToHomePage() { + redirectPath = BASE_PATH + HOME_PAGE_PATH; + } + + public void changePathToSignUpPage() { + redirectPath = BASE_PATH + SINE_UP_PAGE_PATH; } - public void setRedirectSignUpPage() { - path = signUpPage; + public void changePathToLoginFailedPage() { + redirectPath = BASE_PATH + LOGIN_FAILED_PAGE_PATH; } public boolean comparePath(String targetPath) { diff --git a/src/main/java/webserver/UserManager.java b/src/main/java/webserver/UserManager.java new file mode 100644 index 00000000..a7e8a505 --- /dev/null +++ b/src/main/java/webserver/UserManager.java @@ -0,0 +1,76 @@ +package webserver; + +import db.DataBase; +import db.SessionDataBase; +import java.util.Map; +import java.util.UUID; +import model.User; +import util.HttpRequestUtils; + +public class UserManager { + + private final Request request; + + public UserManager(Request request) { + this.request = request; + } + + private void save(URL url, String messageBody) { + Map userInfo = HttpRequestUtils.parseQueryString(messageBody); + User user = new User(userInfo.get("userId"), userInfo.get("password"), + userInfo.get("name"), + userInfo.get("email")); + + if (DataBase.findUserById(userInfo.get("userId")) == null) { + DataBase.addUser(user); + url.changePathToHomePage(); + } else { + url.changePathToSignUpPage(); + } + } + + private String login(URL url, String messageBody) { + Map userInfo = HttpRequestUtils.parseQueryString(messageBody); + User user = DataBase.findUserById(userInfo.get("userId")); + + if (user.getUserId().equals(userInfo.get("userId")) && user.getPassword() + .equals(userInfo.get("password"))) { + String sessionId = UUID.randomUUID().toString(); + SessionDataBase.add(sessionId, userInfo.get("userId")); + + url.changePathToHomePage(); + return sessionId; + } else { + url.changePathToLoginFailedPage(); + return ""; + } + } + + private String logout(URL url, Map headers) { + String value = headers.get("Cookie:"); + String sessionId = value.split("=")[1]; + + SessionDataBase.remove(sessionId); + url.changePathToHomePage(); + + return sessionId; + } + + public String action() { + URL url = request.getUrl(); + String messageBody = request.getMessageBody(); + + if (request.isPostMethodType() && url.comparePath("/user/create")) { + save(url, messageBody); + } + if (request.isPostMethodType() && url.comparePath("/user/login")) { + return login(url, messageBody); + } + if (request.isGetMethodType() && url.comparePath("/user/logout")) { + Map headers = request.getHeaders(); + return logout(url, headers); + } + + return ""; + } +} diff --git a/webapp/index.html b/webapp/index.html index 1675898a..939884b9 100644 --- a/webapp/index.html +++ b/webapp/index.html @@ -71,7 +71,7 @@
  • 로그인
  • 회원가입
  • --> -
  • 로그아웃
  • +
  • 로그아웃
  • 개인정보수정
  • @@ -145,4 +145,4 @@ - \ No newline at end of file +