-

[스프링부트 및 JPA] - Security Configuration과 CSRF토큰 본문

백엔드 기술 정리/스프링 부트

[스프링부트 및 JPA] - Security Configuration과 CSRF토큰

흣차 2022. 1. 28. 23:03
728x90
반응형

스프링부트는 시큐리티 기능을 제공하고 있습니다.

사용자가 로그인을 하고 사이트를 들어가려고 할 때 허가 없이 사이트의 정보를 얻으면 안되겠죠?

스프링부트에서는 이 기능을 제공함으로써 허가된 사용자에게 권한을 줄 수 있습니다.

일단 시큐리티 세팅을 해보도록 하겠습니다.

저는 css, html파일은 인스타그램 클론 코딩의 프론트단을 받아서 사용하고 있어서 따로 프론트 쪽 설명은 하지 않습니다.

이렇게 생겼습니다.

근데 중요한건 localhost:8080만 입력해도 8080/login으로 변경이 됩니다.

왜냐하면 pom.xml보면

시큐리티 라이브러리가 이렇게 등록이 되어 있습니다.

이 라이브러리가 어떤 역할을 하냐면, 클라이언트가 우리 서버에 들어오려고 하면 인증되지 않은 모든 사용자를 로그인창으로 Redirection해버립니다.

아까도 보았듯이 8080을 호출했는데 8080으로 안가고 login창으로 튕기게 만들었죠?

이걸 개발자 도구에서 살펴보면

보면 Status code가 302번이네요.

이건 저번 포스팅에서 나왔던 Redirection을 의미합니다. 

이 작업을 바로 시큐리티 라이브러리가 수행합니다.

자 그래서 이 시큐리티를 섬세하게 다뤄보도록 하겠습니다.

먼저 config패키지를 만들고 SecurityConfig클래스를 만듭니다.

이렇게 SecurityConfig클래스를 생성하고 WebSecurityConfigurerAdapter를 상속받습니다.

그리고 위에 @Contfiguration 어노테이션을 달아서 IoC에 등록합니다.

그리고 또 그 위에 @EnableWebSecurity를 달면 SecurityConfig의 시큐리티를 활성화할 수 있습니다.

클래스 내부에 Ctrl + space바를 누르고 configure(Http~ ) 뜨는게 있는데 선택합니다.

여기에서 super 얘들이 실행되고 있어서 계속 login페이지로 가로채갑니다. 

이애들을 삭제하면 어떻게 되냐면 기존 시큐리티가 가지고 있는 기능이 모두 비활성화 됩니다.

실제로 저장 후 실행하면 시큐리티가 못낚아채고 404에러가 뜹니다.

그러므로 낚아채게 하기 위해서는 인증이 되지 않은 모든 사용자가 어떤 페이지로 이동하게 할 것인데, 이렇게 입력합니다.

이렇게 입력하게 되면 http에 authorizeRequest() 그러니까 인증 요청을 보내는데,

.antMatchers -> 해당 사이트에 기입된 사이트는 

.authenticated() -> 인증이 필요하고

.anyRequest() -> 그 외의 어떤 요청이라도

.permitAll() -> 모두 인증안해도 들어갈 수 있다!

이런 의미입니다. 

그럼 우리는 어떻게 할 것이냐면요.

인증이 필요한 페이지를 요청했을 때 로그인창으로 자동으로 가게 하고싶은 것입니다.

그래서 여기서 끝나는게 아니라 이렇게 입력해줍니다.

위에 상기된 사이트들 모두 formLogin()이 필요한데,

formLogin() -> 위의 사이트는 로그인을 해야한다!

.loginPage("/auth/siginin") -> 여기 사이트가 로그인 페이지다!

.defaultSuccesUrl("/"); -> 로그인에 성공하면 여기로 이동해라

이렇게 작성하시면 시큐리티의 요청 재분배를 이용하여 로그인단을 만들 수 있습니다.

 

회원가입 구현)

자 그럼 이젠 회원가입을 좀 더 자세히 구현을 해보겠습니다.

AuthController라는 클래스를 만들고

file을 return할 것이므로 @Controller어노테이션을 단 뒤 sigininForm()과 signupForm()을 만듭니다.

return할 때 jsp파일을 return할 것이기 때문에 당연히 src/main/webapp/web-inf에 signin과 signup.jsp를 만들어야 합니다.

그리고 signup.jsp에는

이렇게 회원가입 로직을 만들었습니다.

그리고

우리는 signup()함수를 Post방식으로 제출할 것이므로 AuthController에 이렇게 만들었습니다.

signup()함수를 실행하고 나면 결과값으로 auth/signin으로 응답을 받도록 하겠습니다.

위의 @GetMapping의 signupForm()과는 다른 함수입니다.

signupForm()은 해당 사이트를 요청했을 때 회원가입 페이지가 뜨도록 @GetMapping한 것이고 아래의 @PostMapping의 signup()함수는 회원가입이 완료되면 auth/signin페이지가 뜨도록 요청하는 것입니다.

여기서 끝낼게 아니라 회원가입 로직을 이제 만들어야 합니다.

생각을 해봅시다.

회원가입 버튼을 클릭하면 /auth/signup으로 이동하고 회원가입이 완료되면 /auth/signin으로 이동해야 합니다.

그렇지만 막상 실행하면 403에러가 뜨게 됩니다.

그 이유는 CSRF토큰을 해제하지 않아서 그렇습니다.

CSRF토큰이 뭘까요???

자 클라이언트가 로그인을 하려고 했다고 해봅시다.

이걸 웹서버에 전송을 하면 서버가 이것을 받게 되지만 그 전에 이 서버의 외부에는 시큐리티라고 하는 것이 이 서버를 감싸고 있습니다.

그래서 서버에 도착하기 전에 먼저 시큐리티가 이 사용자를 검사를 하게 됩니다.

검사를 하고 난 뒤 서버는 클라이언트에게 응답을 해줄 때 응답해주는 페이지가 signup.jsp가 됩니다.

그런데 시큐리티는 응답해줄 때 이 .jsp파일에 토큰을 심습니다.

그것이 바로 CSRF 토큰입니다.

따라서 사용자의 아이디와 password가 토큰이 심어져서 난수값으로 변형이 됩니다.

우리는 작성을 따로 하지 않아도 시큐리티가 이것을 담아서 준다는 의미입니다.

이 상태에서 회원가입 요청을 마치고 로그인 요청을 하게 되면 이 웹서버는 CSRF토큰이 있는지를 확인합니다.

그런데 검사를 해봤더니 토큰을 가지고 있다면 "아 얘는 정상적인 사용자구나"라고 판단할 수 있습니다.

그렇다면 비정상적인 사용자는 어떻게 구분할 수 있을까요???

우리는 개발자이기 때문에 모든 사용자가 정상적인 접근으로 사이트를 사용한다고 생각하면 큰일난다는 생각을 먼저 할 필요가 있습니다.

예를 들어서 어떤 사용자가 Postman을 이용해서 signin회원가입 페이지를 통해 하지 않고 다른 방법으로 회원가입을 수행했다면 이 사용자는 CSRF토큰을 받을 수 있을까요?

그렇지 않겠죠??

그래서 우리는 이 CSRF토큰을 비활성화 시켜야만 합니다.

얘가 돌고 있으면 나중에 JS로 요청하기에도 힘들어서 시큐리티에서 비활성화시킬 것입니다.

그러므로 SecurityConfig로 가서 

이렇게 csrf()를 비활성화 시키면 됩니다. 간단하죠?

이렇게만 입력해주어도 사용자가 postman을 통해 가입하든, 여타 다른 프로그램을 이용해서 가입을 하든 우리는 구분하지 않게 만들 수 있습니다.

감사합니다.

728x90
반응형
Comments