NodeJS 에서는 내장 모듈을 이용해서 암호화를 했었는데, Spring에서는 어떻게 해야하는지 몰랐다.
Java 자체 내장모듈이 있는지도 살펴보았지만, Spring Security에서 지원을 해준다는것을 알게 되었다.
아무래도 Spring을 이용한 프로젝트를 진행중이니 Spring이름이 붙은 Spring Security를 사용하는것이 좋지 않을까? 하여 해보기로 했다.
어느 블로그에 있던 코드였는데, 우선적으로 예제 코드를 작성해 보았다.
일단, 필자는 security/SecurityConfig.java 파일에 SpringSecurity를 설정할 수 있도록 해주었다.
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception{
http.csrf().disable();
}
@Bean
public PasswordEncoder getPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}
처음보는것들 투성이인데, Docs를 보면서 이해 해보도록 하겠다.
Spring 4.0 이전에는 @EnableWebMvcSecurity
를 사용했는데, 4.0으로 넘어오면서 이는 사용되지 않고, 대체제로 @EnableWebSecurity
를 사용하게 되었다.
SecurityConfig는 SpringSecurity설정파일로, 해당 클래스 안에는 설정을 할 때 Bean을 Java 설정코드를 이용해서 Bean을 생성한다. 하지만, Bean을 설정파일에서 생성하기 위해서는 @Configuration
이 필요한데, @EnableWebSecurity
의 안에 들어가보면 @Configuration
이 추가되어있어 따로 추가할 필요는 없다.
해당 어노테이션 구조를 보면, WebSecurityConfiguration.class,SpringWebMvcImportSelector.class,OAuth2ImportSelector.class,HttpSecurityConfiguration.class
이렇게 총 4개의 class를 import 해 온다.
마음같아서는 저 클래스들이 뭐하는것들인지 알아보고싶지만, docs에서도 해당 클래스들이 뭐하는건지 자세히 나온게 없어 이제까지 조사한 내용들을 정리해보았다.
WebSecurity를 사용하여 SpringSecurity에 대한 웹 기반을 수행하는 FilterChainProxy를 생성한다. 이 후 Bean을 내보내고, WebSecurityConfigurerAdpater를 이용해 WebSecurity를 커스터마이징 할 수 있다.
위 어노테이션과 한 쌍을 이루는 클래스인데, SpringBoot2.0부터는 WebSecurityConfigurerAdpater를 상속받지 않으면 기본적인 자동 구성이 비활성화 되고 사용자 지정이 활성화 된다.
이는 추상클래스로, 해당 문서를 확인하면 좋다.
이 다음부터 보면, configure이 오버라이딩 되어있고, getPasswordEncoder 라는 Bean을 생성하였다.
이 두개가 무엇인지 한번 가볍게 알아보자.
이는, 말 그대로 설정을 하는 메소드로, HttpSecurity타입을 매개변수로 받으며, 해당 매개변수를 통하여 다양한 설정을 할 수 있다.
해당 매개변수 안에는 다양한 메소드들을 사용하여 SpringSecurity를 설정해줄 수 있는데, 그러한 메소드들은 아래 문서에 Method Summary 항목을 확인하기 바란다.
메서드 이름대로, Password를 인코딩 해주는 Encoder를 가져와 Bean 객체로 만들어준다는 것이다.
해당 메서드를 Bean어노테이션을 추가해 Bean으로 명시를 해주었는데, 이를 해줌으로써 해당 메서드는 IoC컨테이너에서 관리하는 Bean이 되었다. 즉, 이는 @Autowired를 통해 PasswordEncoder를 손쉽게 DI를 할 수 있게 되는것이다.
Password를 인코딩 해주는 Encoder는 Spring Security에서 제공하는 BCryptPasswordEncoder() 인데, 해당 객체를 PasswordEndcoder로 사용할 수 있게 해준다. 해당 인코더는 BCrypt라는 해시 함수를 이용해서 패스워드를 암호화하는 구현체 이다.
여기 까지 보앗을 때 SpringSecurity가 어떤건지 대충은 알 수 있을것같다.
PasswordEncoder를 Bean으로 만드는거까지는 알겠는데, 이를 어떻게 DI를 하고 사용해야할까?
간단하게 TestCode를 작성해서 확인을 해보도록 하겠다.
@SpringBootTest
public class EncoderTest{
@Autowired
private PasswordEncoder passwordEncoder;
@Test
void encryptTest(){
String plainPassword = "1231231231231";
String encodedPassword = passwordEncoder.encode(plainPassword);
System.out.println("=================== EncryptPassword ====================");
System.out.println(encodePassword);
System.out.println("=================== EncryptPassword ====================");
assertAll(
() -> assertNotEquals(plainPassword, encodePassword),
() -> assertTrue(passwordEncoder.matches(plainPassword, encodePassword))
);
}
}
해당 테스트코드를 실행하면
=================== EncryptPassword ====================
$2a$10$ARfSDCjwX8f8AyV/wZn/z.AHJoYxUREXzBRAjfs.LlaJ7vIMNIqnK
=================== EncryptPassword ====================
이런식의 로그가 나오게되고, 테스트 결과는 성공으로 나오게 된다.
해당 테스트코드에서 2개의 메서드가 나오게 되는데 바로 encode
와 matches
이다.
메서드 이름만 봐도 어떤일을 하는지 대충은 알겠다.
encode() - Stirng 형태로 넘어온 매개변수를 Bcrypt 방식으로 인코딩 한다.
matches() - 인코딩 전 패스워드와 인코딩 후 패스워드를 비교하여 boolean 형태로 값을 리턴해준다.