파이썬(python) 비밀번호가 문자/숫자/특수문자 조합이고 8자리 이상 검증

728x90
  1. 비밀번호 강도 측정 기능 (get_password_strength):
    • 5점 만점 척도로 비밀번호 강도를 평가
    • 길이, 소문자, 대문자, 숫자, 특수문자 등 다양한 요소를 고려
    • 구체적인 개선 피드백을 제공
  2. 상세 검증 피드백 (validate_password_with_details):
    • 비밀번호가 유효하지 않은 경우 구체적인 이유를 제공
    • 어떤 요구사항이 충족되지 않았는지 알려줌
  3. 확장된 테스트 케이스:
    • 더 다양한 시나리오에 대한 테스트
    • 대소문자, 공백 포함 등 추가 테스트 케이스를 포함

이 코드를 실행하면 다음과 같은 세 가지 테스트 섹션이 출력됨

  1. 기본 검증 테스트 (원래 코드와 유사)
  2. 비밀번호 강도 테스트 (점수와 피드백 제공)
  3. 상세 피드백 테스트 (실패 이유 상세 설명)
import re

def validate_password(password):
    """
    비밀번호가 문자, 숫자, 특수문자 조합으로 8자리 이상인지 검증합니다.

    Args:
        password: 검증할 비밀번호 문자열

    Returns:
        True: 유효한 비밀번호
        False: 유효하지 않은 비밀번호
    """
    regex = r"^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{8,}$"
    return bool(re.fullmatch(regex, password))

def validate_password_char_check(password):
    """
    비밀번호가 문자, 숫자, 특수문자 조합으로 8자리 이상인지 문자 체크 방식으로 검증합니다.

    Args:
        password: 검증할 비밀번호 문자열

    Returns:
        True: 유효한 비밀번호
        False: 유효하지 않은 비밀번호
    """
    if len(password) < 8:
        return False

    has_letter = False
    has_digit = False
    has_special = False
    special_chars = "!@#$%^&*"

    for char in password:
        if 'a' <= char <= 'z' or 'A' <= char <= 'Z':
            has_letter = True
        elif '0' <= char <= '9':
            has_digit = True
        elif char in special_chars:
            has_special = True

    return has_letter and has_digit and has_special

def get_password_strength(password):
    """
    비밀번호의 강도를 평가합니다.

    Args:
        password: 검증할 비밀번호 문자열

    Returns:
        점수(0-5): 0(매우 약함) ~ 5(매우 강함)
        메시지: 강도와 개선 방향에 대한 설명
    """
    score = 0
    feedback = []

    # 기본 검증 조건
    if len(password) >= 8:
        score += 1
    else:
        feedback.append("비밀번호는 최소 8자 이상이어야 합니다.")

    # 문자 포함 여부
    if re.search(r'[a-z]', password):
        score += 0.5
    else:
        feedback.append("소문자를 포함하세요.")

    if re.search(r'[A-Z]', password):
        score += 0.5
    else:
        feedback.append("대문자를 포함하세요.")

    # 숫자 포함 여부
    if re.search(r'[0-9]', password):
        score += 1
    else:
        feedback.append("숫자를 포함하세요.")

    # 특수문자 포함 여부
    if re.search(r'[!@#$%^&*]', password):
        score += 1
    else:
        feedback.append("특수문자(!@#$%^&*)를 포함하세요.")

    # 추가 강화 조건
    if len(password) >= 12:
        score += 1

    # 점수에 따른 강도 메시지
    if score < 1:
        strength = "매우 약함"
    elif score < 2:
        strength = "약함"
    elif score < 3:
        strength = "보통"
    elif score < 4:
        strength = "강함"
    else:
        strength = "매우 강함"

    # 피드백이 없으면 전체 만족
    if not feedback:
        feedback.append("모든 보안 요구사항을 충족합니다.")

    return score, strength, feedback

def validate_password_with_details(password):
    """
    비밀번호를 검증하고 자세한 피드백을 제공합니다.

    Args:
        password: 검증할 비밀번호 문자열

    Returns:
        유효성: 비밀번호 유효 여부 (True/False)
        실패 이유: 실패한 경우 이유 목록
    """
    reasons = []

    # 길이 검증
    if len(password) < 8:
        reasons.append("비밀번호가 8자리 미만입니다.")

    # 문자 포함 여부
    if not re.search(r'[a-zA-Z]', password):
        reasons.append("비밀번호에 문자가 포함되어 있지 않습니다.")

    # 숫자 포함 여부
    if not re.search(r'[0-9]', password):
        reasons.append("비밀번호에 숫자가 포함되어 있지 않습니다.")

    # 특수문자 포함 여부
    if not re.search(r'[!@#$%^&*]', password):
        reasons.append("비밀번호에 특수문자가 포함되어 있지 않습니다.")

    # 유효성 여부
    is_valid = len(reasons) == 0

    return is_valid, reasons

# 테스트
def test_password_validator():
    test_cases = [
        {"password": "Password123!", "expected": True, "description": "모든 조건 충족"},
        {"password": "Pass123", "expected": False, "description": "특수문자 없음"},
        {"password": "Password!", "expected": False, "description": "숫자 없음"},
        {"password": "123456!", "expected": False, "description": "문자 없음"},
        {"password": "short", "expected": False, "description": "8자리 미만"},
        {"password": "UPPERCASE123!", "expected": True, "description": "대문자 포함"},
        {"password": "lowercaseonly123!", "expected": True, "description": "소문자만 포함"},
        {"password": "Pass 123!", "expected": False, "description": "공백 포함 (특수문자 집합에 공백 미포함)"}
    ]

    print("=== 기본 검증 테스트 ===")
    for case in test_cases:
        result1 = validate_password(case["password"])
        result2 = validate_password_char_check(case["password"])

        print(f"비밀번호: '{case['password']}'")
        print(f"설명: {case['description']}")
        print(f"예상: {case['expected']}")
        print(f"정규식 결과: {result1} ({result1 == case['expected'] and '성공' or '실패'})")
        print(f"문자별 결과: {result2} ({result2 == case['expected'] and '성공' or '실패'})")
        print("-" * 40)

    print("\n=== 강도 테스트 ===")
    strength_test_cases = [
        "short",
        "password", 
        "Password",
        "Password1",
        "Password!",
        "Password1!",
        "StrongPassword1!",
        "VeryStrongPassword123!@#"
    ]

    for password in strength_test_cases:
        score, strength, feedback = get_password_strength(password)
        print(f"비밀번호: '{password}'")
        print(f"점수: {score}/5")
        print(f"강도: {strength}")
        print(f"피드백: {', '.join(feedback)}")
        print("-" * 40)

    print("\n=== 상세 피드백 테스트 ===")
    for case in test_cases:
        is_valid, reasons = validate_password_with_details(case["password"])
        print(f"비밀번호: '{case['password']}'")
        print(f"유효성: {is_valid}")
        if not is_valid:
            print(f"실패 이유: {', '.join(reasons)}")
        print("-" * 40)

if __name__ == "__main__":
    test_password_validator()

 

728x90