데이터베이스에 비밀번호 저장 시, 개인정보 보호법에 따라 반드시 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

 

 

 

프록시 패턴(Proxy Pattern)은 호출에 대한 흐름제어가 목적입니다.

의존 역전 원칙(Dependency Inversion Principle, DIP)를 활용한 디자인 패턴입니다.

 

 


 

의존 역전 원칙(Dependency Inversion Principle, DIP)

DIP는 상위 객체가 하위 객체에 의존해서는 안되며 추상화에 의존해야함을 의미합니다.

 

즉, DIP는 상위 객체가 하위 객체에 직접 의존하기보다는 인터페이스나 추상 클래스를 통해 객체에 의존하라는 것입니다.

 

인터페이스를 통해 하위 객체에 접근하게 되면상위 객체와 하위 객체 간 관계가 느슨해지고 이로 인해  코드의 유연성을 높일 수 있습니다.

 

 

 

프록시 패턴(Proxy Pattern)

 

네트워크에서는 웹 프록시가 존재합니다.

웹 프록시를 간단하게 설명하면,

클라이언트와 서버 사이에서 중개자의 역할을 하면서 부가적인 기능을 통해 로직을 수행합니다.

 

이와 비슷하게, 프록시 패턴은 중개자의 역할이며

실제로 객체가 필요할 때까지 생성을 지연하고 접근을 제어하는 것에 목적을 두고 있습니다.

 

이해를 돕기 위한 간단한 예시 코드입니다.

 

public class IService {
    abstract String runSomething();
    
} // end interface

 

public class Service implements IService {
	
    @override
    public String runSomething() {
    	return "runSomething()"
        
    } // runSomething
    
} // end class

 

Service 클래스는 IService 인터페이스를 구현하고 있고 있습니다.

이제 Service 클래스에 접근을 제어하기 위한 Proxy 클래스를 만들겠습니다.

 

@log4j2
public class Proxy implements IService {
    IService service1;
    
    
    @override
    public String runSomething() {
    	log.trace("Proxy runSomething() Method invoked.");
        
        service1 = new Service();  // IService를 참조하는 Service 객체 생성
        return service1.runSomething();
        
    } // runSomething
    
} // end class

 

위 코드는 IService 인터페이스를 구현하는 Proxy 클래스입니다.

 

@log4j2
public class ClientWithProxy {
    
    
    public static class main(String[] args) {
        log.trace("main({}) invoked.", Arrays.toString(args));
        
        
        IService proxy = new Proxy(); // IService를 참조하는 Proxy 객체 생성
        log.info(proxy.runSomething());
    
    } // main
    
} // end class

 

 

Proxy 클래스를 보면

runSomething() 메서드를 실행시켜야 Service 객체를 생성하고 이용할 수 있도록 하였습니다.

 

만약 여기서 실행 클래스가 이용할 Service 클래스가 늘어난다 하더라도 Proxy 클래스를 이용해 객체를 관리하고 접근할 수 있습니다.

 

 

 

 

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

암호화 알고리즘  (0) 2023.09.14
어댑터 패턴  (0) 2023.08.13

 

 

 

어댑터 패턴은 개방 폐쇄 원칙(Open/Closed Principle, OCP)을 활용한 패턴입니다.

OCP에 대해 먼저 이해하고 어댑터 패턴에 대해 다루겠습니다.

 


 

개방 폐쇄 원칙(Open/Closed Principle, OCP)

 

OCP는 자바 개발자라면 의식하지 않아도 항상 접할 수 있는 환경에 놓여져 있습니다.

 

자바 개발자는 코드를 작성할 때,

이 코드가 Window, MacOS, Linux 등 어느 운영체제에서 쓰일지 하나하나 고려하며 작성하는 개발자는 없습니다.

 

이러한 걱정을 덜어주는 이유는 JVM이라는 존재 덕분입니다.

 

JVM으로 인해 개발자는 내가 작성하는 소스코드가 어느 운영체제에 쓰일지 신경쓰지 않아도 되기 때문에 운영체제의 변화에 닫혀있다고 할 수 있습니다.

반면, JVM은 새로운 운영체제나 기존의 운영체제의 변화에 따라 업데이트됩니다. 이러한 특징은 JVM이 운영체제의 확장에 열려있다고 할 수 있습니다.

 

즉, OCP는 클래스같은 소프트웨어 엔티티는 확장(extends)에 열려있어야 하지만 변화에는 닫혀있어야 한다는 원칙입니다.

 


 

어댑터 패턴(Adepter Pattern)

 

마트에 방문한 손님은 물품을 구매합니다.

마트의 포스기는 제품들의 재고상황, 신제품, 없어진 제품 등을 관리합니다.

손님은 포스기 덕분에 마트의 제품들에 대한 변화에 영향을 받지 않고 편하게 제품을 구매할 수 있습니다.

 

@log4j2
public class Product1{
    
    
    void purchase1{
    	log.info("1번 제품을 구매하였습니다.");
    } // purchase1
    
} // end class

 

@log4j2
public class Product2{
    
    
    void purchase2{
    	log.info("2번 제품을 구매하였습니다.");
    } // purchase2
    
} // end class

 

위 코드는 손님이 구매할 제품군입니다. 제품을 구매하기 위해 포스기가 있어야 합니다.

 

 

@log4j2
public class Pos{
    Purchase1 p1 = new Purchase();
    
    void runService1{
    	p1.purchase1;
    } // purchase1
    
     void runService2{
    	p2.purchase2;
    } // runService2
    
} // end class

 

제품을 구매할 포스기가 구비되었으니 이제 손님은 포스기를 이용해 제품을 구매합니다.

 

 

@log4j2
public class Consumer{
	
    
    public static class main(String[] args){
    	Pos pos = new Pos();
        
        
        pos.runService1();  // 1번 제품을 구매하였습니다.
        pos.runService2();  // 2번 제품을 구매하였습니다.
        
    } // main
    
} // end class

 

 

어댑터 패턴은 간단히 말해,

호출자가 변환기(Converter)를 이용해 피호출자를 호출하는 방식의 디자인 패턴입니다.

 

 

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

암호화 알고리즘  (0) 2023.09.14
프록시 패턴  (0) 2023.08.13

+ Recent posts