728x90
300x250

[Spring-Framework] 19. Spring MVC - Spring Framework 5 REST, Jackson, Commons-FileUpload - (1)


프로젝트 위주로 불필요한 라이브러리는 전부 다 제거하고 순정으로 REST를 이해할 수 있도록 프로젝트를 기획하게 되었다.

REST를 제대로 소개하는 분들이 조금 적어서 간단하면서도 알기 쉽게 소개하려고 한다.

Spring Boot 위주의 REST가 많이 있는데, Spring Framework 5로 구현해도 무방하다.

결론부터 이야기하자면, 매우 잘 된다고 할 수 있다.


REST 이론부터 프로젝트까지 실질적으로 소개하려고 한다.


[사전 배경 지식]

- HTML, 간단한 폼 전송 방식 이해
- 자료구조(Data Structures)
  (DB 이런 거 전부 제거함.)


[기타]

복잡하게 SOA 등 어렵고 관련 없는 주제는 다 제거하였다.

실질적으로 사용될 수 있는 수준의 REST에 대해서 소개해보려고 한다.



작업 환경

- IDE: Spring-tool-suite 4-4.7.2. Releases. / 최신(2020-09-29)

- Maven 3.6.3/1.16.0.20200610-1735 / 최신(2020-09-29)

- Java Version: 14이상 (OpenJDK 15) / 최신(2020-09-29)

pom - maven

- Spring Framework 5.2.9. RELEASES. / 최신(2020-09-29)

- javax.servlet-api 4.0.1 (2018-04-20) / 최신(2020-09-29)

   https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api/4.0.1

- jackson-core 2.11.2  - XML (2020-08-02) / 최신(2020-09-29)

- jackson-databind 2.11.2 - XML (2020-08-02) / 최신(2020-09-29)

- commons-fileupload 1.4  (Apache Commons, 2018-12-24) / 최신(2020-09-29)

- WAS: Apache-tomcat-9.0.37-windows-x64 / 최신(2020-09-29)



1. 실험 결과


(1차)

- Maven Project (webapp)

  Resteasy(JBoss 기반)

  -> 웹 브라우저로 출력을 시도했을 때, XML 변환이 안 됨. (GET, POST, PUT, PATCH, DELETE)는 동작함.

     (자료가 많이 부족함.)


  [첨부(Attachments)] (영어)

resteasy-reference-guide-en-US.pdf


결론: 동작은 되나 XML 변환 도구를 찾을 수 없음. (실패)


(2차)

- Maven Project (webapp)

  Jersay (4~5개 이상 POM 요구됨.)

  

과거에는 호환성이 나름대로 있었다고 하나, 현재에는 버전이 전부 제각각이고 사용하기에 부적합하다고 주장함.

(2020-09-28)

참고: 과거에는 com.sun..... jersey 형태의 패키지에서 현재는 "Jersey"가 독립 프로젝트로 분리되었음.

   javax가 일부 명칭이 변경되었음.


  [첨부(Attachments)] (영어)

jersey-documentation-3.0.0-M1-user-guide.pdf


결론: 동작 자체가 안 됨. (실패)


(3차)

- Spring Framework 5.4

  - Spring Legacy Project -> Spring MVC Project

    -> Pom(Maven) : 

        Spring Framework 5.4 - 2020-09-28일 기준 최신

        Servlet 4.0.1 - 2020-09-28일 기준 최신

        Jackson(core, bind) = 용도: XML Parser 2.11.2 - 2020-09-28일 기준 최신

        Apache Commons-FileUpload


결론: 성공





2. REST - 구현 관점에서 이론 소개


복잡하게 다른 걸 알 필요는 없다고 보고, HTTP 프로그램 작성해봤으면 POST/GET 이걸 다뤄본 경험들이 있을 것이다.

해외에 있는 "(Ph.D / 공학/이학 계통의 박사) 로이 필딩이 발표하신 학위 논문이 전 세계에 널리 알려진 거라고 보는 게 좋겠다.


https://roy.gbiv.com/


관심이 있는 분들은 로이 필딩 박사 홈페이지에서 대략적인 이력을 살펴봐도 무방하다.

로이 필딩 박사의 주요 업적은 "Apache Web Server", "REST(학위 논문)" 두 가지라고 보면 된다.


웹 분야에서는 매우 권위있는 분 중 하나라고 생각하면 된다.


전송 방식의 문제인데 "POST/GET"만 웹에서는 가능하다. (HTML5 현재까지도)


REST의 약자인 REST(Representational State Transfer)는 GET/POST의 한계를 현실세계의 상태 개념과 접목 시켜서 고민을 한 모양이다.

(필자는 비록 논문을 읽어보진 못했지만, 작업을 하면서 느껴본 견해이다.)


핵심을 요약하면,


표 1. 다양한 전송방식(REST)


번호 

작업

전송 방식

1

Create

POST

2

Read

GET

3

Update

PUT 또는 Patch

4

Delete

DELETE


REST는 4가지 전송 방식으로 확장되었다.


* 자바스크립트의 (Ajax)로 REST를 전송할 수 있는가?

-> 구현되었다고 주장하는 시중 코드들이 있을 수는 있겠으나 안 될 수도 있다.
   (= 국제 웹 관련 표준기구(W3C) REST를 GET, POST 이외에도 표준으로 인정해주지 않는 이상 어렵다고 본다.)


표 2. 회원(Member) 자원에 대한 전송 방식 정의


작업

전송방식 

URI 

생성

POST

/member/new 

조회(전체)

GET

/member

조회(특정)

GET

/member/{id}

수정

PUT or PATCH

/member/{id}

삭제

DELETE

/member/{id}


꼭 이런 정의가 반드시 표준이라는 건 아니다. 일부 책들을 많이 대조해보고, 인터넷 검색 등을 해봤으나 "이런 형태로 쓸 수 있다." 이렇게 생각하면 좋겠다.

도메인이 달라지면, 달라지는 데로 바꿔서 사용해야 한다.




3. REST - 웹 표준 관점에서 무엇이 문제가 되는가?


코드 구현으로 이러한 기술적 문제를 소개해주려고 한다.


<form method="PUT">

~~~(중략)

</form>


코드 1) 이론적으로는 이렇게 되어야 함. (미지원)


<form method="POST">

<input type="hidden" name="_method" value="PUT"> 

~~~(중략)

</form>


코드 2) 과거 잠시 표준 규격에서 예외를 해주었던 규격 (미지원)


코드 2 형태로 구현하면, 동작될 수도 있었던 잠시 과거가 있긴 있었다.

표준 기구 등에서 제한에 걸어서 동작하지 않은 코드들이다.


(슈도 코드)
<script>

    .....

   


    ajax{

           contents/json

           method = "PUT" action=......


           sucessed{

           }

           failed{



           }

    }

</script>
~~~(중략)

<form method="POST">

<input type="hidden" name="_method" value="PUT"> 

~~~(중략)

</form>


코드 3) ajax 자바스크립트 - 슈도 코드 (미지원)


이러한 코드 형태도 태스트해본 결과로는 동작하지 않는다.



그림 1. RequestMethod.PATCH, RequstMethod.PUT (Spring-Framework 5.4)


그림 1의 코드 형태로 정의했을 때, 전송이 되냐는 것이다.

결론부터 이야기하면, 안 된다.


다 안 된다고 적었으니, 그러면 "REST는 전혀 사용할 수 없는 건가요?"라는 물음이 들 수 있다.

이야기하면, 사용할 수 있다.


그런데 사용할 수 있는 용도가 있다는 것이다.


예1) 안드로이드 - 게시판 개발, 

예2) 각종 규격 문서 또는 메시지 전달


(이런 부분에서 사용하면 적합하다고 본다.)


-> 원격지에서의 파일 전송과 내려받기는 괜찮을까요?

   결론부터 놓고 보면, 안 된다. (원격지 REST를 활용해서 전송할 수 있는가?)

   이론적으로는 Multipart로 해서 하면 될 것 같은데 POST에서는 처리될 수도 있겠으나, 어려울 수 있다.

   = 표 2의 명세에서는 "등록(POST 방식)", "수정(PUT)" 방식을 소개하고 있는데, 다소 어렵다고 하는 이야기가 이런 부분이다.


굳이 어렵게 "전송 해더(Header)" 찾고 등의 고도의 작업을 시도해도 안 되는 이유가 보안이라는 주제 때문에 웹 브라우저 등에서 제약이 되어 버린다.



[호기심 자극하기]

안드로이드 어플을 사용하다 보면, 업로드 기능이 있는 경우에는 순수한 안드로이드 기술로 개발한 게 아니라 다른 웹 사이트를 호출해서 사용한 것이다.

개발 관점에서는 안 된다가 타당하다고 본다.


JSP, PHP, ASPX, ASP 등의 개발이 되니깐 그걸 그냥 그대로 활용한 것이다.




4. URI, URL에 대해서


* URI(Uniform Resource Identifier) : 자원의 식별자라는 의미
* URL(Uniform Resource Location) : 주소


URL은 URI의 하위개념이므로 혼용해도 무방할 수 있다.

이론적으로도 비슷하게 취급될 여지도 있어서 크게 용어를 따로 이중으로 외워둘 필요까진 없겠다.




5. REST의 실질적인 기능 (서버, 클라이언트 관계)


REST는 XML-RPC처럼 통신 계열로 보는 게 적합하다고 본다.

태스트를 하다보면, 육감적으로 왜 그렇게 보는 게 타당한지 이해할 수 있을 것으로 보인다.



그림 2. REST 전용 태스트 도구 - 브라우저 내 추가한 앱스(YARC! Yet Another REST Client)


가장 사용하기에 좋은 REST 태스트 도구라고 본다.

이 방식을 흔히 많이 소개하고 있는데, 코드로도 서버를 하나 구축해서 사용할 수 있다.




그림 3. RestTemplate를 이용한 Server 만들기 - 자바(스프링 프레임워크 5.4)



그림 4. Spring MVC Controller를 이용한 Client 만들기(RestController 등) - 자바(스프링 프레임워크 5.4)


정리를 해보면, 아래의 도식처럼 요약해볼 수 있다.




그림 5. REST Client와 Server 관계


이런 느낌으로 사용하게 된다.

이런 관계인 이유는 메시지 형태로 데이터를 반환하면, "객체, 메시지" 등의 정보를 송/수신 할 수가 있다.


어노테이션 

기능 

@RestController

Controller가 REST 방식을 처리하기 위해 명시한 것 

@ResponseBody

일반적인 JSP와 같은 뷰로 전달되는 게 아니라 데이터 자체를 전달하기 위한 용도 

@PathVariable

URL 경로에 있는 값을 파라미터로 추출하려고 할 떄 사용 

@CrossOrigin

Ajax의 크로스 도메인 문제를 해결해주는 어노테이션  

@RequestBody

JSON 데이터를 원하는 타입으로 바인딩 처리 


실질적으로 @RestController는 필수 정의이다.

@PathVariable과 함께 조합해서 가장 많이 사용할 것으로 보인다.


운이 나쁘면, 크로스 오리진 등은 접할 수도 있고, 안 접할 수도 있다.



6. 프로젝트 - 소개


복잡한 주제는 조금 정리하고 실질적으로 꼭 필요한 형태로 이러한 기능들을 모두 담고 있는 종합적인 REST만 전문으로 하는 프로젝트를 작성하였다.



그림 6. 프로젝트 구성도


다른 프로젝트 등에 비해서 매우 깔끔하고 간단한 형태로 REST를 취급할 수 있도록 신경을 조금 써서 작성한 것이다.

데이터베이스 지식, MyBatis(iBatis) 등 그런 거 일절 신경 안 쓰고 다뤄볼 수 있으니 참고하면 되겠다.



그림 7. 결과 1(client/listMapDelete/{id})



그림 8. 결과 2(/v1/api/board)의 JSON



그림 9. 결과 3(/v1/api/board)의 JSON (결과 출력)




그림 10. 결과 4(/v1/api/board/new)의 JSON(POST 전송)



그림 11. REST 전송 방식들


굉장히 많은 전송 방식이 있다는 것을 알 수 있다.



그림 12. 다중 업로드 프로젝트 (1) - REST 활용



그림 13. 다중 업로드 프로젝트 (2) - REST 활용



그림 14. 다중 업로드 프로젝트 (3) - 업로드 반응



그림 15. 다중 업로드 프로젝트 (4) - 업로드 반응



그림 15. 다중 업로드 프로젝트 (4) - 실제 업로드 모습





7. 프로젝트 생성


프로젝트 생성부터 단계적으로 아주 친절하게 하도록 하겠다.


[선수 준비되어야 할 부분]

- 필자의 글을 보면, 이 부분이 아주 자세히 소개되고 있는데 잊어버리기 쉬워서 다시 계속 강조하는 것이다.

한 번 셋팅해놓으면 크게 무방한데, 안 될 경우 등 문제를 충분히 고민한 후에 포함시킨 것이니깐 이해 해주었으면 좋겠다.


Help-> Eclipse Marketplace -> STS 검색 -> Spring-Tool Add-on



그림 16. Help의 Eclipse Marketplace



그림 17. Help의 Eclipse Marketplace


Spring Tools 3 Add-On for Spring Tools 4 3.9.14 Release를 선택한 후 Install을 해준다.

-> Spring Framework 3.2인가 "Spring Legacy Project" 기능을 최신 버전에서도 사용할 수 있도록 도와주는 도구이다.


Spring Legacy Project만 동작되어도 기본 셋팅하는 데 있어서 매우 수월해진다.




그림 18. Eclipse의 파일 메뉴 모습


File->New->Spring Legacy Project를 클릭한다.



그림 19. Spring MVC Project 생성하기 - Spring Legacy Project 기능


Spring MVC Project를 선택한다.

Project Name을 입력한다.

Next를 누른다.



그림 20. 패키지 정보 입력 - Spring MVC Project 생성하기


패키지명을 입력한다.

Finish를 누른다.




8. 자바 버전 바꾸기 - 프로젝트 Properties의 Build Path, Project-Factes


자바 버전을 바꿔줘야 한다.

이유는 Spring Legacy Project의 기본 셋팅이 하위버전으로 되어있기 때문이다.



그림 21. 프로젝트의 Properties 클릭하기 (프로젝트 마우스 오른쪽 버튼의 메뉴 모습)


프로젝트를 선택한다.

마우스 오른쪽 버튼을 클릭한다.

"Properties"를 클릭한다.



그림 22. Properties - Build Path


JRE System Library를 선택한다.

Edit를 누른다.

JRE를 클릭한다.

14버전으로 바꿔준다.

Apply를 누른다.



그림 23. Properties - Project Factes


Java의 버전을 14버전으로 바꿔준다.

Apply를 누른다.

Apply and Close를 누른다.



9. pom.xml 환경설정하기


꼭 열어봐야 할 사이트가 있다.

http://mvnrespository.com 사이트에 접속해서 버전 등을 확인해줘야 한다.


검색 방법을 잊어버린 사람들을 위해서 특별히 다시 언급하겠다.



그림 24. Spring Framework 검색 결과 - mvnrepository


Spring Framework를 클릭한다.



그림 25. Spring Framework 검색 결과 - mvnrepository


5.2.9.RELEASE를 선택한다.




그림 26. Spring Framework 검색 결과 - mvnrepository


Maven의 POM 배포 코드를 복사한다.



그림 27. pom.xml 수정작업(1) - 자바 버전과 스프링 프레임워크 버전 업그레이드


자바 버전과 Spring-Framework 버전을 변경해준다.

참고로 Spring-Framework의 경우에는 일명 "깔맞춤"으로 자동 버전 셋팅이 되는 경우가 있다.


다 되는 건 아니니깐 찾아보는 작업을 잊지 않아야 한다.



그림 28. pom.xml 수정작업(2)


javax.servlet 버전도 업그레이드 해준다.




그림 29. pom.xml 수정작업(3)


jackon-core, jackson-databind, commons-fileupload를 각각 검색해서 "복사, 붙여넣기"를 해준다.


<?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.example</groupId>

<artifactId>restexample2</artifactId>

<name>restExample2</name>

<packaging>war</packaging>

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

<properties>

<!-- 자바 버전(14로 작성) -->

<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 -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

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

<exclusions>

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

<exclusion>

<groupId>commons-logging</groupId>

<artifactId>commons-logging</artifactId>

</exclusion>

</exclusions>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-webmvc</artifactId>

<version>${org.springframework-version}</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>

<!-- Servlet -->

<!-- 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>

<!-- Test -->

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.7</version>

<scope>test</scope>

</dependency>       

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->

<dependency>

    <groupId>com.fasterxml.jackson.core</groupId>

    <artifactId>jackson-core</artifactId>

    <version>2.11.2</version>

</dependency>

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->

<dependency>

    <groupId>com.fasterxml.jackson.core</groupId>

    <artifactId>jackson-databind</artifactId>

    <version>2.11.2</version>

</dependency>

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->

<dependency>

    <groupId>commons-fileupload</groupId>

    <artifactId>commons-fileupload</artifactId>

    <version>1.4</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




10. web.xml - 환경설정하기


web.xml 파일 셋팅은 무척 중요하다고 볼 수 있다.

반드시 해야 하는 작업이다.


경로명: /src/main/webapp/web.xml




그림 30. web.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">

  <context-param>

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

    <param-value>/WEB-INF/spring/root-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

      /WEB-INF/spring/spring-file-config.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>

  <servlet>

    <description></description>

    <display-name>RestController</display-name>

    <servlet-name>RestController</servlet-name>

    <servlet-class>com.example.restexample2.controller.RestController</servlet-class>

  </servlet>

  <servlet-mapping>

    <servlet-name>RestController</servlet-name>

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

  </servlet-mapping>

</web-app>


파일명: web.xml


[첨부(Attachments)]

web.zip



11. spring-file-config.xml - 파일 생성하기


생성경로: /src/main/webapp/WEB-INF/spring/spring-file-config.xml


왜 작업을 해주냐면, Apache Commons-Fileupload를 사용하기 위해서이다.



그림 31. spring 폴더의 오른쪽 버튼 메뉴 모습


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

New-> File을 클릭한다.


"spring-file-config.xml" 파일을 만들어준다.




그림 32. spring-file-config.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="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

    <property name="maxUploadSize" value="10485760"/>

    <property name="maxUploadSizePerFile" value="10485760"/>

    <property name="maxInMemorySize" value="0"/>

</bean>

</beans>



파일명: spring-file-config.xml


[첨부(Attachments)]

spring-file-config.zip




* 2부에서 만나요.


2부에서는 Controller와 Model, View, Util의 주제를 다뤄보겠다.


1. [Spring-Framework] 19. Spring MVC - Spring Framework 5 REST, Jackson, Commons-FileUpload - (1), Accessed by 2020-09-28

https://yyman.tistory.com/1426


반응형
728x90
300x250

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


이전 글을 보지 않았다면, 꼭 진행해보고 오길 권장한다.


[이전 글]

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




22. View - home.jsp


사용자 인터페이스는 아래의 그림처럼 표현하였다.



그림 26. 로그인 전 - "/" 페이지



그림 27. 로그인 후 - "/" 페이지



그림 28. 로그인 후 - 권한별 기능 표시, 계정 정보 출력



그림 29. 로그인 후 - 권한별 기능 표시, 계정 정보 출력



경로: /src/main/webapp/WEB-INF/views/home.jsp



<%@ 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 (Java 방식)</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(Java 방식)) - 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>

<!-- 방법1. Sec 적용(이름 출력) -->

방법(sec 태그)1: <sec:authentication property="name" />

</p>

<p>

<!-- 방법2. c태그, Controller에서 가져오기 -->

방법(Model 정의)2: ${username}

</p>

</sec:authorize> 


<h3>

<!-- 관리자 권한을 가진 경우만 보이기 -->

<sec:authorize access="hasRole('ROLE_ADMIN')" >

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

</sec:authorize>

<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





23. View - admin/home.jsp


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



그림 30. 관리자 페이지


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

<%@ page session="false" %>

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

<html>

<head>

<meta charset="UTF-8">

<title>Admin - Page(관리자 - 페이지)</title>

<meta charset="UTF-8">

</head>

<body>

<h1>

Hello world!

</h1>


<P>

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

</P>

</body>

</html>



파일명: home.jsp


[첨부(Attachments)]

home.zip




24. View - member/loginForm.jsp


로그인 폼에 대한 사용자 인터페이스이다.



그림 31. 로그인 폼 - 로그인 전



그림 32. 로그인 폼 - 로그인 시도(아이디 또는 비밀번호 틀렸을 때)


<%@ 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="/member/loginForm" 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>

    <p>

        <label for="remember-me">Remember-me(로그인 상태 유지)</label>

        <input type="checkbox" id="remember-me" name="remember-me"/>

    </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




25. View - member/accessDenied.jsp


접근 제한 페이지에 관한 소스이다.



그림 33. 접근 제한된 페이지


<%@ 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



26. 맺음글(Conclusion)


Spring Framework 5.4, Spring Security 5.4, Oracle 19g를 활용하여 보안 영역에 대해서 자세하게 살펴보았다.



* 참고 자료(References)


1. Hello Spring Security Java Config, https://docs.spring.io/spring-security/site/docs/5.0.16.RELEASE/guides/html5/helloworld-javaconfig.html#setting-up-the-sample, Accessed by 2020-09-27, Last Modified 2020-05-06.


추천(30점): 공식 사이트에서 제공하는 메뉴얼인데, 제작 흐름을 파악할 수 있다.


2. Tomcat ->Multiple Contexts have a path of 에러, https://kkangdda.tistory.com/12, Accessed by 2020-09-27, Last Modified 2019-11-06.


3. Spring Security 5 – Java Config, https://howtodoinjava.com/spring5/security/security-java-config-enablewebsecurity-example/, Accessed by 2020-09-27, Last Modified 2020-06-19.


추천(50점): June 19, 2020

- 1. @EnableWebSecurity is not found in any of the 3 jars. 

= 2. "spring-security-config-5.0.7.RELEASE", "spring-security-core-5.0.7.RELEASE", "spring-security-web-5.0.7.RELEASE", 

     - June 19, 2020, Spring version is 5.2.5


4. Spring 4.0 + Java Config - web.xml 없애기..., https://tiveloper.tistory.com/entry/Spring-40-Java-Config-webxml-없애기, 2014-10-24

추천(20점): 조금 코드가 바뀐 부분이 있다. 

public class WebInitializer implements WebApplicationInitializer { }
= 이 부분 클래스가 동작하지 않음. (변동 사항이 있었음.)
   - 2013년 12월 정도에 Spring 4.0이 출시되었으니깐 오래되었다고 봐도 됨.


5.Spring Security : Web MVC + Security - Custom Login Form 만들기, https://kogle.tistory.com/78?category=870263, Accessed by 2020-09-27, Last Modified 2020-05-16.


추천(25점): 커스텀 로그인 페이지 java 버전에 대해서 간결하게 잘 작성되어 있음. (그러나 태스트를 해본 바로는 완벽하게 동작하진 않음.)


6. Spring Security Authentication Provider, https://www.baeldung.com/spring-security-authentication-provider, Accessed by 2020-09-27, Last Modified 2020-08-19.


추천(40점): 국내 자료에는 "CustomAuthenticationProvider()"에 대해서 자세히 잘 적어놓은 글을 찾기 힘들었음.

- shouldAuthenticateAgainstThirdPartySystem()가 동작되지는 않았지만, 많은 도움이 되었음.


6.Spring Security - 인증 절차 인터페이스 구현 (1) UserDetailsService, UserDetails, https://to-dy.tistory.com/86?category=720806, Accessed by 2020-09-27, Last Modified 2018.


추천(25점): 인증 절차(쉽게 소개하면, "DB처리에 대한 방법과 과정")에 대해서 잘 작성하였음.

- iBatis(MyBatis)로 작성되어 있긴 한데, 구현 원리를 살펴볼 수 있었음.


7.How to refer brcypt encoder to customized authentication provider?, https://stackoverflow.com/questions/35900053/how-to-refer-brcypt-encoder-to-customized-authentication-provider, Accessed by 2020-09-27, Last Modified 2016.


추천(13점): customized authentication provider에 대해서 구현 원리와 암호 처리 등에 대해서 소개하고 있다.

이 글에서 알 수 있었던 중요한 부분은 Spring Security 5.4로 구현하면서 암호가 랜덤으로 처리되는 것을 확인하였는데, 암호 확인하는 방법을 찾던 도중에 Bcrypt 함수 내에 확인하는 함수가 있는 것을 알게 되었다.


8. [Spring/Security] 초보자가 이해하는 Spring Security, https://postitforhooney.tistory.com/entry/SpringSecurity-%EC%B4%88%EB%B3%B4%EC%9E%90%EA%B0%80-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-Spring-Security-%ED%8D%BC%EC%98%B4, Accessed by 2020-09-27, Last Modified 2017-03-31.


9. [SPRING SECURITY] 4.스프링 시큐리티 로그인 커스터마이징, https://debugdaldal.tistory.com/89, Accessed by 2020-09-27, Last Modified 2016.

추천(15점): 자바 기반으로 커스터마이징 구현하는 방법에 대해서 소개되어 있다.


10. Spring Security Custom AuthenticationProvider example, https://javaengine.tistory.com/entry/Spring-Security-Custom-AuthenticationProvider-example, Accessed by 2020-09-27, Last Modified 2017-02-06.

추천(20점): Spring Security with Java 프로젝트 환경설정만 잘 되어 있다면, 시도해봐도 괜찮은 글이다.


11. Spring Security Remember Me, https://www.baeldung.com/spring-security-remember-me, Accessed 2020-09-27, Last Modified 2020-08-15.

추천(15점): 로그인 유지 및 자동 로그인에 관한 자바 버전으로 구현하는 방법에 대해서 소개하고 있다.
쿠키 제거하고 몇 가지 간단한 사용방법만 소개하고 있다.


12. 6 Spring Security - Access Denied Handler(error page 처리), https://jungeunlee95.github.io/java/2019/07/18/6-Spring-Security-Access-Denied-Handler(errorpage-%EC%B2%98%EB%A6%AC)/, Accessed by 2020-09-27, Last Modified 2019-7-18.

-> 비고: 접근 제어 페이지에 대해서 소개하고 있음.


13. 3 Spring Security - Authorization(권한) 설정(ROLE), TagLib authorize 추가, https://jungeunlee95.github.io/java/2019/07/18/3-SpringSecurity-Authorization(%EA%B6%8C%ED%95%9C)-%EC%84%A4%EC%A0%95(ROLE),-TagLib-authorize-%EC%B6%94%EA%B0%80/, Accessed by 2020-09-27, Last Modified 2020-07-18.

-> 비고: 권한별 페이지 꾸리는 방법에 대해서 소개하고 있음.

반응형
728x90
300x250

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


이번에는 이전 글에 이어서 Spring-Security를 구현해보려고 한다.

조금 이번 글부터는 난이도가 있어지니깐 개발 전략을 잘 숙지해서 작업하면 좋겠다.


1. [Spring-Framework] 16. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-Java) (1), 2020-09-27

https://yyman.tistory.com/1422



14. 개발 전략


하나 만들면 다 되는 게 아니다. 계속 연속해서 복합적으로 수정작업을 해줘야 한다.

그래서 개발 작업이 힘이 든다. 쉽지만 않다.


그림 24. 개발 전략도


SecurityWebApplicationInitializer.java, SecurityConfig.java는 Spring Security 구현에 있어서 핵심이라고 해도 무방하다.

두 개를 잘 구현한다면, 셈플 로그인 페이지는 볼 수 있다.


문제는 자바 버전으로 구현했을 때 보안 토큰 절차가 xml방식에 비해서 매우 까다롭게 반응한다는 것이다.

그래서 간단한 코드로 태스트를 해보기도 전에 DB 설계를 할 수 밖에 없었다.


이유는 토큰 인증 때문에 그렇다.


SqlMapSessionFactory도 계속 반복해서 다양한 영역에서 재사용될 것이다.



15. config의 SecurityWebApplicationInitializer.java (필수 파일)


이 파일을 보면, 제일 황당한 생각이 들 수 밖에 없는 이유가 코드는 몇 줄 안 되는데, 없으면 동작이 안 된다는 것이다.


package com.springMVC.javaSecurity5.config;


import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;


public class SecurityWebApplicationInitializer 

                        extends AbstractSecurityWebApplicationInitializer {


}


파일명: SecurityWebApplicationInitializer.java


[첨부(Attachments)]

SecurityWebApplicationInitializer.zip



16. config의 SecurityConfig.java (필수 파일)


"SecurityConfig.java" 이 파일도 없으면 Spring Security with 자바 버전이 동작되지 않는다.


패키지 경로: com.springMVC.javaSecurity5.config


package com.springMVC.javaSecurity5.config;


import javax.sql.DataSource;


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

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;

import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

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

import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;

import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import org.springframework.security.web.csrf.CsrfFilter;

import org.springframework.web.filter.CharacterEncodingFilter;


import com.springMVC.javaSecurity5.db.SqlMapSessionFactory;

 

@Configuration

@EnableWebSecurity

public class SecurityConfig extends WebSecurityConfigurerAdapter {

 

    @Autowired

    PasswordEncoder passwordEncoder;

    

    @Autowired

    private CustomAuthenticationProvider authProvider;

    

    protected void configure(AuthenticationManagerBuilder auth, HttpSecurity http) throws Exception {

      

    CharacterEncodingFilter filter = new CharacterEncodingFilter();


    /* UTF-8 한글 보완 */

        filter.setEncoding("UTF-8");

        filter.setForceEncoding(true);

        http.addFilterBefore(filter,CsrfFilter.class);

   

        auth

        .authenticationProvider(authProvider);

        

    /* 현재 - 임시

        auth.inMemoryAuthentication()

        .passwordEncoder(passwordEncoder)

        .withUser("user").password(passwordEncoder.encode("1234")).roles("RULE_USER")

        .and()

        .withUser("admin").password(passwordEncoder.encode("1234")).roles("RULE_USER", "RULE_ADMIN");

        

         */

        

        // withUser("admin").password(passwordEncoder.encode("1234")).roles("USER", "ADMIN");

      

        /* 임시

   

    UserBuilder users = User.withDefaultPasswordEncoder();

      auth.inMemoryAuthentication()

        .withUser(users.username("admin").password("1234").roles("USER"))

        .withUser(users.username("user").password("1234").roles("ADMIN"));

    */

    }

 

 

    @Override

    protected void configure(HttpSecurity http) throws Exception {


        http.authorizeRequests()

        

        // index

        .antMatchers("/")

            .permitAll()


         // 접근 오류

     .antMatchers("/member/accessDenied")

         .permitAll()

         

         .antMatchers("/member/accessDeniedView")

         .permitAll()    


         // 회원 로그인 기능

     .antMatchers("/member/loginForm")

         .permitAll()

         

     // 관리자 페이지 기능

        .antMatchers("/admin/**")

        .hasRole("ADMIN")

        // "RULE_ADMIN이라고 DB에 입력되어 있다면, RULE_은 제거하고 입력해야 인식함."

        

        // 폼 로그인 명세

        .and()

            .formLogin()

        .permitAll()

                .loginPage("/member/loginForm")

                .failureForwardUrl("/member/loginForm?error")

                .defaultSuccessUrl("/")

                .usernameParameter("id")

                .passwordParameter("password")

       

        // 로그아웃 처리

.and()

                .logout()

                .logoutUrl("/logout")

                .logoutSuccessUrl("/")

                .invalidateHttpSession(true)

                .deleteCookies("JSESSION_ID")

                .deleteCookies("remember-me")

        // 로그인 

            .and()

            .rememberMe()

            .tokenValiditySeconds(604800)

            .tokenRepository(persistentTokenRepository())

            // 예외처리(

            .and()

        .exceptionHandling()

        .accessDeniedPage("/member/accessDenied")

        // csrf 설정

            .and()

                .csrf().disable();

       

        

            

        

    /*

        http.authorizeRequests()

        .antMatchers("/login")

            .permitAll()

        .antMatchers("/**")

            .hasAnyRole("ADMIN", "USER")

            .hasAnyAuthority("RULE_ADMIN", "RULE_USER")

        .and()

            .formLogin()

            .loginPage("/login")

            .defaultSuccessUrl("/")

            .failureUrl("/login?error=true")

            .permitAll()

            

            .loginPage("/login")

            .usernameParameter("email")

            .passwordParameter("password")

            .successHandler(successHandler())      

            .failureHandler(failureHandler())

            .permitAll();

            

        .and()

            .logout()

            .logoutSuccessUrl("/login?logout=true")

            .invalidateHttpSession(true)

            .permitAll()

        .and()

            .csrf()

            .disable();

    */

        

    }

    

    // 로그아웃 Persistent_Logins에 관한 설정   (주석 해도 무방)...

    @Bean 

public PersistentTokenRepository persistentTokenRepository() {

JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();

DataSource usrDS = getDataSource();

db.setDataSource(usrDS);

return db;

}

    


    // DataSource 불러오기    (주석 해도 무방)

@Bean

public DataSource getDataSource() {

       // BasicDataSource dataSource = new BasicDataSource(); - Apache DBCP2

SqlMapSessionFactory factory = SqlMapSessionFactory.getInstance();

       return factory.getOracleDataSource(); // 오라클 적용함.

}

    

// 비밀번호 생성 - 암호(BCryptPasswordEncoder)

    @Bean

    public PasswordEncoder passwordEncoder() {

        return new BCryptPasswordEncoder();

        

    }

    

}


파일명: SecurityConfig.java


[첨부(Attachments)]

SecurityConfig.zip



비고: 주석 잘 쳐서 정리해서 빌드해보면, 내장 로그인 페이지를 볼 수 있다.
       - SecurityWebApplicationInitializer.java, SecurityConfig.java 두 개 파일의 힘이 얼마나 큰지 실감 해볼 수 있다.

         Spring-Framework 설정 전체를 제어해버린다고 해도 된다.




17. 로그인 인증 - Spring Security (CustomAuthenticationProvider.java)


이 코드 부분은 찾아보려고 해도 쉽게 나오지 않는다. 어려운 부분 중 하나이다.

공개하는 이유는 삽질을 적게 하라는 의미이다.


패키지 경로: com.springMVC.javaSecurity5.config


package com.springMVC.javaSecurity5.config;


import java.util.List;


import org.springframework.security.authentication.AuthenticationProvider;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

import org.springframework.security.core.Authentication;

import org.springframework.security.core.GrantedAuthority;

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

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

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import org.springframework.stereotype.Component;


import com.springMVC.javaSecurity5.service.CustomUserDetailsService;


@Component

public class CustomAuthenticationProvider implements AuthenticationProvider {

    

    private UserDetailsService userDeSer;

 

    @Override

    public Authentication authenticate(Authentication authentication) {

        

        String username = (String) authentication.getPrincipal();

        String password = (String) authentication.getCredentials();

        

        if ( username.equals("fail")) {

        System.out.println("(에러)아이디: 실패");

        return null;

        }

        

        // DB 정보 읽기

        userDeSer = new CustomUserDetailsService();

        UserDetails userDetail = userDeSer.loadUserByUsername(username);

        

        @SuppressWarnings("unchecked")

  List<GrantedAuthority> roles = (List<GrantedAuthority>) userDetail.getAuthorities();

        

        // 권한

        System.out.println("DB불러오기-권한:" + userDetail.getAuthorities());

        System.out.println("DB불러오기-비밀번호:" + userDetail.getPassword());

        System.out.println("roles:" + roles.get(0));

        

        if ( !matchPassword(password, userDetail.getPassword())) {

        System.out.println("(에러)비밀번호: 불일치" + password);

        return null;

        }

        

        UsernamePasswordAuthenticationToken result =

        new UsernamePasswordAuthenticationToken(username, password, roles);

        

        result.setDetails(userDetail);

        

        return result;

    }

 

    @Override

    public boolean supports(Class<?> authentication) {

        return true;

    }

    

    private boolean matchPassword(String loginPwd, String password) {

   

        BCryptPasswordEncoder secure = new BCryptPasswordEncoder();

        return secure.matches(loginPwd, password);

    }

 

}


파일명: CustomAuthenicationProvider.java


[첨부(Attachments)]

CustomAuthenticationProvider.zip


어디에 구체적으로 사용되는 부분인가?


사용하는 영역은 SecurityConfig.java에 http의 auth의 .authenicationProvider()에 사용된다.



그림 25. SecurityConfig.java


왜 이 코드를 사용하는지 소개해본다면,

"No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken"

이 문제가 디버그 오류창에 뜨는 것을 볼 수 있다.


기본 내장형 계정 생성 등으로 인증을 시도하면 xml방식에서는 처리해줬는데, java방식에서는 인증받지 못한다.


     /* 현재 - 임시


        auth.inMemoryAuthentication()

        .passwordEncoder(passwordEncoder)

        .withUser("user").password(passwordEncoder.encode("1234")).roles("RULE_USER")

        .and()

        .withUser("admin").password(passwordEncoder.encode("1234")).roles("RULE_USER", "RULE_ADMIN");

        

        */


[문제가 발생되는 기본형 - 코드]


이 코드로 작업하면, xml에서는 동작되었던 부분이 동작되질 않는다.


토큰 인증도 해결할 겸 "CustomAuthenicationProvider.java를 설계해서 문제를 해결한 것이다.




18. SQL (Factory) - SqlMapSessionFactory.java


나중에 CP(Connection Pool, 커넥션 풀)이라고 불리는 것으로 구현해봐도 좋을 듯 싶다.

iBatis를 남용해서 프로젝트에 기본마냥 소개하는 책들이 무척 많은데 기본은 순수한 DB를 사용하는 것부터 출발하는 것이다.


iBatis는 SQL 코드 개발 등에서 생산성이 좋아지는 도구 중 하나이지만, 필수 사항은 아니라고 본다.

차라리 필수 사항을 꼽아본다면, 커넥션 풀을 하나 추천해보고 싶다.


아무튼 커넥션 풀 주제가 아니기 때문에 생략한다.


패키지 경로: com.springMVC.javaSecurity5.db


package com.springMVC.javaSecurity5.db;


import java.io.IOException;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.Properties;


import javax.sql.DataSource;


import oracle.jdbc.pool.OracleDataSource;


public class SqlMapSessionFactory {

private static SqlMapSessionFactory factory = new SqlMapSessionFactory();


private SqlMapSessionFactory() {}


private final String driverName = "oracle.jdbc.driver.OracleDriver";

private final String dbUrl = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";

private final String userName = "{사용자계정명}";

private final String userPassword = "{비밀번호}";

public static SqlMapSessionFactory getInstance() {

return factory;

}


/*

*     public static DataSource getMySQLDataSource() {

        Properties props = new Properties();

        FileInputStream fis = null;

        MysqlDataSource mysqlDS = null;

        try {

            fis = new FileInputStream("db.properties");

            props.load(fis);

            mysqlDS = new MysqlDataSource();

            mysqlDS.setURL(props.getProperty("MYSQL_DB_URL"));

            mysqlDS.setUser(props.getProperty("MYSQL_DB_USERNAME"));

            mysqlDS.setPassword(props.getProperty("MYSQL_DB_PASSWORD"));

        } catch (IOException e) {

            e.printStackTrace();

        }

        return mysqlDS;

    }

    */

    public DataSource getOracleDataSource(){

        

    OracleDataSource oracleDS = null;

        

    try {

            oracleDS = new OracleDataSource();

            oracleDS.setURL(dbUrl);

            oracleDS.setUser(userName);

            oracleDS.setPassword(userPassword);

        } catch (SQLException e) {

            e.printStackTrace();

        }

        return oracleDS;

        

    }

public Connection connect() {


Connection conn = null;


try {

Class.forName(driverName);

conn = DriverManager.getConnection(dbUrl, userName, userPassword);

}

catch(Exception ex) {

System.out.println("오류 발생: " + ex);

}


return conn;


}


public void close(Connection conn, PreparedStatement ps, ResultSet rs) {


if ( rs != null ) {


try {

rs.close();

}

catch(Exception ex) {

System.out.println("오류 발생: " + ex);

}


close(conn, ps); // Recursive 구조 응용(재귀 함수)


} // end of if


}


public void close(Connection conn, PreparedStatement ps) {


if (ps != null ) {

try {

ps.close();

}

catch(Exception ex) {

System.out.println("오류 발생: " + ex);

}

} // end of if


if (conn != null ) {

try {

conn.close();

}

catch(Exception ex) {

System.out.println("오류 발생: " + ex);

}

} // end of if


}

}



파일명: SqlMapSessionFactory.java


[첨부(Attachments)]

SqlMapSessionFactory.zip



비고: 재사용이 가능한 형태로 설계하였다.



19. Model - CustomUserDetails.java


순수한 Model 형태는 아니고, 부분 개량하였다.

Spring-Security에서 제공하는 UserDetails(인터페이스)를 Model 클래스에 구현해야 한다.

List<role> 기능 문제 등으로 인해서 String authorities를 List<GrantedAuthority>로 변경하였다.


@Override 된 부분들이 UserDetails에 정의된 내용이다.


패키지 경로: com.springMVC.javaSecurity5.model


package com.springMVC.javaSecurity5.model;


import java.util.ArrayList;

import java.util.Collection;

import java.util.List;


import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.authority.SimpleGrantedAuthority;

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


public class CustomUserDetails implements UserDetails{


private static final long serialVersionUID = 1L;

private String username;

    private String password;

    // 개량함. (다중 권한 고려)

    private List<GrantedAuthority> authorities;

    private boolean enabled;

        

public String getUsername() {

return username;

}


public void setUsername(String username) {

this.username = username;

}


public String getPassword() {

return password;

}

public void setPassword(String password) {

this.password = password;

}

     public void setAuthority(String authority) {

// 권한 객체 생성

if ( authorities == null ) {

authorities = new ArrayList<GrantedAuthority>();

}

// 권한 추가

SimpleGrantedAuthority grantObj = new SimpleGrantedAuthority(authority);

authorities.add(grantObj);

     }


public boolean getEnabled() {

return enabled;

}

public void setEnabled(boolean enabled) {

this.enabled = enabled;

}


@Override

public Collection<? extends GrantedAuthority> getAuthorities() {


        return authorities;


}


@Override

public boolean isAccountNonExpired() {

// TODO Auto-generated method stub

return false;

}


@Override

public boolean isAccountNonLocked() {

// TODO Auto-generated method stub

return false;

}


@Override

public boolean isCredentialsNonExpired() {

// TODO Auto-generated method stub

return false;

}


@Override

public boolean isEnabled() {

// TODO Auto-generated method stub

return false;

}

    

}


파일명: CustomUserDetails.java


[첨부(Attachments)]

CustomUserDetails.zip





20. Service - CustomUserDetailsService.java


CustomUserDetailsService는 Spring-Security의 "UserDetailsService(인터페이스)"로 정의된 내용을 구현하는 것이다.

인터페이스의 영향도 있지만, DB를 실제로 불러올 때 사용자 관점에서 처리되는 부분이라고 본다.


package com.springMVC.javaSecurity5.service;


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

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


import com.springMVC.javaSecurity5.dao.SqlSessionTemplate;

import com.springMVC.javaSecurity5.model.CustomUserDetails;


public class CustomUserDetailsService implements UserDetailsService {

    

    private SqlSessionTemplate sqlSession = SqlSessionTemplate.getInstance();

 

    public CustomUserDetails getUserById(String username) {

        return sqlSession.selectOne("user.selectUserById", username);

    }

 

    public CustomUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {


    CustomUserDetails user = sqlSession.selectOne("null", username);

        

        if(user==null) {

            throw new UsernameNotFoundException(username);

        }

        return user;

    }

    

}


파일명: CustomUserDetailsService.java


[첨부(Attachments)]

CustomUserDetailsService.zip




21. DAO - SqlSessionTemplate.java


실제 DB를 구현하는 부분이다.


package com.springMVC.javaSecurity5.dao;


import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;


import com.springMVC.javaSecurity5.db.SqlMapSessionFactory;

import com.springMVC.javaSecurity5.model.CustomUserDetails;


public class SqlSessionTemplate {


private SqlSessionTemplate() {}

private static SqlSessionTemplate sqlTemplate;

    private static SqlMapSessionFactory session; 

    

    public static SqlSessionTemplate getInstance(){

   

        if(sqlTemplate == null){

        sqlTemplate = new SqlSessionTemplate();

            session = SqlMapSessionFactory.getInstance();

        }


        return sqlTemplate;

    }

    

// 추후 iBatis 고려

public CustomUserDetails selectOne(String id, String username) {

Connection conn = null;

    PreparedStatement pstmt = null;

    ResultSet rs = null;

   

    CustomUserDetails node = null;


    String sql = "select g1.username, g1.password, g2.authority, " + 

       "g1.enabled from comp_users g1, comp_authorities g2 where g1.username = g2.username " +

       "and g1.username = ?";


    System.out.println(sql);


    try {


    conn = session.connect();


    pstmt = conn.prepareStatement(sql);

    pstmt.setString(1, username);

   

    rs = pstmt.executeQuery();


    while ( rs.next() ) {

   

    // 데이터가 존재할 때, 노드 생성

    node = new CustomUserDetails();

    node.setUsername(rs.getNString(1));

    node.setPassword(rs.getNString(2));

    node.setAuthority(rs.getNString(3));

    System.out.println("rs:" + rs.getNString(3));

    node.setEnabled(rs.getBoolean(4));

    }


    }catch(Exception ex) {

    System.out.println("오류 발생: " + ex);

    }

    finally {

    session.close(conn, pstmt, rs);

    }


    return node;

}

}



파일명: SqlSessionTemplate.java


[첨부(Attachments)]

SqlSessionTemplate.zip



* 3부에서는 View에 대해서 구현하는 방법을 소개하겠다.


3부에서는 "jsp 파일" 등 사용자 인터페이스 화면에 대해서 소개하겠다.


1. [Spring-Framework] 17. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-Java) (3), 2020-09-27

https://yyman.tistory.com/1424


반응형
728x90
300x250

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


이번에 소개할 프로젝트는 13, 14, 15번 게시글을 Java 방식으로 구현한 프로젝트이다.

동일하게 진행할 것이다. 실질적으로 제대로 소개하고 있는 책이나 게시글은 찾아보질 못했다.

많은 오류가 있었다는 이야기이다.


순수한 Spring-Framework에서도 Spring Security를 원만히 잘 사용할 수 있도록 하는 게 글의 핵심이다.

이번 게시글도 양이 많아서 나눠서 작성하였으니 잘 따라해보면 도움이 될 것으로 보인다.



[작업환경]


[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/)



[이전 글 주제]

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

https://yyman.tistory.com/1419

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

https://yyman.tistory.com/1420

3. [Spring-Framework] 13. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-XML) (3)  // 자동 로그인 유지, 유지 세션

https://yyman.tistory.com/1421




1. XML 기반 프로젝트 vs 자바 기반 프로젝트


이번에 소개할 주제는 자바 기반 프로젝트이다.

이전 게시물과 지금 구현할 게시물의 차이점에 대해서 간단하게 소개하겠다.


 


그림 1. XML 방식


 


그림 2. 자바 방식(Java)




(특징)
- Spring 폴더가 있으며, xml 파일이 존재함.

- web.xml 파일이 있음.

- 기본 프로젝트 pom.xml을 별도로 설정하지 않아도 됨.

(특징)
- Spring 폴더가 없음. (있으면 충돌나서 실행이 안 됨.)

- web.xml 파일이 없음. 

- 기본 프로젝트 생성 후 pom.xml을 별도로 설정해야 함.

(구현 난이도) 무난함.

(셋팅 값 및 지정된 변수 등을 잘 사용하는 것이 매우 중요함)


- 적당한 지식이면 무난할 것으로 보임.

(구현 난이도) 머리를 조금 사용해야 함.
- 초기 구축을 놓고 보면,
  코드가 늘어나고 조금 복잡할 수 있음.
  (쉽지만 않다. API도 읽어내야 하고 많은 실험이 요구됨.)


- 많은 프로그래밍 지식이 다소 필요함.

사용해보면, 장/단점이 있음.

-> 간단하게 관리하는 면에서는 편할 수도 있음.

   (xml)

-> 튜닝에 있어서 제약이 있을 수도 있음.

......


[이전 - XML 기반 게시글]

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

https://yyman.tistory.com/1419


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

https://yyman.tistory.com/1420


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

https://yyman.tistory.com/1421



2. 결과(1) - (Result)


출력 결과는 이전 프로젝트(13, 14, 15번)과 동일하게 구현 하였다.



그림 3. 결과(자바 버전) - 메인



그림 4. 결과(자바 버전) - 로그인 폼


그림 5. 결과(자바 버전) - 로그인 실패



그림 6. 결과(자바 버전) - 로그인 후



그림 7. 결과(자바 버전) - 비밀번호 출력의 예(암호화 패키지) - 업데이트 전



그림 8. 결과(자바 버전) - 로그인 후 모습 - 업데이트 전




그림 9. 결과(자바 버전) - 권한이 없는 계정이 특정 페이지를 접속할 때 모습(1) // 순정 (업데이트 전)



그림 9-1. 결과(자바 버전) - 권한이 없는 계정이 특정 페이지를 접속할 때 모습(2) // 커스텀 (업데이트 완료함)



그림 9-2. 결과(자바 버전) - 권한이 있는 계정의 메뉴 모습 // 커스텀 (업데이트 함)



그림 9-3. 결과(자바 버전) - 권한이 없는 계정의 메뉴 모습 // 커스텀 (업데이트 함)






3. 결과(2) - 프로젝트 구성


프로젝트 구성에 대한 것이다.

상당히 양이 많다. 초기 셋팅만 잘 해주면, 나머지는 순조로울 것으로 보인다.



그림 10, 11. 프로젝트 구성


다소 구현할 양이 많다는 것을 확인할 수 있다.


완성된 프로젝트 구성에 대해서 살펴보는 것은 무엇을 진행할 것인지 예상할 수 있는 방법 중 하나라고 주장해본다.



4. 데이터베이스 설계


크게 데이터베이스는 공식적으로 설계하라고 제시하는 규격 테이블 1개와 사용자 정의 테이블 5개로 총 6개의 테이블을 작성해야 한다.
아마 이런 부분은 시중 책이나 강의 등에서는 다뤄보지 못한 부분일 수도 있다.
권한이 있는 계정을 그룹별 권한, 페이지별 권한으로 체계화시킨 설계도이다.

Spring Security에서 요구하는 기능을 기본적으로 담은 설계이니 활용하면 참고하면 도움이 될 것으로 보인다.



그림 12. ER-D


테이블을 작성하는 데, 순번을 대략적으로 잡아본 이유는 외래키, 참조키 등에 기준이 되는 테이블을 먼저 생성하고 작업하는 것이 좋기 때문이다.

편집해서 외래키, 참조키 등 지정해줘도 무방하다. 단, 무결성이나 각종 조건 등 때문에 데이터가 존재하지 않은 상태에서 진행해야 한다.



그림 13. 테이블: COMP_GROUP



그림 14. 테이블: COMP_USERS



그림 15. 테이블: COMP_GROUP_AUTHORITIES



그림 16. 테이블: COMP_GROUP_MEMBERS



그림 17. 테이블: COMP_AUTHORITIES




그림 18. 테이블: PERSISTENT_LOGINS




5. 테이블 - SQL(Create table - DML)


테이블 작성에 관한 사항이다.


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

);


CREATE TABLE persistent_logins (

username VARCHAR(64) NOT NULL,

series VARCHAR(64) PRIMARY KEY,

token VARCHAR(64) NOT NULL,

last_used TIMESTAMP 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



비고: 꼭 오라클만 되는 것은 아니고, 살짝 튜닝하면 MySQL 등에서도 사용할 수 있는 형태로 작성하였다.

(SQL 명령어가 일부 데이터베이스에서는 차이가 있을 수도 있음. 특정 DB의 함수 등)



6. 프로젝트 - 신규 생성하기


Spring-Legacy-Project를 생성한다.



그림 19. 테이블: Spring MVC Project 생성하기


Spring MVC Project를 선택한다.

Project Name의 항목을 입력한다.

Next를 누른다.




그림 20. 테이블: Spring MVC Project 생성하기


top-level-package를 입력한 후 Finish를 누른다.




7. 프로젝트 - pom.xml 수정작업


양이 많긴 하지만, 작성해본다.


<?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.springMVC</groupId>

<artifactId>javaSecurity5</artifactId>

<name>SpringSecurity5-Java</name>

<packaging>war</packaging>

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

<properties>

<!-- web.xml 사용 안함 표기 -->

<failOnMissingWebXml>false</failOnMissingWebXml>

<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 -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

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

<exclusions>

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

<exclusion>

<groupId>commons-logging</groupId>

<artifactId>commons-logging</artifactId>

</exclusion>

</exclusions>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-webmvc</artifactId>

<version>${org.springframework-version}</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>

<!-- Servlet -->

<!-- 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>

<!-- Test -->

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.7</version>

<scope>test</scope>

</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>

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

<dependency>

    <groupId>javax.validation</groupId>

    <artifactId>validation-api</artifactId>

    <version>2.0.1.Final</version>

</dependency>


<dependency>

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

<artifactId>ojdbc8</artifactId>

<version>19.7.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>




- oracle jdbc를 못 찾는 사람들을 위해서, oracle jdbc라고 검색하거나 ojdbc8.jar 파일을 구해서 사용하면 된다.
- Oracle Databases를 설치해서 사용중인 경우에는 아래처럼 하면 사용할 수 있다.


그림 21. pom.xml 파일 선택 후 마우스 오른쪽 버튼 메뉴 모습


pom.xml을 선택한다.

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

maven-> Add Dependency를 클릭한다.



그림 22. Add Dependency에서 Oracle 검색하기


oracle을 검색한다.
com.oracle.database.jdbc  | ojdbc8을 선택한 후 OK를 누른다.






8. 프로젝트 - web.xml, spring 폴더 제거 작업


파일: /src/main/web-app/WEB-INF/web.xml   (삭제할 것)

폴더: /src/main/web-app/WEB-INF/spring      (삭제할 것)




9. 프로젝트 - Build Path, Project factes 버전 바꾸기


초기 Spring MVC Project를 생성하면 1.6버전으로 설정되어 있다. 14버전으로 바꿔주겠다.



그림 23. Properties 속성 바꿔주기(1) / Build-Path - JavaSE 14로


JRE System Library [JavaSE-14]로 Edit 버튼을 통해서 수정해준다.



그림 23. Properties 속성 바꿔주기(2) / Project Factes - JavaSE 14로


Java 버전을 1.6에서 14로 바꿔준다.




10. Controller - web.xml 제거 (핵심작업)


web.xml 제거한 Spring MVC라는 주제로 접근하여 작성하려고 한다.

지금 작업, pom.xml 수정 작업을 중심으로 HomeController.java(Servlet) 파일을 구성해서 view파일 jsp를 만들면 web.xml없이 동작하는 화면을 

볼 수 있다.


package com.springMVC.javaSecurity5.config;


import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;


public class SpringMvcAnnotation extends AbstractAnnotationConfigDispatcherServletInitializer {


  @Override

  protected Class<?>[] getRootConfigClasses() {

    return null;

  }


// bean 설정과 spring container 설정을 위한 Config 클래스를 등록한다.

// Config 클래스는 web.xml의 dispatcher servlet 초기화에 사용된 xml과 같은 기능을 한다.

  @Override

  protected Class<?>[] getServletConfigClasses() {

    return new Class[] { WebConfig.class };

  }


// web.xml의 servlet mapping 부분을 대체한다.

  @Override

  protected String[] getServletMappings() {

    return new String[] { "/" };

  }


}


파일명: SpringMvcAnnotation.java


[첨부(Attachments)]

SpringMvcAnnotation.zip




이 파일을 시작점으로 한다.


package com.springMVC.javaSecurity5.config;


import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.ComponentScan;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;

import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;

import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import org.springframework.web.servlet.view.InternalResourceViewResolver;

 

@EnableWebMvc // <mvc:annotation-driven>에 해당.

// @ComponentScan(basePackages = {"com.figo.web"})  // <context:component-scan base-package="”com.figo.web”/">에 해당됨.

@ComponentScan("com.springMVC.javaSecurity5")

@Configuration

public class WebConfig extends WebMvcConfigurerAdapter {

 

    // <resources mapping="/resources/**" location="/resources/">에 해당됨.

    @Override

    public void addResourceHandlers(ResourceHandlerRegistry registry) {

        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/").setCachePeriod(31556926);

    }

 

    // <mvc:default-servlet-handler>에 해당됨.

    @Override

    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {

        configurer.enable();

    }   

     

    // web.xml에서 봤던 내용들임.

    @Bean

    public InternalResourceViewResolver getInternalResourceViewResolver() {

        InternalResourceViewResolver resolver = new InternalResourceViewResolver();

        resolver.setPrefix("/WEB-INF/views/");

        resolver.setSuffix(".jsp");

        return resolver;

    }

    

    /*

    @Override

    public void addViewControllers(ViewControllerRegistry registry) {

   

        // registry.addViewController("/web").setViewName("home");

        registry.addViewController("/").setViewName("home");

        

    }

    */

 

}


파일명: WebConfig.java


[첨부(Attachments)]

WebConfig.zip



참고: HomeController의 초기내용으로 두고 서버 상에서 실행시켜봤다면, 동작했을 것으로 보인다.
(10번까지 잘 따라왔으면 화면 출력을 볼 수 있음.)



11. Controller - HomeController.java


HomeController에 관한 내용이다. 자세히 보면, "13, 14, 15번"글하고 거의 동일하다는 것을 알 수 있다.

쉽게 이야기하면, web.xml 파일부터 xml로 구성된 환경설정 파일을 프로젝트 내에서 제거한 것이다.

그러니 Controller 등은 동일할 수 밖에 없다고 본다.



package com.springMVC.javaSecurity5.controller;


import java.security.Principal;

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.bcrypt.BCryptPasswordEncoder;

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;


//@CrossOrigin(origins = "*", allowedHeaders = "*")

@Controller

public class HomeController {

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


@RequestMapping("/")

public String home(Locale locale, Model model, Principal principal) {


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);

String username = null;

// Principal 예제

if (principal != null) {

username = principal.getName();

System.out.println("타입정보 : " + principal.getClass());

System.out.println("ID정보 : " + principal.getName());

}

model.addAttribute("username", username);

model.addAttribute("serverTime", formattedDate );


return "home";

}

@RequestMapping("/admin")

public String admin(Locale locale, Model model) {

return "admin";

}


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

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


// 1. xml 방식에서 java 방식으로 전환

// web.xml 파일 제거로 인한 사용 불가(ServletConfig sc 연결됨)

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

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


PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

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

        String encode = passwordEncoder.encode(password);


        model.addAttribute("encode", encode);

        System.out.println(encode);

        

        return "home";

        

}


}


파일명: HomeController.java


[첨부(Attachments)]

HomeController.zip





12. Controller - AdminController.java


관리자 페이지에 관한 내용이다.


package com.springMVC.javaSecurity5.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



13. Controller - MemberController.java


MemberController.java에 관한 내용이다.


package com.springMVC.javaSecurity5.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")

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




* 2부에서는


2부에서는 Spring-Security 설정에 대해서 집중적으로 다뤄보도록 하겠다.

조금 어려울 수도 있으니 마음을 편안하게 하고 따라해보면 좋겠다.


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

https://yyman.tistory.com/1423


반응형
728x90
300x250

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


이번에 소개할 내용은 1, 2부에 이어서 "자동 로그인"과 "로그아웃 시 쿠키 삭제"에 대해서 소개하겠다.


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

https://yyman.tistory.com/1419


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

https://yyman.tistory.com/1420



1. 결과(Result)

로그인 상태 유지에 관한 기술이다.

이 기능은 쉽게 말하면, "로그인 기억하기", "로그인 상태 유지"를 체크하면 일정 시간 동안 다시 로그인을 하지 않아도 되는 기능을 말한다.




그림 1. 로그인 상태 유지 - 기술 페이지




그림 2. 로그인 상태 유지 - 기술 페이지 (로그인 후)



그림 3. 로그인 상태 유지 - 데이터베이스



2. 데이터베이스 - 영구 토큰(Persistent Token)


설계를 따로 해야 하는 건 아니고, 스프링 시큐리티의 공식 문서에 정의가 되어 있는 부분이다.


https://docs.spring.io/spring-security/site/docs/current/reference/html5/#remember-me-persistent-token



그림 4. Spring-Security References 사이트에 정의된 Persistent Login(Remember-Me) 스키마


자동 로그인에 대한 스키마가 기술되어 있다. 



그림 5. Spring-Security References 사이트에 기술된 Persistent Token Approach


사용 방법에 대해서 기술되어 있다.



3. 데이터베이스 - SQL 코드


CREATE TABLE persistent_logins (

username VARCHAR(64) NOT NULL,

series VARCHAR(64) PRIMARY KEY,

token VARCHAR(64) NOT NULL,

last_used TIMESTAMP NOT NULL

);


이 코드를 DBMS로 질의하면 기본적인 준비는 끝난다고 보면 된다.



그림 6. Oracle SQL Developer - 질의 창



그림 7. PERSISTENT_LOGINS 테이블



그림 8. PERSISTENT_LOGINS 테이블 ER-D




3. security-content.xml - 수정


경로: /src/main/webapp/WEB-INF/spring/security-context.xml


아래의 그림처럼 수정해주면 된다.



그림 9. 쿠키 제거 기능 주석 부분과 자동 로그인 주석 부분 - 수정 및 추가 할 것


<!-- 쿠키 제거 기능 개선 -->

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

invalidate-session="true" delete-cookies="remember-me,JSESSION_ID" />


<!-- 자동 로그인(2020-09-26 // 추가 작업 -->

<remember-me data-source-ref="dataSource" token-validity-seconds="604800" />


dataSource의 정의에 대해서 다시 한번 기술하도록 하겠다.

건강한 비판을 해보면, 일부 시중 책을 보면, 전혀 DataSource 내용은 누락해서 나온 책들도 꽤 있다.

삽질하는 시간이 줄어들고 잠도 푹 자는 개발자가 되길 희망한다.


(중략)

<!-- 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>


(중략)


파일명: security-context.xml


[첨부(Attachments)]

security-context-modify.zip




4. View = src/main/webapp/WEB-INF/views/member/loginForm.jsp


아래처럼 화면을 설계해주면 된다.



그림 10. 로그인 상태 유지 - 기술 페이지(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" %>

<!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>

    <p>

        <label for="remember-me">Remember-me(로그인 상태 유지)</label>

        <input type="checkbox" id="remember-me" name="remember-me"/>

    </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-modify.zip



* 맺음글(Conclusion)


간단하게 "자동-로그인(remember-me)" 기능에 대해서 Spring-Framework 5.x, Spring-Security 5.4를 기반으로 살펴보았다.


다음 글은 "자동 로그인 또는 로그인 상태 유지"라는 주제로 글을 작성하였으니 참고하면 좋겠다.


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

https://yyman.tistory.com/1422


반응형
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

[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


반응형

+ Recent posts