[Spring] CH21 스프링부트 OAuth2.0 카카오 로그인

jaegeunsong97·2023년 4월 3일
0

[Fast Campus] Spring

목록 보기
39/44
post-thumbnail

📕 1교시


CORS에 대한 설명

프론트 개발자랑 같이 개발하기 위한 필수 정책

도메인이 각각 다르다는 것이다.

서버쪽 8080 포트
프론트 3000 포트

포트만 달라도 도메인이 다르다(다른 프로그램이라고 생각하자)

fetch에는 도메인 풀로 다 적어야한다.

그럼 서버입장에서는 이 부분에서 들어오게 되고, 크로스 도메인으로 인식하한다.

가장 문제가 되는 요청은 JS이다.

왜냐하면 JS로 공격이 가능하기 때문에, JS요청으로 오면 전부 막아버려야한다.

따라서 CORS의 정책은 JS 요청을 거부한다. 크로스 도메인일 떄 거절한다.

만약 같은 도메인의 jsp코드인 경우에는 막지않는다.

오직 다른 도메인에서 오는 것을 막는다.

즉 불특정 다수에서 공격이 올수도 있는 것이다.

우리는 어디만 허용하면 될까?

이 부분만 허용을 하면 되는 것이다.

Controller에 @CrossOrigin을 걸면 모든 IP를 허용한다는 것이다.

근데 이렇게 하면 Controller마다 달아야하기 떄문에, fileter에서 전역적으로 막아야한다.

따라서 front 쪽에서 오는 것만 막아주는 filter를 만들면 되는 것이다.

이런식인데, 이렇게 하지말고 전역적으로 걸어야한다.

우리는 나중에 API를 만들건데, 이 문서를 Front에게 주면 CORS 정책때문에 거절당할 것이다.

따라서 우리는 다음과 같이 해야한다.

기본적으로 same origin policy 라고 한다.

이런 정책에서 front쪽에서 오류가 나는데, front 에서 처리해야하는게 아니라, 서버쪽에서 처리를 해야한다.

CORS의 목적은 리소르를 들고있는 서버가 JS로 공격하는 특정 누군가에게 자원을 주지 않겠다는 것이다.

이것은 서버가 주도권을 가지고 있다.

여기서 access control allow origin을 한다는 것은

다음과 같이 코드를 작성하는 것이고, 브라우저에게 알려주는 것이다.

저 포트만 데이터를 오픈을 하고 그게 아니면 브라우저가 닫는 것이다.

즉, 자바스크립트의 제어권은 브라우저가 가지고있는 것이다.

XSS 공격

XSS와 CSRF 비교

그림으로 이해

프론트로도 막아도 포스트맨으로 가능하다...

따라서 이게 DB에 그대로 갈 것이다.

브라우저가 script를 읽을때 히스토리 백이 발동한다. 가장 기본적인 XSS 공격이다.

따라서 막을려면 애초에 들어올 때 filter에서 막게 하면 된다.

요즘은 브라우저가 똑똑해서 알아서 막아준다.

보면 jsp는 이런 공격을 막지 못한다.

하지만 다른 템플릿은 이런것들은 막아준다.

따라서 애초에 Jsp쪽에서는 처음부터 막아줘야한다.

머스테치

막힌다.

이것들이 전부 서버사이트 렌더링이다.

우리는 오직 JS가 들어오는 filter만 신경을 써주면된다.

이게 가장 단순한 XSS 공격이다.

이 주소는 인증이 필요하다. 즉 세션이 필요하다는 것이다.

그리고 세션의 userId와 주소의 id가 동일할때 해당 코드가 발동하도록하는 권한도 필요하다.

예를 들어 DB가 있다.

실행이 되고

이거를 실행할 수 있는 사람은 인증도 되고, userId가 1번인 사람이다.

이거를 JS로 뚫을 수 있다.

다시 예를 들어보자

자유게시판이 있다.

이런 포인트들은 관리자들은 전부 컨트롤이 가능하다.

자유게시판에 글을 적는다고 하자.

그리고 fectch 주소에 아래의 주소가 들어간다.

관리자는 모든 것에 접근이 가능하니까, 관리자가 열어보는 순간 fetch가 발동을 한다.

관리자가 발동을 해당 주소를 발동을 시킨다.

이게 XSS 공격이다.

이거는 본인 사이트에서 공격을 하는 방법 2가지이다.

최종 예시

관리자 시점

다 뚫림

CSRF 예시

홍길동 로그인된 상태(세션이 있는 상태)

다른 서버에 글 적기

홍길동이 해당 JS가 적혀있는 글(fetch)을 클릭

타사 사이트 클릭 하는 순간, 자기의 권한 + 세션이 있으니까. 자기 돈을 다른사람에게 보낼 수 있다.

즉, CSRF는 타사 사이트를 우회한다. 그래서 홍길동쪽 정보를 가진 서버는 CORS를 해야한다.

즉, 타사 사이트 공격 전부 막아버리기!

그래서 절대 get요청으로 insert를 하면 안된다.

따라서 get은 read만 해야한다.

get요청으로 delete도 하면 안된다.

세션이 살아있기 떄문에(=로그인 상태 유지) 털릴 수 있다.




📕 2교시


요즘은 1줄로 막을 수 있다.

하지만 CORS는 알아야한다. 서버 개발자가 직접 풀어줘야하기 때문에

JS 공격이 가능하기 때문에

전부 JS 공격과 관련이 되어있다. JS에 대한 보안이 올라가고 있다.

OAuth는 권한을 받는 것이다.

카카오, 페이스북 이런곳에 접근할 수 있는 사람은 권한이 있는 사람 즉, 우리이다.

먼저 그림으로 보자

여기서 Oauth는 open Auth 인증을 열어준다는 것이다.

이렇게 절대로 접근할 수 없다. 권한이 없는 것ㅇ디ㅏ.

여기에 접근할 권한은 리소스 오너이다.

Oauth는 리소스 오너가 클라이언트에게 리소스 서버에 접근할 권한을 주는 것이다.

핵심은 리소스 오너가 우리가 만든 스프링 서버에 리소스 서버에 접근할 수 있는 권한을 부여하는 것이다.

2가지가 있다

토큰을 받는다.

은행은 key로 토큰을 생성할 수 있다.

여기서 중개인은 토큰을 검증을 할 필요가 없고, 토큰을 들고 은행에게 간다.

그럼 은행은 토큰 검증을 해야한다.

검증을 key로한다.

이떄 key는 대칭키 기반 방식이다. 왜냐하면 공개할 필요가 없기 떄문이다.

대표적으로 key는

만약 공개키로 만든다면 어떻게 될까?

공개키 기반방식에서는 은행을 갈 필요가 없다. 스스로 공개키로 검증을 할 수 있다.

굳이 공개키로 할 필요가 없다. 왜냐하면 속도가 비슷하기 떄문에

카카오는 어떻게 할까?

리소스 오너가 탈취 당할 수 있으니까, 클라이언트가 카카오에 먼저 신뢰성 등록을 한다.

등록이 되어있지 않으면 카카오는 절대로 받지 않는다.

등록이 끝나면

임시코드를 받으면 임시코드를 다시 날린다.

카카오는 이떄 2가지를 검증한다. 신뢰성과 코드 2개를 그리고 나서 토큰을 준다.

그러면 토큰은 리소스오너가 받지 않고 클라이언트만 가지게 된다. 그러면 클라이언트는 카카오 DB에 리로스 서버의 리소스 오너부분에만 접근이 가능하다.

클라이언트가 토큰을 받는 순간, 마지막으로 리소스에 접근을 해봐야한다.(유효성 검사)

그러면 Oauth 제공정보를 할 떄 이메일, 이름 체크한 정보만 접근이 가능하다.

이메일을 받는 순간 홍길동은 정상적인 user구나를 알 수 있다.

그 이후부터는 인증이 끝나기 때문에 해당 부분을 무시해도 된다.

이전 중개인 로직에서는 중개인과 은행이 움직였다.

이번에는 이민자 파트를 보자

그림

이번에는 토큰을 받는다.

아까 꺼랑 비교를 해보자

중개인은 토큰을 가지고 은행에 접근이 가능하다.

위에꺼와 아래꺼의 차이는 인증 방식이 다르다.

이민국 부분은 필요한 부분이(목적)

중개인의 목적

여기서 카카오는 코드 방식을 사용한다. 그리고 목적은 중개인과 홍길동이 대화를 해야한다.

코드 방식의 목적은 중개인에게 토큰을 위임하는 것이다.

즉, 인증방식에는 2가지

카카오

리엑트 또는 휴대폰 앱

카카오 로그인 가능, 전부 CC 방식으로 함

프론트에서는 여기 까지만 한다.

이제 서버 개발자가 프론트에게 토큰 달라고 해야한다.

그러면 서버는 API를 만들어야한다.

하지만 아직 토큰을 신뢰할 수 없다.

서버는 받자마자 카카오에게 물어봐야한다. 왜물어볼까? 대칭키니까, 만약에 공개키였으면 스스로 검증이 가능하다.

여기서 토큰 검증을 한다는 개념은 리소스 서버 자원에 접근을 해보는 것이다.

그러면 결국 뭔가를 받아올 수 있다. 동의를 몇개를 했냐에 따라서 email, username, 등등 들고올 수 있는 것이다.

들고오고 난 후부터는 2이서만 자원을 공유하면 된다. 카카오는 무시해도 된다.

그리고 매번 카카오 토큰을 받아서 할 수 없다. 비효율적이다.

따라서 JWT를 사용해 개인 토큰을 만들어야한다.

1) email, username을 가지고 강제 회원가입을 시킨다.
2) 나만의 토큰을 만든다.(JWT)
3) 나만의 토큰을 응답

그럼 이제 Front 쪽은 나의 토큰을 들고 있기 떄문에 서버쪽에 요청을 하면 검증을 할 수 있따.

굳이 카카오까지 갈 필요가 없다.




📕 3교시


이거는 Oauth에서 제공을 한다.

만약에 코드 방식이면

토큰을 받았으니까

1) 강제 회원가입
2) 나만의 토큰 생성

이렇게 하지않으면 카카오로 앱에게 전달하고 스프링 서버가 받고, 카카오에게 전달하고를 계속 반복해야한다.

정리하면

code 방식이 안전(많이 사용)

code 방식의 목적은 중개인과 은행이지만, 목적과 다르게 중개인과 고객으로 바꿔서 사용하는 것이다.

카카오

카카오 로그인

왜하냐? 나의 서버를 등록하기 위해서

서버쪽에 나의 app이 등록된 것! 회원가입도 했으니까 나는 신뢰할 수 있는 사람

나는 중개인 이니까

이거를 하면 카카오가 리스트에 나의 app(스프링 서버) 저장

스프링 서버가 email에만 접근 가능

restAPI

세팅은 이거만 하면 끝

코드

아까전 callback은 이 부분 홍길동이 받은 코드를 중개인에게 전달하는 것

세션 허용을 위해서

loginForm

RestApi보고 하이퍼링크 만듬

주소

client_id, 콜백 주소, 임시코드, scope email

이거보고 만듬

코드를 새롭게 테스트 용

보낼 때 (request)

그러면

리다이렉션 주소(콜 백 주소)

받은 과정 그림으로

API중에callback 함수 있다.

전달 3개 client_id, call back 주소, scope(email)

client_id - 검증
call back 주소 - 확인
scope(email) - 확인

이떄 주소는

누가 받냐?

콜백 주소를 받는 이유 -> 리다이렛션을 이용해서 받는다. 302를 통해

지금 이제부터 이 코드를 카카오에게 던져서 검증 필요

body O (query X)

이 중에서는 code만 받으면 되고 나머지는 신원검증을 하는 것이다.

Ajax 할 때랑 똑같다.

Response

중요한 것은 토큰(Json 형태로 온다)

따라서 Json을 받을 Dto 만들기

사용자 정보 요청

위에 문서보고 만듬

만든 근거?

직접 뿌려보자

@GetMapping("/callback")
    public @ResponseBody String callback(String code) throws JsonProcessingException {
        // 1. code 값 존재 유무 확인
        if(code == null || code.isEmpty()){
            return "redirect:/loginForm";
        }

        // 2. code 값 카카오 전달 -> access token 받기 (토큰 받기 문서 확인)
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "authorization_code"); // 신원 검증
        body.add("client_id", "1f7061f93b3e8f6cbb2a143f64f71f0c"); // 신원 검증
        body.add("redirect_uri", "http://localhost:8080/callback"); // 2차 검증, 신원 검증
        body.add("code", code); // 핵심

        ResponseEntity<String> codeEntity = Fetch.kakao("https://kauth.kakao.com/oauth/token", HttpMethod.POST, body);

        // 3. access token으로 카카오의 홍길동 resource 접근 가능해짐 -> access token을 파싱하고
        ObjectMapper om = new ObjectMapper();
        om.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); // SNAKE_CASE -> commel 표기법으로 바꾼다!
        KakaoToken kakaoToken = om.readValue(codeEntity.getBody(), KakaoToken.class); // json data -> KakaoToken.class로 받기

        // 4. access token으로 email 정보 받기 (ssar@gmail.com)
        ResponseEntity<String> tokenEntity = Fetch.kakao("https://kapi.kakao.com/v2/user/me", HttpMethod.POST, kakaoToken.getAccessToken());

        return tokenEntity.getBody();
    }

scpoe가 많이 체크되어있으면 email 뿐만 아니라 여러개가 나온다.

DB에 있는지 확인! -> 나머지는 강제로 회원가입하고 강제로 세션만들기

가장 중요한 것은 순서 1, 2, 3, 4가 중요하다!!

수업 끝

profile
블로그 이전 : https://medium.com/@jaegeunsong97

0개의 댓글