Security의 crypto라이브러리 사용하기
비밀번호 암호화를 위한 라이브러리 추가
회원 도메인의 회원가입 기능을 구현할 때, 비밀번호를 그냥 저장하기보다는 암호화를 해서 저장하면 보안성이 더 뛰어날 것이다.
암호화를 위해서 제공되는 여러 라이브러리들이 있는 것 같다.
대표적으로 crpto라이브러리가 있는데, spring-security모듈에도 포함되어있기 때문에, spring-security 의존성을 추가하였다.
우선 spring-security라는 모듈을 추가할 때, spring-boot의 자동설정 기능이 적용이 된다.
나는 여기서 암호화 기능만을 우선 적용하고 싶고, 별도의 security기능을 적용시키지 않을 예정이므로, 자동으로 설정되어있는 기능들중 필요없는 기능은 초기화해주던가 새로 설정을 추가해줘야 한다.
security를 추가하고 GET /members 등과 같이 요청을 보내면 401에러가 발생하며 security보안기능에 걸리는 것을 확인할 수 있다. 이러한 기본설정들을 아래처럼 초기화 해줄 수 있다.
@Configuration
public class SecurityConfiguration {
// Security 의존성을 추가하면서 SpringBoot의 자동설정에 의해 기본 시큐리티 설정이 적용되어있다.
// 우선은 인증/인가등에 대한 기능은 사용하지 않기 떄문에 filterChain을 새로운 빈으로 재동록해서 기존에 등록된 설정을 초기화한다.
// 우선은 모든 요청에 대해서 그냥 통과되도록(인증하지않도록) 설정만 추가한다.
// 요청은 모두 filterChain을 거치면서 수행되므로, 아래의 filterChain에 나중에 설정값을 추가해서 Security의 다양한 기능일 이용할 수 있다.
@Bean
public SecurityFilterChain filterChain(final HttpSecurity http) throws Exception {
// 일단 들어오는 모든 요청에 대해 '인증' 기능을 적용하지 않는다.
http.authorizeHttpRequests(auth -> auth.anyRequest().permitAll());
// CSRF -> POST, PUT ... 설정
http.csrf(csrf -> csrf.disable());
return http.build();
}
}
csrf에 대해서는 다음 글에서 학습해보겠습니닷.
빈으로 등록하고 암호화 적용해보기
라이브러리 안에 보면 PasswordEncoder라는 '인터페이스'가 있다. 그렇다면 이걸 구현한 구현체도 있을 것 같은데 찾아보니 BCryptPasswordEncoder가 있어서 우선 이걸 사용해봤다.
사용법은 아래 인터페이스만 봐도 쉽게 사용할 수 있을 것 같다.
public interface PasswordEncoder {
String encode(CharSequence rawPassword);
boolean matches(CharSequence rawPassword, String encodedPassword);
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}
BCryptPasswordEncoder라는 클래스를 빈으로 등록하기 위해 아래처럼 설정해준다.
그럼 의존성 주입을 통해 어플리케이션에서 쉽게 불러와서 사용할 수 있다.
@Configuration
class PasswordEncoderConfiguration {
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
빈으로 등록했으니까, 아래처럼 Service계층에서 빈을 불러와 사용할 수 있다.
public class MemberService {
private final MemberRepository memberRepository;
private final BCryptPasswordEncoder passwordEncoder;
@Autowired
public MemberService(final MemberRepository memberRepository, final BCryptPasswordEncoder passwordEncoder) {
this.memberRepository = memberRepository;
this.passwordEncoder = passwordEncoder;
}
. . . 생략
}
가져온 BCryptPasswordEncoder객체를 통해서 비밀번호를 암화하고 저장하도록 로직을 수정한다.
@Transactional
public Long register(final MemberCreateRequest request) {
checkDuplicatedEmail(request.getEmail());
// 암호화 하기
String encodedPassword = passwordEncoder.encode(request.getPassword());
final Member member = Member.create(
request.getName(),
request.getEmail(),
encodedPassword,
request.getCellPhone(),
request.getMemberRole()
);
. . . 생략
}
암호화가 되었는지 확인해보자.
아래와 같은 데이터로 회원가입 요청을 보낸다.
{
"name" : "정지훈",
"email" : "aaaaa@naver.com",
"password" : "1234abcd",
"cellPhone" : "010-1111-2222",
"memberRole" : "CUSTOMER"
}
암호화를 위하여 라이브러리를 사용하였는데, 이때 사용한 암호화 방식은 BCrypt방식을 사용하는 BCryptPasswordEncoder객체를 사용하였다. 이외에 SCryptPasswordEncoder클래스도 있는것을 확인하였다.
암호화 하는 방식이 조금씩 다를거라는걸 예상해볼 수 있는데, Bcrypt는 무엇일까?
Bcrypt는 비밀번호 암호화를 목적으로 설계된 단방향 해시함수이다.
여기서 단방향은 원본 메세지를 알면 암호화된 메세지를 구할 수 있지만, 암호화된 메세지에서 원본 메세지를 구할 수 없다는 특징을 가지고 있다.
비슷한 종류의 해시 알고리즘으로 PBKDF2, Scrypt도 있는데, 해시방식을 이용해서 원본 메세지를 임의의 메시지로 바꿔준다.
이때 해시의 빠른 탐색때문에 브루트 포스 공격으로 암호화된 메세지가 해킹될 우려가 있는데, 이러한 단점을 보완하여 '솔팅'과 '키 스트레칭'이란 기법을 활용한 해시 암호화 방식이다.
자세한 내용은 아래의 블로그에 읽기좋게 나와있다.(학습참고)
https://d2.naver.com/helloworld/318732
'프로젝트 > 배달 REST API' 카테고리의 다른 글
[프로젝트] 주문상태 변경 시 데이터 동시성 문제 (1) | 2023.12.24 |
---|---|
[프로젝트] 예외처리하고 응답하기 (0) | 2023.11.16 |