Framework/Spring
Spring Security로 비밀번호 암호화 하기
뚜sh뚜sh
2024. 1. 27. 20:26
1. 의존성 주입
- build.gradle에 아래 코드 추가
implementation 'org.springframework.boot:spring-boot-starter-security'
2. 스프링 시큐리티를 사용하여 웹 보안을 구성하는 데 사용되는 설정 클래스 생성
- 'SecurityConfig' 클래스는 '@EnableWebSecurity' 어노테이션을 사용하여 활성화되고, 암호화를 위해 BCrypt 알고리즘을 사용하며 기본적인 HTTP 보안 구성을 설정함
package qt.qr_backend.config;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
// 이 클래스가 Spring 설정 클래스임을 나타냄
// 이 어노테이션을 사용하면 클래스 내에서 @Bean 어노테이션이 붙은 메소드로부터 생성된 빈 객체들이 스프링 컨테이너에 등록됨
@Configuration
// @EnableWebSecurity 어노테이션은 스프링 시큐리티를 활성화하는 어노테이션
// 이 어노테이션을 사용하면 스프링 시큐리티와 관련된 설정을 구성할 수 있는 클래스로 인식됨
@EnableWebSecurity
public class SecurityConfig {
// passwordEncoder 메서드는 비밀번호를 해싱하기 위한 BCryptPasswordEncoder 빈을 생성하여 반환함
// 이 해시 알고리즘은 안전한 비밀번호 저장을 위해 사용됨
// 사용자의 비밀번호는 이 해시 알고리즘을 사용하여 저장됨
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// filterChain 메서드는 SecurityFilterChain 빈을 생성하여 반환함
// 이 빈은 스프링 시큐리티 필터 체인을 정의하는 역할을 함
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 이 부분은 모든 HTTP 요청에 대해 인증(authenticated)이 필요하다고 설정하는 부분
// 즉, 사용자는 로그인 후에만 접근이 허용됨
.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated())
// HTTP Basic 인증을 사용하도록 설정
// 이것은 기본적인 HTTP 인증 방식으로, 사용자 이름과 비밀번호를 전달하여 인증
.httpBasic(Customizer.withDefaults());
return http.build();
}
}
- filterChain을 따로 설정하지 않으면 스프링 시큐리티의 기본 설정에 따라 동작함
- 모든 요청에 대한 접근이 허용되지 않으며, 인증이 필요합니다. (인증된 사용자만 접근 가능)
- HTTP Basic 인증 방식을 사용하여 사용자를 인증합니다.
- 한 마디로 위의 코드에 설정한 방식이 기본 설정이랑 같다는 말임
3. password를 포함한 엔티티에 암호화 함수를 생성
public class User {
private String password;
public void encodePassword(PasswordEncoder passwordEncoder, String password) {
this.password = passwordEncoder.encode(password);
}
}
4. 비밀번호를 객체에 저장할 때 encodePassword(password)의 값을 저장함
[ERROR] 스프링 시큐리티를 추가하고 나서 빌드에 계속 실패했다
원인을 알고 보니 PasswordEncoder의 빈 이름과 PasswordEncoder를 주입할 때의 변수명을 다르게 지정해놓은 것 때문이었다..
부끄러웠지만 다음부터는 이런 실수를 하지 않기 위해 기록한다..
알고보니 다른 근본적인 문제가 있었다. 2-3시간을 찾다가 Service의 PasswordEncoder에 빈이 주입되지 않아서 빌드에 실패한다는 것을 알게 되었고, 그 원인은 SecurityConfig에 @Configuration을 붙이지 않아서였다.
기능 구현도 좋지만 구현해야 하는 기능에 대한 기본적인 지식에 대해 자세히 살펴보는 게 필요할 것 같다..
이젠 진짜 되는 줄 알았는데 401 에러를 뱉었다..
하지만 .csrf(AbstractHttpConfigurer::disable)를 추가해서 해결했다!!!!!!!
401 에러가 해결된 이유는 클라이언트가 서버로 요청을 보낼 때 CSRF 토큰을 포함하지 않으면, Spring Security는 요청을 거부하고 401 Unauthorized 오류를 반환한다.
하지만 'csrf'를 비활성화하면 이러한 CSRF 토큰 검증이 수행되지 않아 401 에러가 발생하지 않는다!!!!!
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// Cross-Site Request Forgery 보호 기능을 비활성화함
// CSRF는 웹 애플리케이션에서 사용자가 자신의 의지와는 무관하게 위조된 요청을 보내는 공격을 막기 위한 기능
// 이 기능을 비활성화하면 서버는 CSRF 토큰 없이도 요청을 받아들임
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests((authz) -> authz
// 'requestMatchers' 메서드로 특정 경로를 지정하고
// 'permitAll'로 그 경로들에 대한 접근을 인증되지 않은 모든 사용자에게 허용함
.requestMatchers("/signup", "/login").permitAll()
// 위에서 지정한 경로들을 제외한 모든 요청에 대해서는 인증된 사용자만 접근할 수 있도록 설정함
.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults());
return http.build();
}
}