Spring

[Spring] Bean을 수동으로 등록하는 방법 및 BCryptPasswordEncoder

Young_Han 2024. 10. 8. 23:18

일반적으로 @Component를 사용하여 Bean을 자동으로 등록하는 것이 좋다. 

  • @Component를 사용하면 @ComponemtScan에 의해 자동으로 스캔되어 클래스를 Bean으로 등록해 준다.
  • 왜냐하면 프로젝트의 규모가 커질 수 록 등록할 Bean들이 많아지기 때문이다.

 

그러면 Bean 수동 등록은 언제 사용될까?

  • 기술적인 문제나 공통적인 관심사를 처리할 때 사용하는 객체들은 수동으로 등록하는 것이 좋다.
  • 수동등록된 Bean에서 문제가 발생했을 때 해당 위치를 파악하기 쉽다는 장점이 있다.

 

Bean 수동 등록하는 방법

@Configuration
public class PasswordConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
  • Bean으로 등록하고자 하는 객체를 반환하는 메서드를 선언하고 @Bean을 설정한다.
  • 해당 클래스에 @Configuration을 설정한다.
  • 그러면 Spring 서버가 뜰 때 Spring IoC 컨테이너에 'Bean'으로 저장된다.
  •  
// 1. @Bean 설정된 메서드 호출
PasswordEncoder passwordEncoder = passwordConfig.passwordEncoder();

// 2. Spring IoC 컨테이너에 빈 (passwordEncoder) 저장된다.
// passwordEncoder -> Spring IoC 컨테이너
  • public PasswordEncoder passwordEncoder() {..} → passwordEncoder

 


Bean 등록해 보기

1. PasswordEncoder의 구현체 BCryptPasswordEncoder를 Bean으로 수동등록

package com.sparta.springauth.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class PasswordConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

 

 

BCryptPasswordEncoder란?

BCryptPasswordEncoder는 SpringSecurity 프레임 워크에서 제공하는 클래스로 비밀번호를 암호화(해시)하는데 사용된다. 해시 함수에는 다양한 종류가 있지만 BCrypt는 단순히 압력을 1회 해시하는 것이 아니라 솔트(salt)를 부여하여 여러번 해싱하므로 더 안전하게 암호를 관리할 수 있다.

 BCrypt는 같은 비밀번호를 암호화하더라도 해시 값은 매번 다른 값이 도출된다.

 

 

 

 

 

2. 등록한 passwordEncoder ‘Bean’을 사용하여 문자열을 암호화

package com.sparta.springauth;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.password.PasswordEncoder;

@SpringBootTest
public class PasswordEncoderTest {

    @Autowired
    PasswordEncoder passwordEncoder;

    @Test
    @DisplayName("수동 등록한 passwordEncoder를 주입 받아와 문자열 암호화")
    void test1() {
        String password = "Robbie's password";

        // 암호화
        String encodePassword = passwordEncoder.encode(password);
        System.out.println("encodePassword = " + encodePassword);

        String inputPassword = "Robbie";

        // 해시된 비밀번호와 사용자가 입력한 비밀번호를 해싱한 값을 비교
        boolean matches = passwordEncoder.matches(inputPassword, encodePassword);
        System.out.println("matches = " + matches); // 암호화할 때 사용된 값과 다른 문자열과 비교했기 때문에 false
    }
}

 

3. 결과

 


 

예외)

 만약 테스트 결과에서 @DisplayName이 콘솔창에 나타나지 않을 수 있다.

  • 그럴 땐 Setting의 Build and using과 Run test using에 들어가 Gradle(기본값)에서 IntelliJ IDEA로 변경하면 된다.

  • Setting → Build Tools → Gradle 

 

 

 


BCryptPasswordEncoder

 

BCryptPasswordEncoder에서 제공되는 메소드는 부모클래스인 Object의 메소드를 제외하고 총 3개가 있다.

 

1. String encode(CharSequence rawPassword)

  • 패스워드를 암호화해주는 메소드
  • 8바이트 이상의 무작위로 생성된 솔트와 결합된 SHA-1 이상의 해시를 적용한다.
  • java.lang.CharSequence 타입의 패스워드를 매개변수로 입력하면 암호화된 패스워드를 String타입으로 반환해준다.

 

2. boolean matches(CharSequence  rawPassowrd, String encodedPassword)

  • 인코딩되지 않은 암호와 인코딩된 암호가 일치하는지 확인한다.
  • 저장된 비밀번호 자체는 절대로 디코딩되지 않는다.

 

3. boolean upgradeEncoding(String encodedPassword)

  • 더 나은 보안을 위해 인코딩된 암호를 다시 인코딩해야 하는 경우 true를 반환하고, 그렇지 않으면  false를 반환한다.
  • 해당 메소드는 기본적으로 항상 false를 반환하므로 필요하다면 오버라이딩 하여 encode된 암호는 안전성을 체크하는 로직을 구현해서 사용해야 한다.

 

 

 


 

구현 방법

 

1. build.gradle에 spring-security 추가

dependencies {	
	implementation 'org.springframework.boot:spring-boot-starter-security'
}

 

 

2. SecuritConfig에 BCryptpasswordEncoder Bean으로 등록

package com.sparta.springauth.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class PasswordConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
  • SecurityConfig에서 BCryptPasswordEncoder를 Bean으로 등록하여 스프링 컨테이너에 등록한다.

3. BCryptPasswordEncoder 사용

@Service
@RequiredArgsConstructor
public class TestService {
//Bean으로 등록된 BCryptPasswordEncoder 의존성 주입
	private final PasswordEncoder passwordEncoder;

	public void register(String id, String password) {
			//사용자가 입력한 암호를 encode하여 저장
			String encodedPassword = passwordEncoder.encode(password);
			Member member = new Member(id, encodedPassword);

			TestRepository.save(member);
	}	

	public boolean login(String id, String password) {
			Member member = TestRepository.find(id);
			if(passwordEncoder.matches(password, member.getPassword()) {
					return true; // 입력한 비밀번호와 저장소의 비밀번호가 일치
			} else {
					return false;
			}
	}
}

 

 

 

[출처]

https://januaryman.tistory.com/453

 

Junit테스트에서 설정한 @DisplayName이 Console창에 나타나지 않을때

테스트 코드 위와 같이 Test 코드에 @DisplayName 을 적용하여 테스트 이름을 설정해주었건만, 위에 빨간박스 안에가 현재처럼 @DisplayName에 설정한 명칭으로 나오는것이 아니고, helloJunitTest 라는 메소

januaryman.tistory.com