728x90
300x250

[Spring-Framework] 14. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-XML) (2)


1부에 이어서 글을 작성하도록 하겠다.


1. [Spring-Framework] 14. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-XML) (1), 2020-09-26

https://yyman.tistory.com/1419




11. Controller - HomeController.java


HomeController.java에 관한 것이다.

파일은 처음 프로젝트(이하 "Spring MVC Project")를 생성하면, 자동으로 만들어진다.


package com.web.springsecurity5.controller;


import java.text.DateFormat;

import java.util.Date;

import java.util.Locale;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.security.core.context.SecurityContextHolder;

import org.springframework.security.core.userdetails.UserDetails;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;


@Controller

public class HomeController {

private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

/**

* Simply selects the home view to render by returning its name.

*/

@RequestMapping(value = "/", method = RequestMethod.GET)

public String home(Locale locale, Model model) {

logger.info("Welcome home! The client locale is {}.", locale);

Date date = new Date();

DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

String formattedDate = dateFormat.format(date);

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

String username = null;

String password = null;

try {

UserDetails userDetails = (UserDetails)principal;

username = userDetails.getUsername();

password = userDetails.getPassword();

}catch(Exception e) {

e.getStackTrace();

}


model.addAttribute("username", username);

model.addAttribute("password", password);

model.addAttribute("serverTime", formattedDate );

return "home";

}

}



파일명: HomeController.java


[첨부(Attachments)]

HomeController.zip


비고: 의외로 Controller 코드는 간단하게 되어있는 것을 알 수 있다.




12. Controller - MemberController.java


MemberController에 관한 것이다.

자세히 코드를 살펴보면, "@RequestMapping"을 변경해봐도 되는 부분이 있다. (크게 무방하다.)

함수에 직접 경로를 입력한 형태로 servlet을 구성하였다.


package com.web.springsecurity5.controller;


import java.util.Locale;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;


@Controller

public class MemberController {

private static final Logger logger = LoggerFactory.getLogger(MemberController.class);

@RequestMapping(value = "/member/loginForm", method = RequestMethod.GET)

public String loginForm(Locale locale, Model model) {

logger.info("안녕 - 로그인 폼(Hello - Login Form");

// model.addAttribute("serverTime", formattedDate );

return "member/loginForm";

}


@RequestMapping(value = "/member/accessDenied")

public String accessDenied(Locale locale, Model model) {


logger.info("접근 금지 - 이동(Accessed Denied)");

// model.addAttribute("serverTime", formattedDate );

return "redirect:/member/accessDeniedView";

}

@RequestMapping(value = "/member/accessDeniedView")

public String accessDeniedView(Locale locale, Model model) {


logger.info("접근 금지 - 출력(Accessed Denied)");

// model.addAttribute("serverTime", formattedDate );

return "member/accessDenied";


}

}


파일명: MemberController.java


[첨부(Attachments)]

MemberController.zip




13. Controller - AdminController.java


관리자 페이지에 관한 Controller이다.


package com.web.springsecurity5.controller;


import java.util.Locale;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;


@Controller

public class AdminController {

private static final Logger logger = LoggerFactory.getLogger(AdminController.class);

@RequestMapping(value = "/admin/home", method = RequestMethod.GET)

public String home(Locale locale, Model model) {

logger.info("Welcome - 관리자 페이지(Admin Home)!");

return "admin/home";

}

}


파일명: AdminController.java


[첨부(Attachments)]

AdminController.zip




14. View - 구성도


출력되는 화면에 관한 프로젝트 구성도이다.



그림 25. view 프로젝트 구성도





15. View - home.jsp


코드와 실제 디자인 화면을 동시에 소개하겠다.


경로: src/main/java/webapp/WEB-INF/views


그림 26. 로그인 페이지(home.jsp) - 로그인 전



그림 27. 로그인 페이지(home.jsp) - 로그인 후





그림 27. 로그인 페이지(home.jsp) - 로그인 후 - 비밀번호 생성 페이지


비고: UI(이하 "User Interface")설계하는데 도움이 되었으면 한다.



<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>


<%@ page session="false" %>

<html>

<head>

<title>Spring-Security 5 (XML 방식)</title>

<meta charset="UTF-8">

<style>

body{

font-family:'Arial';

font-size:12px;

}

a{

text-decoration:none;

color:#666;

}

</style>

</head>

<body>

<h1>

Hello world!(Spring-Security 5(XML 방식)) - DB연동(Oracle)

</h1>

<hr />

<sec:authorize access="isAnonymous()">

<!-- 로그인 전 -->

<p>

<a href="<c:url value="/member/loginForm" />">로그인</a>

</p>

</sec:authorize> 


<sec:authorize access="isAuthenticated()">

<!-- 로그인 성공 -->

<form:form action="${pageContext.request.contextPath}/logout" method="POST">

<input type="submit" value="로그아웃" />

</form:form>

<p>

${username} &nbsp; ${password}

</p>

</sec:authorize> 


<h3>

<a href="<c:url value="/admin/home" />">관리자 홈</a>&nbsp;&nbsp;

<a href="<c:url value="/encode-password?password=pass" />">비밀번호</a>

</h3>


<!-- 비밀번호 생성기 -->

<c:set var="gene_pwd" value="${encode}" />

<c:if test="${gene_pwd != null}">

    <c:out value="${gene_pwd}" />

</c:if>


</body>

</html>



파일명: home.jsp


[첨부(Attachments)]

home.zip



16. View - admin/home.jsp


관리자 페이지에 관한 것이다.


경로: src/main/java/webapp/WEB-INF/views/admin



그림 28. 관리자 홈 화면 - (User-Interface) 설계


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

<!DOCTYPE html>

<html lang="ko">

<head>

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

    <title>관리자(ROLE_ADMIN) - 홈</title>

    <style>

    body{

    font-family:'Arial';

    font-size:12px;

    }

   

    a{

    text-decoration:none;

    color:#666;

    }

    </style>

</head>


<body>


<h1>관리자 홈 화면!</h1>

<hr />


<h3>[<a href="<c:url value="/" />">홈으로(Home)</a>]</h3>


</body>

</html>


파일명: home.jsp


[첨부(Attachments)]

home.zip




17. View - member/loginForm.jsp


로그인 페이지에 대한 것이다.


경로: src/main/java/webapp/WEB-INF/views/member



그림 29. 로그인 폼 - 화면



그림 30. 로그인 폼 - 계정 불일치(아이디 또는 비밀번호)



<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<!DOCTYPE html>

<html lang="ko">

<head>

<meta charset="UTF-8">

    <title>로그인 - 페이지(Login - Page)</title>

    <style>

    body{

    font-family:'Arial';

    font-size:12px;

    }

   

    a{

    text-decoration:none;

    color:#666;

    }

   

    </style>

</head>


<body>


<h1>아이디와 비밀번호를 입력해주세요.</h1>

<hr />


<c:url value="/login" var="loginUrl" />


<form:form name="f" action="${loginUrl}" method="POST">

    <p>

        <label for="username">아이디</label>

        <input type="text" id="id" name="id" />

    </p>

    <p>

        <label for="password">비밀번호</label>

        <input type="password" id="password" name="password"/>

    </p>

    <%-- <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> --%>

    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>

    <button type="submit" class="btn">로그인</button>

    

    <!-- 에러 메시지 영역 -->

    <c:if test="${param.error != null}">

        <p>아이디와 비밀번호가 잘못되었습니다.</p>

    </c:if>

    <c:if test="${param.logout != null}">

        <p>로그아웃 하였습니다.</p>

    </c:if>

    

</form:form>

<h3>[<a href="<c:url value="/" />">홈으로(Home)</a>]</h3>


</body>

</html>



파일명: loginForm.jsp


[첨부(Attachments)]

loginForm.zip




18. View - member/accessDenied.jsp


인가되지 않은 화면 또는 오류 처리에 대한 것이다.



그림 31. Access Denied 페이지


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<!DOCTYPE html>

<html lang="ko">

<head>

    <meta charset="UTF-8">

    <title>Access Denied</title>

</head>


<body>


<h1>Access Denied!</h1>


<h3>[<a href="<c:url value="/" />">홈</a>]</h3>


</body>

</html>



파일명: accessDenied.jsp


[첨부(Attachments)]

accessDenied.zip



19. 공식 사이트에서 배포하고 있는 Spring-Boot 기반 - 예제


https://github.com/spring-projects/spring-security/tree/5.4.0/samples/boot/helloworld


Spring Security 5.4를 자바 파일 환경 설정 방식으로 구현한 것이다.

(구현 방식에는 크게 "XML 방식"과 "Java 파일" 방식이 있다.)

-> 구현하는데 있어서, 차이점이 있다.



그림 32. github - Spring Security Official 사이트



* 맺음글(Conclusion)


아무쪼록 Spring-Security 5 적용에 있어서 어려움이 해소되길 진심으로 기원한다.



* 참고자료(References)


1. 스프링프레임웍 - Spring Security(2) : 커스텀 로그인 화면 및 권한에 따른 접근 제어, https://offbyone.tistory.com/91, Accessed by 2020-09-26, Last Modified 2018-04-08.

-> 추천(50점): 잘 되어 있는 블로그 중 하나이다.


2. 스프링 시큐리티 5 - There is no PasswordEncoder mapped for the id "null", https://meaownworld.tistory.com/129, Accessed by 2020-09-26, Last Modified 2018-03-11.


3. [spring security/스프링 시큐리티] 비밀번호 암호화와 로그인 하기, https://simsimjae.tistory.com/36, Accessed by 2020-09-26, Last Modified 2017-08-28.


4. BCryptPasswordEncoder : 암호 해시, http://www.devkuma.com/books/pages/1124, Accessed by 2020-09-26, Last Modified 2017-12-30.

-> 비고: Spring Security 암호 처리에 대해서 잘 소개하고 있음.


5. Spring Security JdbcDaoImpl Example, https://www.concretepage.com/spring-5/spring-security-jdbcdaoimpl#XML, Accessed by 2020-09-26, Last Modified 2019-12-11.

-> 추천(35점): Java방법론, XML방법론 두 타입을 자세하고 잘 설명하고 있음.


6. [Spring Security] 현재 로그인한 사용자 정보 가져오기, https://itstory.tk/entry/Spring-Security-%ED%98%84%EC%9E%AC-%EB%A1%9C%EA%B7%B8%EC%9D%B8%ED%95%9C-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%A0%95%EB%B3%B4-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0, Accessed by 2020-09-26, Last Modified 2017-09-07.


7. [spring] 시큐리티 비밀번호 bcrypt 간단한 암호화, https://blog.naver.com/PostView.nhn?blogId=ljpark6&logNo=221578990976, Accessed by 2020-09-26, Last Modified 2019-07-06.


9. Spring Web MVC Security Basic Example Part 1 with XML Configuration, https://www.codejava.net/frameworks/spring/spring-web-mvc-security-basic-example-part-1-with-xml-configuration, Accessed by 2020-09-26, Last Modified 2019-06-24.

-> 비고: Spring Security 4를 기준으로 작성하였음. 

Spring-Framework 3.2~4.x 이전의 경우에는 내장 로그인 인터페이스가 해당 사이트와 동일하거나 흡사함.

이전 인터페이스보다는 Spring-Framework 4.x~5 이후부터는 디자인이 있는 형태의 내장 로그인 인터페이스로 출력됨.


10. [SPRING] Controller에서 redirect하기, https://sime.tistory.com/119, Accessed by 2020-09-26, Last Modified 2016-11-17.

-> 비고: Spring-Security의 Access-error-page처리에 있어서 메시지만 출력하고, accessDenied.jsp화면이 출력되지 않는 문제를 해결하는데

          redirect 방법을 참고하여 적용하였음.


11. Spring Security 5.4 - Docs, https://docs.spring.io/spring-security/site/docs/5.4.0/reference/html5/, Accessed by 2020-09-26, Last Modified 2020-09-09.

-> 비고: 공식 사이트이긴 하나, spring-boot을 주력으로 작성되어 있어서 크게 도움이 되지 않을 수도 있음.


12. Spring Security 5.4 - API, https://docs.spring.io/spring-security/site/docs/5.4.0/api/, Accessed by 2020-09-26, Last Modified .

-> 비고: API 사이트이니 구현해놓고 분석할 때 참고하면 될 것으로 보인다.


13. spring-security/samples/boot/helloworld at 5.4.0 · spring-projects/spring-security · GitHub, https://github.com/spring-projects/spring-security/tree/5.4.0/samples/boot/helloworld, Accessed by 2020-09-26, Last Modified .

-> 추천(15점): 간결하게 Java 환경설정 방식으로 예제를 소개하고 있다. (Spring-boot로 작성), Spring Security에서 공식 운영하는 github사이트이다.

반응형
728x90
300x250

[Spring-Framework] 13. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-XML) (1)


많은 고민과 엄선하여 2020-09월 기준의 최신 프레임워크를 적용한 Spring Security 5.4 프로젝트를 소개하려고 한다.

이전의 돌아다니는 방법으로 Spring Security를 학습하려고 한다면, 많은 오류부터 경험하게 될 가능성이 높아서 수 많은 실험 끝에 완성하였다.


인터넷 검색 등을 시도해보면, "Spring Boot"를 적용하면 훨씬 쉽고 만들 수 있다는 가능성에 대한 글들을 많이 보았다.


순수한 Spring-Framework에 대한 관련 주제를 담은 글의 비중은 매우 적어서 수 차례 연구를 완료한 후에 글을 작성하게 되었다.


[작업환경]

[WAS(Web Application Server), 웹 애플리케이션 서버]

1. apache-tomcat-9.0.37-windows-x64


[DBMS(DataBase Management System - Tools]

2. sqldeveloper-19.2.1.247.2212-x64


[DB(DataBase)]

3. Oracle Databases 19.3.0.0


[IDE(Integration Development Environment)]

4. Spring-Tool Suites 4-4.7.2. Releases.


[Framework(프레임워크)]

5. spring-security-taglibs(5.4)

6. spring-security-config(5.4)

7. spring-security-web(5.4)

8. spring-security-core(5.4)

9. javax.servlet-api(4.0.1)

10. spring-webmvc(5.2.9.RELEASE)

11. spring-context(5.2.9.RELEASE)
12. Maven 3.6.3/1.16.0.20200610-1735


[Java]

OpenJDK-14.0.2 (https://openjdk.java.net/)


원리나 배경 등 이런 것도 물론 중요하나 Spring Security는 다소 프로젝트 위주로 경험 후에 터득하는 것이 더 적합할 것으로 보인다.


* 다음 주제 - (자바 버전으로 구현)

1. [Spring-Framework] 16. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-Java) (1)
https://yyman.tistory.com/1422


2. [Spring-Framework] 17. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-Java) (2)

https://yyman.tistory.com/1423


3. [Spring-Framework] 18. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-Java) (3)

https://yyman.tistory.com/1424



1. 결과


어떤 프로젝트를 하는지 소개하려고 한다. 지금 구성하는 화면은 스프링 시큐리티의 "커스텀 로그인"이라는 것이다.

커스텀 로그인 코드만 주석으로 정리해보면, 내장된 로그인 화면을 살펴볼 수 있다.



그림 1. 결과(로그인 폼) - 로그인 전




그림 2. 결과(로그인 폼) - 계정 정보의 오류




그림 3. 관리자 페이지 - "권한이 없는 사용자가 접속했을 경우"




그림 4. 로그인된 화면




그림 5. 비밀번호 랜덤 암호(bcrypt 기법 적용)



그림 6. 로그인된 화면 - "user 계정"




그림 7. 권한이 있는 사용자 - 관리자 홈 접속 화면



그림 7-1. 접근 제한 페이지 출력




1-1. 결과 - 프로젝트 구성도


어떤 형태로 프로젝트가 구성되어있는지 소개하겠다.

작업해야 할 양이 조금 많다.



그림 8. 프로젝트 구성도





2. 데이터베이스 설계


데이터베이스 기능도 지원한다. 데이터베이스를 사용 안하는 방법에 대해서도 소개하고 있으니 참고하면 되겠다.


데이터베이스 설계 기준은 Spring Security에서 제공하는 계정, 권한, 그룹 시스템 로직을 바탕으로 작성하였다.

데이터베이스 기준으로 보면, 스프링 시큐리티는 하위버전부터 현재까지 큰 차이는 없다.


참고: 기본적인 기능으로 데이터베이스 설계를 하지 않고도 Spring Security를 적용하여 사용할 수도 있다.


그림 9. Membership - ERD 설계도




그림 10. "comp_users" 테이블



그림 11. "comp_authorities" 테이블



그림 12. "comp_groups" 테이블



그림 13. "comp_group_members" 테이블



그림 14. "comp_group_authorities" 테이블



3. SQL (Create table)


SQL로 작업해야 할 테이블에 대해서 기술하였으니 참고하면 도움이 되겠다.


[암호화된 암호]

계정: user -> 임시비밀번호: password

계정: admin -> 임시비밀번호: pass


CREATE TABLE comp_users (

username VARCHAR(50) NOT NULL,

password VARCHAR(300) NOT NULL,

enabled INT NOT NULL,

PRIMARY KEY (username)

);


CREATE TABLE comp_authorities (

  username VARCHAR(50) NOT NULL,

  authority VARCHAR(50) NOT NULL,

  CONSTRAINT fk_authorities_users FOREIGN KEY (username) REFERENCES comp_users (username)

); 


CREATE TABLE comp_groups(

id VARCHAR2(20) NOT NULL,

group_name VARCHAR2(20) NULL

);


CREATE TABLE comp_group_authorities(

group_id VARCHAR2(20) NOT NULL,

authority VARCHAR2(20) NOT NULL

);


CREATE TABLE comp_group_members(

group_id VARCHAR2(20) NOT NULL,

username VARCHAR2(20) NOT NULL

);



-- 계정

INSERT INTO comp_users (username, password, enabled) VALUES ('user', '$2a$10$x04djNV2e9rpcPPRyXoLk.rMm6iZe2/vYdzpqHQcLeNSYdt7kc30O', 1);

INSERT INTO comp_users (username, password, enabled) VALUES ('admin', '$2a$10$QUddY3O/6ZgkYCR6MFlv9.nqA501Fm0cc/ZxQHX5pwb1o0CYCTiIS', 1);


-- 사용자 권한

INSERT INTO comp_authorities (username, authority) VALUES ('user', 'ROLE_ADMIN');

INSERT INTO comp_authorities (username, authority) VALUES ('admin', 'ROLE_USER');


-- 그룹

INSERT INTO comp_groups (id, group_name) VALUES ('G01', '관리자 그룹');

INSERT INTO comp_groups (id, group_name) VALUES ('G02', '사용자 그룹');


-- 그룹 권한

INSERT INTO comp_group_authorities (group_id, authority) VALUES ('G01', 'ROLE_ADMIN');

INSERT INTO comp_group_authorities (group_id, authority) VALUES ('G01', 'ROLE_USER');

INSERT INTO comp_group_authorities (group_id, authority) VALUES ('G02', 'ROLE_USER');


-- 그룹 회원

INSERT INTO comp_group_members (group_id, username) VALUES ('G01', 'user');

INSERT INTO comp_group_members (group_id, username) VALUES ('G02', 'admin');



파일명: sampleDb-oracledb.sql


[첨부(Attachments)]

sampleDb-oracledb.zip





4. 프로젝트 생성하기


그림으로 자세하게 표현하고 있는 이유는 Spring-Framework 순수한 프로젝트로 초기 개발환경을 셋팅하려고 하면 정말 많은 시간이 소요된다.

그리하여 매우 자세하게 소개하고 있으니 참고하면 도움이 될 것 같다.


비고: Spring Legacy Project가 보이지 않는다면, Help->Eclipse Marketplace에서 "STS"를 검색하여 AddOn을 설치하면 된다.



그림 15. Eclipse Marketplace의 화면


Spring Tools 3 Add-On for Spring Tools 4 3.9.14.RELEASES를 설치해주면 된다.





그림 16. Spring Legacy Project 생성하기


SLP(Spring Legacy Project)의 Spring MVC Project는 기본 버전이 매우 낮은 "3.2.2 RELEASES"이다.


비고: pom.xml을 변경해주면, 현재의 최신 프레임워크 버전으로 변경할 수 있다.



그림 17. Spring Legacy Project 생성하기


프로젝트명을 지정한 후 Next를 누른다.



그림 18. Spring Legacy Project 생성하기


상위 경로를 입력한 후 "Finish"를 누른다.



5. pom.xml 수정하기


수정할 부분이 조금 많다.

어려운 건 아니니깐 따라하면 된다.


http://mvnrepository.com에 들어가서 정보를 잘 찾아서 변경해주면 된다.


변경하거나 추가한 부분은 굵은 글자로 표기하였다.


<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.web</groupId>

<artifactId>springsecurity5</artifactId>

<name>SpringSecurity5</name>

<packaging>war</packaging>

<version>1.0.0-BUILD-SNAPSHOT</version>

<properties>

<java-version>14</java-version>

<org.springframework-version>5.2.9.RELEASE</org.springframework-version>

<org.aspectj-version>1.6.10</org.aspectj-version>

<org.slf4j-version>1.6.6</org.slf4j-version>

</properties>

<dependencies>

<!-- Spring -->

<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->

<dependency>

    <groupId>org.springframework</groupId>

    <artifactId>spring-context</artifactId>

    <version>5.2.9.RELEASE</version>

<exclusions>

<!-- Exclude Commons Logging in favor of SLF4j -->

<exclusion>

<groupId>commons-logging</groupId>

<artifactId>commons-logging</artifactId>

</exclusion>

</exclusions>

</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->

<dependency>

    <groupId>org.springframework</groupId>

    <artifactId>spring-webmvc</artifactId>

    <version>5.2.9.RELEASE</version>

</dependency>


<!-- AspectJ -->

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjrt</artifactId>

<version>${org.aspectj-version}</version>

</dependency>

<!-- Logging -->

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-api</artifactId>

<version>${org.slf4j-version}</version>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>jcl-over-slf4j</artifactId>

<version>${org.slf4j-version}</version>

<scope>runtime</scope>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-log4j12</artifactId>

<version>${org.slf4j-version}</version>

<scope>runtime</scope>

</dependency>

<dependency>

<groupId>log4j</groupId>

<artifactId>log4j</artifactId>

<version>1.2.15</version>

<exclusions>

<exclusion>

<groupId>javax.mail</groupId>

<artifactId>mail</artifactId>

</exclusion>

<exclusion>

<groupId>javax.jms</groupId>

<artifactId>jms</artifactId>

</exclusion>

<exclusion>

<groupId>com.sun.jdmk</groupId>

<artifactId>jmxtools</artifactId>

</exclusion>

<exclusion>

<groupId>com.sun.jmx</groupId>

<artifactId>jmxri</artifactId>

</exclusion>

</exclusions>

<scope>runtime</scope>

</dependency>


<!-- @Inject -->

<dependency>

<groupId>javax.inject</groupId>

<artifactId>javax.inject</artifactId>

<version>1</version>

</dependency>


<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->

<dependency>

    <groupId>javax.servlet</groupId>

    <artifactId>javax.servlet-api</artifactId>

    <version>4.0.1</version>

    <scope>provided</scope>

</dependency>


<dependency>

<groupId>javax.servlet.jsp</groupId>

<artifactId>jsp-api</artifactId>

<version>2.1</version>

<scope>provided</scope>

</dependency>

<dependency>

<groupId>javax.servlet</groupId>

<artifactId>jstl</artifactId>

<version>1.2</version>

</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->

<dependency>

    <groupId>org.springframework.security</groupId>

    <artifactId>spring-security-core</artifactId>

    <version>5.4.0</version>

</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->

<dependency>

    <groupId>org.springframework.security</groupId>

    <artifactId>spring-security-web</artifactId>

    <version>5.4.0</version>

</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->

<dependency>

    <groupId>org.springframework.security</groupId>

    <artifactId>spring-security-config</artifactId>

    <version>5.4.0</version>

</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-taglibs -->

<dependency>

    <groupId>org.springframework.security</groupId>

    <artifactId>spring-security-taglibs</artifactId>

    <version>5.4.0</version>

</dependency>

<!-- Test -->

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.7</version>

<scope>test</scope>

</dependency>

<!-- Oracle DB(19 Version) -->

<dependency>

<groupId>com.oracle.database.jdbc</groupId>

<artifactId>ojdbc8</artifactId>

<version>19.3.0.0</version>

</dependency>

</dependencies>

    <build>

        <plugins>

            <plugin>

                <artifactId>maven-eclipse-plugin</artifactId>

                <version>2.9</version>

                <configuration>

                    <additionalProjectnatures>

                        <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>

                    </additionalProjectnatures>

                    <additionalBuildcommands>

                        <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>

                    </additionalBuildcommands>

                    <downloadSources>true</downloadSources>

                    <downloadJavadocs>true</downloadJavadocs>

                </configuration>

            </plugin>

            <plugin>

                <groupId>org.apache.maven.plugins</groupId>

                <artifactId>maven-compiler-plugin</artifactId>

                <version>2.5.1</version>

                <configuration>

                    <source>1.6</source>

                    <target>1.6</target>

                    <compilerArgument>-Xlint:all</compilerArgument>

                    <showWarnings>true</showWarnings>

                    <showDeprecation>true</showDeprecation>

                </configuration>

            </plugin>

            <plugin>

                <groupId>org.codehaus.mojo</groupId>

                <artifactId>exec-maven-plugin</artifactId>

                <version>1.2.1</version>

                <configuration>

                    <mainClass>org.test.int1.Main</mainClass>

                </configuration>

            </plugin>

        </plugins>

    </build>

</project>



파일명: pom.xml


[첨부(Attachments)]

pom.zip



6. 자바 버전 바꾸기 - Build Path, Project Facts


자바 버전을 변경해줘야 한다.



그림 19. Spring의 Properties 속성 메뉴


프로젝트를 클릭한다.

마우스 오른쪽 버튼을 누른다.

"Properties"를 클릭한다.



그림 20. Properties의 Build Path


JRE System Library [JavaSE-버전]을 14로 변경해준다. (1.7 이상으로만 해주면 됨.)

단, 버전은 Project Factes, Build Path, Pom.xml를 깔맞춤해주는 게 좋다.



그림 21. Properties의 Project Factes


Project Factes의 버전을 14로 변경해준다.




7. security-context.xml 파일 - 새로 만들기


"security-context.xml"은 초기에 파일이 존재한 건 아니다.

만들어줘야 한다.


폴더: src/main/java/webapp/WEB-INF/spring/

파일명: security-context.xml



그림 22. spring 폴더를 오른쪽 버튼 클릭했을 때 팝업 메뉴


spring 폴더를 오른쪽 버튼으로 클릭한다.

New->File을 클릭한다.



그림 23. xml 파일 생성하기


security-context.xml을 입력한 후, Finish를 누른다.



그림 24. xml 파일 생성하기





8. security-context.xml 파일 - 코드 편집(입력)


코드를 입력해준다.


<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"

    xmlns:beans="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/security 

http://www.springframework.org/schema/security/spring-security.xsd

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

<!-- 보안 미적용 -->

<!-- <http pattern="/**/*.js" security="none" />  -->

<http auto-config="true" use-expressions="true">

      

<intercept-url pattern="/member/accessDenied" access="permitAll" />

<intercept-url pattern="/member/accessDeniedView" access="permitAll" />

<intercept-url pattern="/member/loginForm" access="permitAll" />

<intercept-url pattern="/" access="permitAll" />

<intercept-url pattern="/encode-password" access="permitAll" />

<intercept-url pattern="/admin/**" access="hasRole('ADMIN')" />

<intercept-url pattern="/**" access="hasAnyRole('USER')" />

<form-login login-page="/member/loginForm"

default-target-url="/"

authentication-failure-url="/member/loginForm?error"

username-parameter="id"

password-parameter="password" />

<!-- Form-login의 항목 -->

<!-- Default-Target-URL: 로그인 성공할 경우, 접속할 사이트 -->

<logout logout-url="/logout" logout-success-url="/" />

<!-- Servlet 3.0부터 access-denied-handler 미지원 -->

<!-- web.xml으로 제어할 것 -->

<!-- <access-denied-handler ref="customAccessDeniedHandler"/> -->

<access-denied-handler error-page="/member/accessDenied" />

<csrf disabled="true" />

</http>

<!-- DataSource 추후 지원 -->

<!-- 1. HSQLDB -->

<!-- ClassDriver = org.hsqldb.jdbcDriver -->

<!-- Url = jdbc:hsqldb:hsql://localhost:9001 -->

<!-- 2. Oracle JDBC -->

<!-- ClassDriver = oracle.jdbc.driver.OracleDriver -->

<!-- Url = jdbc:oracle:thin:@127.0.0.1:1521:orcl -->

<beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">

<beans:property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />

<beans:property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl" />

<beans:property name="username" value="{사용자계정}" />

<beans:property name="password" value="{비밀번호}" />

</beans:bean>

<!-- 사용자 세부 계정 서비스 -->

<beans:bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">

<beans:property name="dataSource" ref="dataSource" />

<beans:property name="usersByUsernameQuery" value="select username,password,enabled from comp_users where username = ?" />

<beans:property name="authoritiesByUsernameQuery" value="select username,authority from comp_authorities where username = ?" />

<beans:property name="groupAuthoritiesByUsernameQuery" value="select g.id, g.group_name, ga.authority from comp_groups g, 

  comp_group_members gm, comp_group_authorities ga where gm.username = ? 

  and g.id = ga.group_id and g.id = gm.group_id" />

</beans:bean>

<!-- provider -->

<authentication-manager>

<authentication-provider user-service-ref="userDetailsService">

<!-- <authentication-provider> -->

<!-- <user-service>-->

<!-- <user name="user" password="password" authorities="ROLE_USER" /> -->

<!-- xml 내에 사용자 계정 등록 -->

<!-- <user name="user" password="$2a$10$Gkr61IXH0YI/.Yh5T6fzteGLCLT6nOmMkID/DmFhWtPmu1WwPrDKq" authorities="ROLE_USER" /> -->

<!-- <user name="admin" password="password" authorities="ROLE_ADMIN" /> -->

<!-- </user-service> -->

<password-encoder ref="passwordEncoder"/>

</authentication-provider>

</authentication-manager>

<!-- 암호화 패키지 -->

<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" /> 


</beans:beans>


파일명: security-context.xml


[첨부(Attachments)]

security-context.zip



[순정 버전]


<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"

    xmlns:beans="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/security 

http://www.springframework.org/schema/security/spring-security.xsd

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

<http>


<intercept-url pattern="/member/accessDenied" access="permitAll" />

<intercept-url pattern="/member/accessDeniedView" access="permitAll" />

<intercept-url pattern="/member/loginForm" access="permitAll" />

<intercept-url pattern="/" access="permitAll" />

<intercept-url pattern="/encode-password" access="permitAll" />

<intercept-url pattern="/admin/**" access="hasRole('ADMIN')" />

<intercept-url pattern="/**" access="hasAnyRole('USER')" />

<form-login login-page="/member/loginForm"

default-target-url="/"

authentication-failure-url="/member/loginForm?error"

username-parameter="id"

password-parameter="password" />

<!-- Form-login의 항목 -->

<!-- Default-Target-URL: 로그인 성공할 경우, 접속할 사이트 -->

<logout logout-url="/logout" logout-success-url="/" />

<access-denied-handler error-page="/member/accessDenied" />

</http> 


      

<authentication-manager>

<authentication-provider>

<user-service>

                              <user name="user" password="password" authorities="ROLE_USER" />

      <user name="admin" password="password" authorities="ROLE_ADMIN" />

</user-service>

</authentication-provider>

</authentication-manager>

</beans:beans>




물론 순정버전으로 구현해봐도 동작은 할 것이다. 실험해보려면 해봐도 무방하다.

최신 Spring Security 5의 경우에는 비밀번호 암호화에 대해서 엄격하게 확인을 하고 있다.
- 로그인 페이지는 열리는데, 암호화 패키지 문제 등이 발생할 수도 있다.


해결 방법1) 암호화 미적용이라고 지정해주기


<user name="user" password="{noop}password" authorities="ROLE_USER" />

<user name="admin" password="{noop}password" authorities="ROLE_ADMIN" />


{noop}를 넣어주면서 해결한다.


해결 방법2) 암호화 미적용이라고 지정해주기

(1단계: 비밀번호를 "
org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" 클래스를 활용해서 인코딩한다.)


(2단계: 메모한 복잡한 암호를 password에 입력해준다.)


= <user name="user" password="$2a$10$Gkr61IXH0YI/.Yh5T6fzteGLCLT6nOmMkID/DmFhWtPmu1WwPrDKq" authorities="ROLE_USER" />


(3단계: 암호화 패키지를 사용하고 있다는 것을 정의해준다.)


                       (중략)

            <authentication-provider>

                       (중략)

<password-encoder ref="passwordEncoder"/>

</authentication-provider>


            <!-- 암호화 패키지 -->

           <beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" /> 


</beans:beans>


"org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" 클래스는 Controller나 jsp 등에서도 계속 활용할 수 있다.




9. web.xml 파일 - 코드 편집


서블릿 버전이 낮게 정의된 web.xml 파일의 코드와 더불어 security-context.xml을 사용하고 있다는 것을 정의해줘야 한다.


<?xml version="1.0" encoding="UTF-8"?>

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

  <filter>

    <filter-name>springSecurityFilterChain</filter-name>

    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

  </filter>

  <filter-mapping>

    <filter-name>springSecurityFilterChain</filter-name>

    <url-pattern>/*</url-pattern>

  </filter-mapping>

  <context-param>

    <param-name>contextConfigLocation</param-name>

    <param-value>

/WEB-INF/spring/root-context.xml

/WEB-INF/spring/security-context.xml

</param-value>

  </context-param>

  <listener>

    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

  </listener>

  <servlet>

    <servlet-name>appServlet</servlet-name>

    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

    <init-param>

      <param-name>contextConfigLocation</param-name>

      <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>

    </init-param>

    <load-on-startup>1</load-on-startup>

  </servlet>

  <servlet-mapping>

    <servlet-name>appServlet</servlet-name>

    <url-pattern>/</url-pattern>

  </servlet-mapping>

  <!-- <welcome-file-list>  -->

  <!--  <welcome-file>index.do</welcome-file> -->

  <!-- </welcome-file-list>    -->

  

  

  <!-- <error-page> -->

  <!--  <error-code>500</error-code> -->

  <!--  <location>/member/accessDenied</location> -->

  <!-- </error-page> -->

  

  <!-- <location>/WEB-INF/views/common/accessDenied.jsp</location> -->

</web-app>


파일명: web.xml


[첨부(Attachments)]

web.zip


(중략)

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">


(중략)


변경 이력1: servlet 2.x -> 3.1 스펙으로 변경


(중략)


<filter>

    <filter-name>springSecurityFilterChain</filter-name>

    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

  </filter>

  <filter-mapping>

    <filter-name>springSecurityFilterChain</filter-name>

    <url-pattern>/*</url-pattern>

  </filter-mapping>

  <context-param>

    <param-name>contextConfigLocation</param-name>

    <param-value>

/WEB-INF/spring/root-context.xml

/WEB-INF/spring/security-context.xml

</param-value>

  </context-param>


(중략)


변경 이력2: springSecurityFillter와 security-context.xml 정의하기



10. Controller - CryptController.java


암호화 패키지에 관한 것이다. 기본 코드를 잘 활용하여 재사용해봐도 좋을 듯 싶다.


package com.web.springsecurity5.controller;


import java.text.DateFormat;

import java.util.Date;

import java.util.Locale;


import javax.servlet.http.HttpServletRequest;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.security.crypto.password.PasswordEncoder;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.context.WebApplicationContext;

import org.springframework.web.context.support.WebApplicationContextUtils;


@Controller

public class CryptController {


private static final Logger logger = LoggerFactory.getLogger(CryptController.class);

/**

* Simply selects the home view to render by returning its name.

*/

@RequestMapping(value = "/encode-password", method = RequestMethod.GET)

public String bcrypt(Locale locale, Model model, HttpServletRequest req) {

logger.info("Welcome home! The client locale is {}.", locale);

WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(req.getServletContext());

        PasswordEncoder passwordEncoder = context.getBean(PasswordEncoder.class);


        String password = req.getParameter("password");

        String encode = passwordEncoder.encode(password);


        model.addAttribute("encode", encode);

        System.out.println(encode);

        

return "home";

}

}



파일명: CryptController.java


[첨부(Attachments)]

CryptController.zip



* 2부에서 만나요.


2부에서 추가로 소개하도록 하겠다.


1. [Spring-Framework] 13. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-XML) (2), 2020-09-26

https://yyman.tistory.com/1420


반응형
728x90
300x250

[JSP] 13. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(2)


이전의 글에 이어서 작성하도록 하겠다.


이전의 글

- [JSP] 12. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(1), 2020-09-24

   https://yyman.tistory.com/1414



14. Controller - BoardInsertMultiResultController 


고민을 나름대로 하여 만든 것이다. 인터넷 자료 등도 열심히 참고하고, 삽질을 많이 하였다.


package com.fileWeb.controller;


import java.io.File;

import java.io.IOException;

import java.io.PrintWriter;

import java.util.HashMap;

import java.util.Iterator;

import java.util.List;

import java.util.Map;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import org.apache.commons.fileupload.FileItem;

import org.apache.commons.fileupload.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.ServletFileUpload;


public class BoardInsertMultiResultController implements Controller {


private Map<String, Object> reqMap = null; // req 정보(MultiRequest)

private Map<Integer, Map<String, Object>> fileMap = null; // 다중 파일 지원

private static int num = 0; 

@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException {

fileMap = new HashMap<Integer, Map<String, Object>>(); 

reqMap = new HashMap<>();

num = 1;

        

        PrintWriter out = res.getWriter();

        out.println("<HTML><HEAD><TITLE>Multipart Test</TITLE></HEAD><BODY>");

try {

            

            //디스크상의 프로젝트 실제 경로얻기

            //String contextRootPath = "c:" + File.separator + "upload";

String dirName = "upload" ; 

String contextRootPath = req.getSession().getServletContext().getRealPath("/") + dirName;

            System.out.println("실제경로:" + contextRootPath);

            

            //1. 메모리나 파일로 업로드 파일 보관하는 FileItem의 Factory 설정

            DiskFileItemFactory diskFactory = new DiskFileItemFactory(); //디스크 파일 아이템 공장

            diskFactory.setSizeThreshold(4096); //업로드시 사용할 임시 메모리

            diskFactory.setRepository(new File(contextRootPath + "/WEB-INF/temp")); //임시저장폴더

            

            //2. 업로드 요청을 처리하는 ServletFileUpload생성

            ServletFileUpload upload = new ServletFileUpload(diskFactory);

            upload.setSizeMax(3 * 1024 * 1024); //3MB : 전체 최대 업로드 파일 크기

            

            //3. 업로드 요청파싱해서 FileItem 목록구함​​

            List<FileItem> items = upload.parseRequest(req); 


            Iterator<FileItem> iter = items.iterator(); //반복자(Iterator)로 받기​            

            while(iter.hasNext()) { //반목문으로 처리​    

                FileItem item = (FileItem) iter.next(); //아이템 얻기

                 //4. FileItem이 폼 입력 항목인지 여부에 따라 알맞은 처리

                

                if(item.isFormField()){ 

                //파일이 아닌경우

                    processFormField(out, item);

                    

                } else {

                //파일인 경우

                // System.out.println("오류:");

                    processUploadFile(out, item, contextRootPath);

                // System.out.println("오류2:");

                }

            }

            

        } catch(Exception e) {

            out.println("<PRE>");

            e.printStackTrace(out);

            out.println("</PRE>");

        }

out.println( "usrID(Map): " + reqMap.get("usrID") );

out.println( "usrPasswd(Map):" + reqMap.get("usrPasswd") );

        

        out.println("</BODY></HTML>");

// req.setAttribute("usrID", reqMap.get("usrID"));

// req.setAttribute("login", 1);//Object Type으로 넘어감

        req.setAttribute("reqMap", reqMap);

        req.setAttribute("fileMap", fileMap);

        

        // 방법3

        for( Integer key : fileMap.keySet() ){


            Map<String, Object> fileMapNode = fileMap.get(key);

            System.out.println( String.format("키 : %s, 값: %s", key, fileMapNode.get("fileName") ));

            

        }

        

// System.out.println("오류3:" + reqMap.get("usrID"));

HttpUtil.forward(req, res, "/WEB-INF/view/board/insertResult.jsp");

}


//업로드한 정보가 파일인경우 처리

private void processUploadFile( PrintWriter out, FileItem item, String contextRootPath)

throws Exception {

Map<String, Object> fileNode = new HashMap<String, Object>();

String name = item.getFieldName(); // 파일의 필드 이름 얻기

String fileName = item.getName(); // 파일명 얻기

// 임시 - 실제 원본 이름 추출

File originalFile = new File(fileName);

String originalFileName = originalFile.getName();

// System.out.println("임시:" + originalFileName );

String contentType = item.getContentType(); // 컨텐츠 타입 얻기

long fileSize = item.getSize(); // 파일의 크기 얻기

// 업로드 파일명을 현재시간으로 변경후 저장

String fileExt = fileName.substring(fileName.lastIndexOf("."));

String uploadedFileName = System.currentTimeMillis() + ""; 

System.out.println(fileExt);

System.out.println(uploadedFileName);

// 저장할 절대 경로로 파일 객체 생성

File uploadedFile = new File(contextRootPath + "/upload/" + uploadedFileName);

item.write(uploadedFile); //파일 저장

//========== 뷰단에 출력 =========//

out.println("<P>");

out.println("파라미터 이름:" + name + "<BR>");

out.println("파일 이름:" + fileName + "<BR>");

out.println("콘텐츠 타입:" + contentType + "<BR>");

out.println("파일 사이즈:" + fileSize + "<BR>");

//확장자가 이미지인겨우 이미지 출력

if(".jpg.jpeg.bmp.png.gif".contains(fileExt.toLowerCase())) {

out.println("<IMG SRC='upload/" 

+ uploadedFileName 

+ "' width='300'><BR>");

}

out.println("</P>");

out.println("<HR>");

out.println("실제저장경로 : "+uploadedFile.getPath()+"<BR>");

out.println("<HR>");

// 파일 정보

fileNode.put("name", name);

fileNode.put("fileName", originalFileName);

fileNode.put("contentType", contentType);

fileNode.put("fileSize", fileSize);

fileNode.put("fileExt", fileExt);

fileNode.put("uploadedFileName", uploadedFileName);

fileNode.put("realName", uploadedFile.getName());

fileNode.put("realPath", uploadedFile.getPath());

fileMap.put(num, fileNode);

num++;

}

private void processFormField(PrintWriter out, FileItem item) 

throws Exception{

String name = item.getFieldName(); //필드명 얻기

Object value = item.getString("UTF-8"); //UTF-8형식으로 필드에 대한 값읽기

// out.println(name + ":" + value + "<BR>"); //출력

reqMap.put(name, value);

}


}



파일명: BoardInsertMultiResultController.java


[첨부(Attachments)]

BoardInsertMultiResultController.zip



15. View - insertResult.jsp


사소해보이지만, jsp파일 내에서도 자료구조를 사용할 수 있다.


JSP: <%@ page import="java.util.*" %>

Java: import java.util.*;


<%@ page language="java" contentType="text/html; charset=UTF-8"

    pageEncoding="UTF-8"%>

<%@ page import="java.util.*" %>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>업로드 - 결과</title>

</head>

<body>

<h3>업로드 결과</h3>

<%


//Object name = request.getAttribute("usrID");

//Object login = request.getAttribute("login");

Object obj = request.getAttribute("reqMap");

Map<String, Object> map = null;

if(obj != null){

map = (HashMap<String, Object>)obj;

}


%>


<table style="width:700px;border:1px solid #e2e2e2;">

<tr>

<td style="width:20%;">

<%

        out.println("name : " + map.get("usrID") + "<br />");

%>

</td>

<td style="width:20%;border-left:1px solid #e2e2e2;">

<%

out.println("login : " + map.get("usrPasswd") + "<br />");

%>

</td>

</tr>

<tr>

<td>

</td>

<td>

</td>

</tr>

</table>


</body>

</html>


파일명: insertResult.jsp


[첨부(Attachments)]

insertResult.zip



16. 라이브러리(Libraries)


pom.xml으로 자동 생성된 라이브러리이긴 한데 수동으로 추출해보았다.





17. 맺음글(Conclusion)


실질적으로 다중 파일 업로드와 다운로드 구현에 대해서 자세히 살펴보았다.



* 참고자료(References)


1. [서블릿/JSP] Apache Commons FileUpload를 이용한 파일업로드 구현하기, https://dololak.tistory.com/720?category=636501, Accessed by 2020-09-24, Last Modified 2019-07-24.


2. FrontController & Command Pattern - 프론트 컨트롤러와 커맨드 패턴, https://dailyworker.github.io/servlet-advened/, Accessed by 2020-09-24, Last Modified 2019-07-09.


3. 파일전송/업로드(Multipart) , https://gunbin91.github.io/jsp/2019/05/28/jsp_11_file.html, Accessed by 2020-09-24, Last Modified 2019-05-28.

-> Multipart 방식으로 소개되어 있는데, 현재 서블릿 버전에서는 충돌 발생함. (www.servlets.com)


4. Servlets.com | com.oreilly.servlet, http://www.servlets.com/cos/, Accessed by 2020-09-24, Last Modified 2002-03-01.

-> 비추천: 2002년에서 멈춰버린 프로젝트


5. .[jsp] form 타입이 enctype="multipart/form-data" 일때 request.getParameter() 불가 값이 null로 오는 문제, http://blog.naver.com/PostView.nhn?blogId=software705&logNo=220551397421&parentCategoryNo=&categoryNo=&viewDate=&isShowPopularPosts=false&from=postView, Accessed by 2020-09-24, Last Modified 2015-11-27.

-> 비추천: oreilly.servlet jar 파일로 구현한 MultipartRequest인데, 서블릿 충돌을 경험하게 된다.


6. [jsp/servlet] commons-fileupload 를 이용한 파일업로드 (서블릿), https://m.blog.naver.com/javaking75/220056175936, Accessed by 2020-09-24, Last Modified 2014-07-10.

-> 추천(70점): 오래된 것처럼 보이지만, 현실적으로 돌아가고 commons-io, commons-fileupload에 대해서 자세히 잘 소개하고 있음.


7. [Day49][JSP] HashMap을 사용하여 전체 회원 목록 조회 / 상세 회원 목록 조회 / JSTL, https://clapdev.tistory.com/49, Accessed by 2020-09-24, Last Modified 2019-11-05.

-> 추천(30점): c태그(JSTL)에 대해서 잘 소개하고 있음.


8. JSP 기본 실습 : 데이터 전송 받기2 [개인정보입력폼] -request-, https://whdvy777.tistory.com/entry/JSP-%EA%B8%B0%EB%B3%B8-%EC%8B%A4%EC%8A%B5-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%86%A1-%EB%B0%9B%EA%B8%B02-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4%EC%9E%85%EB%A0%A5%ED%8F%BC-request, Accessed by 2020-09-24, Last Modified 2013-03-08.

-> 추천(35점): 오래되어 보이지만, 기본에 충실하고 있음.


9. Java Map 반복(Iteration)시키는 3가지 방법, https://stove99.tistory.com/96, Accessed by 2020-09-24, Last Modified 2011-11-13.


-> 자주 까먹을 수 있는 방법이라고 본다. 다중 프로그래밍을 다루고 있어서 까먹는 일이 많아서 적어본다.


Map<String, String> map = new HashMap<String, String>();

         

map.put("키1", "값1");

map.put("키2", "값2");

map.put("키3", "값3");

map.put("키4", "값4");

map.put("키5", "값5");

map.put("키6", "값6");

         

         

        // 방법1

        Iterator<String> keys = map.keySet().iterator();

        while( keys.hasNext() ){

            String key = keys.next();

            System.out.println( String.format("키 : %s, 값 : %s", key, map.get(key)) );

        }

         

        // 방법2

        for( Map.Entry<String, String> elem : map.entrySet() ){

            System.out.println( String.format("키 : %s, 값 : %s", elem.getKey(), elem.getValue()) );

        }

         

        // 방법3

        for( String key : map.keySet() ){

            System.out.println( String.format("키 : %s, 값 : %s", key, map.get(key)) );

        }


- https://stove99.tistory.com/96 [스토브 훌로구]


반응형
728x90
300x250

[Sun Sys] 자바(Java) - Properties 파일 읽기


자바를 통해서 Properties 파일을 읽는 방법에 대해서 정리하였다.



1. properties 파일


폴더: /src/main/resources


database=localhost

dbuser=helloUser

dbpassword=password


* 파일명: configuration.properties



2. 자바 - 코드


public class GetPropertyValue {


      private String dbpassword;

private String database;

private String dbuser;


      InputStream inputStream;


      public String getPropertyValue() throws Exception{

try{

Properties prop = new Properties();

String propFileName = "configuration.properties";

inputStream = getClass().getClassLoader().getResourceAsStream(propFileName);

if (inputStream != null) {

prop.load(inputStream);

} else {

throw new FileNotFoundException("property file '" + propFileName + "' not found in the classpath");

}


dbpassword = prop.getProperty("dbpassword");

database = prop.getProperty("database");

dbuser = prop.getProperty("dbuser");

 

} catch(Exception e){

logger.info("Exception : " + e);

} finally {

inputStream.close();

}

return result;

}


}


* 파일명: GetPropertyValue.java

반응형
728x90
300x250

[Spring-Framework] 11. Spring MVC - HikariCP 3.4.2, Oracle 19g 연동하기(Properties, Java) - (2)


1부에서는 환경설정하는 방법에 대해서 소개하였다.

2부에서는 코드로 구현하는 방법을 위주로 소개하겠다.


[1부] [Spring-Framework] 10. Spring MVC - HikariCP 3.4.2, Oracle 19g 연동하기(Properties, Java) - (1), 2020-09-23 14:20

https://yyman.tistory.com/1410



10. Properties 방식 - DataSource.java


코드를 하나 만들어보았다.


패키지: com.springMVC.web.persistance


package com.springMVC.web.persistance;


import java.io.IOException;

import java.io.InputStream;

import java.io.Reader;

import java.util.Properties;


import java.sql.Connection;

import java.sql.SQLException;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Value;


import com.zaxxer.hikari.HikariConfig;

import com.zaxxer.hikari.HikariDataSource;


public class DataSource {


private static final Logger logger = LoggerFactory.getLogger(DataSource.class);


    private static String CLASSNAME;

    private static String JDBC_URL;

    private static String USERNAME;

    private static String PASSWORD;

    private static String CACHE_PREP_STMTS;

    private static HikariDataSource ds;

    

/*

(자바 방식)

    private static HikariConfig config = new HikariConfig();

    

    static {

    config.setDriverClassName("oracle.jdbc.OracleDriver");

        config.setJdbcUrl( "jdbc:oracle:thin:@localhost:1521:orcl" );

        config.setUsername( "사용자계정명" );

        config.setPassword( "비밀번호" );

        

        config.addDataSourceProperty( "cachePrepStmts" , "true" );

        config.addDataSourceProperty( "prepStmtCacheSize" , "250" );

        config.addDataSourceProperty( "prepStmtCacheSqlLimit" , "2048" );

        ds = new HikariDataSource( config );

    }

    */

    

    private HikariConfig config;

    

    public DataSource() {

   

    InputStream inputStream;

    config = new HikariConfig();

   

    String resource = "db.properties";

        Properties properties = new Properties();

        

        try {

        inputStream = getClass().getClassLoader().getResourceAsStream(resource);

            properties.load(inputStream);

            System.out.println(properties.getProperty("jdbcUrl"));

            System.out.println(properties.getProperty("dataSourceClassName"));

            

            

            CLASSNAME = properties.getProperty("dataSourceClassName");

            JDBC_URL = properties.getProperty("jdbcUrl");

            USERNAME = properties.getProperty("dataSource.user");

            PASSWORD = properties.getProperty("dataSource.password");

            

            CACHE_PREP_STMTS = properties.getProperty("cachePrepStmts");


            config.setDriverClassName(CLASSNAME);

            config.setJdbcUrl( JDBC_URL );

            config.setUsername( USERNAME );

            config.setPassword( PASSWORD );

            

            config.addDataSourceProperty( "cachePrepStmts" , CACHE_PREP_STMTS );

            config.addDataSourceProperty( "prepStmtCacheSize" , "250" );

            config.addDataSourceProperty( "prepStmtCacheSqlLimit" , "2048" );

            

            ds = new HikariDataSource( config );

            

        } catch (IOException e) {

            e.printStackTrace();

        }


    }


    public Connection getConnection() throws SQLException {


        return ds.getConnection();

    }


}


파일명: DataSource.java


[첨부(Attachments)]

DataSource-java-type-properties.zip


위의 소스를 static 방식으로 간결하게 코드를 변경할 수 없는 이유는 "getClass().getClassLoader().getResourceAsStream(resource);" 이 부분이 static 변수에서는 처리가 되지 않는다.


비고: 클래스명에 대해서 "DataSource"로 해도 되나요?

       javax.sql.DataSource가 존재한다. 예제의 DataSource 클래스명은 좋은 명칭은 아니다. 

       (동작은 될 수 있겠으나 javax.sql.DataSource를 사용할 때는 명칭에 대해서 다시 생각을 해봐야 할 것이다.)



10-1. Controller - HomeController.java 수정하기(Properties 방식)


HomeController.java 파일로 작업하는 방법이다.


package com.springMVC.web.controller;


import java.text.DateFormat;

import java.util.Date;

import java.util.Locale;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;


import com.springMVC.web.dao.AddressBookDao;

import com.springMVC.web.persistance.DataSource;


/**

 * Handles requests for the application home page.

 */

@Controller

public class HomeController {

/*

// Controller 안에서만 사용가능.

@Value("#{props['dataSourceClassName']}")

private String CLASSNAME;

@Value("#{props['jdbcUrl']}")

private String JDBC_URL;

@Value("#{props['dataSource.user']}")

private String USERNAME;

@Value("#{props['dataSource.password']}")

private String PASSWORD;

*/

private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

// @Value("#{props['jdbcUrl']}")

// private String url;

/**

* Simply selects the home view to render by returning its name.

*/

@RequestMapping(value = "/", method = RequestMethod.GET)

public String home(Locale locale, Model model) {

logger.info("Welcome home! The client locale is {}.", locale);

Date date = new Date();

DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

String formattedDate = dateFormat.format(date);

try {

DataSource ds = new DataSource();

//ds.getConnection();

AddressBookDao dao = new AddressBookDao();

dao.allSelect();

//logger.info("URL:{}", JDBC_URL);

//dao.allSelect();

}

catch(Exception e) {

logger.info("msg: {}", e.getMessage());

}

model.addAttribute("serverTime", formattedDate );

return "home";

}

}


파일명: HomeController.java


[첨부(Attachments)]

HomeController-java-type-properties.zip




10-2. DAO - AddressBookDao.java (Properties 방식)


package com.springMVC.web.dao;


import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;


import com.springMVC.web.persistance.DataSource;


public class AddressBookDao {


private static final Logger logger = LoggerFactory.getLogger(AddressBookDao.class);


public void allSelect() {

try {

DataSource ds = new DataSource();

Connection conn = ds.getConnection();

String query = "select * from addressbook";

PreparedStatement pstmt = conn.prepareStatement(query);

ResultSet rs = pstmt.executeQuery();

while(rs.next()) {


String msg = "번호:" + rs.getString("num") + "/이름:" + rs.getString("name") + "/주소:" + rs.getString("address");

logger.info("Msg: {}.", msg);

}

rs.close();

conn.close();

} catch (SQLException e) {

logger.info("msg:{}",  e.getMessage());

}

}

}



파일명: AddressBookDao.java


[첨부(Attachments)]

AddressBookDao-type-properties.zip




11. Java 방식 - DataSource.java


앞에 Properties 방식으로 구성해봐도 되고, 자바 코드 방식으로 해도 무방하다.


package com.springMVC.web.persistance;


import java.io.IOException;

import java.io.InputStream;

import java.io.Reader;

import java.util.Properties;


import java.sql.Connection;

import java.sql.SQLException;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Value;


import com.zaxxer.hikari.HikariConfig;

import com.zaxxer.hikari.HikariDataSource;


public class DataSource {


private static final Logger logger = LoggerFactory.getLogger(DataSource.class);


    private static String CLASSNAME;

    private static String JDBC_URL;

    private static String USERNAME;

    private static String PASSWORD;

    private static String CACHE_PREP_STMTS;

    private static HikariDataSource ds;

    

    private static HikariConfig config = new HikariConfig();

    

    static {

    config.setDriverClassName("oracle.jdbc.OracleDriver");

        config.setJdbcUrl( "jdbc:oracle:thin:@localhost:1521:orcl" );

        config.setUsername( "사용자계정명" );

        config.setPassword( "비밀번호" );

        

        config.addDataSourceProperty( "cachePrepStmts" , "true" );

        config.addDataSourceProperty( "prepStmtCacheSize" , "250" );

        config.addDataSourceProperty( "prepStmtCacheSqlLimit" , "2048" );

        ds = new HikariDataSource( config );

    }

    

    /*

    private HikariConfig config;

    

    public DataSource() {

   

    InputStream inputStream;

    config = new HikariConfig();

   

    String resource = "db.properties";

        Properties properties = new Properties();

        

        try {

        inputStream = getClass().getClassLoader().getResourceAsStream(resource);

            properties.load(inputStream);

            System.out.println(properties.getProperty("jdbcUrl"));

            System.out.println(properties.getProperty("dataSourceClassName"));

            

            

            CLASSNAME = properties.getProperty("dataSourceClassName");

            JDBC_URL = properties.getProperty("jdbcUrl");

            USERNAME = properties.getProperty("dataSource.user");

            PASSWORD = properties.getProperty("dataSource.password");

            

            CACHE_PREP_STMTS = properties.getProperty("cachePrepStmts");


            config.setDriverClassName(CLASSNAME);

            config.setJdbcUrl( JDBC_URL );

            config.setUsername( USERNAME );

            config.setPassword( PASSWORD );

            

            config.addDataSourceProperty( "cachePrepStmts" , CACHE_PREP_STMTS );

            config.addDataSourceProperty( "prepStmtCacheSize" , "250" );

            config.addDataSourceProperty( "prepStmtCacheSqlLimit" , "2048" );

            

            ds = new HikariDataSource( config );

            

        } catch (IOException e) {

            e.printStackTrace();

        }


    }

    

    

    */


    public static Connection getConnection() throws SQLException {


        return ds.getConnection();

    }

}


파일명: DataSource.java


[첨부(Attachments)]

DataSource-java-type-java.zip




11-1. Controller - HomeController.java 수정하기(Java 방식)


HomeController.java 파일로 작업하는 방법이다.


package com.springMVC.web.controller;


import java.text.DateFormat;

import java.util.Date;

import java.util.Locale;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;


import com.springMVC.web.dao.AddressBookDao;

import com.springMVC.web.persistance.DataSource;


/**

 * Handles requests for the application home page.

 */

@Controller

public class HomeController {

/*

// Controller 안에서만 사용가능.

@Value("#{props['dataSourceClassName']}")

private String CLASSNAME;

@Value("#{props['jdbcUrl']}")

private String JDBC_URL;

@Value("#{props['dataSource.user']}")

private String USERNAME;

@Value("#{props['dataSource.password']}")

private String PASSWORD;

*/

private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

// @Value("#{props['jdbcUrl']}")

// private String url;

/**

* Simply selects the home view to render by returning its name.

*/

@RequestMapping(value = "/", method = RequestMethod.GET)

public String home(Locale locale, Model model) {

logger.info("Welcome home! The client locale is {}.", locale);

Date date = new Date();

DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

String formattedDate = dateFormat.format(date);

try {

//ds.getConnection();

AddressBookDao dao = new AddressBookDao();

dao.allSelect();

//logger.info("URL:{}", JDBC_URL);

//dao.allSelect();

}

catch(Exception e) {

logger.info("msg: {}", e.getMessage());

}

model.addAttribute("serverTime", formattedDate );

return "home";

}

}


파일명: HomeController.java


[첨부(Attachments)]

HomeController-java-type-java.zip





11-2. DAO - AddressBookDao.java (JAVA 방식)


자바 방식으로 dao를 구성하는 방법이다.


package com.springMVC.web.dao;


import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;


import com.springMVC.web.persistance.DataSource;


public class AddressBookDao {


private static final Logger logger = LoggerFactory.getLogger(AddressBookDao.class);


public void allSelect() {

try {

Connection conn = DataSource.getConnection();

String query = "select * from addressbook";

PreparedStatement pstmt = conn.prepareStatement(query);

ResultSet rs = pstmt.executeQuery();

while(rs.next()) {


String msg = "번호:" + rs.getString("num") + "/이름:" + rs.getString("name") + "/주소:" + rs.getString("address");

logger.info("Msg: {}.", msg);

}

rs.close();

conn.close();

} catch (SQLException e) {

logger.info("msg:{}",  e.getMessage());

}

}

}



파일명: AddressBookDao.java


[첨부(Attachments)]

AddressBookDao.java-Type-java.zip



12. 출력 결과


작업한 데이터베이스의 모습이다.

작업한 결과를 보면, 무슨 작업을 하려고 했는지 직관적으로 이해될 것으로 보인다.

DB를 처리하거나 호출하는 건 똑같은데, 호출할 때 미리 저장된 풀을 활용하여 부담을 줄여주는 효과가 있다.



그림 11. Oracle DB - SQL Developer 모습(Addressbook)



그림 12. Oracle DB - SQL Developer 모습(Addressbook)



그림 13. 화면 출력 결과 - STS4(Eclipse)



* 맺음글(Conclusion)


시중의 HikariCP에 대한 글이 오류가 많아서 한번 정확하게 최신 버전으로 작성해보고 싶었다.

수차례 태스트 작업을 완료하였으니 사용하는 데 지장없을 것으로 보인다.


공식 사이트: https://github.com/brettwooldridge/HikariCP

-> 공식 사이트의 메뉴얼이 다소 부족한 면이 있다.



* 참고자료(Reference)


1. [Spring] 커넥션 풀(Connection pool)이란?, https://linked2ev.github.io/spring/2019/08/14/Spring-3-%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80%EC%9D%B4%EB%9E%80/ , Accessed by 2020-09-23, Last Modified 2019-08-14.

2. Spring-Hikari-Oracle연동 시 나는 oracle.jdbc.driver.OracleDriver not found 에러, https://apiclass.tistory.com/entry/Spring-Hikari-Oracle%EC%97%B0%EB%8F%99-%EC%8B%9C-%EB%82%98%EB%8A%94-oraclejdbcdriverOracleDriver-not-found-%EC%97%90%EB%9F%AC, Accessed by 2020-09-23, Last Modified 2019-10-16.

3. Introduction to HikariCP, https://www.baeldung.com/hikaricp, Accessed by 2020-09-23, Last Modified 2020-06-02.

4. Maven Repository: mysql » mysql-connector-java » 8.0.21, https://mvnrepository.com/artifact/mysql/mysql-connector-java/8.0.21, Accessed by 2020-09-23, Last Modified .

5. GitHub - brettwooldridge/HikariCP: 光 HikariCP・A solid, high-performance, JDBC connection pool at last., https://github.com/brettwooldridge/HikariCP, Accessed by 2020-09-23, Last Modified .

6. [Spring][04]커넥션 풀 설정(HikariCP), https://kimvampa.tistory.com/57, Accessed by 2020-09-23, Last Modified 2020-04-10.

7. [Spring] Spring Test 오류 해결 모음(SpringJUnit4ClassRunner , Resource specification), https://life-with-coding.tistory.com/373, Accessed by 2020-09-23, Last Modified 2020-07-08.

8. [오류, 에러] Spring Junit Test 환경설정 / Class<SpringJUnit4ClassRunner> cannot be resolved to a type, https://developerntraveler.tistory.com/1, Accessed by 2020-09-23, Last Modified 2020-05-14.

9. 오라클 드라이버 에러 - Registered driver with driverClassName=oracle.jdbc.driver.OracleDriver was not found, trying direct instantiation, https://wrkbr.tistory.com/410, Accessed by 2020-09-23, Last Modified 2018.

10. SpringBoot error: Registered driver with driverClassName=oracle.jdbc.driver.OracleDriver was not found, trying direct instantiation, https://stackoverflow.com/questions/52014595/springboot-error-registered-driver-with-driverclassname-oracle-jdbc-driver-orac, Accessed by 2020-09-23, Last Modified 2018.

11. Spring에서 Properties 사용, https://yookeun.github.io/java/2015/12/22/spring-properties/, Accessed by 2020-09-23, Last Modified 2015-12-22.

12. Java에서 Properties 파일 읽기, https://prettymucho.tistory.com/7, Accessed by 2020-09-23, Last Modified 2017-06-12.

반응형
728x90
300x250

[Spring-Framework] 10. Spring MVC - HikariCP 3.4.2, Oracle 19g 연동하기(Properties, Java) - (1)


Spring MVC 프로젝트로 HikariCP 3.4.2를 연동하는 방법에 대해서 소개하겠다.

Apache DBCP(커넥션 풀) 프로젝트도 있으나 최근에는 HikariCP가 성능이 더 우수하다고 한다.


양이 많은 주제이므로 2부로 나눠서 작성하였다.



1. Introduce(소개) - 커넥션 풀


1. 커넥션 풀(DBCP)이란?

웹 컨테이너(WAS)가 실행되면서 DB와 미리 connection(연결)을 해놓은 객체들을 pool에 저장해두었다가, 클라이언트 요청이 오면 connection을 빌려주고, 처리가 끝나면 다시 connection을 반납받아 pool에 저장하는 방식을 말한다.



2. 작업환경


이 글에서 사용한 작업 환경이다.


* IDE: Spring Tool-Suite 4-4.7.2 Releases(Eclipse)


* Web Server(웹서버):

Apache Tomcat 9


* 데이터베이스(Databases)

Oracle Databases 19

MySQL 8.0.21


* 프레임워크

Spring Framework 3.1.1 Releases


* 라이브러리

POM - Oracle JDBC 19

POM - MySQL 8.0.21

Maven Project - 3.6.3/1.16.0.20200610-1735


특징: MyBatis 제거 등 영속 프레임워크 미적용




3. 프로젝트 구성


작업할 프로젝트의 구성이다.



그림 1. 프로젝트 구성




4. 새 프로젝트 생성


File -> New -> Others...

                 Spring 폴더-> Spring Legacy Projects



5. pom.xml 설정





그림 2. POM.xml 수정 - 자바 버전 수정하기


[JAVA JDK 버전 오류]

오류 : Resource specification not allowed here for source level below 1.7


자바 버전을 변경하는 이유는 1.6으로는 버전이 낮아서 미지원한다고 오류가 발생하기 때문이다.

pom.xml 셋팅 후 project에서 변경을 해줘야 한다.


그림 3. POM.xml 수정 - Spring Test 영역 수정하기



그림 4. POM.xml 수정 - HikariCP, Oracle JDBC


      <properties>

<java-version>14</java-version>

<org.springframework-version>3.1.1.RELEASE</org.springframework-version>

<org.aspectj-version>1.6.10</org.aspectj-version>

<org.slf4j-version>1.6.6</org.slf4j-version>

</properties>


(중략)


<!-- spring-test -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-test</artifactId>

<version>${org.springframework-version}</version>

</dependency>


(중략)


<!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP -->

<dependency>

    <groupId>com.zaxxer</groupId>

    <artifactId>HikariCP</artifactId>

    <version>3.4.2</version>

</dependency>


<dependency>

<groupId>com.oracle.database.jdbc</groupId>

<artifactId>ojdbc8</artifactId>

<version>19.3.0.0</version>

</dependency>    


* 파일명: pom.xml


* 참고 - MySQL 연동할 때


아래의 코드는 pom.xml에 넣어준다.


<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->

<dependency>

    <groupId>mysql</groupId>

    <artifactId>mysql-connector-java</artifactId>

    <version>8.0.21</version>

</dependency>




6. Project의 Properties의 Java Build Path 내 Libraries의 JRE 버전 바꾸기


pom.xml은 maven 환경에 대한 자바 버전 설정이었는데, 근본적인 Build Path에 관한 속성을 변경하는 작업이다.


[JAVA JDK 버전 오류]

오류 : Resource specification not allowed here for source level below 1.7



그림 5. Properties 클릭하기


프로젝트를 클릭한다.

오른쪽 버튼을 누른 후, Properties를 클릭한다.




그림 6. Java Build Path 설정하기 - Libraries


JRE System Library 1.6 버전을 Remove한다.

Add Library를 클릭하여 버전을 1.7 버전 이상으로 올려준다.



7. root-context.xml 수정하기


-> src/main/webapp/spring/root-context.xml에 있음.



그림 7. root-context.xml 파일 작업내용


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- Root Context: defines shared resources visible to all other web components -->

<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">

<!-- Oracle 9 이상으로 작성. -->

<!-- 오라클 9 이후에 oracle.jdbc.driver.OracleDriver는 중단되었음.  -->

<!-- oracle.jdbc.pool.OracleDataSource -->

<!-- jdbc:oracle:thin:@localhost:1521:orcl -->


  <property name="driverClassName" value="oracle.jdbc.OracleDriver"></property>

<property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:orcl"></property> 

<property name="username" value="사용자계정명"></property>

<property name="password" value="비밀번호"></property>

</bean>

<bean id="datasource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">

<constructor-arg ref="hikariConfig"></constructor-arg>

</bean>

</beans>



파일명: root-context.xml (Oracle 11 이상) - 태스트 확인: Oracle 18g


* [첨부(Attachments)]

root-context-oracle.zip



* 참고 - MySQL 연동(root-context.xml)


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- Root Context: defines shared resources visible to all other web components -->

<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">

  <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>

<property name="jdbcUrl" value="jdbc:mysql://localhost:{포트번호}/{DB명}?serverTimezone=UTC"></property> 

<property name="username" value="사용자 계정명"></property>

<property name="password" value="비밀번호"></property>

</bean>

<bean id="datasource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">

<constructor-arg ref="hikariConfig"></constructor-arg>

</bean>

</beans>


파일명: root-context.xml(mysql 버전)


* [첨부(Attachments)]

root-context-mysql.zip



* 정정: jdbc:mysql://localhost:3306/web?serverTimezone=UTC&characterEncoding=utf8



8. servlet-context.xml 파일 수정하기


servlet-context.xml 파일을 수정해준다.


파일 경로: /src/main/webapp/WEB-INF/spring/appServlet



그림 8. servlet-context.xml - 수정된 모습


<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/mvc"

xmlns:util="http://www.springframework.org/schema/util"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:beans="http://www.springframework.org/schema/beans"

xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd

http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd

http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">


<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->

<!-- Enables the Spring MVC @Controller programming model -->

<annotation-driven />


<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->

<resources mapping="/resources/**" location="/resources/" />


<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->

<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">

<beans:property name="prefix" value="/WEB-INF/views/" />

<beans:property name="suffix" value=".jsp" />

</beans:bean>

<!-- 추가(Hikara4 -->

<!-- <context:property-placeholder location="classpath:/db.properties" />  -->

<util:properties id="props" location="classpath:/db.properties" />     

<context:component-scan base-package="com.springMVC.web.controller" />

</beans:beans>


파일명: servlet-context.xml


* [첨부(Attachments)]

servlet-context.zip




9. /src/main/resources/db.properties 만들기


Spring MVC 프로젝트로 작성하였으면, Resources 폴더는 생성되어 있을 것이다.

없다면, 별도로 만들어야 한다.


db.properties 파일은 존재하지 않기 때문에 따로 만들어줘야 한다.



그림 9. 파일 만들기


resources 폴더를 클릭한 후 오른쪽 버튼을 클릭한다.

New->File을 클릭한다.



그림 10. 프로젝트 구성 - dbProperties


jdbcUrl=jdbc:oracle:thin:@localhost:1521:orcl

dataSourceClassName=oracle.jdbc.OracleDriver

dataSource.user={username}

dataSource.password={password}

cachePrepStmts=true

prepStmtCacheSize=250

prepStmtCacheSqlLimit=2048


파일명: db.properties (oracle 버전)


[첨부(Attachments)]

db-properties-oracle.zip


jdbcUrl=jdbc:mysql://localhost:3306/web?serverTimezone=UTC

dataSourceClassName=com.mysql.cj.jdbc.Driver

dataSource.user={UserName}

dataSource.password={Password}

cachePrepStmts=true

prepStmtCacheSize=250

prepStmtCacheSqlLimit=2048


파일명: db.properties (mysql버전)


[첨부(Attachments)]

db-properties-mysql.zip



* 2부에서 만나요.


초기 셋팅은 이 정도면 끝났으니, 2부에서 연재하도록 하겠다.


[Spring-Framework] 11. Spring MVC - HikariCP 3.4.2, Oracle 19g 연동하기(Properties, Java) - (2), 2020-09-23 14:28

https://yyman.tistory.com/1411


반응형
728x90
300x250

[JSP] 10. JSP 폴더 구성에 대한 것


이전 게시글을 따라 수행하면, 허점이 하나 있다는 것을 알 수 있다.

뷰 파일로 만든 .jsp파일을 직접 주소만 알게 되면, 접근할 수도 있다는 취약점이 있다.


1. [JSP] 9. MVC 디자인 패턴 - 초간단 이해, FrontController 패턴(1), 2020. 9. 20. 17:24

   https://yyman.tistory.com/1401


이러한 점을 설명하면서 알아두면 좋은 폴더의 구성에 대해서 소개하려고 한다.



[JSP 폴더에 대한 설명]


1) 프로젝트명/java resources/src/

   servlet, Class, Interface 등 예를 들면 java파일을 담는 곳

    - src 밑에 폴더 만들어서 작성  

      예) someFolder, SomeClass.java 식으로 사용


프로젝트를 개설해보면, 일부 살짝 차이가 있을 수 있겠으나 통상적으로 비슷하다.


2) 프로젝트명 "webContent"  

   - jsp 문서를 작성하면 된다.


3) 프로젝트명/webContent/WEB-INF/ 

   - 아래는 접근이 안됨, 보안 영역임.

    - 프로젝트명/webContent/WEB-INF/lib/  폴더에 각종

      jar 파일, ojdbc6 등을 붙여 넣어야 실행이 가능함.


※ jsp는 주소가 노출되어 있어서 공격을 받을 수 있다

    그래서, 

    @webServlet("*.do")와  같은 URL Mapping 어노테이션을 사용하여 URL 숨김 효과를 본다.

    또는 web.xml 에 정의하여 사용.





2. 9번 글 - 프로젝트에 적용해보기


WebContest에 있는 addressInsert.jsp와 addressList.jsp를 WEB-INF안에 view 폴더를 만들어서 이동시킨다.



그림 1) WEB-INF\view 폴더에 있는 jsp파일 실행시키기


jsp파일 경로를 입력하였으나 동작되지 않는 것을 확인할 수 있다.



2-1. 소스코드 변경하기


서블릿으로 WEB-INF\View에 있는 jsp파일을 사용하려면, 

AddressInsertController.java와 AddressListController.java의 파일 내용을 수정한다.



그림 2) 수정된 AddressInsert.java 파일



그림 3) 수정된 AddressList.java 파일


package com.eduJsp.controller;


import java.io.IOException;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class AddressInsertController implements Controller {


@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException{


HttpUtil.forward(req, res, "/WEB-INF/view/addressInsert.jsp");

}


}




package com.eduJsp.controller;


import java.io.IOException;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class AddressListController implements Controller {


@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException {

HttpUtil.forward(req, res, "/WEB-INF/view/addressList.jsp");

}


}




3. 결과


서블릿을 통해서 결과를 확인해보면, 이해할 수 있을 것으로 보인다.



그림 4) Servlet 파일(*.do) - 호출하기



4. 작업내용 정리해보기


변경작업을 수행한 이력을 체크해본 것이다. 조금 알기 쉽게 표기해보았으니 참고하여 정리해보면 좋을 듯 하다.



그림 5) 프로젝트 구조 - 변경 사항


반응형
728x90
300x250

[Spring-Framework] 3. Eclipse와 Apache Maven 프로젝트 - 구축하기(1)


Spring-Framework의 배경이 되는 Maven(이하 "Apache Maven") 프로젝트에 대해서 소개하려고 한다.


비고: 최신 Eclipse 개발도구에는 Maven Project가 내장되어 있음.


수동으로 콘솔 환경 등의 명령어로 Maven 개발환경을 구축하는 방법부터 시작해서 Eclipse 내장기능으로 사용하는 방법에 대해서 소개하겠다.



[Maven 개발방법]

1. 콘솔 등으로 수작업 개발 및 코딩
   (1, 2항목을 잘 읽어보면 도움이 될 것으로 보인다.)

2. Eclipse IDE 등을 활용한 Maven 개발환경 구축





1. Apache Maven Project


Apache Maven Project 사이트에 들어가면, Maven 프로젝트에 대해서 소개 및 자세한 내용을 살펴볼 수가 있다.


아파치 메이븐?

Apache Maven은 소프트웨어 프로젝트 관리 및 이해 도구이다.
프로젝트 개체 모델 (POM)의 개념을 기반으로 Maven은 중앙 정보에서 프로젝트의 빌드,보고 및 문서를 관리 할 수 있다.


URL: https://maven.apache.org/



그림 1. Apache Maven Project



그림 2. Apache Maven - Download


그림 2의 사이트에서 apache-maven-3.6.3.bin.zip을 내려받아서 수동으로 설치할 수 있다.





2. Maven 개발환경 - 설치하기


내려받은 apache-maven-3.6.3-bin을 압축푼다.



그림 3. apache-maven-3.6.3-bin 압축 풀기


apache-maven-3.6.3-bin.zip을 압축 푼다.

그러면, 폴더 하나가 생성되는 것을 확인할 수 있다.



그림 4. apache-maven-3.6.3


경로를 메모해둔다.

-> D:\apache-maven-3.6.3-bin\apache-maven-3.6.3





3. Maven 개발환경 - 시스템 환경설정


방금 전에 압축 푼 폴더를 바탕으로 Maven 개발환경을 셋팅할 것이다.



그림 5. apache-maven-3.6.3 - 시스템 환경 변수(N)


"시스템 -> 오른쪽 버튼 -> 속성"을 누른다.

"고급 시스템 설정 -> 고급 탭 -> 환경 변수(N)"을 클릭한다.



그림 6. apache-maven-3.6.3 - 시스템 환경 변수(N)


시스템 변수(S)에서 "Path" 변수를 찾아서 클릭한다.

"편집(I)"을 누른다.




그림 7. apache-maven-3.6.3 - 시스템 환경 변수(N)


맨 아래 항목에 아까 메모해둔 경로를 입력해준다. ("복사", "붙여넣기"를 권장함.)



4. Maven 개발환경 - 명령 프롬프트로 구축환경 확인하기


앞에 단계를 잘 했다면, 확인해야 할 것이다.

아래의 과정을 잘 따라하도록 하자.



그림 8. 명령 프롬프트 - 관리자 권한으로 실행하기


시작 메뉴에 cmd라고 입력 후 "관리자 권한으로 실행"을 클릭한다.



그림 9. 명령 프롬프트 - 관리자 권한으로 실행하기


mvn -version


이라고 명령어를 입력하면, 그림 9의 출력화면을 볼 수 있다.



5. Maven - Repository 경로 변경


기본 Maven Repository는 ${user.home}/.m2/repository 라는 폴더에서 관리한다.
dev 폴더에서 관리하도록 저장소를 로컬로 바꿀것이다.
dev 폴더에서 관리하길 원하지 않는다면, 이 단계는 넘어가도 된다.


[경로 변경 방법]

1. D:\.........중략......\apache-maven-3.6.2 에서 repository 폴더를 생성한다.

2. D:\.........중략......\apache-maven-3.6.2\conf 에서 settings.xml 파일을 연다. 

3. setting.xml 파일에서 <localRepository> 주석 처리된 부분을 찾은 뒤, 그 위에다 로컬 저장소를 등록한다.



그림 10. 폴더 만들기


임의의 폴더명 "repository"라는 폴더를 만들어본다.



그림 11. 환경설정 변경하기(1) - 변경 전


약 53줄에 있는 <localRepository>~</localRepository>를 복사해서 (빈 줄) 55줄 정도에 붙여넣는다.

[주석이 없는 빈줄에 넣어주기]



그림 12. 환경설정 변경하기(1) - 변경 후


입력을 완료하였다면, 저장을 누른다.





6. 응용 - Eclipse 프로젝트에 수동 설치한 Apache Maven 적용하기


최근 eclipse는 기본적으로 Apache Maven을 내장하고 있다.

하지만, 굳이 수동으로 설치한 Maven을 사용하고 싶다면, 다음처럼 사용할 수도 있다.



그림 13. Preferences 설정하기


"Window->Preferences"를 클릭한다.



그림 14. Preferences 셋팅하기


Maven 탭에서 Installations을 클릭한다.

그리고 "Add..." 버튼을 클릭한다.




그림 15. Preferences 셋팅하기


Directory 버튼을 누른다.



그림 16. Preferences 셋팅하기


apache-maven-3.6.3의 설치 경로를 선택한 후 "폴더 선택"을 누른다.




그림 17. Preferences 셋팅하기


설정을 확인한 후 "Finish"를 누른다.



그림 18. Preferences 셋팅하기


"apache-maven-3.6.3"을 체크한다.

그리고 "Apply"를 누르면 적용이 완료된다.



그림 19. Preferences 셋팅하기


사용자 환경설정에 관한 것이다.

Maven 탭에서 User Settings를 클릭한다.


[기본값]

User Settings: C:\Users\사용자계정명\.m2\settings.xml

Local Repository: C:\Users\사용자계정명\.m2\repository




그림 20. Preferences 셋팅하기


앞서 환경설정을 변경한 경로를 찾아서 선택한 후 "settings"를 열기한다.

(비고: Apache-maven-3.6.3\conf폴더 내에 settings.xml 파일이 존재함)



그림 21. Preferences 셋팅하기


그림 21처럼 변경한 후 "Apply"를 누르면 적용이 완료된다.



7. 맺음글(Conclusion)


Apache Maven 프로젝트를 설치하는 방법과 개발 환경 구축이라는 주제를 소개하였다.



* 참고 자료(Reference)


1. Maven - Welcome to Apache Maven, https://maven.apache.org/, Accessed by 2020-09-19, Last Modified 2020-09-10

반응형

+ Recent posts