Developer/Spring eGov4.0 (Java11, Tomcat9)

Spring , passwordEncoder

단님 2024. 10. 28. 00:25
728x90

 

PasswordEncoder의 주요 개념과 기능

 

  • 비밀번호 인코딩

비밀번호를 데이터베이스에 직접 저장하지않고 , 해시(Hash) 알고리즘을 통해 변환한 값을 저장하는 방식.

PasswordEncoder는 이를 위해 encode() 메서드를 제공

encode(): 평문 비밀번호를 해싱하여 변환

String rawPassword = "myPassword123";
String encodedPassword = passwordEncoder.encode(rawPassword);

 

  • 비밀번호 매칭

로그인 시 입력한 비밀번호가 저장된 해시 값과 일치하는지 검증하기 위해 matches() 메서드를 사용

matches(): 입력한 비밀번호와 저장된 해시 값의 일치 여부를 확인

비밀번호가 일치하면 true, 그렇지 않으면 false를 반환

boolean isMatch = passwordEncoder.matches(rawPassword, encodedPassword);

 

 

주입방식에 따른 차이

 

1. 직접 생성방식
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

 

2. @Configuration 및 @Bean을 사용한 방식
@Configuration
public class DemoConfig {
	@Bean
	public PasswordEncoder getPasswordEncoder() {
		return new BCryptPasswordEncoder();
	}
}

 

 

  • new 키워드 방식은 간단하지만 객체 관리 측면에서 재사용이 어렵고, 코드 중복 및 관리 복잡성을 증가시킬 수 있다.
  • @Bean 방식은 스프링 컨텍스트 내에서 싱글톤으로 관리되어 애플리케이션 전체에서 효율적으로 재사용할 수 있고, 의존성 주입으로 필요한 곳에 간편하게 주입할 수 있어 더 유지보수에 유리하다.
BCryptPasswordEncoder

 

스프링 시큐리티에서 가장 흔히 사용하는 PasswordEncoder 구현체는 BCryptPasswordEncoder입니다.

  • BCrypt 알고리즘: 해시 함수로, 비밀번호 해싱에 매우 안전한 BCrypt 알고리즘을 사용합니다. 이 알고리즘은 속도를 조절할 수 있어 무차별 대입 공격(Brute Force Attack)에 강합니다.
  • 솔트(Salt): BCrypt는 자동으로 솔트(Salt)를 생성하여 해시 값에 포함시킵니다. 솔트는 같은 비밀번호여도 항상 다른 해시 값이 생성되도록 하는 추가 데이터입니다.
  • 강도 조절: 생성 시 강도를 설정하여 해싱에 걸리는 시간을 조정할 수 있습니다. 기본 강도는 10이며, 강도를 높일수록 해시 작업이 느려져서 공격에 더 강해집니다.
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(12); // 강도를 12로 설정

 

 

 

PasswordEncoder의 장점
  • 안전한 비밀번호 저장 
    • 비밀번호가 직접 노출되지 않도록 보호하고 , 해시값으로 저장함으로 보안성을 높임
  • 다양한 구현체 ( PasswordEncoder 인터페이스를 통해 다양한 해싱 알고리즘 사용)
    • BCryptPasswordEncoder: 안전성 높은 BCrypt 알고리즘 기반.
    • Pbkdf2PasswordEncoder: PBKDF2 알고리즘 기반.
    • SCryptPasswordEncoder: 메모리 집약적인 Scrypt 알고리즘 기반.

 

 

로그인 passwordEncoder (matches() 사용)
@AllArgsConstructor
@Controller
@RequestMapping(value = "/member")
public class MemberController {

	
	//@Autowired(required = false)
	JoService joService;
	MemberService service;
	PasswordEncoder passwordEncoder;
	//PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
	//주입받기 위해 , 생성되어있어야 하는데 , java-Config (DemoConfig.java)파일을 이용한다.

DemoConfig.java에 Bean을 만들어 둠.

	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public String mLogin(Model model, HttpServletRequest request, HttpSession session,MemberDTO dto, JoDTO jdto) {
		//String id = request.getParameter("id");
        // => 요청을 처리하는 매핑메서드의 인자로 ~DTO 등의 객체를 정의하면,
        //    Parameter 들의 name 과 일치하는 필드의 값들은 자동으로 담겨짐(setter 사용함)
		String password = dto.getPassword();
		String uri = "redirect:/home";
		dto = service.selectOne(dto.getId());
		if(dto !=null && passwordEncoder.matches(password, dto.getPassword())) {
			session.setAttribute("loginID", dto.getId());
			session.setAttribute("loginName", dto.getName());
			session.setAttribute("loginJno", dto.getJno());
			System.out.println("성공 !");

		}else {
			System.out.println("실패 !");
			model.addAttribute("message", "로그인 다시 하세요.");
			uri="member/loginForm";
		}
		return uri;
	}

 

회원가입시 passwordEncoder(encode() 사용)
	@RequestMapping(value = "/mjoin", method = RequestMethod.POST)
	public String mjoin(Model model ,HttpServletRequest request , MemberDTO dto) throws IOException {
		String uri = "member/loginForm";
		//* passwordEncoder 적용
		dto.setPassword(passwordEncoder.encode(dto.getPassword()));
		//* 이미지 업로드처리 =====================================
		...
		...
		// ========================================================
		if(service.insert(dto)>0) {
			model.addAttribute("message","회원가입 성공 ! 로그인 후 이용해주세요");
		}else {
			model.addAttribute("message","회원가입 실패 ! 다시 이용해주세요");
			uri="member/joinForm";
		}// 문자열로 , 경로를 보낼때는 폴더명부터 입력해줘야함.
		return uri;
	}