콘텐츠로 이동

2021 07 18

2021-07-18

생활코딩 OAuth

  • 개요 - 사용자 -> 서비스 -> Google 캘린더에 기록 - 사용자의 id/pw를 받아서 서비스가 기록해주기 - 모두가 불행해지는 지름길 - OAuth를 통해 극복! - 사용자의 요청에 따라 accessToken을 Google이 발급해줌 - accessToken을 서비스가 넘겨받음 - accessToken을 사용하여 서비스가 Google이 허락한 범위 내에서 기능 처리 뚝딱
  • 역할 - Resource Owner (user) - Client (서비스) - Resource Server (Google) - Authorization Server (인증 전담 서버) 포함
  • 등록 - Client --[register]--> Resource Server - [register] - Client ID, Client Secret, Authorized redirect URIs (https://client/callback) - 구글님! 저 당신의 OAuth 서비스를 쓸려고 합니다. - 우리 서비스는 이런 이름을 가지고 있고, 이런 URL을 사용해요. - 인증 다 해주시고 https://client/callback으로 요청 주세요!
  • Resource Owner의 승인 - 이제 Resource Owner가 우리 서비스 들어와서 구글로 로그인 하기 클릭했어 - 그러면 우리 서비스에서 구글에게 요청을 보내줘야해 - https://resource.server?client_id=1&scope=B,C&redirect_uri=https://client/callback - 발급 받은 client_id : 1 - 사용하고자 하는 기능 : B, C - 리다이렉트 할 URI : https://client/callback - 이제 Resource Owner가 구글에 로그인하면,,, - 구글에서 user_id = 1이, 기능 B,C에 대해 나의 서비스에서 접근 가능하도록 요청했다 기록
  • Access Token - Resource Server랑 서비스에서 Authorization_Code를 지움! - Resource Server가 AccessToken 발급하고, 서비스에게 넘겨줌! - Access Token을 이제 HTTP 헤더 에다가 끼워서 넣어주세요~
  • Refresh Token - Access Token은 수명이 있어! - 그래서 Resource Server가 처음에 Access Token이랑 Refresh Token을 같이 줘 - Access Token이 Invalid라면 Refresh Token으로 찔러서 새로운 Access Token 받아!

Spring RestDocs

  • 참고: https://techblog.woowahan.com/2597/
  • RestDocs vs Swagger - RestDocs 장점 - Production 코드 영향 X - 테스트 코드 성공 시 문서작성 완료 - RestDocs 단점 - 적용하기 어려움
  • MockMvc vs RestAssured - MockMvc를 통해 @WebMvcTest로 테스트 수행하자 - Controller Layer만 테스트함 - Spring RestDocs를 통해 문서 작성하기엔 MockMvc가 더 좋음
  • build.gradle 살펴보기 - junit, MockMvc(@WebMvcTeat), asciiDoc을 통해 최소한의 라이브러리로 문서화 제공 가능
    buildscript {
        ext {
            springBootVersion = '2.1.1.RELEASE'
        }
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath("org.springframework.boot")
        }
    }
    
    plugins {
        id "org.asciidoctor.convert" version "1.5.9.2" // (1) AsciiDoc 파일 컨버팅하고, Build 폴더에 복사
    }
    
    apply plugin: 'java'
    apply plugin: 'eclipse'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'
    
    group = 'com.example'
    version = '0.0.1-SNAPSHOT'
    sourceCompatibility = 1.8
    
    asciidoctor {
        dependsOn test // (2) gradle build 시 test -> asciidoctor 순으로 수행
    }
    
    bootJar {
        dependsOn asciidoctor // (3) gradle build 시 asciidoctor -> bootJar 순으로 수행
        from ("$/html5") { // (4) ./build/asciidoc/html5/에 html 파일 생성
            into 'static/docs' // 이후 /static/docs/ 폴더에 복사가 됨
        }
    }
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        implementation('org.springframework.boot:spring-boot-starter-web')
        implementation('org.springframework.boot:spring-boot-starter-data-jpa')
        implementation('org.projectlombok:lombok')
        runtimeOnly('com.h2database:h2')
        testImplementation('org.springframework.boot:spring-boot-starter-test')
        testImplementation('org.springframework.restdocs:spring-restdocs-mockmvc') // (5)
    }
    
  • 기능 살펴보기 - getDocumentRequest() 로 호스트 이름을 바꿔주자!ㄴ
    @ActiveProfiles("test")
    @AutoConfigureRestDocs
    @MockBean(JpaMetamodelMappingContext.class)
    public class ControllerTest {
        @Autowired
        private WebApplicationContext wac;
    
        @Autowired
        private RestDocumentationContextProvider restDocumentationContextProvider;
    
        @Autowired
        public ObjectMapper objectMapper;
    
        @MockBean
        public AuthService authService;
    
        public MockMvc mockMvc;
    
        @BeforeEach
        void setUp() {
            mockMvc = MockMvcBuilders.webAppContextSetup(wac)
                    .addFilters(new CharacterEncodingFilter("UTF-8", true))
                    .alwaysDo(print())
                    .apply(MockMvcRestDocumentation.documentationConfiguration(
                            restDocumentationContextProvider
                    ))
                    .build();
        }
    
        protected static OperationRequestPreprocessor getDocumentRequest() {
            return preprocessRequest(
                    modifyUris()
                            .scheme("https")
                            .host("nolto.kro.kr")
                            .removePort(),
                    prettyPrint());
        }
    
        protected static OperationResponsePreprocessor getDocumentResponse() {
            return preprocessResponse(prettyPrint());
        }
    }
    

HTTP Redirect

  • 참고 1: https://dololak.tistory.com/147
  • 참고 2: https://mangkyu.tistory.com/51
  • 리다이렉트란? - 웹서버는 HTTP 응답 상태코드를 302로 보내고, 헤더 중 Location 값으로 리다이렉트 되어야 할 주소 설정해 리턴
  • Forward vs Redirect - Forward - 브라우저는 다른 페이지로 이동한지 몰라 - 브라우저에는 최초 호출한 URL 표시되고, 이동한 URL의 정보 확인 X - 현재 실행중인 페이지와 forward에 의해 호출될 페이지는 Request, Response 객체 공유함 - 이동할 URL로 요청정보를 그대로 전달 - 사용자가 최초로 요청한 요청정보는 다음 URL에서도 유효 - Redirect - 웹 브라우저에게 다른 페이지로 이동하라고 명령 - URL을 지시된 주소로 바꾸고 이동 - 새로운 Request, Response 객체 생성