Servlet/JSP로 사용자 관리 시스템 구현하기 과정 - 7
JSP/Servlet과JSP로 사용자 관리 시스템 구현

Servlet/JSP로 사용자 관리 시스템 구현하기 과정 - 7

반응형

Servlet/JSP로 사용자 관리 시스템 구현하기 과정


강의링크 : https://www.slipp.net/wiki/pages/viewpage.action?pageId=25526405





지난 게시글에서, 개인정보수정을 서블릿을 통해 구현하고 form.jsp에 view 페이지를 통합해서 중복을 제거하는 과정을 진행했습니다.


하지만, 우리가 현재 만든 개인정보수정 폼은 보안적인 측면에서 큰 문제가 있는데요. 어떤게 문제일까요?



실제로 수정된 정보를 입력해서 로직을 처리하는 UpdateUserServlet에서 현재 로그인된 사용자가 맞는지 확인하는 단계가 이루어지지 않고 있습니다.


이는, 제대로 된 경로가 아닌 접근을 통해 이 서블릿으로 어떠한 사용자가 접근한다면 맘대로 다른 사용자의 정보를 수정할 수도 있는 문제를 초래할 수 있습니다. 또한 다른 사용자의 userId를 직접 입력하며 수정도 가능하기에 해킹 등 심각한 보안 문제가 발생하게 됩니다.


따라서 우리가 만든 Update와 관련된 서블릿에서 보안적으로 안전할 수 있도록 코드를 추가하여 수정해보겠습니다.




유저 패키지 안에 현재 세션에 존재하는 아이디가 존재하는지, 존재하면 String 변수로 가져와 비교할 수 있는 두 메소드를 가진 SessionUtils.java를 만들어줍니다.


SessionUtils.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package net.slipp.user;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
 
public class SessionUtils {
    public static boolean isEmpty(HttpSession session, String key) {
        
        Object object = session.getAttribute(key);
        
        if(object == null){
            return true;
        }
        return false;
    }
    
    public static String getStringValue(HttpSession session, String key){
        if(isEmpty(session, key)){
            return null;
        }
        
        return (String)session.getAttribute(key);
        
    }
}
 
cs


isEmpty는 매개변수로 session과 key를 받아서 object에 세션에 존재하는 key를 받아오는 boolean 형 메소드입니다.

여기서 세션으로 받아오는 값이 object에 저장이 된다면 empty하지 않은 것이기 때문에 false로 리턴을 하는 모습을 볼 수 있습니다.


getStringValue는 매개변수로 session과 key를 받아서 세션 값이 존재한다면 이를 String 변수로 만들어 return해주는 메소드입니다.


이제 이를 활용해서 우리가 Update를 진행하는 두 서블릿에서 현재 로그인 정보가 존재하는지 확인 과정을 거친 뒤에 개인정보수정을 할 수 있도록 수정하면 되겠습니다.




우선 UpdateFormUserServlet에서는, 세션에 존재하는 유저의 데이터 값들을 가져오는 로직을 처리하고 있습니다.


따라서 LoginServlet을 통해 세션 아이디를 가져와 userId 변수에 저장하는 코드로 바꿔주었습니다. 만약 userId가 값이 없다면 redirect를 통해 root로 보내면서 수정이 진행될 수 없도록 합니다.


1
2
3
4
5
6
7
HttpSession session = req.getSession();
        String userId = SessionUtils.getStringValue(session, LoginServlet.SESSION_USER_ID);
        
        if(userId == null) {
            resp.sendRedirect("/");
            return;
        }
cs


이처럼 SessionUtils의 메소드를 통해 작성하니 훨씬 코드가 간단해지고 가독성이 좋게 수정된 것을 볼 수 있습니다.




UpdateFormUserServlet.java 전체 소스 코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package net.slipp.user;
 
import java.io.IOException;
import java.sql.SQLException;
 
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
 
@WebServlet("/users/updateForm")
public class UpdateFormUserServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        
        HttpSession session = req.getSession();
        String userId = SessionUtils.getStringValue(session, LoginServlet.SESSION_USER_ID);
        
        if(userId == null) {
            resp.sendRedirect("/");
            return;
        }
        
        System.out.println("User Id : " + userId);
        UserDAO userDAO = new UserDAO();
        try {
            User user = userDAO.findByUserId(userId);
            req.setAttribute("user", user);
            RequestDispatcher rd = req.getRequestDispatcher("/form.jsp");
            rd.forward(req, resp);
        } catch (SQLException e) {
        }
    }
}
 
cs



이번에는 우리가 form.jsp를 이용해 수정을 하고, 완료를 누르면 데이터베이스에서 업데이트 과정을 거치는 UpdateUserServlet을 수정해봅시다!


우선 UpdateFormUserServlet에서 수정했던 것 처럼 아래와 같이 똑같이 구성됩니다.


1
2
3
4
5
6
7
HttpSession session = request.getSession();
        String sessionUserId = SessionUtils.getStringValue(session, LoginServlet.SESSION_USER_ID);
        
        if(sessionUserId == null){
            response.sendRedirect("/");
            return;
        }
cs



추가적으로 이 서블릿에는, 현재 접속중인 아이디와 수정할 아이디가 일치하는지 확인작업이 필요합니다. 이를 진행하지 않으면 해당 로그인 유저가 다른 사용자의 아이디를 입력해서 수정할 수 있는 문제가 발생할 수도 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
String userId = request.getParameter("userId");
        
        if(!sessionUserId.equals(userId)){ //현재 접속 아이디와 수정할 아이디가 같지 않을 때
            response.sendRedirect("/");
            return;
        }
        
        String password = request.getParameter("password");
        String name = request.getParameter("name");
        String email = request.getParameter("email");
        
        User user = new User(userId, password, name, email);
cs


지금처럼 request를 통해 받아오는 유저의 정보에서 userId를 받아온 이후에 equals 메소드를 통해 같은 아이디인지 확인해주는 작업을 추가합니다. 이 조건을 성립하지 않으면 root 페이지로 redirect 하며 수정 작업이 이루어지지 않도록 구성했습니다.


이제 해당 로그인 유저가 자신의 정보만 수정이 가능한 개인정보수정 폼이 완성되었습니다.

이처럼 웹페이지 상에서 데이터베이스를 이용하는 일은 보안적인 측면에서 문제가 없도록 관리하는 것이 필수입니다.




UpdateUserServlet.java 전체 소스 코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package net.slipp.user;
 
import java.io.IOException;
import java.sql.SQLException;
 
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
 
@WebServlet("/users/update")
public class UpdateUserServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        HttpSession session = request.getSession();
        String sessionUserId = SessionUtils.getStringValue(session, LoginServlet.SESSION_USER_ID);
        
        if(sessionUserId == null){
            response.sendRedirect("/");
            return;
        }
        
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8"); // 한글 깨지지 않도록
        
        String userId = request.getParameter("userId");
        
        if(!sessionUserId.equals(userId)){ //현재 접속 아이디와 수정할 아이디가 같지 않을 때
            response.sendRedirect("/");
            return;
        }
        
        String password = request.getParameter("password");
        String name = request.getParameter("name");
        String email = request.getParameter("email");
        
        User user = new User(userId, password, name, email);
        UserDAO userDAO = new UserDAO();
        try {
            userDAO.updateUser(user);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        
        response.sendRedirect("/");
    }
}
 
cs





Gson 라이브러리를 통한 json 데이터 변환하기


모바일을 통해서 웹 서버를 연동하거나, 웹 클라이언트에서 ajax 등을 통해 웹 서버와 통신을 해야하는 경우 여러가지 데이터 형식을 통해 통신을 할 수 있는 gson에 대해서 알아보도록 하겠습니다.


gson을 이용한다면 데이터 형식으로 주고받는 것이 가능한데요. 기본적으로 java object로 되어있는 웹 서버를 gson 데이터로 변환해주는 라이브러리를 활용해 볼 것입니다.


gson download를 검색하신 후, jar파일을 다운받아서 프로젝트 webapp/WEB-INF/lib 폴더에 복사하여 넣어주고 build path에 추가해주도록 합시다.


이제 우리는 현재 프로젝트에서 gson 라이브러리를 사용할 수 있게 되었습니다.


이제 로그인된 사용자의 데이터를 가져올 수 있도록 서블릿을 하나 생성해봅시다. 이름은 api를 통해 유저 데이터를 찾는 것이니 ApiFindUserServlet로 생성하겠습니다.


지금은 유저 데이터를 json으로 확인해보는 단계이니 보안적인 면은 크게 고려하지 않는 상황이구요. 자신이 원하는 어노테이션 경로 이름을 설정해 WebServlet으로 매핑해주시면 됩니다. 만약 'api/users/find'라는 경로를 설정했다면, 데이터베이스에 저장된 kim1234라는 아이디 정보를 이처럼 url에 검색할 수 있습니다.


http://localhost:8080/api/users/find?userId=kim1234 


그 전에 코드부터 완성해야겠죠? get 방식으로 데이터를 받아오고, request를 통해 userId를 받아와 현재 로그인 된 상황인지 체크합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        
        String userId = req.getParameter("userId");
        if(userId == null){
            resp.sendRedirect("/");
            return;
        }
        
        UserDAO userDao = new UserDAO();
        try {
            User user = userDao.findByUserId(userId);
            if(user == null){ // 유저가 없으면 리턴
                return;
            }
cs



만약 유저가 로그인되어 있다면, try문이 계속 진행되겠죠? 이어서 gson을 활용해 데이터를 출력해보면 됩니다.


1
2
3
4
5
6
7
8
9
final GsonBuilder builder = new GsonBuilder();
builder.excludeFieldsWithoutExposeAnnotation();
final Gson gson = builder.create();
            
String jsonData = gson.toJson(user);
resp.setContentType("application/json;charset=UTF-8");
            
PrintWriter out = resp.getWriter();
out.print(jsonData);
cs


(Gson 라이브러리 사용법에 대해 더 알아볼 것)


GsonBuilder를 통해 어노테이션에 Expose가 없는 필드를 다 빼버리는 함수가 진행되고 있고, 이를 gson을 통해 create하는 모습입니다.


jsonData라는 string 변수에 toJson 메소드를 활용해서 데이터를 json으로 변환시켜주는 과정이 이어지구요.



resp.setContentType("application/json;charset=UTF-8");

ContentType을 통해 json과 한글이 출력될 수 있도록 UTF-8을 설정해주는 모습입니다. 마지막으로 PrintWriter을 통해서 출력을 해주고 있습니다.



우리가 Expose가 없는 필드는 빼는 함수를 진행했는데요. 데이터에서 비밀번호까지 출력해줄 필요는 없겠죠? 따라서 User 클래스의 모든 필드 변수에 Expose를 지정해주고, 비밀번호만 false로 설정해준다면 json으로 출력되는 유저의 데이터에 비밀번호는 빠지게 될 것입니다.


1
2
3
4
5
6
7
8
9
public class User {
    @Expose
    private String userId;
    @Expose(serialize = false)
    private String password;
    @Expose
    private String name;
    @Expose
    private String email;
cs



api에서 유저를 찾아주는 서블릿의 전체 소스 코드는 아래와 같습니다.


ApiFindUserServlet.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package net.slipp.user;
 
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.SQLException;
 
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
 
@WebServlet("/api/users/find")
public class ApiFindUserServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        
        String userId = req.getParameter("userId");
        if(userId == null){
            resp.sendRedirect("/");
            return;
        }
        
        UserDAO userDao = new UserDAO();
        try {
            User user = userDao.findByUserId(userId);
            if(user == null){ // 유저가 없으면 리턴
                return;
            }
            
            final GsonBuilder builder = new GsonBuilder();
            builder.excludeFieldsWithoutExposeAnnotation();
            final Gson gson = builder.create();
            
            String jsonData = gson.toJson(user);
            resp.setContentType("application/json;charset=UTF-8");
            
            PrintWriter out = resp.getWriter();
            out.print(jsonData);
        } catch (SQLException e) {
            
        }
    }
}
 
cs


이제 서버를 재시작하고, http://localhost:8080/api/users/find?userId=kim1234 경로로 접속해보겠습니다.



비밀번호 제외한 로그인된 유저 데이터의 정보가 json으로 잘 출력되는 모습을 볼 수 있습니다!







반응형