Spring 간단 정리 - Kim-Taesu/study GitHub Wiki
- www
- 인터넷에 연결된 컴퓨터를 통해 사람들이 정보를 공유할 수 있는 전 세계적인 정보 공간을 말한다.
- 간단히 웹(Web)이라 부르는 경우가 많다.
- 인터넷
- 컴퓨터로 연결하여 TCP/IP 라는 통신 프로토콜을 이용해 정보를 주고받는 컴퓨터 네트워크이다.
-
인터넷 프로토콜 스위트 (TCP/IP 프로토콜)
- 인터넷에서 컴퓨터들이 서로 정보를 주고받는 데 쓰이는 통신규약(프로토콜)의 모음이다.
- 인터넷 프로토콜 슈트 중 TCP와 IP가 가장 많이 쓰이기 때문에 TCP/IP 프로토콜 슈트라고도 불린다.
- 네트워크 인터페이스 계층
- PPP(점대점 프로토콜)
- 네트워크 분야에서 두 통신 노드 간의 직접적인 연결을 위해 일반적으로 사용되는 데이터 링크 프로토콜이다.
- 점대점 프로토콜은 인증, 암호화[1]를 통한 전송 및 데이터 압축 기능을 제공한다.
- 이더넷
- 컴퓨터 네트워크 기술의 하나로, 일반적으로 LAN, MAN 및 WAN에서 가장 많이 활용되는 기술 규격이다.
- PPP(점대점 프로토콜)
- ARP
- ARP(주소 결정 프로토콜)
- 네트워크 상에서 IP 주소를 물리적 네트워크 주소로 대응(bind)시키기 위해 사용되는 프로토콜이다.
- ARP(주소 결정 프로토콜)
- 인터넷 계층
- 네트워크 경계를 거쳐 호스트로부터 기원한 네트워크 패킷을 필요하면 IP 주소로 지정된 목적지 호스트로 전송하기 위해 사용된다.
- IPv4, IPv6, ICMP
- ICMP : 네트워크 컴퓨터 위에서 돌아가는 운영체제에서 오류 메시지(Requested service is not available 등)를 전송받는 데 주로 쓰이며 인터넷 프로토콜의 주요 구성원 중 하나로 인터넷 프로토콜에 의존하여 작업을 수행한다.
- 전송 계층
- 계층 구조의 네트워크 구성요소와 프로토콜 내에서 송신자와 수신자를 연결하는 통신 서비스를 제공한다.
- 연결 지향 데이터 스트림 지원, 신뢰성, 흐름 제어, 그리고 다중화와 같은 편리한 서비스를 제공한다.
- TCP
- 인터넷 프로토콜 스위트(IP)의 핵심 프로토콜 중 하나로, IP와 함께 TCP/IP라는 명칭으로도 널리 불린다.
- TCP는 근거리 통신망이나 인트라넷, 인터넷에 연결된 컴퓨터에서 실행되는 프로그램 간에 일련의 옥텟을 안정적으로, 순서대로, 에러없이 교환할 수 있게 한다.
- UDP
- 인터넷 프로토콜 스위트의 주요 프로토콜 가운데 하나이다.
- UDP의 전송 방식은 너무 단순해서 서비스의 신뢰성이 낮고, 데이터그램 도착 순서가 바뀌거나, 중복되거나, 심지어는 통보 없이 누락시키기도 한다.
- UDP는 일반적으로 오류의 검사와 수정이 필요 없는 애플리케이션에서 수행할 것으로 가정한다.
- 응용 계층
- 인터넷 프로토콜(IP) 컴퓨터 네트워크를 통하는 프로세스 간 통신 접속을 위해 설계되어 통신 프로토콜과 방식을 위해 보유된 추상 계층이다.
- DNS, TLS/SSL, FTP, HTTP, IMAP, IRC, SMTP, SSH, 텔넷, 비트토렌트, RTP
- HTTP
- WWW 상에서 정보를 주고받을 수 있는 프로토콜이다.
- 주로 HTML 문서를 주고받는 데에 쓰인다.
- TCP와 UDP를 사용하며, 80번 포트를 사용한다.
- 클라이언트와 서버 사이에 이루어지는 요청/응답(request/response) 프로토콜
- 클라이언트인 웹 브라우저가 HTTP를 통하여 서버로부터 웹페이지나 정보를 요청하면, 서버는 이 요청에 응답하여 필요한 정보를 해당 사용자에게 전달하게 된다. 이 정보가 모니터와 같은 출력 장치를 통해 사용자에게 나타나는 것이다.
- DHCP
- 호스트 IP 구성 관리를 단순화하는 IP 표준이다.
- DHCP 서버를 사용하여 IP 주소 및 관련된 기타 구성 세부 정보를 네트워크의 DHCP 사용 클라이언트에게 동적으로 할당하는 방법을 제공
- HTTPS
- 월드 와이드 웹 통신 프로토콜인 HTTP의 보안이 강화된 버전이다.
- HTTPS는 소켓 통신에서 일반 텍스트를 이용하는 대신에, SSL이나 TLS 프로토콜을 통해 세션 데이터를 암호화한다.
- SSL : 인터넷 보안 프로토콜
- TLS : 컴퓨터 네트워크에 통신 보안을 제공하기 위해 설계된 암호 규약
- 데이터의 적절한 보호를 보장.
- HTTPS의 기본 TCP/IP 포트는 443이다.
- SSH
- 네트워크 상의 다른 컴퓨터에 로그인하거나 원격 시스템에서 명령을 실행하고 다른 시스템으로 파일을 복사할 수 있도록 해 주는 응용 프로그램 또는 그 프로토콜
- SSH는 암호화 기법을 사용하기 때문에, 통신이 노출된다고 하더라도 이해할 수 없는 암호화된 문자로 보인다.
-
패킷 교환 방식
- TCP/IP 환경에서 데이터는 패킷 단위로 교환
- 축적 교환 방식 사용 : 구간 별로 중계 루트가 변하는 형태로 상대방에게 전송
- 장점 : 전송 대역을 효율적으로 이용할 수 있다, 패킷 단위로 데이터의 흐름이 생기고, 패킷에 웃너 순위를 부여할 수 있다.
-
IP
- 인터넷 상에서 식별할 수 있는 주소
- 호스트간의 통신만 담당
- IP 정보는 패킷 혹은 데이터그램이라고 하는 덩어리로 나뉘어 전송
- 특징
- 비신뢰성, 비연결성
- 흐름에 관여하지 않기 때문에 보낸 정보가 제대로 갔는지 보장하지 않는다.
- 패킷 전송과 정확한 순서를 보장하려면 TCP 프로토콜을 사용해야 한다.
- 비신뢰성, 비연결성
-
도메인 주소
- IP 주소를 그대로 사용하기에 어려움이 있어 IP 주소를 문자로 표현
-
웹서버
- 클라이언트가 서버에 페이지 요청을 하면 요청을 받아 정적 컨텐츠(.html, .png, .css등)를 제공하는 서버
-
웹 서버 작동 원리
①② 사용자가 웹 브라우저를 통해 찾고 싶은 웹 페이지의 URL 주소를 입력함.
③ 사용자가 입력한 URL 주소 중에서 도메인 네임(domain name) 부분을 DNS 서버에서 검색함.
④ DNS 서버에서 해당 도메인 네임에 해당하는 IP 주소를 찾아 사용자가 입력한 URL 정보와 함께 전달함.
⑤⑥ 웹 페이지 URL 정보와 전달받은 IP 주소는 HTTP 프로토콜을 사용하여 HTTP 요청 메시지를 생성함.
이렇게 생성된 HTTP 요청 메시지는 TCP 프로토콜을 사용하여 인터넷을 거쳐 해당 IP 주소의 컴퓨터로 전송됨.
⑦ 이렇게 도착한 HTTP 요청 메시지는 HTTP 프로토콜을 사용하여 웹 페이지 URL 정보로 변환됨.
⑧ 웹 서버는 도착한 웹 페이지 URL 정보에 해당하는 데이터를 검색함.
⑨⑩ 검색된 웹 페이지 데이터는 또 다시 HTTP 프로토콜을 사용하여 HTTP 응답 메시지를 생성함.
이렇게 생성된 HTTP 응답 메시지는 TCP 프로토콜을 사용하여 인터넷을 거쳐 원래 컴퓨터로 전송됨.
⑪ 도착한 HTTP 응답 메시지는 HTTP 프로토콜을 사용하여 웹 페이지 데이터로 변환됨.
⑫ 변환된 웹 페이지 데이터는 웹 브라우저에 의해 출력되어 사용자가 볼 수 있게 됨.
- HTML
- 웹 페이지를 위한 지배적인 마크업 언어다.
- HTML은 제목, 단락, 목록 등과 같은 본문을 위한 구조적 의미를 나타내는 것뿐만 아니라 링크, 인용과 그 밖의 항목으로 구조적 문서를 만들 수 있는 방법을 제공한다.
- 이미지와 객체를 내장하고 대화형 양식을 생성하는 데 사용될 수 있다.
-
WAS
- 동적컨텐츠를 제공하기 위해 만들어진 애플리케이션 서버
-
WAS와 웹 서버 차이
- 동적 컨텐츠 처리를 수행 가능한가 아닌가.
- WAS는 정적,동적 처리 둘다 가능하지만 정적처리를 WAS가 하게되면 부하가 많이 걸려서 좋지 않음
- Container
- 보통 객체 인스턴스의 생명주기를 관리하며, 생성된 인스턴스들에게 추가적인 기능을 제공하도록하는 것
- Servlet
- Container가 이해할 수 있게 구성된 순수 자바코드로만 이루어짐
- 요청 당 쓰레드 (만들거나, 풀에서 가져다가 ) 사용
- java에서 html 코드를 작성
- 서블릿은 MVC 접근 방식에서 컨트롤러 역할을 한다.
- Servlet 수정 시 server를 recompile 하거나 restart 해야 한다.
- Business logic과 Presentation logic을 분리할 수 없다.
- JSP
- html 기반에 JAVA 코드를 블록화하여 삽입한 것으로 Servlet을 좀 더 쉽게 접근할 수 있도록 만들어 진 것
- JSP를 Servlet(Java 코드)로 변환하고 컴파일 하기 때문에 Servlet 보다는 느리다.
- Business logic과 Presentation logic을 분리할 수 있다.
- Beans라고 하는 자바 컴포넌트 사용 가능
- JSP 동작 원리
- 웹 브라우저에서 http://blog.naver.com:8080/index.jsp를 입력합니다.
- 웹 브라우저는 http://blog.naver.com라는 도메인을 가진 ip 주소를 DNS서버로부터 받습니다.
- IP주소의 해당 서버 8080번 포트로 접속을 시도합니다.
- http://blog.naver.com의 웹 서버는 8080번 포트로 접속을 기다리다 접속 요청이 들어오면 받습니다.
- 웹 서버는 내용을 분석하고, 서블릿 컨테이너에게 요청을 넘깁니다.
- 서블릿 컨테이너는 jsp파일에 해당하는 서블릿이 있는지 확인하고, 없을 경우 jsp파일을 서블릿으로 컴파일 합니다.
- 컴파일된 jsp는 서블릿으로 변환되어 컨테이너에 적재됩니다.
- 서블릿 내용 중 데이터베이스 처리 부분이 있으면, 데이터베이스에서 데이터를 가져옵니다.
- 화면에 보일 내용을 정리해서 html문서형태로 클라이언트에 전송합니다.
- 웹 브라우저는 웹 서버에서 보낸 텍스트 내용 중 html태그를 분석해서 적절히 변환하여 화면을 구성합니다.
- Servlet Life Cycle
- servlet 생성시
init()
을 호출한다. - Client로부터 발생한 모든 요청은
service()
를 통해 다루게 된다. - Servlet이 서비스를 마치게 되면
destroy()
를 통해 종료되고, GC에 의해 메모리 정리가 된다.
- servlet 생성시
-
Servlet Container
-
servlet의 생성부터 소멸까지의 일련의 과정(Lifer Cycle)을 관리한다.
- 대표적인 Servlet Container가 Tomcat 이다.
-
-
Servlet Config
- Servlet이 초기화 될 때 생성되는 객체
- ServletConfig가 공유한는 정보는 특정 Servlet에 대한 것이다.
- Servlet 초기화 단계에서 ServletContainer가 생성하고 Servlet에게 전달하는 초기 파라미터 또는 설정 정보를 담고 있는 체이다.
- ServletConfig는 특정 Servlet을 위한 것이다.
- Servlet 정보를 web.xml에 저장하고 객체를 사용하여 검색하야 한다.
-
Servlet Context
- Servlet이 초기화 될 때 생성되는 객체
- 웹 애플리케이션의 모든 서블릿에서 사용 가능하다.
- ServletContext는 ServletContainer가 전체 응용 프로그램과 초기 매개 변수 또는 구성 정보를 공유하기 위해 만든 객체이다.
-
ServletContextListener
- 웹 어플리케이션의 생명주기를 감시하는 Listener
- XML형태의 Deploy Descriptor에서는
param-value
가 String으로만 가능한 단점.param-value
에 Object를 넣기 위해 등장.
- 웹 어플리케이션의 시작과 종료시 호출
- contextInitialized()
- contextDestroyed()
-
Root Application Context
- 전체 계층구조에서 최상단에 위치한 컨텍스트
- 서로 다른 서블릿 컨텍스트에서 공유해야하는 Bean들을 등록해놓고 사용할 수 있다.
- 웹 어플리케이션 전체에 적용가능한 DB 연결, 로깅기능등에 이용
- Servlet Context에 등록된 Bean 이용 불가능 하다
- Servlet Context와 동일한 Bean이 있을 경우 Servlet Context Bean이 우선된다
- 하나의 컨텍스트에 정의된 AOP 설정은 다른 컨텍스트의 빈에는 영향을 미치지 않는다.
- Bean
- 스프링 IoC 컨테이너가 관리하는 객체
- 장점
- 의존성 관리
- 스코프
- 싱글톤 : 하나의 객체를 반환
- 프로토타입 : 매번 다른 객체 반환
- IoC
- 인스턴스의 생성과 소멸의 제어권이 컨테이너로 넘어가게 되어 컨테이너가 인스턴스 생성과 소멸을 관리
- 스프링 컨테이너가 애플리케이션의 흐름을 제어하는 권한이 없다면
@Autowired
를 통한 의존성 주입(DI)을 할 수 없다.
- 스프링 IoC 컨테이너
- ApplicationContext
- 스프링에서 IoC Container 역할
- IoC Container도 bean이다.
- 빈 설정 소스 로 부터 빈 정의 를 읽어들이고, 빈을 구성하고 제공 한다.
- BeanFactory 인터페이스를 구현
- DI의 기본사항을 제공하는 가장 단순한 컨테이너
- 팩토리 디자인 패턴을 구현
- Bean을 생성하고 분배하는 책임을 지는 클래스
- Bean이 필요할 때까지 인스턴스화 하지 않는다.
- 추가적인 ApplicationContext만의 기능
- 메시지 소스 처리 기능 (i18n)
- 이벤트 발행 기능
- 리소스 로딩 기능
- 스프링에서 IoC Container 역할
- ApplicationContext
- DI
- Spring 프레임워크에서 지원하는 IoC의 형태이다.
- IoC로 특정 객체에 필요한 객체를 외부에서 결정해서 연결 시키는 것을 의미한다.
- DI의 세가지 방법
- Contructor Injection : 생성자 삽입
- Method(Setter) Injection : 메소드 매개 변수 삽입
- Field Injection : 멤버 변수 삽입
@ComponentScan
- 특정 패키지 이하의 모든 클래스 중에
@Component
애노테이션을 사용한 클래스를 빈으로 자동으로 등록 해 줌.
- 특정 패키지 이하의 모든 클래스 중에
- 스프링 프레임워크
- 자바 엔터프라이즈 개발을 편하게 해주는 경량급 오픈소스 애플리케이션 프레임워크
- 목표 : POJO 기반의 Enterprise Application 개발을 쉽고 편하게 할 수 있도록 한다.
- Spring이 Java Application을 개발하는데 필요한 하부구조를 처리하기 때문에 개발자는 Application 개발에 집중할 수 있다.
- 특징
- 경량 프레임워크 : 각각의 객체 생성, 소멸과 같은 라이프 사이클을 관리하며 스프링으로부터 필요한 객체를 얻어올 수 있다.
- IoC : 이라는 기술을 통해 애플리케이션의 느슨한 결합을 도모
- AOP
- 애플리케이션 객체의 생명주기와 설정을 포함하고 관리한다는 점에서 일종의 컨테이너(Container)라고 할 수 있음.
- 트랜잭션 관리
- 간단한 컴포넌트로 복잡한 애플리케이션을 구성하고 설정할 수 있음.
- 스프링 MVC
- Spring MVC 는 MVC 패턴 기반의 웹 프레임워크이다.
- MVC 는 Model-View-Controller의 약자로 Model, View, Controller 로 구성
- MVC 패턴의 장점
- 동시 다발적(Simultaneous) 개발 : 백엔드 개발자와 프론트엔드 개발자가 독립적으로 개발을 진행할 수 있다.
- 높은 결합도 : 논리적으로 관련있는 기능을 하나의 컨트롤러로 묶거나, 특정 모델과 관련있는 뷰를 그룹화 할 수 있다.
- 낮은 의존도 : 뷰, 모델, 컨트롤러는 각각 독립적이다.
- 개발 용이성 : 책임이 구분되어 있어 코드 수정하는 것이 편하다.
- 한 모델에 대한 여러 형태의 뷰를 가질 수 있다.
- MVC 패턴의 단점
- 코드 네비게이션 복잡함
- 코드 일관성 유지에 노력이 필요함
- 높은 학습 곡선
- 스프링 MVC 구성요소
- Model
- 도메인 객체 또는 DTO로 화면에 전달할 또는 화면에서 전달 받은 데이터를 담고 있는 객체
- View
- 데이터를 보여주는 역할.
- 다양한 형태로 보여줄 수 있다
- Controller
- 사용자 입력을 받아 모델 객체의 데이터를 변경하거나, 모델 객체를 뷰에 전달하는 역할.
- Model
- 서블릿 리스너
- 웹 애플리케이션에서 발생하는 주요 이벤트를 감지하고 각 이벤트에 특별한 작업이 필요한 경우에 사용할 수 있다.
- 서블릿 컨텍스트 수준의 이벤트
- 세션 수준의 이벤트
- Servlet Container 위에 있는 개념
- 웹 애플리케이션에서 발생하는 주요 이벤트를 감지하고 각 이벤트에 특별한 작업이 필요한 경우에 사용할 수 있다.
- 서블릿 필터
- 들어온 요청을 서블릿으로 보내고, 또 서블릿이 작성한 응답을 클라이언트로 보내기 전에 특별한 처리가 필요한 경우에 사용할 수 있다.
- ServletContextListener
- Context의 라이프사이클을 감시
- 웹애플리케이션의 시작과 종료시 다음의 메소드가 자동으로 호출
- ContextLoaderListener
- WenApplicationContext를 만들어 준다.
- Root WebApplicationContext 를 생성한다.
- WenApplicationContext를 ServletContext 라이프사이클에 따라 등록하고 소멸시켜준다.
- 서블릿에서 IoC 컨테이너를 ServletContext를 통해 꺼내 사용할 수 있다.
- WenApplicationContext를 만들어 준다.
- DispatcherServlet
- Front Controller의 역할
- Servlet Context에 등록되어 있는 ApplicationContext(Root WebApplicationContext)를 상속하는 ApplicationContext(Servlet WebApplicationContext)를 생성
- 상속하는 이유 : 다른 Servlet에서 Root WebApplicationContext를 사용할 수 있기 때문
- Root WebApplicationContext
- 웹과 관련된 Bean들은 등록되지 않는다.
- Services, Repositories
- Servlet WebApplicationContext
- 웹과 관련된 Bean이 등록
- Controllers, ViewResolver, HandlerMapping
- WebApplicationContext
- ApplicationContext를 확장한 WebApplicationContext 인터페이스의 구현체
- ApplicationContext에
getServletContext()
메서드가 추가된 인터페이스입니다.- 이 메서드를 호출하면 서블릿 컨텍스트를 반환
- ApplicationContext
- 스프링이 관리하는 빈들이 담겨 있는 컨테이너
- Spring AOP
- OOP를 보완하는 수단으로, 흩어진 Aspect를 모듈화 할 수 있는 프로그래밍 기법.
- 용어
- Concerns
- 여러 메소드나 클래스에서 사용하는 비슷한 코드
- Aspect
- Crosscutting Concerns들을 모듈화한 것
- Target
- Aspect의 Advice를 적용할 대상
- Advice
- 해야할 일들
- Join point
- 특정 실행 시점에 Advice를 끼워 넣을 수 있는 지점
- Pointcut
- 어디에 적용해야 하는지
- Concerns
- 적용 방법
- 컴파일
- 로드 타임
- 런타임
-
MVC 1
- JSP 페이지 안에서 로직 처리를 위해 자바 코드가 함께 사용된다.
- 요청이 오면, 직접 자바빈이나 클래슬르 이용해 작업을 처리하고, 이를 클라이언트에 출력
- 구조가 단순하지만 JSP 내에서 html 코드와 자바 코드가 같이 사용되면서 복잡해지고 유지보수가 어려운 단점이 있습니다.
-
MVC 2
- 모든 처리를 MVC 1 방식처럼 JSP에서만 담당하지 않고 서블릿을 만들어 역할 분담을 하는 패턴
- 요청 결과를 출력해주는 뷰만 JSP가 담당하고, 흐름을 제어해주고 비즈니스 로직에 해당하는 컨트롤러의 역할을 서블릿이 담당
- 유지보수가 용이해지는 장점과 초기 학습의 어려움과 구조가 복잡해지는 단점이 있다.
- 모든 처리를 MVC 1 방식처럼 JSP에서만 담당하지 않고 서블릿을 만들어 역할 분담을 하는 패턴
- Filter vs Interceptor
- 차이점
- 실행되는 시점
- Filter는 웹 애플리케이션에 등록을 하고 Interceptor는 스프링의 context에 등록한다.
- Filter는 Dispatcher 앞단에 존재하고 Interceptor는 Controller의 앞단에 존재합니다.
- 차이점
- DispatcherServlet 동작 순서
- 클라이언트의 요청을 분석한다. (로케일, 테마, 멀티파트 등)
- 핸들러 맵핑에게 위임하여 요청을 처리할 핸들러를 찾는다.
- 등록되어 있는 핸들러 어댑터 중에 해당 핸들러를 실행할 수 있는 핸들러 어댑터를 찾는다.
- 핸들러 어댑터에서 자바 리플렉션을 사용하여 컨트롤러를 실행하고 응답을 처리한다.
- 핸들러의 리턴값을 보고 어떻게 처리할지 판단한다.
- 뷰 이름에 해당하는 뷰를 찾아서 모델 데이터를 랜더링한다.
- @ResponseEntity가 있다면 Converter를 사용해서 응답 본문을 만들고.
- (부가적으로) 예외가 발생했다면, 예외 처리 핸들러에 요청 처리를 위임한다.
- 최종적으로 응답을 보낸다.
- Spring MVC 구성요소
- Dispatcherservlet
- 클라이언트의 요청을 전달 받아 컨트롤러에게 요청을 전달하고 컨트롤러가 리턴할 결과 값을 view에게 전달하여 알맞은 응답을 하도록 하는 것
- 클라이언트의 요청을 중앙에서 처리하는 스프링 MVC의 핵심 구성 요소
- web.xml에 한개 이상의 DispatcherServlet을 설정한다.
- HandlerMapping
- 클라이언트의 요청 URL을 어떤 컨트롤러가 처리할지를 결정
- Controller
- 클라이언트의 요청을 처리한 뒤 그 결과를 DispatcherServlet에게 알려준다.
- ModelAndView
- 컨트롤러가 처리한 결과 정보 및 뷰 선택에 필요한 정보를 담아 놓는 세트
- ViewResolver
- 컨트롤러의 처리 결과를 생성할 뷰를 결정
- View
- 컨트롤러에서 리턴 받은 처리 결과를 화면에 출력한다.
- Dispatcherservlet
- REST(Representational State Transfer)
- 네트워크 상에서 클라이언트와 서버 사이의 통신 방법 중 하나로 자원을 이름으로 구분하여 정보를 주고 받는 것
- HTTP URI를 통해 자원(Resource)을 명시하고, HTTP Method(POST, GET, PUT, DELETE)를 통해 해당 자원에 대한 CRUD Operation을 적용하는 것을 의미한다.
- 구성 요소
- 자원 : URI
- 행위 : HTTP Method
- 표현
- RESTful
- RESTful은 일반적으로 REST라는 아키텍처를 구현하는 웹 서비스를 나타내기 위해 사용되는 용어이다.
- ‘REST API’를 제공하는 웹 서비스를 ‘RESTful’하다고 할 수 있다.
- API
- 프로그램을 쉽게 제작할 수 있게 미리 만들어 놓은 것들의 모음
- Springboot의 자동설정
- application main클래스에 설정된
@SpringBootApplication
의@EnableAutoConfiguration
때문 - Bean을 등록하는 방법
- 1단계 :
@ComponentScan
으로 스캔하여 Bean을 등록@Component
를 가진 클래스를 스캔해서 Bean으로 등록
- 2단계 :
@EnableAutoConfiguration
로 추가적인 Bean 등록- spring.factories 파일에 정의된 설정파일로 Bean 등록
- 조건에 따라 Bean의 등록 여부 설정
- 1단계 :
- application main클래스에 설정된
- 스프링 Test
@SpringBootTest
@RunWith(SpringRunner.class)
랑 같이 써야 함.- 빈 설정 파일은 설정을 안해주나? 알아서 찾습니다. (
@SpringBootApplication
) - webEnvironment
- MOCK: mock servlet environment. 내장 톰캣 구동 안 함.
- RANDON_PORT, DEFINED_PORT: 내장 톰캣 사용 함.
- NONE: 서블릿 환경 제공 안 함.
- Spring HATEOAS
- Hypermedia As The Engine Of Application State
- 서버는 현재 리소스와 연관된 링크 정보 를 클라이언트에게 제공하고 클라이언트는 연관된 링크 정보 를 바탕으로 리소스에 접근한다.
- SOP과 CORS
- Single-Origin Policy
- 같은 Origin에만 요청을 보낼 수 있다.
- Cross-Origin Resource Sharing
- Single-Origin Policy를 우회하기 위한 표준 기술
- 서로 다른 Origin에서 요청을 보낼 수 있다.
- Origin?
- URI 스키마 (http, https)
- hostname (localhost)
- 포트 (8080, 18080)
- 스프링 MVC
@CrossOrigin
@Controller
나@RequestMapping
에 추가하거나- WebMvcConfigurer 사용해서 글로벌 설정
- Single-Origin Policy
- 스프링 트랜잭션
- 트랜잭션의 정의
- 데이터베이스의 상태를 변환시키는 하나의 논리적 기능을 수행하기 위한 작업의 단위 또는 한꺼번에 모두 수행되어야 할 일련의 연산들을 의미한다.
- 트랜잭션 성질 (ACID)
- 원자성 : 한 트랜잭션 내에서 실행한 작업들은 하나로 간주(성공 or 실패)
- 일관성 : 트랜잭션은 일관성 있는 데이터베이스 상태를 유지
- 격리성 : 동시에 실행되는 트랜잭션들이 서로 영향을 미치지 않도록 격리
- 지속성 : 트랜잭션을 성공적으로 마치면 결과가 항상 저장
- 스프링에서는 트랜잭션 처리를 지원하는데 그중 어노테이션 방식으로
@Transactional
을 선언하여 사용하는 방법이 일반적이며, 선언적 트랜잭션이라 부른다.- 클래스, 메서드위에
@Transactional
이 추가되면, 이 클래스에 트랜잭션 기능이 적용된 프록시 객체가 생성된다. - 이 프록시 객체는
@Transactional
이 포함된 메소드가 호출 될 경우, PlatformTransactionManager를 사용하여 트랜잭션을 시작하고, 정상 여부에 따라 Commit 또는 Rollback 한다
- 클래스, 메서드위에
- 트랜잭션의 정의
- 연관관계 매핑
- 엔티티들이 서로 어떤 연관관계를 맺는지 파악하는 것
- 객체의 참조와 테이블의 외래키를 매핑하는 것
- 방향
- 단방향 : 두 엔티티가 관계를 맺을 때, 한 쪽의 엔티티만 참조하고 있는 것을 의미
- 양방향 : 두 엔티티가 관계를 맺을 때, 양 쪽이 서로 참조하고 있는 것을 의미
- 다중성
- 어떤 엔티티를 중심으로 상대 엔티티를 바라 보느냐에 따라 다중성이 다르다.
- 엔티티 자신을 기준으로 다중성을 결정
- Many To One (
@ManyToOne
)@JoinColumn
외래 키를 매핑 할 때 사용 (필드명 + "_" + 참조 테이블 기본 키 컬럼)
- One To Many (
@ManyToOne
) - One To One (
@ManyToOne
)- mappedBy 옵션을 사용하여 주인 여부를 알 수 있다.
- Many To Many (
@ManyToOne
)
- ORM
- 애플리케이션의 클래스와 SQL 데이터베이스의 테이블 사이의 맵핑 정보를 기술한 메타데이터 를 사용하여, 자바 애플리케이션의 객체를 SQL 데이터베이스의 테이블에 자동으로 (또 깨끗하게) 영속화 해주는 기술입니다
- JPA
- JPA는 Java Persistence API의 약자로, 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스
- Hibernate는 JPA의 구현체이다
-
JPA 프로그래밍 : Entity 매핑
@Entity
: 객체 세상에서 부르는 이름@Table
: 릴레이션,@Entity
의 이름이 기본 값@Id
: 엔티티의 주키를 맵핑할 때 사용@GeneratedValue
: 주키의 생성 방법을 맵핑하는 애노테이션- 기본키 자동생성 방법
- IDENTITY
- 기본 키 생성을 데이터베이스에 위임하는 방법 (데이터베이스에 의존적)
- 주로 MySQL, PostgresSQL, SQL Server, DB2에서 사용합니다.
- SEQUENCE
- 데이터베이스 시퀀스를 사용해서 기본 키를 할당하는 방법 (데이터베이스에 의존적)
- 주로 시퀀스를 지원하는 Oracle, PostgresSQL, DB2, H2에서 사용합니다.
@SequenceGenerator
를 사용하여 시퀀스 생성기를 등록하고, 실제 데이터베이스의 생성될 시퀀스이름을 지정해줘야 합니다.
- TABLE
- 키 생성 테이블을 사용하는 방법
- 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만드는 방법입니다.
- 테이블을 사용하므로, 데이터베이스 벤더에 상관없이 모든 데이터베이스에 적용이 가능합니다.
- AUTO(default)
- 데이터베이스 벤더에 의존하지 않고, 데이터베이스는 기본키를 할당하는 방법
- 데이터베이스에 따라서 IDENTITY, SEQUENCE, TABLE 방법 중 하나를 자동으로 선택해주는 방법입니다.
- 예를들어, Oracle일 경우 SEQUENCE를 자동으로 선택해서 사용합니다. 따라서, 데이터베이스를 변경해도 코드를 수정할 필요가 없습니다.
- IDENTITY
- 기본키 자동생성 방법
@Column
: 컬럼 관련 속성 지정- unique, nullable, length, columnDefinition, ...
@Temporal
: 날짜 관련 변수에 사용@Transient
: 컬럼으로 맵핑하고 싶지 않은 멤버 변수에 사용
-
Value 타입 맵핑
- 종류
- 기본 타입 (String, Date, Boolean, ...)
- Composite Value 타입
- Collection Value 타입
- 기본 타입의 Collection
- Composite 타입의 Collection
- Composite Value 타입 맵핑
@Embeddable
@Embedded
@AttributeOverrides
@AttributeOverride
@Embeddable public class Address { private String street; private String city; private String state; private String zipCode; } --- @Entity public class Account { @Id @GeneratedValue Long Id; ... @Embedded @AttributeOverrides({ @AttributeOverride(name = "street", column = @Column(name = "home_street")) }) private Address address;
- 종류
-
JPA Cascade
- 엔티티의 상태 변화를 전파 시키는 옵션
- 엔티티의 상태
- Transient : JPA가 모르는 상태
- Persistent : JPA가 관리중인 상태
- Detached : JPA가 더이상 관리하지 않는 상태
- Removed : JPA가 관리하긴 하지만 삭제하기로 한 상태
-
JPA Fetch
- 연관 관계의 엔티티를 어떻게 가져올 것인지 설정
- Eager
- Lazy
@OneToMany
의 기본값은 Lazy@ManyToOne
의 기본값은 Eager
- 연관 관계의 엔티티를 어떻게 가져올 것인지 설정
-
영속성 컨텍스트의 특징
- 영속성 컨텍스트와 식별자 값영속성 컨텍스트는 엔티티를 식별자 값(@Id로 테이블의 기본 키와 매핑한 값)으로 구분한다.따라서 영속 상태는 식별자 값이 반드시 있어야 한다!
- 영속성 컨텍스트와 데이터베이스 저장이전 포스팅에서 말했듯이 JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 데이터베이스에 반영한다. 이를 플러시(flush) 라고 한다.
- 영속성 컨텍스트가 엔티티를 관리하면 얻게되는 장점
- 1차 캐시
- 동일성(identity) 보장
- 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
- 변경 감지(Dirty Checking)
- 지연 로딩(Lazy Loading)
-
1차 캐시
- 영속성 컨텍스트는 내부에 캐시를 가지고 있는데 이것을 1차 캐시라 한다.
- 영속 상태의 엔티티는 모두 이곳에 저장된다.
- 쉽게 말해 영속성 컨텍스트 내부에 Map이 하나 있는데 (1차 캐시), 키는 @Id로 매핑한 식별자고 값은 엔티티 인스턴스다.
- 장점
- 엔티티를 조회하는 find 메서드가 실행되면
- 1차 캐시에서 식별자 값으로 엔티티를 찾는다.
- 만약 찾는 엔티티가 1차 캐시에 있으면 데이터베이스를 조회하지 않고 메모리에 있는 1차 캐시에서 엔티티를 조회
- 1차 캐시에 찾는 엔티티가 없으면 데이터베이스에서 조회
-
변경 감지(dirty checking)
- 엔티티의 변경사항을 데이터베이스에 자동으로 반영하는 기능
- JPA는 엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 복사해서 저장해두는데 이것을 스냅샷 이라 한다
- 과정
- 트랜잭션을 커밋하면 엔티티 매니저 내부에서 먼저 플러시(
flush()
)가 호출된다. - 엔티티와 스냅샷을 비교해서 변경된 엔티티를 찾는다.
- 변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기 지연 SQL 저장소에 보낸다.
- 쓰기 지연 저장소의 SQL을 데이터베이스에 보낸다.
- 데이터베이스 트랜잭션을 커밋한다.
- 트랜잭션을 커밋하면 엔티티 매니저 내부에서 먼저 플러시(
- 변경 감지는 영속성 컨텍스트가 관리하는 영속 상태의 엔티티에만 적용된다
- 비영속, 준영속처럼 영속성 컨텍스트의 관리를 받지 못하는 엔티티는 값을 변경해도 데이터베이스에 반영되지 않는다
- JPA의 기본 전략은 엔티티의 모든 필드를 업데이트 한다.
- 단점 : 모든 필드를 업데이트하면 데이터베이스에 보내는 데이터 전송량이 증가
- 장점
- 모든 필드를 사용하면 수정 쿼리가 항상 같다. 따라서 애플리케이션 로딩 시점에 수정 쿼리를 미리 생성해두고 재사용할 수 있다.
- 데이터베이스에 동일한 쿼리를 보내면 데이터베이스는 이전에 한 번 파싱된 쿼리를 재사용할 수 있다.
-
엔티티 삭제
remove()
에 삭제 대상 엔티티를 넘겨주면 엔티티를 삭제한다.- 물론 엔티티를 즉시 삭제하는 것이 아니라, 엔티티 등록과 비슷하게 삭제 쿼리를 쓰기 지연 SQL 저장소에 등록한다.
- 이후 트랜잭션을 커밋해서 플러시를 호출하면 실제 데이터베이스에 삭제 쿼리를 전달
-
플러시
- 플러시(
flush()
)는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다. - 플러시를 실행하면 다음과 같은 일이 일어난다.
- 변경 감지가 동작해서 영속성 컨텍스트에 있는 모든 엔티티를 스냅샷과 비교해서 수정된 엔티티를 찾는다.
- 수정된 엔티티는 수정 쿼리를 만들어 쓰기 지연 SQL 저장소에 등록한다.
- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다.(등록, 수정, 삭제 쿼리)
- 플러시(
-
영속성 컨텍스트를 플러시 하는 3가지 방법
- em.flush() - 직접 호출
- 엔티티 매니저의 flush() 메서드를 직접 호출해서 영속성 컨텍스트를 강제로 플러시한다.
- 트랜잭션 커밋 - 플러시 자동 호출
- 데이터베이스에 변경 내용을 SQL로 전달하지 않고 트랜잭션만 커밋하면 어떤 데이터도 데이터베이스에 반영되지 않는다.
- 따라서 트랜잭션을 커밋하기 전에 꼭 플러시를 호출해서 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영해야 한다.
- JPA는 이런 문제를 예방하기 위해 트랜잭션을 커밋할 때 플러시를 자동으로 호출한다.
- JPQL 쿼리 실행 - 플러시 자동 호출
- JPQL이나 Criteria 같은 객체지향 쿼리를 호출할 때도 플러시가 실행된다.
- em.flush() - 직접 호출
- URL vs URI
- URI
- 통합 자원 식별자(Uniform Resource Identifier, URI)는 인터넷에 있는 자원을 나타내는 유일한 주소
- URL과 URN은 URI의 종류이다.
- URL
- 네트워크 상에서 자원이 어디 있는지를 알려주기 위한 규약
- URI
- lombok 관련
- 왜
@EqualsAndHasCode
에서 of를 사용하는가- entity 간의 연관관계가 있을 때 stackOverflow가 발생할 수 있으므로 특정 필드를 설정한다.
- 다른 Entity를 묶는 것은 좋지 않다.
- 왜
@Builder
를 사용할 때@AllArgsConstructor
가 필요한가@Builder
를 사용하면 기본 생성자가 추가 되지 않는다.- Default 생성자로 추가된다.
- 다른 패키지에서 객체를 만들기 어렵기 때문
@Data
를 쓰지 않는 이유- 상호 참조 때문에 stackOverflow 발생 할 수 있음
- 애노테이션 줄일 수 없나
- lombok은 메타 에노테이션을 지원하지 않는다.
- 아직은 줄일 수 없다.
- 왜
- MockMvc
- 웹 서버를 띄우지 않아서 테스트의 속도가 빠르다.
- DispatcherServlet를 만들어야 하기 때문에 단위 테스트 보다는 느리다.
- 스프링 시큐리티
-
웹 시큐리티
- 웹 요청에 보안 인증
- Filter 기반 시큐리티
- Sevlet Filter와 연관
-
메소드 시큐리티
- 메소드가 호출되었을 때 보안 인증
- AOP
-
동작 방식
- 웹 요청이 들어온다.
- 요청을 Servlet Filter가 가로채서 Bean으로 등록되어 있는 SecurityInterceptor 쪽으로 요청을 보낸다.
- SecurityInterceptor 에 요청이 들어오면 인증 정보를 확인한다.
- 인증 정보를 SecurityContext Holder 라는 Thread Local 구현체에서 가져온다.
- Thread Local : 한 쓰레드 내에서 공유하는 자원
- 종류
- Filter SecurityInterceptor
- Method SecurityInterceptor
- 3번 결과 로그인이 안되어 있다면(인증 정보가 없다면) AuthenticationManager를 사용하여 로그인한다.
- 로그인 시 UserDetailsService, PasswordEncoder 의 2개의 인터페이스 사용
- Basic Authentication : 인증 요청 해더에 Authentication, basic, (userName + userPassword를 인코딩 한 문자열) 담아서 보낸다**.**
- UserDetailsService에서 password를 가져오고 PasswordEncoder를 통해 입력한 password가 맞는지 확인한다.
- 맞으면 Authentication 객체를 만들어서 SecurityContext Holder 에 저장한다.
- 요청한 리소스에 대한 권한이 있는지 확인한다. (AccessDecisionManager 사용)
- User의 Role을 이용한다.
- 인증된 Account가 해당 리소스를 사용할 권한이 있는지 확인한다.
-
User 객체 생성
- 사용자 정보를 웹에서 받는다. (Account 객체)
- passwordEncoder를 통해 password를 인코딩 하고 Account객체를 저장
public Account saveAccount(Account account) { account.setPassword(this.passwordEncoder.encode(account.getPassword())); return this.accountRepository.save(account); }
-
User 객체 인증
-
loadUserByUsername
메소드로 username 을 기준으로 사용자 정보를 찾는다.public class AccountService implements UserDetailsService { ... @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Account account = this.accountRepository.findByEmail(username) .orElseThrow(() -> new UsernameNotFoundException(username)); return new AccountAdaptor(account); } ... }
-
찾은 Account 객체를 AccountAdaptor 객체로 변환한다.
public class AccountAdaptor extends User { ... public AccountAdaptor(Account account) { super(account.getEmail(), account.getPassword(), authorities(account.getRoles())); this.account = account; } ... }
- User 객체를 만들기 위해 객체 생성시 (사용자 id, 사용자 pw, 인, authorities 정보)
- Role을 authorities로 변환 해야한다.
public class AccountAdaptor extends User { ... public AccountAdaptor(Account account) { super(account.getEmail(), account.getPassword(), authorities(account.getRoles())); this.account = account; } ... private static Collection<? extends GrantedAuthority> authorities(Set<AccountRole> roles) { return roles.stream() .map(r -> new SimpleGrantedAuthority("ROLE_" + r.name())) .collect(Collectors.toSet()); } ... }
- User 객체를 만들기 위해 객체 생성시 (사용자 id, 사용자 pw, 인, authorities 정보)
-
-
-
스프링 시큐리티 OAuth 2.0
- AuthorizationServer : OAuth2 토큰 발행(/oauth/token) 및 토큰 인증(/oauth/authorize)
- ResourceServer : 리소스 요청 인증 처리 (OAuth 2 토큰 검사)
-
UserDetailsServiceAutoConfiguration
-
초기 스프링 시큐리티 설정 시 기본 user id와 password를 제공
-
SecurityProperties
에서 기본 User 정보와 Password를 가져온다. -
application.properties
에서 설정가능 (좋지 않다.)spring.security.user.name=admin spring.security.user.password=admin spring.security.user.roles=ADMIN
-
-
UserDetailsService
Authentication
을 관리할 때 DAO interface를 통해 유저 정보를 사용- 저장소에 들어있는 유저정보를 사용하여 인증
- 역할 : user 정보를 받아와서 저장소로부터 user와 일치하는 정보를 찾고
UserDetails
으로 반환- 일치하는 정보가 없으면
UsernameNotFoudException()
발생 User.builder()
를 사용하여UserDetails
타입의 객체 생성
- 일치하는 정보가 없으면
-
PasswordEncdoer
- 사용을 위해 Bean으로 등록해야 한다.
- password 를 특정한 encoder로 encode
- 다양한 password encoder 지원
@Bean public PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); }
-
스프링 시큐리티 설정
-
시큐리티 관련 설정을 하기위해 SecurityConfig 객체 생성 후
WebSecurityConfigurerAdapter
를 상속 받는다.- Spring boot의 자동 설정이 해제
-
AppConfig 객체를 생성하여 기본 사용자 추가 및 passwordEncoder 빈으로 추가
-
UserDetailService
와passwordEncoder
를 주입받는다.- AccountService == UserDetailService
-
TokenStore
을 빈으로 등록- OAuth Token을 저장하는 곳
- 저장소 선택을 하고 return 한다.
-
AuthenticationManager 를 Bean으로 노출시킨다.
@Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); }
-
AuthenticationManager 를 재정의 하기 위해 builder를 만든다.
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(accountService) .passwordEncoder(passwordEncoder); }
- Filter 설정 (Web에서 걸러 낼지)
@Override public void configure(WebSecurity web) throws Exception { web.ignoring().mvcMatchers("/docs/index.html"); web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations()); }
-
정적 리소스와 restDocs 들은 Filter 설정에서 무시
- 스프링 시큐리티는 적용하되 http로 걸러낼수 있다.
-
일단 스프링 시큐리티 안으로 들어온 상태
@Override protected void configure(HttpSecurity http) throws Exception { http .anonymous() // 익명 사용자 허용 .and() .formLogin() // Form 인증을 사용한다. .and() .authorizeRequests() // 허용할 메소드가 있는데 .mvcMatchers(HttpMethod.GET, "/api/**").authenticated() // /api/ 라고 시작하는 모든 GET 요청은 인증이 필요하지 않다. .anyRequest().authenticated(); // 나머지 요청도 인증이 필요하다. }
- OAuth 인증 서버 설정
-
서버가 설정되면 인증 토큰을 발급 받을 수 있어야 한다.
-
AuthServerConfig 객체 생성 후
AuthorizationServerConfigurerAdapter
를 상속 받는다.@EnableAuthorizationServer
어노테이션 추가- 메소드에서 사용할 객체를 주입 받는다.
... private final PasswordEncoder passwordEncoder; private final AuthenticationManager authenticationManager; private final AccountService accountService; private final TokenStore tokenStore; private final AppProperties appProperties; public AuthServerConfig(PasswordEncoder passwordEncoder, AuthenticationManager authenticationManager, AccountService accountService, TokenStore tokenStore, AppProperties appProperties) { this.passwordEncoder = passwordEncoder; this.authenticationManager = authenticationManager; this.accountService = accountService; this.tokenStore = tokenStore; this.appProperties = appProperties; } ...
-
3 개의
configure
메소드를@Override
하여 정의한다.- security configure에는 passwordEncoder 설정
@Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.passwordEncoder(passwordEncoder); }
- client configure에는
@Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() // in-memory로 token을 관리 .withClient(appProperties.getClientId()) // client 설정 .authorizedGrantTypes("password", "refresh_token") // grant type 설정 .scopes("read", "write") // scope 설정 .secret(this.passwordEncoder.encode(appProperties.getClientSecret())) // 앱의 secret 설정 .accessTokenValiditySeconds(10 * 60) // access token 의 유효기간 설정 .refreshTokenValiditySeconds(6 * 10 * 60); // token 을 refresh 하는 시간 }
- endpoints configure 에는
@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager) .userDetailsService(accountService) .tokenStore(tokenStore); }
-
인증 토큰을 받기 위해서는 Grant Type을 설정해야 한다.
-
password 방식 : 홉이 1번이다.
- 필요 파라미터
- grant_type : request parameter 형태
- username : request parameter 형태
- password : request parameter 형태
- client_id : 헤더로 넣을 수 있다. bearer authentication 형태
- client_secret : 헤더로 넣을 수 있다. bearer authentication 형태
this.mockMvc.perform(post("/oauth/token") .with(httpBasic(appProperties.getClientId(), appProperties.getClientSecret())) .param("username", appProperties.getUserUsername()) .param("password", appProperties.getUserPassword()) .param("grant_type", "password"))
- password를 직접 입력하기 때문에 해당 서비스를 지원하고 있는 앱에서만 사용 가능 (서드파티 이용x)
- 필요 파라미터
-
- ResourceServer 설정
- OAuthServer랑 연동하여 사용
- 외부 요청이 resource에 접근 할때 OAuthServer에 토큰을 보내 토큰을 확인 후 접근 허용 또는 접근 제한
- ResourceServerConfig 객체 생성 후
ResourceServerConfigurerAdapter
를 상속 - configure 메소드 오버라이딩하여 재정의
- resources configure 설정
@Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { // resource id 설정 resources.resourceId("event"); }
- resources http 설정
@Override public void configure(HttpSecurity http) throws Exception { http .anonymous() // 익명 사용자 접근을 허용 .and() .authorizeRequests() // request 설정 .mvcMatchers(HttpMethod.GET, "/api/**") // /api/로 시작하는 GET 요청은 .permitAll() // 모두 허용한다. .anyRequest() // 다른 요청들은 .authenticated() // 인증이 필요 .and() .exceptionHandling() // 인증 잘못되거나 권한이 없는경우 .accessDeniedHandler(new OAuth2AccessDeniedHandler()); // 권한이 없는경우 에러 처리 위임 }
- 현재 사용자 조회
-
@AuthenticationPrincipal
를 사용하여User
객체를 받을 수 있다.- Account 객체로 받기위해 expression을 사용하여 null case 처리
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account")
-
-
spring security
-
principal : 인증된 사용자가 있다면 principal interface 타입으로 저장하여 사용
-
security를 의존성 추가하면 기본으로 id는
user
pw는 콘솔에 출력된다. -
WebSecurityConfigurerAdapter를 상속받아 security 설정
Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .mvcMatchers("/","/info").permitAll() .mvcMatchers("/admin").hasRole("ADMIN") .anyRequest().authenticated(); } }
-
사용자 계정 추가
{noop}
은 spring security의 기본 password encoder- 암호화를 하지 않았다.
- db에 들어가는 값이다.
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("keesum").password("{noop}123").roles("USER").and() }
-
spring security는 password 패턴을 요구
- {PASSWORD_ENCODER}비밀번호
-
PasswordEncoder 사용
- 여러가지 인코더 사용가능
@Bean public PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); }
-
-
-
Authentication
- Principal과 GrantAuthority 제공
-
스프링 시큐리티 과정
- 서블릿 필터에 요청이 들어온다.
- 서블릿 필터 중
DeligatingFilterProxy
필터는 스프링 부트가 자동으로 등록해준다.- 부트를 사용하지 않으면
AbstractSecurityWebApplicationInitializer
를 상속하여 만든다.
- 부트를 사용하지 않으면
- 서블릿 필터 중
DeligatingFilterProxy
특정한 Bean 이름으로 해당 요청의 필터 처리를 위임한다.- 해당 Bean은
FilterChainProxy
로 만든다.- Bean의 이름은
springSecurityFilterChain
- 여러 필터들을 체인 형태로 가지고 있다.
- 체인들은
Websecurity
를 만든다. (HttpSecurity
도 같이 사용해서 만든다.) - 때문에 만든 Security 설정 파일이
WebSecurityConfigurerAdapter
를 상속한다.
- 체인들은
- Bean의 이름은
- 해당 Bean은
- 인증
AuthenticationManager
가 중요 (ProviderManager
가 구현체)ProviderManager
는 다른AuthenticationProvider
를 사용하여 인증을 처리AuthenticationProvider
중DaoAuthenticationProvider
는UserDetailsService
라는 인터페이스를 사용하여 데이터에서 읽어온 유저정보를 사용하여 인증을 한다.
- 인증이 성공하면 인증 정보를
SecurityContextHolder
에 넣어놓는다.SecurityContextHolder
→SecurityContext
→Authentication
→Principal
,GrantedAuthorities
- 위 정보들은 Session에 저장된다.
- 저장된 Session은
SecurityContextPersistenceFilter
에 의해 읽혀진다.
- 저장된 Session은
- 인가
FilterSecurityInterceptor
인가를 처리하는 인터셉터FilterSecurityInterceptor
가AccesDecisionManager
를 사용하여 인가를 처리한다.- 현재
SecurityContextHolder
의Authentication
이 특정 리소스에 접근 가능한지 확인- 확인방법 :
AffirmativeBased
(기본 전략) AffirmativeBased
가 사용하는Voter
중WebExpressionVoter
를 사용WebExpressionVoter
는SecurityExpressionHandler
를 사용하여 Exception 처리RoleHierarchyImpl
를 사용하여 커스터마이징 가능
- 확인방법 :
- 현재
- 서블릿 필터에 요청이 들어온다.
-
WebAsyncManagerIntegrationFilter
-
시큐리티 필터 중 가장 위에 있는 필터
-
스프링 MVC async 핸들러를 지원
-
쓰레드간
SecurityContext
를 공유 -
@Async
: 특정 Bean안의 메소드를 호출할 때 새로 Thread를 만들어 처리- Application Class에
@EnableAsync
추가 - 기본만 설정하면
SecurityContext
를 공유하지 않는다.SecurityConfig
Class에 아래 코드를 추가하여SecurityContext
를 공유할 수 있다.
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
- Application Class에
-
-
SecurityContextPersistenceFilter
- 여러 요청 간의
SecurityContext
를 공유 HttpSession
에서 기존에 만들어져 있는SecurityContext
를 읽어온다.- 없다면 비어있는
SecurityContext
를 만든다.
- 없다면 비어있는
- 여러 요청 간의
-
HeaderWriterFilter
- 응답 헤더에 시큐리티 관련 헤더를 추가해주는 필터
- 설정할 일이 거의 없다.
-
CsrfFilter
- CSRF 어택 : 인증된 유저의 계정을 사용해 악의적인 변경 요청을 만들어 보내는 기법
- CSRF 토큰을 사용하여 방어
- CSRF 어택 : 인증된 유저의 계정을 사용해 악의적인 변경 요청을 만들어 보내는 기법
-
LogoutFilter
- 로그아웃을 처리
- LogoutHandler 설정
-
UsernamePasswordAuthenticationFilter
- 로그인 요청이 서버로 오면
UsernamePasswordAuthenticationFilter
가AuthenticationManager
를 사용하여 인증을 진행username
과password
를 이용하여AuthenticationToken
을 만든다.AuthenticationToken
를 확인한다.
- 로그인 요청이 서버로 오면
OAuth Roles
- resource owner
protected resource
에 대한 액세스 권한을 부여 할 수있는 엔티티.resource owner
가 개인인 경우 이를end-user
라고합니다.- 리소스의 소유주 (일반 사용자)
- resource server
- access token을 사용하여
protected resource
요청을 수락하고 응답 할 수있는protected resource
를 호스팅하는 서버. - google, facebook, ... (데이터를 가지고 있는 서버)
- access token을 사용하여
- client
resource owner
를 대신하여 권한을 부여하여 보호 된 자원 요청을 하는 응용 프로그램.- "클라이언트"라는 용어는 특정 구현 특성 (예 : 응용 프로그램이 서버, 데스크톱 또는 기타 장치에서 실행되는지 여부)을 의미하지 않습니다.
- 우리가 만든 애플리케이션 서버
- authorization server
resource owner
를 성공적으로 인증하고 권한을 얻은 후 서버가 클라이언트에 액세스 토큰을 발행합니다.- 인증과 관련된 처리를 전담하는 서버
OAuth
-
스프링 2.x 이면 설정을 자동으로 해준다.
-
OAuth를 사용하기 전에 OAuth 2.0 credential를 획득해야 한다.
-
redirect URI : 최종 사용자의 사용자 에이전트가
Google
에서 인증 한 후 동의 페이지의 OAuth 클라이언트에 대한 액세스 권한을 부여한 후 다시 리디렉션되는 애플리케이션의 경로입니다.- default :
{baseUrl}/login/oauth2/code/{registrationId}
- default :
-
Google에서 받은 OAuth Client 정보를
application.yml
에 저장spring: security: oauth2: client: registration: google: client-id: google-client-id client-secret: google-client-secret
-
OAuth 인증 과정
- 용어 설명
- Client : 애플리케이션 서버 (Spring boot application)
- Resource Server : 데이터를 가지고 있는 서버 (Google, ...)
- Resource Owner : Resource의 주인 (사용자)
- Client가 Resource Server를 사용하기 위해 Resource Server로 부터 승인을 받는다. (register)
- Client에 Client Id, Client Secret, Authrized redirect URI를 보유하고 있어야함
- Scope를 설정하여 Resource Server의 기능을 선택적으로 사용
- 결과 : Client와 Resource Server는 Client Id, Client Secret, Authrized redirect URI를 모두 가지고 있다.
- Resource Owner가 Client에게 Resource Server의 자원을 사용한다는 요청을 보냄
- Client는 인증을 위해 Resource Owner에게 Resource Server의 링크를 보낸다.
- Resource Server가 현재 Resource Owner의 로그인 여부를 판단 후 로그인 처리
- 로그인이 되어 있지않으면 로그인 창을 응답
- 결과 : Resource Owner가 Resource Server로 로그인 완료
- Client가 보낸 링크의 redirect_uri값과 Resource Server에서 가지고 있는 redirect_uri 값을 비교
- 서로 값이 다르면 더이상 진행하지 않고 멈춘다.
- 값이 같으면 2번에서 Client가 Resource Owner에게 보낸 링크 중 scope를 확인
- Resource Server가 Resource Owner에게 해당 scope의 정보를 제공하기 위해 동의를 얻는다.
- 결과 : Resource Server에 user_id와 scope 값을 저장
- Resource Server는 Resource Owner에게 redirect URL값과 authorization code를 전송
- 결과 : Resource Owner는 authorization code를 담아서 Client에게 redirect URL 주소로 접근
- Client가 Resource Server로 접근
- 요청 URL과 grant_type(authorization_code), code, redirect_uri, client_id, client_secret 값이 전송
- Resource Server는 5번 요청에 담긴 값을 검사
- 5번 요청의 authorization_code값과 Resource Server에 저장되어있는 authorization_code 값을 비교하여 Client Id를 획득
- 5번 요청의 client_Id, client_secret 값을 비교
- 전부 일치하면 Resource Server는 authorization_code 값을 지운다. (다시 인증을 안하려고)
- Resource Server는 Access Token 발급하고 Client에게 Access Token 전송
- Access Token 값으로 해당 user_id는 어떤 scope 값이 있는지 확인 가능
-
OIDC
- OpenID Connect
- OpenID Connect가 OAuth 2.0을 기반으로 상위계층에서 좀 더 간편하게 인증을 처리할 수 있도록 고안
- OpenID Connect는 Access 토큰과 함께 ID 토큰을 전달합니다. 이 JWT(JSON Web Tokens)를 통해 암호화 된 토큰 안에 사용자 정보를 비롯한 다양한 정보를 HTTP 헤더의 최대 사이즈인 4KB 이내로 저장
- Access 토큰을 사용하여 한 번 더 OAuth 2.0 API를 호출할 필요 없이 사용자 정보가 담긴 ID 토큰을 복호화 하여 바로 사용할 수 있게 됩니다.
-
OAuth 인증 과정 in Spring boot
-
OAuth2AuthorizationRequestRedirectFilter
접근- OAuth 2.0 Authorization Request 를 생성
- OAuth를 사용한 authorization request에 반응한다.
- /oauth2/authorization/{registrationId}
-
OAuth2LoginAuthenticationFilter
클래스에서attemptAuthentication()
호출하여 인증 시도-
authenticationRequest
: 인증을 위한 정보
-
-
인증을 위한
ProvideManager
로OidcAuthorizationCodeAuthenticationProvider
의authenticate()
실행-
OidcUserService
의loadUser()
실행-
loadUser()
실행 중OidcUserAuthority
로 ROLE 설정- 기본값은 ROLE_USER
-
SimpleGrantedAuthority
타입으로 SCOPE 설정 -
OidcUser
생성 (DefaultOidcUser
타입)- 생성시 authorities, Id Token, userInfo를 파라미터로
- name은 idToken의 sub 값으로 설정
- sub 값은 JWT의 principal을 식별
-
결과로 oidcUser 를 return
-
-
OidcUser의 authority를
GrantedAuthoritiesMapper
로 매핑 -
OAuth2LoginAuthenticationToken
객체 생성 후 return
-
-
3번의 결과를 이용하여 로
OAuth2AuthenticationToken
를 생성-
생성자 파라미터로
-
-
3번, 4번 리턴 값을 사용하여
OAuth2AuthorizedClient
를 생성 -
5번에서 생성한
OAuth2AuthorizedClient
을OAuth2AuthorizedClientRepository
에 저장OAuth2AuthorizedClientService
를 implement를 하여 customize 했다면@Override
한saveAuthorizedClient()
를 실행한다.
-
OAuth2LoginAuthenticationFilter
의 결과로 4번에서 만든OAuth2AuthenticationToken
을 리턴
-