Spring boot

Bank App 만들기 - 사용자 비밀번호 암호화 처리

ryeonng 2024. 8. 16. 12:48

 

1. SpringSecurityCrypto 의존성 추가 및 BCryptPasswordEncoder 빈 클래스로 등록
2. 회원가입 시 회원 비밀번호 암호화 처리
3. 로그인 시 암호호 된 비밀번호 확인 및 기능 추가
4. 코드 확인

* 암호화 처리 후 H2 초기 데이터로 들어간 유저로는 로그인이 안됨
패스워드 확인 로직인 변경 됨, 신규 유저 가입 후 테스트 하자

 

마이그레이션이란 데이터베이스 스키마의 변경 내역을 버전 관리하여, 변경에 대한 이력을 남기고, 데이터베이스를 이전 버전에서 최신 버전으로 옮기는 일련에 과정들을 의미한다. 즉, 일반적으로 마이그레이션은 스키마를 변경하거나 새로운 테이블이나 컬럼을 추가하는 등에 작업을 포함하고 따라서 우리가 할 작업 H2 데이터베이스에서 MySQL 로 변경할 때도 마이그레이션을 수행 한다고 할 수 있다. 이러한 이경우에 테이터 스키마를 변경하거나 데이터를 이전하는 작업등이 포함 될 수 있다.

 

SpringSecurityCrypto 의존성 추가 및 BCryptPasswordEncoder 빈 클래스로 등록

주의 : spring-security 에서 제공하는 crypto라는 라이브러리를 활용한다. 이번 프로젝트에서는 Spring Security를 활용하지 않는다.

 

Spring Security Crypto

org.springframework.security:spring-security-crypto 모듈은 스프링 시큐리티 프레임워크의 일부로, 암호화와 해싱을 위한 기능을 제공한다. 이 모듈은 비밀번호 저장, 데이터 암호화 및 디지털 서명을 위한 API를 포함하며, 개발자가 보안 관련 작업을 보다 쉽게 구현할 수 있도록 돕는다. 주로 사용되는 기능은 비밀번호 인코딩(해싱)과 텍스트 데이터의 암호화/복호화이다.

 

의존성 추가 하기 - gradle 파일에 추가 후 새로 고침

// 암호화 
implementation 'org.springframework.security:spring-security-crypto'

 

주요 import 확인

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

 

 

WebMvcConfig - 코드 추가 (PasswordEncoder Bean 처리)
package com.tenco.bank.config;

import org.springframework.beans.factory.annotation.Autowired;
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;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.tenco.bank.handler.AuthInterceptor;

import lombok.RequiredArgsConstructor;

// WebMvcConfigurer : 약속 - 설정파일
@Configuration // 하나의 클래스를 IOC 하고 싶을때 사용
@RequiredArgsConstructor
public class WebWvcConfig implements WebMvcConfigurer{

	@Autowired // DI
	private final AuthInterceptor authInterceptor;
	
	// @RequiredArgsConstructor : 생성자 대신 사용 가능
	
	// 우리가 만들어 놓은 AuthInterceptor를 등록해야 한다.
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(authInterceptor)
				.addPathPatterns("/account/**")
				.addPathPatterns("/auth/**");
	}
	
	@Bean // IoC 대상 (싱글톤 처리)
	PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
}

 

회원가입 시 회원 비밀번호 암호화 처리

/**
	 * 회원 등록 서비스 기능
	 * 트랜잭션 처리
	 * @param dto
	 */
	@Transactional // 트랜잭션 처리는 반드시 습관화!
	public void createUser(SignUpDTO dto) { // 회원가입 처리 (CRUD 기반 네이밍)
		
		
		int result = 0;
		try {
			
			// 코드 추가 부분
			// 회원가입 요청 시 사용자가 던진 비밀번호 값을 암호화 처리 해야 한다.
			String hashPwd = passwordEncoder.encode(dto.getPassword());
			System.out.println("hashPwd : " + hashPwd);
			dto.setPassword(hashPwd);
			
			result = userRepository.insert(dto.toUser());
			
		} catch (DataAccessException e) {
			throw new DataDeliveryException("중복된 이름을 사용할 수 없습니다.", HttpStatus.INTERNAL_SERVER_ERROR);
		} catch (Exception e) {
			throw new RedirectException("알 수 없는 오류", HttpStatus.SERVICE_UNAVAILABLE);
		}
		if(result != 1) {
			throw new DataDeliveryException("회원가입 실패", HttpStatus.INTERNAL_SERVER_ERROR);
		}
	}

 

로그인 시 암호화 된 비번 확인 및 기능 추가

package com.tenco.bank.repository.interfaces;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import com.tenco.bank.repository.model.User;

// UserRepository 인터페이스와 user.xml 파일을 매칭 시킨다. 
@Mapper // 반드시 선언을 해야 동작한다. 
public interface UserRepository {
	
	public int insert(User user);
	public int updateById(User user);
	public int deleteById(Integer id);
	public User findById(Integer id);
	public List<User> findAll();
	
	  
	public User findByUsernameAndPassword(@Param("username") String username, @Param("password") String password);
	
	// 코드 추가 
	public User findByUsername(@Param("username") String username);
	
}

 

user.xml
	
	<select id="findByUsername" resultType="com.tenco.bank.repository.model.User" >
		select * from user_tb where username = #{username}	
	</select>

 

public User readUser(SignInDTO dto) {
		 
		User userEntity = null;  // 지역 변수 선언 
		try {
			userEntity = userRepository.findByUsername(dto.getUsername());
			
		} catch (DataAccessException e) {
			throw new DataDeliveryException("잘못된 처리 입니다.", HttpStatus.INTERNAL_SERVER_ERROR);
		} catch (Exception e) {
			throw new RedirectException("알수 없는 오류", HttpStatus.SERVICE_UNAVAILABLE);
		}
		
		if(userEntity == null) {
			throw new DataDeliveryException("존재하지 않는 아이디 입니다.", HttpStatus.BAD_REQUEST);
		}
		
		boolean isPwdMatched = passwordEncoder.matches(dto.getPassword(), userEntity.getPassword());
		if(isPwdMatched == false) {
			throw new DataDeliveryException("비밀번호가 잘못되었습니다", HttpStatus.BAD_REQUEST);
		}
		
		return userEntity;
	}