데이터베이스에 비밀번호 저장 시, 개인정보 보호법에 따라 반드시 SHA2-256 해쉬 암호화된 결과로 저장해야 합니다.

암호화 알고리즘을 사용하기 위해 Spring-Security Sub-Project가 제공하는 의존성을 추가해야 합니다.

 

[아파치 메이븐/의존성] - 라이브러리

 

 


 

 

비밀번호의 암호화 저장도 중요한만큼, 입력된 평문암호와 DB에 저장되어 있는 해쉬 알고리즘의 비교를 통해 암호 일치여부를 검증하는 방법도 알아야 합니다.

 

해쉬 알고리즘은 원본 데이터를 불가역적인 변환을 통해 암호화된 결과(Hash)를 얻습니다.

즉, 해쉬 알고리즘은 복원이 불가능한 단뱡향 암호화 알고리즘 중 하나라고 할 수 있습니다.

 

가장 많이  사용하는 비밀번호 암호화 알고리즘은 'BCrypt'라는 알고리즘이며 스프링 시큐리티는 BCryptPasswordEncoder 클래스를 제공합니다.

 

 

평문 암호(Plain Text)와 해쉬 암호(Cipher Text)를 콘솔에 로그로 출력하였습니다.
@Log4j2
@NoArgsConstructor

@TestInstance(Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class PasswordEncoderTests {
	
//	@Disabled
    @Tag("fast")
    @Test
    @Order(1)
    @DisplayName("testBCryptPasswordEncoder")
    @Timeout(1L)
    void testBCryptPasswordEncoder() {
        log.trace("testBCryptPasswordEncoder() invoked.");
		

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        log.info("\t encoder({}) invoked.", encoder);
		
        String plainText = "0123456789";
        String cipherText = encoder.encode(plainText);
        
        log.info("\t cipherContext: {}", cipherText);
		
		
	} // testPasswordEncoder
	
} // end class

암호화된 평문 암호(해쉬 암호)

 

 


 

 

평문 암호와 해쉬 암호를 assertj를 활용하여 비교합니다.
@Log4j2
@NoArgsConstructor

@TestInstance(Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class PasswordEncoderTests {
	
    @Tag("fast")
    @Test
    @Order(1)
    @DisplayName("testBCryptPasswordEncoder")
    @Timeout(1L)
    void testBCryptPasswordMatches() {
        log.trace("testBCryptPasswordMatches() invoked.");

		
        String plainText = "0123456789";
		
        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        // BCryptPasswordEncoder encoder2 = new BCryptPasswordEncoder();
        String cipherText = encoder.encode(plainText);
		
        boolean isMatched = encoder.matches(plainText, cipherText);
		
        assertThat(isMatched).isEqualTo(true);
        log.info("\t isMatched: {}", isMatched);
		
    } // testBCryptPasswordMatches
	
} // end class

평문 암호와 해쉬 암호를 비교 검증

 

 

BCrypt는 동일한 평문 암호를 입력하더라도 매번 다른 해시 암호를 도출합니다.

 

 

 

'Java > Design Patterns' 카테고리의 다른 글

프록시 패턴  (0) 2023.08.13
어댑터 패턴  (0) 2023.08.13

 

 

 

FrontController, Command 디자인 패턴에 대한 발행글입니다.

 


 

 

 

FrontController

FrontController는 클라이언트의 요청을 처리하기 위한 최초 진입점을 정의하는 디자인패턴입니다.

모든 사용자의 요청을 한 곳에 집중시켜 중복된 코드를 제거하고 요청을 분산시키는 방식으로 동작합니다.

 

FrontController 패턴을 적용한 서블릿에서 고려해야 되는 사항은 클라이언트가 어떤 동작을 요청했는지 식별할 수 있어야 합니다.

 

클라이언트 요청 식별 매커니즘
http://서버IP번호:포트번호/context명/식별값

 

클라이언트는 명시적으로 URL에 식별값을 추가하고 요청하고, 서블릿은 '식별값'을 비교해서 어떤 요청인지를 구별합니다.

여기서 '식별값'은 임의의 문자열값입니다. 일반적으로 "XXX.do" 같은 방식으로 지정합니다.

 

 

 

Command

Command는 클라이언트의 요청을 클래스 객체로 처리하는 것을 의미합니다.객체의 형태로 사용하면 서로 다른 클라이언트의 요청값을 필요에 의해서 저장하거나 취소가 가능하며요청을 처리할 작업을 일반화시켜 요청의 종류와 무관하게 프로그램 작성이 가능하게 구현할 수 있습니다.

 

구현방법은 Command 패턴을 적용한 Service 클래스를 추가하며 이름은 "XXXXService" 처럼 짓습니다.
EX) InsertService  |  DeleteService  |  UpdateService  |  SelectService

 

Command 패턴을 적용한Service 클래스는 작업 수행을 요청하는 객체인 서블릿실제 작업을 수행하는

객체

분리시켜주기 때문에 관계를 느슨하게 할 수 있습니다.

 

 

 

 

 

 

 

'웹 표준 > Servlet' 카테고리의 다른 글

Cookie  (0) 2023.09.15
Session Tracking  (0) 2023.09.14
Request  (0) 2023.09.13
DTO, DAO, VO  (0) 2023.09.12
Connection Pool  (0) 2023.09.08

[ 최종 수정일 : 2023년 09월 20일 ]

 

 

 

 

요청 포워딩을 구현하는 방법은 두 가지가 있습니다.

  • RequestDispatcher 클래스를 활용한 forwarding
  • RequestDispatcher 클래스를 활용한 include
  • HttpServletResponse 클래스를 활용한 redirect

 


 

Request Forwarding - (RequestDispatcher)


클라이언트가 서블릿에 요청을 전송하면 request 객체가 자동으로 생성됩니다.

이때 request 객체를 활용해서 응답을 처리하는 웹 컴포넌트에게 요청을 위임하는 것이 요청 포워딩입니다.

 

RequestDispatcher Request Forwaridng의 기본적인 Syntax입니다.
RequestDistatcher dis = request.getRequestDispatcher(target);
dis.forward( request, response );

 

요청 포워딩을 수행한 웹 컴포넌트의 모든 응답은 모두 자동 취소됩니다.

만약 워딩 수행 전, 버퍼가 플러싱되면 요청 포워딩은 수행되지 않습니다.

 

 

 

 

Request Include - (RequestDispatcher)


웹 페이지 내에서 다른 리소스를 현재 페이지에 포함시키는 방법입니다.

일반적으로 스크립팅 언어(JSP, PHP 등)을 활용하여 웹 페이지의 일부분을 동적으로 구현할 때 사용합니다.

 

RequestDispatcher Request Include의 기본적인 Syntax입니다.
RequestDistatcher dis = request.getRequestDispatcher(target);
dis.include( request, response );

 

쉽게 말해, A 서블릿의 로직 수행 결과를 B 서블릿으로 요청을 넘길 때, 로직 수행 결과를 포함해서 넘겨주게 됩니다.

 

 

 

Request Redirection - (HttpServletResponse)


요청 포워딩과 비슷하게 다른 컴포넌트에게 요청을 위임하는 방식입니다.

요청 포워딩과 차이점은 응답을 먼저 하고 요청을 위임하기 때문에, 동일한 request가 아니라 새로운 request 객체가 생성됩니다.따라서 웹 브라우저의 URL 값이 변경되고 속성에 설정된 값을 가져오지 못합니다.

그래서 삭제 단위가 브라우저인 Session Scope에 저장해야 합니다.

 

Request Redirect의 기본적인 Syntax입니다.
HttpSession session = req.getSession();
session.sendRedirect("/target");

 

즉, 클라이언트는 서블릿에게 요청을 전송하고 응답을 받으면 JSP에 재요청을 하고 응답을 받는 원리로 수행됩니다.

 

 

 

 

 

 

 

 

 

'웹 표준 > Servlet' 카테고리의 다른 글

Session Tracking  (0) 2023.09.14
FrontController, Command Patterns  (0) 2023.09.13
DTO, DAO, VO  (0) 2023.09.12
Connection Pool  (0) 2023.09.08
톰캣 사양 별 DTD  (0) 2023.09.05

 

 

 

보안(Security)의 핵심은 접근 제어(Access Control)입니다.

 

 


 

 

보안에 대한 접근 제한의 대상은 바로 자원(Resource)입니다.

 

접근 제어의 수단은 인증(Authentication)과 인가(Authorization)이 있습니다.

 

 

 


 

 

인증(Authentication)

현실에서 인증은 그 사람이 누구인지 확인하는 과정을 의미합니다.

웹에서 이러한 인증을 구현한다면 다음과 같습니다.

 

 1. 아이디/ 암호 기반의 인증

 2. Access Token 기반의 인증(GitHub)

 3. OTP, 생체 인식 기반(홍채, 지문 등)

 

 

 

인가(Authorization)

인가는 인증을 전제로 합니다.

접근하려는 자원에 대한 접근 권한을 확인하고 인증이 완료된 자원에게 접근을 허가합니다.

 

 

 

 

 

 

 

 

 

DTO (Data Transfer Object), DAO (Data Access Object), VO (Value Object)는 3계층 아키텍처에서 사용됩니다.

 

 


 

 

1. 3-Tire Architecture

 웹 애플리케이션을 설계하고 개발할 때, 구조적 명료성과 유지보수를 위해 일반적으로 3계층 아키텍처(3-Tire Architecture)를 사용합니다. 3계층 아키텍처에서 두 가지 패턴은 데이터의 전송 및 캡슐화를 담당하게 됩니다.

 

 다음은 3계층 아키텍처의 구성요소입니다.

 

  • 프레젠테이션 계층(Presentation Layer)
    이 계층에서는 사용자의 입력을 받아 처리하고 사용자에게 결과를 표시합니다.

  • 비즈니스 계층(Business Logic Layer)
    애플리케이션의 다양한 비즈니스 로직이 구현되어 있는 계층입니다.

  • 영속성 계층(Persistence Layer)
    데이터베이스에 접근하여 데이터의 CRUD 연산을 처리하는 계층입니다.

 

위 3계층 아키텍처를 기반으로 DTO와 DAO를 알아보겠습니다.

 

 


2. DTO (Data Transfer Object)

 DTO는 일반적으로 Presentation, Business Layer에서 활용됩니다.

클라이언트의 요청을 DTO에 저장하고 DAO에게 DTO를 인자값으로 주면서 데이터 조작을 요청합니다.

이 때, DTO는 서블릿 공유 컨테이너의 Rquest Scope에 위치하므로 요청에 대한 응답을 전송하는 즉시 DTO는 파괴됩니다.

 

 

 

3. DAO (Data Access Object)

 DAO는 DTO를 기반으로 데이터베이스에 접근하여 데이터를 CRUD하고, 그 조작 결과를 VO(Value Object)로 반환합니다.

 

 

 

4. VO (Value Object)

VO는 Business Layer에서 DAO에게 요청한 값을 받아올 때 사용하며,  DAO는 데이터 조작을 통해 얻은 데이터를 반환할 때 VO를 통해 반환합니다.  즉, VO의 필드들은 DQL문장들의 결과셋으로 이루어진다고 할 수 있습니다.

 

 

 

 

'웹 표준 > Servlet' 카테고리의 다른 글

Session Tracking  (0) 2023.09.14
FrontController, Command Patterns  (0) 2023.09.13
Request  (0) 2023.09.13
Connection Pool  (0) 2023.09.08
톰캣 사양 별 DTD  (0) 2023.09.05

 

 

 

[2023_08_09] 해당 발행글은 Oracle을 기준으로 작성되었습니다.

 

 

 

 

Connection Pool은 연결 객체를 생성하고 영구소멸하는 것이 아니라 대여와 반납의 개념으로 이루어집니다.

 

 

 


 

 

Connection Pool은 DataSource Interface의 규격대로 DataSource를 제공합니다.

이러한 DataSource는 Tomcat같은 WAS 내부에서 생성하게 됩니다.

 

WAS가 DataSource를 생성하는 방법은 두 가지가 있습니다.

 

  • Global DataSource
  • 애플리케이션 별 Local DataSource

 

Global DataSource는 표준에 따라 생성 후 제공하지만, 각 벤더의 WAS마다 설정방법이 모두 다르기 때문에

WAS가 바뀌어도 소스를 변경할 필요가 없는 Local DataSource를 사용하는 것을 권장합니다.

 

 

다음 경로에 프로젝트 내에 META-INF 폴더를 만들고 context.xml을 생성합니다.

 

 


 

 

기초적인 context.xml Syntax입니다.

 

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

<Context>

    <!-- <Resource 
        name="jdbc/OracleLocalDB" 
        auth="Container"
        type="javax.sql.DataSource"
        username="이름작성"
        password="비밀번호작성"
        driverClassName="oracle.jdbc.OracleDriver"
        url="jdbc:oracle:thin:@db이름?TNS_ADMIN=db설치주소"
        maxTotal="8"
        maxIdle="2"
    /> -->

    <Resource 
        name="jdbc/OracleCloudATP" 
        auth="Container"
        type="javax.sql.DataSource"
        username="이름작성"
        password="비밀번호작성"
        driverClassName="oracle.jdbc.OracleDriver"
        url="jdbc:oracle:thin:@tns_name?TNS_ADMIN=클라우드지갑주소"
        maxTotal="8"
        maxIdle="2"
    />

<!--     <Resource 
        name="jdbc/OracleLocalDBWithDriverSpy" 
        auth="Container"
        type="javax.sql.DataSource"
        username="이름작성"
        password="비밀번호작성"
        driverClassName="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"
        url="jdbc:log4jdbc:oracle:thin:@db이름?TNS_ADMIN=db설치주소"
        maxTotal="8"
        maxIdle="2"
    /> -->

    <Resource 
        name="jdbc/OracleCloudATPWithDriverSpy" 
        auth="Container"
        type="javax.sql.DataSource"
        username="이름작성"
        password="비밀번호작성"
        driverClassName="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"
        url="jdbc:log4jdbc:oracle:thin:@tns_name?TNS_ADMIN=클라우드지갑주소"
        maxTotal="8"
        maxIdle="2"
    />

</Context>

 

주석된 부분은 LocalDB이므로 필요하신 분은 주석 해제 후 개발환경에 맞게 작성하시면 됩니다.

 

 


 

 

위의 코드를 참고해서 Connection Pool을 두개를 사용하겠습니다.이제 Eclipse에서 Tomcat Server를 실행하고 Servlet을 작성합니다.

 

Servlet을 작성할 때, LifeCycleMethods를 모두 생성합니다.

 

 

다음 코드를 작성합니다.

 

@Log4j2
@NoArgsConstructor

@WebServlet("/UsingDataSource")
public class UsingDataSourceServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
    // 의존성 주입
    @Resource(name="jdbc/OracleCloudATP")
    private DataSource dataSource;


    public void init(ServletConfig config) throws ServletException {
        log.trace("init({}) invoked.", config);
		
        Objects.requireNonNull(dataSource);
        log.info("\t + dataSource: " + dataSource);
    } // init()


    public void destroy() {
        log.trace("destroy() invoked.");
    } // destroy()


    protected void service(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {
        log.trace("service(req, res) invoked.");
		
        try {
            Connection conn = this.dataSource.getConnection();
			
            try(conn){
                Objects.requireNonNull(conn);
                log.info("\t + conn: {}", conn);
            } // try
			
        } catch (SQLException e) {
            throw new IOException(e);
        } // try-catch
		
    } // service()

} // end class

 

@Resource 어노테이션은 지정된 자원의 레퍼런스를 획득 후 DataSource의 필드에 의존성 주입을 하게 됩니다.

이제 서버에 작성된 Servlet을 올릴 때마다, Connection Pool에 Data Resource를 계속해서 추가하게 됩니다.

 

 

 

'웹 표준 > Servlet' 카테고리의 다른 글

Session Tracking  (0) 2023.09.14
FrontController, Command Patterns  (0) 2023.09.13
Request  (0) 2023.09.13
DTO, DAO, VO  (0) 2023.09.12
톰캣 사양 별 DTD  (0) 2023.09.05

 

 

 

'Record'는 변경할 수 없는(immutable) 데이터 개체를 쉽게 만들 수 있도록 하는 자바의 새로운 유형의 클래스입니다.

Java14 이후 버전부터 지원합니다.

 

 


 

 

Record

 

데이터 모델 POJO(Plain Old Java Object)에서 반복적인 상용구 코드를 줄이기 위해 'Record'가 도입되었습니다.

즉, 'Record'는 반복적인 개발을 단순화하고 효율성을 개선해서 오류의 위험을 최소화합니다.

 

Record는 파라미터에 접근제한자, final, 생성자 사용이 불가능합니다.

 

 

다음은 Record의 Syntax입니다.

 

record className() {

} // 클래스명

 

 

이해를 돕기 위해 간단한 코드를 작성하였습니다.

 

@Log4j2
public class Record_2 {

	
    public static void main(String[] args) {
        Person person = new Person("Leees", 23);
        log.info("name: {}, age: {}", person.name(), person.age());
        log.info("toString: {}", person.toString());

    } // main

} // end class


// @AllArgsContructor
// @NoArgsConstructor
// 파라미터에 final, 접근제한자 사용불가
record final Person(String name, int age) {;;}

 

위의 코드에서도 알 수 있듯이, 'Record'는 필드 선언과 생성자 선언을 하나로 결합한 것으로 볼 수 있습니다.

 

 

Record

 

그런데 로그를 출력할 때, 기존의 Getter가 아닌 단순 필드명으로 필드의 값을 받아오는 것을 확인할 수 있습니다.

이러한 방식을 Fluent API라고 합니다.

 

 


 

 

Fluent

 

일반적으로 필드의 값을 받아올 때, get + 필드()를 사용합니다.

그런데 Getter 메서드는 두 가지의 종류가 있습니다.

 

  1. get + 필드명()
  2. 필드명()

2번처럼 선언하는 Getter 방식을 Fluent API라고 합니다.

Lombok의 @Values 어노테이션은 다양한 기능을 제공하는데 대표적으로 Getter가 있습니다.

 

여기서 @Values가 제공하는 Getter는 일반적인 get + 필드명()으로 제공되는데, Fluent API 방식으로 변경할 수 있습니다.

 

@Log4j2
public class Record_2 {

	
    public static void main(String[] args) {
        Humun humun = new Humun("홍길동", 24);
		
    } // main

} // end class


// @Accessors 어노테이션에서 fluent는 default가 false입니다.
// 여기서 fluent를 true로 변경하면 @Value 어노테이션의 Getter를 
// Fluent API 방식으로 사용할 수 있습니다.
@Accessors(fluent = true)
@Value
class Humun {
    String name;
    int age;
    
} // end class

 

 

Lombok의 @Accessors 어노테이션을 사용하면 Fluent 방식으로 변경된다. 

 

 

@Value 어노테이션은 필드 보호를 위해 private final을 강제합니다.

 

 

 

 

 

 

 

 

'Java' 카테고리의 다른 글

객체 입출력 스트림  (0) 2023.08.18
보조 스트림  (0) 2023.08.17
파일 클래스  (0) 2023.08.17
Exception Handling  (0) 2023.07.28
Exception  (0) 2023.07.27

 

 

 

curl은 명령줄에서 웹 리소스를 가져오거나 데이터를 전송하는 데 사용됩니다.

기본적으로 해당 주소에 대한 정보를 가져오는 명령어만 다루었습니다.

 

 


 

 

 

1. GET(본문만 출력)

 

curl [주소]

 

 

2. 헤더 정보, 본문 출력

 

curl -i [주소]

 

 

3. 헤더 정보만 출력

 

curl -I [주소]

 

 

4. 상세 정보 출력

 

curl -v [주소]

 

 


 

# 추가 옵션 확인

 

curl --help

 

 

 

 

+ Recent posts