Spring Security 의존성 주입
비밀번호 암호화에 사용되는 PasswordEncoder를 사용하기 위해서는 Spring Security 의존성을 주입해줘야 한다.
build.gradle: implementation 'org.springframework.boot:spring-boot-starter-security'
⇒ security를 넣으면 잘 되던 join 기능이 안된다 그 이유는
"Spring Security가 Dispatcher Servlet으로 요청이 전달되기 전에
Filter Chain에서 인증과정을 추가로 진행하고 인증되지 않은 사용자는 접근을 막기 때문이다. 그래서 SecuirtyConfig에서 필요한 인증설정을 추가해야한다."
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.httpBasic().disable() //HTTP 기본 인증 구성
.csrf().disable()//csrf보호 활성화, 비활성화(disable) 가능
.cors().and()//CorsFilter를 사용한다.
.authorizeRequests() //사용권장
.antMatchers("/api/**").permitAll() // 경로지정 맵핑
.antMatchers("/api/v1/users/join", "/api/v1/users/login").permitAll() // join, login은 언제나 가능
.and()
.sessionManagement()// 세션관리구성
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // jwt사용하는 경우 씀, 스프링 시큐리티는 HttpSession을 생성하지 않고, SecurityContext를 얻기 위해 사용하지 않는다
.and()
// .addFilterBefore(new JwtTokenFilter(userService, secretKey), UsernamePasswordAuthenticationFilter.class) //UserNamePasswordAuthenticationFilter적용하기 전에 JWTTokenFilter를 적용 하라는 뜻 입니다.
.build();
}
}
Java
복사
CSRF란 웹 애플리케이션의 취약점 중 하나로, 이용자가 의도하지 않은 요청을 통한 공격을 의미합니다. 즉 CSRF 공격이란, 인터넷 사용자(희생자)가 자신의 의지와는 무관하게 공격자가 의도한 행위(등록, 수정, 삭제 등)를 특정 웹사이트에 요청하도록 만드는 공격 입니다.
이러한 CSRF 공격을 방어하기 위해 대표적으로 다음과 같은 방법들이 있습니다.
•
Referrer 검증
•
Spring Security CSRF Token 사용
Referrer 검증
서버단에서 request의 referrer을 확인하여 domain이 일치하는지 검증하는 방법입니다.
referrer 검증만으로 대부분의 CSRF 공격을 방어할 수 있습니다.
Spring Security CSRF Token
임의의 토큰을 발급한 후 자원에 대한 변경 요청일 경우 Token 값을 확인한 후 클라이언트가 정상적인 요청을 보낸것인지 확인합니다.
만약 CSRF Token이 존재하지 않거나, 기존의 Token과 일치하지 않는 경우 4XX 상태코드를 리턴합니다.
요청한 파라미터에 Token을 포함한 뒤 전송하여 정상적인 요청인지 혹은 공격자로부터 의도된 요청인지 판단할 수 있습니다.
그러나 rest api 서버의 경우는 사용자 정보를 session에 저장하지 않습니다. jwt 토큰을 사용해서 인증을 하고 있기 때문에 csrf에 공격방어에 안전합니다! 그래서 csrf().distable()과 같은 설정을 줍니다.
해당 내용은 Spring security 공식문서에 나와있습니다.
@Configuration
public class EncrypterConfig {
@Bean
public BCryptPasswordEncoder encodePwd() {
return new BCryptPasswordEncoder();// password를 인코딩 해줄때 쓰기 위함
}
}
Java
복사
BcryptPasswordEncoder는 BCrypt라는 해시 함수를 이용하여 패스워드를 암호화하는 구현체이다. Config 객체 내부에서 PasswordEncoder의 구현체로 BCryptPasswordEncoder를 지정해주었으니 이를 스프링 프레임워크에서 사용하도록 스프링 빈(Bean)으로 등록해주어야 한다.
@Configuration을 클래스 이름 위에 선언하면 스프링 프레임워크가 빈을 주입하는 클래스로 인식
@Configuration이라고 하면 설정파일을 만들기 위한 애노테이션 or Bean을 등록하기 위한 애노테이션이다.
public String login(String userName, String password){
//userName없음
User selectedUser = userRepository.findByUserName(userName)
.orElseThrow(() -> new AppException(ErrorCode.USERNAME_NOTFOUND, userName + "이 없습니다."));
//password틀림
if(!encoder.matches(password,selectedUser.getPassword())){
throw new AppException(ErrorCode.INVALID_PASSWORD,"패스워드를 잘못 입력헀습니다.");
}
String token = JwtTokenUtil.createToken(selectedUser.getUserName(),key,expireTimeMs);
//앞에서 Exception안났으면 토큰 발행
return token;
}
Java
복사
암호화는 BCryptPasswordEncoder로 구현된 encoder()를 이용한다. 파라미터에 패스워드를 주입하면, 암호화된 패스워드를 반환해준다.
그리고 패스워드와 암호화된 패스워드가 서로 같다라는 사실이 증명되어야 로그인을 구현할 수 있을 것이다. 이 때 이 두 문자열을 비교해주는 메소드가 matches()이다.
•
boolean matches(String raw, String encoded)
•
제출된 인코딩 되지 않은 패스워드(일치 여부를 확인하고자 하는 패스워드)와 인코딩 된 패스워드의 일치 여부를 확인해줍니다.
•
첫 번째 매개변수는 일치 여부를 확인하고자 하는 인코딩 되지 않은 패스워드를 두 번째 매개변수는 인코딩 된 패스워드를 입력합니다.