Java 프레임워크 만들기 - JSP

자바 시큐리티 필터 만들기 - Java Security Filter, 이클립스(Eclipse)

carrotweb 2021. 4. 8. 13:32
728x90
반응형

스프링 프레임워크에서는 Handler Interceptor(핸들러 인터셉터)를 이용하거나 spring security를 이용하여 접근 제어를 처리할 수 있습니다. 그러나 스프링 프레임워크를 사용하지 않는 일반적인 웹 프로젝트에서는 필터를 이용하거나 페이지에서 인증처리로 처리해야 합니다.

 

필터를 이용하여 사용자가 로그인되지 않았으면 로그인 페이지로 이동하고 로그인되어 있으면 이동하려는 페이지로 이동하게 시큐리티 필터를 만들겠습니다.

1. "test2" 프로젝트의 "Java Resources"에서 "src/main/java"의 "com.home.project.test2.filter"를 선택한 후 오른쪽 버튼을 클릭하여 컨텍스트 메뉴 [New > Filter]를 클릭합니다.

"Create Filter"창이 나타납니다.

패키지를 선택하고 컨텍스트 메뉴 [New > Filter]를 클릭하면 자동으로 "Java package"가 지정되어 나옵니다.

"Class name"에 "TestSecurityFilter"를 입력하고 "Next >"버튼을 클릭합니다.

"Initialization parameters"를 추가합니다.

"Description"은 필터의 설명으로 "Security Filter Test"을 입력합니다. 설명은 입력하지 않아도 됩니다.

"Initialization parameters"에서 "Add..."버튼을 클릭하여 파라메타 명은 "login-page-url", 파라메타 값은 "/login.do"을 입력하고 "OK"버튼을 클릭합니다. 파라메타 설명은 입력하지 않아도 됩니다.

"login-page-url"은 로그인 폼으로 이동할 URL입니다.

"Initialization parameters"를 더 추가합니다.

"Initialization parameters"에서 "Add..."버튼을 클릭하여 파라메타 명은 "login-process-url", 파라메타 값은 "/loginprocess.do"을 입력하고 "OK"버튼을 클릭합니다. 파라메타 설명은 입력하지 않아도 됩니다.

"login-process-url"은 로그인 인증을 처리할 URL입니다.

"Initialization parameters"를 더 추가합니다.

"Initialization parameters"에서 "Add..."버튼을 클릭하여 파라메타 명은 "login-ignore-url", 파라메타 값은 "/test1.do,/test2.do"을 입력하고 "OK"버튼을 클릭합니다. 파라메타 설명은 입력하지 않아도 됩니다.

"login-ignore-url"은 로그인 인증이 필요하지 않는 URL들을 ",(콤마)"로 구분하여 입력합니다.

"Filter mappings"를 추가합니다.

"Filter mappings"의 "/TestSecurityFilter"를 선택하고 "Edit..."버튼을 클릭한 후 "Pattern"를 "/*"로 입력합니다. 모든 웹 경로에 필터를 적용하기 위해서 입니다. "OK"버튼을 클릭합니다.

"Finish"버튼을 클릭합니다.

"TestSecurityFilter.java"파일이 생성됩니다.

 

현재 "Dynamic Web Module 3.1"로 구성되어 있어 "TestSecurityFilter.java"에 "@WebFilter" 어노테이션을 사용하고 있습니다.

@WebFilter(
		description = "Security Filter Test", 
		urlPatterns = { "/*" }, 
		initParams = { 
				@WebInitParam(name = "login-page-url", value = "/login.do", description = "login page url"), 
				@WebInitParam(name = "login-process-url", value = "/loginprocess.do", description = "login process url"), 
				@WebInitParam(name = "login-ignore-url", value = "/test1.do,/test2.do", description = "login ignore url")
		})

"web.xml"에서 구성하고 싶다면 "TestSecurityFilter.java"에 "@WebFilter" 어노테이션을 삭제한 후 "web.xml"에 다음과 같이 추가하면 됩니다.

<filter>
	<filter-name>SecurityFilter</filter-name>
	<filter-class>com.home.project.test2.filter.TestSecurityFilter</filter-class>
	<init-param>
		<param-name>login-page-url</param-name>
		<param-value>/login.do</param-value>
	</init-param>
	<init-param>
		<param-name>login-process-url</param-name>
		<param-value>/loginprocess.do</param-value>
	</init-param>
	<init-param>
		<param-name>login-ignore-url</param-name>
		<param-value>/test1.do,/test2.do</param-value>
	</init-param>
</filter>
<filter-mapping>
	<filter-name>SecurityFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

처리 방식은 동일합니다.

 

 

"TestSecurityFilter.java"에 "loginPageUrl", "loginProcessUrl" 문자열 지역 변수와 "loginIgnoreUrl" 문자열 리스트를 추가하고 "init"에는 필터 정보로 부터 파라메타 값을 가져오는 "getInitParameter"를 이용합니다.

private String loginPageUrl = "";
private String loginProcessUrl = "";
private List<String> loginIgnoreUrl = new ArrayList<>();

public void init(FilterConfig fConfig) throws ServletException {
	loginPageUrl = fConfig.getInitParameter("login-page-url") == null ? "" : fConfig.getInitParameter("login-page-url");
	loginProcessUrl = fConfig.getInitParameter("login-process-url") == null ? "" : fConfig.getInitParameter("login-process-url");
	String loginIgnoreUrls = fConfig.getInitParameter("login-ignore-url") == null ? "" : fConfig.getInitParameter("login-ignore-url");
	if (0 < loginIgnoreUrls.length()) {
		loginIgnoreUrl.addAll(Arrays.asList(loginIgnoreUrls.split(",|\\s+")));
	}
	loginIgnoreUrl.add(loginPageUrl);
	loginIgnoreUrl.add(loginProcessUrl);
}

"login-ignore-url"은 ",(콤마)", " (공백)"로 구분되어 있어 "split"으로 분리하고 "List<String> loginIgnoreUrl"에 추가합니다. 또한 로그인 URL과 로그인 인증처리 URL로 추가합니다.

"TestSecurityFilter.java"의 "doFilter"에 "httpServletRequest.getSession"를 통해 로그인 인증이 있는지 확인하고 로그인 인증이 없으면 로그인 페이지로 이동합니다.

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
	HttpServletRequest httpServletRequest = (HttpServletRequest)request;

	String contextPath = httpServletRequest.getContextPath();
	String requestURI = httpServletRequest.getRequestURI();
	if (null != contextPath && 0 < contextPath.length()) {
		requestURI = requestURI.replaceFirst(contextPath, "");
	}

	boolean exception = false;
	if (loginIgnoreUrl.contains(requestURI)) {
		exception = true;
	}

	if (exception) {
		System.out.println("예외처리된 URL입니다.");
		chain.doFilter(request, response);
	} else {
		HttpSession httpSession = httpServletRequest.getSession(false);
	
		boolean auth = false;
		if (null != httpSession) {
			if (null != httpSession.getAttribute("auth")) {
				auth = true;
			}
		}

		if (auth) {
			System.out.println("인증되었습니다.");
			chain.doFilter(request, response);
		} else {
			System.out.println("인증되지 않았습니다.");
			request.setAttribute("errorMessage", "");
			request.setAttribute("returnUrl", httpServletRequest.getRequestURI());
			RequestDispatcher requestDispatcher = request.getRequestDispatcher(loginPageUrl);
			requestDispatcher.forward(request, response);
		}
	}
}

필터에 들어은 URL이 로그인 인증이 필요하지 않는 URL들인지 확인하 하고 인증이 필요하지 않는 URL은 필터체인으로 넘어 갑니다.

인증이 필요하는 URL은 섹션에 인증 정보가 있는지 확인 한 후 인증되지 않았으면 로그인 페이지로 이동합니다.

 

2. "TestDispatcherServlet.java"의 "service"메소드에 "login.do"으로 요청이 들어오면 "login.jsp"이 연동되도록 추가합니다.

} else if ("/login.do".equals(requestURI)) {
	requestDispatcher = request.getRequestDispatcher("/WEB-INF/jsp/login.jsp");
}

 

3. "test2" 프로젝트의 "src > main > webapp > WEB-INF > jsp"에서 오른쪽 버튼을 클릭하여 컨텍스트 메뉴 [New > JSP File]를 클릭하여 "login.jsp"파일을 생성합니다.

"login.jsp"파일에 "<form>"를 추가합니다.

<form action="/test2/loginprocess.do" method="post">
	<input type="hidden" name="returnUrl" value="<%=request.getAttribute("returnUrl")%>">
	<input name="id" type="text"><br/>
	<input name="pwd" type="password"><br/>
	<button type="submit">전송</button>
</form>
<div><%=request.getAttribute("errorMessage")%></div>

 

반응형

 

4. "Servers"탭에서 "Tomcat8"를 선택하고 "start"버튼(start the server)을 클릭합니다. 웹 브라우저에서 "http://localhost:8080/test2/testform.do"를 입력합니다.

"testform.jsp"대신 "login.jsp"가 호출되어 나타납니다.

"Console"탭를 클릭해보면 "인증되지 않았습니다."가 출력되는 것을 확인 할 수 있습니다.

"Servers"탭에서 "Tomcat8"를 선택하고 "stop"버튼(stop the server)을 클릭합니다.

 

5. "test2" 프로젝트의 "Java Resources"에서 "src/main/java"의 "com.home.project.test2"를 선택한 후 오른쪽 버튼을 클릭하여 컨텍스트 메뉴 [New > Package]를 클릭하여 "Name"의 기존 패키지명에 ".vo"를 추가하고 "Finish"버튼을 클릭합니다. 추가된 "com.hom.project.test2.vo"에서 오른쪽 버튼을 클릭하여 컨텍스트 메뉴 [New > Class]를 클릭하여 "UserVO.java" 생성합니다.

/**
 * 사용자 아이디
 */
private String id = "";

/**
 * 사용자 이름
 */
private String name = "";

/**
 * 사용자 아이다를 가져옵니다.
 * @return 사용자 아이디
 */
public String getId() {
	return id;
}

/**
 * 사용자 아이디를 설정합니다.
 * @param id 사용자 아이디
 */
public void setId(String id) {
	this.id = id;
}

/**
 * 사용자 이름을 가져옵니다.
 * @return 사용자 이름
 */
public String getName() {
	return name;
}

/**
 * 사용자 이름을 설정합니다.
 * @param name 사용자 이름
 */
public void setName(String name) {
	this.name = name;
}

인증 후 인증 정보로 사용할 사용자 클래스입니다.

 

6. "test2" 프로젝트의 "Java Resources"에서 "src/main/java"의 "com.hom.project.test2.service"에서 오른쪽 버튼을 클릭하여 컨텍스트 메뉴 [New > Class]를 클릭하여 "TestLoginService.java" 생성합니다.

"process"메소드를 생성합니다.

public void process(HttpServletRequest request) {
	String id = request.getParameter("id");
	String password = request.getParameter("pwd");
	System.out.println("id : " + id + ", password : " + password);

	HttpSession httpSession = request.getSession(false);
	httpSession.removeAttribute("auth");
	if ("testid".equals(id) && "testpwd".equals(password)) {
		UserVO user = new UserVO();
		user.setId(id);
		user.setName("사용자");
		httpSession.setAttribute("auth", user);
		request.setAttribute("errorMessage", "");
	} else {
		request.setAttribute("errorMessage", "아이디/패스워드가 정확하지 않습니다.");
	}
}

"login.jsp"의 "<form>"안에 있는 "<input>"의 "id", "pwd"의 값을 가져와서 비교한후 일치하면 인증하고 일치하면 "UserVO"에 사용자 정보를 저장하고 인증에 적용합니다. 그렇지 않으면 에러메시지를 저장합니다.

 

7. "loginprocess.do"으로 요청이 들어오면 "testform.jsp"이 연동되도록 "TestDispatcherServlet.java"의 "service"메소드에 추가 코딩하고 저장합니다.

} else if ("/loginprocess.do".equals(requestURI)) {
	String returnUrl = request.getParameter("returnUrl");
	request.setAttribute("returnUrl", returnUrl);
	TestLoginService testLoginService = new TestLoginService();
	testLoginService.process(request);
	boolean auth = false;
	HttpSession httpSession = request.getSession(false);
	if (null != httpSession) {
		if (null != httpSession.getAttribute("auth")) {
			auth = true;
		}
	}
	if (auth) {
		System.out.println("returnUrl : " + returnUrl);
		response.sendRedirect(returnUrl);
	} else {
		requestDispatcher = request.getRequestDispatcher("/login.do");
	}
}

인증이 되면 "returnUrl"에 저장되어 있는 URL로 이동하고 그렇지 않으면 로그인 페이지로 이동합니다.

 

8. "Servers"탭에서 "Tomcat8"를 선택하고 "start"버튼(start the server)을 클릭합니다. 웹 브라우저에서 "http://localhost:8080/test2/testform.do"를 입력합니다.

"testform.jsp"대신 "login.jsp"가 호출되어 나타납니다.

첫번째 입력란은 아이디, 두번째 입력란은 패스워드입니다. 아이디는 "1"를 패스워드는 "2"를 입력하고 "전송"버튼을 누릅니다.

그러면 "아이디/패스워드가 정확하지 않습니다."라는 에러 메시지가 나타납니다.

아이디는 "testid"를 패스워드는 "testpwd"를 입력하고 "전송"버튼을 누릅니다.

정상적으로 인증처리되고 "testform.do"로 이동하였습니다.

"Console"탭를 클릭해보면 "인증되었습니다."가 출력되는 것을 확인 할 수 있습니다.

 

이처럼 필터를 통해 인증을 확인하고 접근 제어를 할 수 있습니다. 여기서는 스프링 프레임워크의 "security"의 원리를 이해하기 위해 만들어진 코딩이지만 일반 웹 프로젝트에서는 몇몇 부분만 수정한다면 바로 적용하여 사용할 수 있습니다.

728x90
반응형