마트철수

[069] Rest API 본문

KB IT's Your Life/교육

[069] Rest API

마트스 2024. 8. 19. 17:23

 

2024.08.19(월)
 
Spring 10일차


json을 이용해서 객체의 모양으로 정보를 송수신함

그러면 클라이언트의 제약이 없어짐!

 


 

Spring


 

PART01

  • CH02. 스프링의 특징과 의존성 주입
  • CH03.1 스프링 MVC의 기본 구조
  • CH03.2 스프링 MVC의 Controller 1
  • CH03.3 스프링 MVC의 Controller 2
  • CH03.4 SpringLegacy 업데이트
  • CH04.1 스프링과 MySQL Database
  • CH04.2 MyBatis와 스프링 연동
  • CH05.1 영속, 비즈니스 계층의 CRUD 구현
  • CH05.2 비즈니스 계층
  • CH05.3 프레젠테이션(웹) 계층의 CRUD 구현
  • CH06.1 화면 처리
  • CH06.2 File, Upload, Download

Part 4. Rest API

  • CH07 Rest Controller
  • CH08.1 OpenApi
  • CH08.2 RestTemplate
  • Spring Security

 


CH08.1 OpenApi


OpenApi - Openweather

 

Open Api 호출

https://api.openweathermap.org/data/2.5/weather?q=seoul&APPID=xxxxxxxxx&lang=kr

 

png 호출

01d.png

https://openweathermap.org/img/w/01d.png

 

 

CH08.2 RestTemplate

 

 

# 라이브러리를 사용해서 클래스로 정의?

 

의존성 추가

implementation 'org.apache.httpcomponents:httpcore:4.4.15'
implementation 'org.apache.httpcomponents:httpclient:4.5.13'

 

RoboPOJOGenerator 설치

  • 결과 json에 해당하는 걸 플러그인 해줌

RoboPOJOGenerator

 

POJO 생성

  • Lombok, Jackson 중복 선택
  • 최상위 클래스명을 지정해주어야함
  • main, WeatherItem이 중요한 클래스 !!

 

RestTemplate 메서드

  • getForObject : # 객체로 해석해달라 → GET 요청을 보내고 객체로 결과 반환

RestTemplate 메서드

 

RestTemplate의 동작 원리

  • HTTPClient: HTTP를 사용하여 통신하는 범용 라이브러리
  • RestTemplate: HttpClient를 추상화해서 제공

 

WeaterController.java

  • null 여부에 따라 분류
  • RestTemplate 준비하고 fromHttpUrl
@GetMapping({"", "/{city}"})
public String weather(Model model, @PathVariable(value="city", required = false) String city) {
city = city == null ? "seoul" : city;

 

package org.scoula.weather.controller;

import org.springframework.beans.factory.annotation.Value;
import lombok.extern.slf4j.Slf4j;
import org.scoula.weather.dto.WeatherDTO;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

@Controller // 메소드의 반환값을 뷰로 받는다
@Slf4j
@RequestMapping("/weather")
@PropertySource({"classpath:/application.properties"}) // 어디서 리소스를 가져올지 명시
public class WeatherController {
    @Value("${weather.url}")
    private String URL;
    @Value("${weather.icon_url}")
    private String ICON_URL;
    @Value("${weather.api_key}") //application.properties에서 키값으로 설정을 가져옴
    private String API_KEY;

//    도시명을 PathVariable로 받겠다
    @GetMapping({"", "/{city}"}) // 주소 형식이 /weather, /weather/{city}로 올 경우 둘 다 처리
    public String weather(Model model, @PathVariable(value="city", required = false) String city) {
//        requires=false로 주면 해당 값이 옵션 처리된다
        city = city == null ? "seoul" : city; // city가 null이면 city는 서울. null이 아니면 넣어준 값 그대로 return 한다.

        RestTemplate restTemplate = new RestTemplate();
        String url = UriComponentsBuilder.fromHttpUrl(URL)
                .queryParam("q", city)
                .queryParam("units", "metric")
                .queryParam("APPID", API_KEY)
                .queryParam("lang", "kr")
                .toUriString(); // 기본 url에 쿼리스트링을 연결해서 새로운 주소 생성
//        생성한 url을 통해서 WeatherDTO 결과값 반환받아옴
        WeatherDTO weather = restTemplate.getForObject(url, WeatherDTO.class);

        // List<WeatherItem>에서 get(얻어와) 0번째를
        String iconUrl = ICON_URL.formatted(weather.getWeather().get(0).getIcon());
        // WeatherDTO의 weather => WeatherItem의 리스트 중 첫번째 아이템의 아이콘으로 ICON_URL 포맷
        // 제대로 받았는지 log로 확인하고
        log.info("오늘의 날씨: " + weather);

        // 도시, 날씨, 아이콘 url이 무엇인지 확인 ?
        // 뷰쪽에서 사용하기 위해 model에 데이터 저장
        model.addAttribute("city", city);
        model.addAttribute("weather", weather);
        model.addAttribute("iconUrl", iconUrl);

        // @Controller에서는 리턴값이 곧 뷰 이름
        return "weather/today";
    }
}

 

 


http://localhost:8080/weather

http://localhost:8080/weather


 

 

Spring Security

 

 

Spring Web Security 설정

  • filter 기반 → 비지니스 로직에는 영향을 주지 않는다
  • controller까지 오는 과정에서 보안 처리 = filter 레벨에서 처리하겠다
  • # 보안은 이전에 있던 라이어브러리를 사용해라
  • # 왜냐면 보안은 내(초보 개발자)가 직접 하게되면 다 뚫려버리기 때문 ..

 

스프링 시큐리티 아키텍처

1. AuthemticiationFilter

  • 분석 시작
  • 로그인하면 post 요청으로 username, password가 전달됨
  • User ~ Token을 만들어줌
  • 어디서 username과 password를 관리해주나? 다 interface로 추상화해둠

2. AuthenticationManager

  • 준비되어있는 user 정보 관리자에게
  • 주로 데이터베이스에 저장하지만, 메모리나 file 등에도 가능

3. AuthenticationProvider

  • 이 user에 대한 password가 무엇인지 확인

4. UserDetailsService

  • 실제 DB에 접근하는 서비스

5. UserDetails

  • 올바른 사용자인지 판단 후 리턴

※ 정상적인 이용자라면 SecurityContextHolder에 쌓임

View가 이 홀더에 접근해서 사용자에 대한 정보를 얻어낼 수 있음

 

위 스프링 시큐리티 아키텍처 흐름을 이해해야함
UserDetailsService, UserDetails는 우리가 구현해야하는 부분

 

 

SecurityConfig.java

  • 보안에서 가장 중요한 Config
package org.scoula.security.config;

import lombok.extern.log4j.Log4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
@Log4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}

 

인증과 권한 부여

  • 인증: 자신을 증명하는 것
  • 인가: 남에 의해서 자격이 부여됨

 

스프링 시큐리티를 커스터마이징 하는 방식

  • AuthenticationProvide를 직접 구현하는 방식
  • 실제 처리를 담당하는 UsrDetailsService를 구현하는 방식

AuthenticationManager(인증 매니저)

  • 인증 담당
  • 다양한 방식의 인증을 처리

AuthenticationManager(인증 매니저)

 

Security Filter Chain

 

 

Security Filter Chain

 

로그인 로그아웃

 

 

SecurityConfig.java (★)

  • 문자셋 필터를 Override 앞에 위치 시켜라
  • 매우 중요한 Config
package org.scoula.security.config;

import lombok.extern.log4j.Log4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.web.filter.CharacterEncodingFilter;

@Configuration
@EnableWebSecurity
@Log4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // 문자셋 필터
    public CharacterEncodingFilter encodingFilter() {
        CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
        encodingFilter.setEncoding("UTF-8");
        encodingFilter.setForceEncoding(true);
        return encodingFilter;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(encodingFilter(), CsrfFilter.class);

    }
}

 

 

접근 제한 설정

  • 경로별 인증/권한 설정
    • 내부적으로 builder 패턴적용되어 있음.
    • 대부분 메서드의 리턴값이 HttpSecurity임  메서드 체인닝으로 설정해 나감

 

http.authorizeRequests() // 모두 허용

.antMatchers("/security/all").permitAll() // 특정 역할에게만 허용 .antMatchers("/security/admin").access("hasRole('ROLE_ADMIN')") .antMatchers("/security/member").access("hasRole('ROLE_MEMBER')")

// 로그인 사용자에게 허용

.antMatchers("/board/write",

                      "/board/modify",

                      "/board/delete").authenticated();

 


 

지금까지 완료한 페이지는 아래와 같다.

 

실제 로그인이 되지 않은 상태이나,

서버로 연결되었다니 너무 신기하군 ..!

 

server login

 

'KB IT's Your Life > 교육' 카테고리의 다른 글

[071] JWT, Api Server Security  (0) 2024.08.21
[070] Spring 로그인과 로그아웃 처리  (0) 2024.08.20
[068] REST API  (2) 2024.08.14
[067] Spring 화면 처리 - BoardController  (0) 2024.08.13
[066] Spring: 서버로 화면 처리  (0) 2024.08.12