$ curl https://start.spring.io/starter.tgz -d style=web \
-d style=security -d name=authserver | tar -xzvf -
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 스웨거
- 앵귤러JS
- 레디스
- 스프링 세션
- 스프링 시큐리티
- Authentication
- 스프링
- api 문서화
- Exception Handling
- AUthorization
- angularjs
- 스프링 시큐리티와 앵귤러 js
- 예외처리
- 앵귤러
- spring boot
- spring security and angularjs
- oauth2
- Spring Framework
- spring security oauth2
- spring framwork
- 스프링부트
- api documentation
- controlleradvice
- swagger-ui
- angular
- Spring Security
- rest api
- spring session
- spring
- 스프링 부트
- Today
- Total
스프링부트는 사랑입니다
Spring Security and AngularJS Part V 본문
스프링 시큐리티와 앵귤러JS Spring Security and AngularJS
Part I : 단일 페이지 보안 어플리케이션 (A Secure Single Page Application)
Part II: 로그인페이지 (The Login Page)
Part III: 리소스 서버 (The Resource Server)
Part IV: API 게이트웨이 (The API Gateway)
Part V: OAuth2와 싱글사인온 (Single Sign On with OAuth2)
Part VI: 다중 UI 어플리케이션과 게이트웨이 (Multiple UI Applications and a Gateway)
Part VII: 모듈화한 AngularJS 어플리케이션 (Modular AngularJS Application)
Part VIII: Angular 어플리케이션 테스트 (Testing an Angular Application)
--------------------------------------------------------------------------------
OAuth2를 활용한 싱글사인온 Single Sign On with OAuth2
이 섹션에서 우리는 어떻게 스프링 시큐티리와 앵귤러JS로 단일 페이지 어플리케이션을 만드는지 계속 얘기해볼 것이다. 이제 우리는 API게이트웨이가 백엔드 리소스에 OAuth2 토큰 인증과 싱글사인온을 지원하도록 스프링 클라우드와 함께 스프링 시큐리티 OAuth를 어떻게 사용하는지 보여줄것이다. 이 글을 시리즈의 5번째 섹션으로 당신이 어플리케이션의 기본구성을 이해하거나 처음부터 빌드해보려면 첫번째 섹션부터 읽도록 하자 또는 그냥 Github의 소스코드로 바로 가도 된다. 이전 섹션에서 우리는 UI서버에 내장된 API 게이트웨이를 구현하기 위해 스프링 클라우드를, 백엔드 리소스의 인증을 위해 스프링 세션을 사용하여 배포가능한 작은 어플리케이션을 만들었다. 이번 섹션에서 우리의 UI서버를 인증서버로 잠재적으로 많은 싱글사인온 어플리케이션의 하나로 만들 것이다. 이것은 엔터프라이즈나 쇼셜 스타트업에서나 요즘 수많은 어플리케이션에서 흔한 패턴이다. 우리는 OAuth2 서버를 인증자로서 사용할 것이다. 또한 백엔드 서버를 위한 토큰 인증을 위해 사용할 것이다. 스프링 클라우드는 자동으로 우리의 백엔드에서 억세스 토큰access token을 중계하여 우리로 하여금 UI와 리소스 서버 양쪽의 구현을 더욱 간단하게 만들어준다.
기억하기: 만일 당신이 이 섹션의 샘플을 동작시키려면 브라우저의 쿠키와 HTTP Basic credential에 대한 캐시값을 지워주어야 한다. 크롬에선 새 incognito 창을 쓰는게 최선의 방법이다.
이 섹션에서 우리는 어떻게 스프링 시큐티리와 앵귤러JS로 단일 페이지 어플리케이션을 만드는지 계속 얘기해볼 것이다. 이제 우리는 API게이트웨이가 백엔드 리소스에 OAuth2 토큰 인증과 싱글사인온을 지원하도록 스프링 클라우드와 함께 스프링 시큐리티 OAuth를 어떻게 사용하는지 보여줄것이다. 이 글을 시리즈의 5번째 섹션으로 당신이 어플리케이션의 기본구성을 이해하거나 처음부터 빌드해보려면 첫번째 섹션부터 읽도록 하자 또는 그냥 Github의 소스코드로 바로 가도 된다. 이전 섹션에서 우리는 UI서버에 내장된 API 게이트웨이를 구현하기 위해 스프링 클라우드를, 백엔드 리소스의 인증을 위해 스프링 세션을 사용하여 배포가능한 작은 어플리케이션을 만들었다. 이번 섹션에서 우리의 UI서버를 인증서버로 잠재적으로 많은 싱글사인온 어플리케이션의 하나로 만들 것이다. 이것은 엔터프라이즈나 쇼셜 스타트업에서나 요즘 수많은 어플리케이션에서 흔한 패턴이다. 우리는 OAuth2 서버를 인증자로서 사용할 것이다. 또한 백엔드 서버를 위한 토큰 인증을 위해 사용할 것이다. 스프링 클라우드는 자동으로 우리의 백엔드에서 억세스 토큰access token을 중계하여 우리로 하여금 UI와 리소스 서버 양쪽의 구현을 더욱 간단하게 만들어준다.
기억하기: 만일 당신이 이 섹션의 샘플을 동작시키려면 브라우저의 쿠키와 HTTP Basic credential에 대한 캐시값을 지워주어야 한다. 크롬에선 새 incognito 창을 쓰는게 최선의 방법이다.
OAuth2 인증서버 만들기 Creating an OAuth2 Authorization Server
인증과 토근관리를 처리하는 새 서버를 만드는 첫 걸음은, Spring Boot Initializr로 시작하는 첫번째 섹션의 절차를 따르는 것이다. 유닉스계열 시스템에서는 다음과 같이 curl을 사용할 수 있다:
그 후에 당신이 선호하는 IDE에서 프로젝트를 import하면 된다. 또는 커맨드라인에서 "mvn" 명령어를 써서 파일로 동작해도 된다.
인증과 토근관리를 처리하는 새 서버를 만드는 첫 걸음은, Spring Boot Initializr로 시작하는 첫번째 섹션의 절차를 따르는 것이다. 유닉스계열 시스템에서는 다음과 같이 curl을 사용할 수 있다:
그 후에 당신이 선호하는 IDE에서 프로젝트를 import하면 된다. 또는 커맨드라인에서 "mvn" 명령어를 써서 파일로 동작해도 된다.
OAuth2 의존성 추가하기 Adding the OAuth2 Dependencies
Spring OAuth 의존성을 추가해줘야한다 POM에 다음과 같이 추가하자:
pom.xml<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
인증서버의 구현은 매우 쉽다. 최소한의 구현은 다음과 같다:
AuthserverApplication.java
@SpringBootApplication
public class AuthserverApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(AuthserverApplication.class, args);
}
@Configuration
@EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("acme")
.secret("acmesecret")
.authorizedGrantTypes("authorization_code", "refresh_token",
"password").scopes("openid");
}
}
다음의 2가지 일만 해주면 된다. (@EnableAuthorizationServer
를 추가한 후):
클라이언트 "acme"를 secret과 "authorization_code"를 포함하는 인증타입을 가지도록 등록한다.
스프링 부트 자동설정으로 부터 기본 AuthenticationManager를 주입하고 이것을 OAuth2 endpoint와 연동한다.
이제 테스트를 위해 패스워드를 명시하고 9999포트에서 돌려보자:
application.propertiesserver.port=9999
security.user.password=password
server.contextPath=/uaa
또한 기본값("/")을 사용하지않게 하기 위해 context path를 명시한다. 이것은 이렇게 하지않으면 로컬호스트의 다른 서버로부터 얻은 쿠키를 잘못된 서버로 보낼 수 있기 때문이다. 다음과 같이 서버가 동작하는 것을 확인할 수 있다:
$ mvn spring-boot:run
또는 당신의 IDE에서 main()
메소드에서 시작하자.
Spring OAuth 의존성을 추가해줘야한다 POM에 다음과 같이 추가하자:
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
인증서버의 구현은 매우 쉽다. 최소한의 구현은 다음과 같다:
@SpringBootApplication
public class AuthserverApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(AuthserverApplication.class, args);
}
@Configuration
@EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("acme")
.secret("acmesecret")
.authorizedGrantTypes("authorization_code", "refresh_token",
"password").scopes("openid");
}
}
다음의 2가지 일만 해주면 된다. (@EnableAuthorizationServer
를 추가한 후):
클라이언트 "acme"를 secret과 "authorization_code"를 포함하는 인증타입을 가지도록 등록한다.
스프링 부트 자동설정으로 부터 기본 AuthenticationManager를 주입하고 이것을 OAuth2 endpoint와 연동한다.
이제 테스트를 위해 패스워드를 명시하고 9999포트에서 돌려보자:
server.port=9999
security.user.password=password
server.contextPath=/uaa
또한 기본값("/")을 사용하지않게 하기 위해 context path를 명시한다. 이것은 이렇게 하지않으면 로컬호스트의 다른 서버로부터 얻은 쿠키를 잘못된 서버로 보낼 수 있기 때문이다. 다음과 같이 서버가 동작하는 것을 확인할 수 있다:
$ mvn spring-boot:run
또는 당신의 IDE에서 main()
메소드에서 시작하자.
인증서버 테스트하기 Testing the Authorization Server
스프링 기본 시큐리티 설정을 사용하는 우리 서버는 HTTP Basic 인증에 의해 보호되는 첫번째 섹션의 서버와 같다. authorization code token grant 를 초기화 하기위해, 당신은 인증endpoint를 거쳐야한다. 이를테면 http://localhost:9999/uaa/oauth/authorize? response_type=code&client_id=acme&redirect_uri=http://example.com. 일단 인증을 받았으면 인증코드가 첨부되어 example.com으로 리다이렉트될 것이다. 예를 들면 http://example.com/?code=jYWioI.
샘플 어플리케이션에서 등록을 위한 리다이렉트없이 "acme"클라이언트를 만든 목적은, 우리가 example.com의 리다이렉트를 얻을 수 있게 하기위함이다. 제품화수준의 어플리케이션에셔는 항상 리다이렉트를 등록해줘야한다 (HTTPS를 사용해서)
토큰 endpoint의 "acme"클라이언트 credential을 사용하는 억세스토큰을 위해 코드를 맞교환한다:
$ curl acme:acmesecret@localhost:9999/uaa/oauth/token \
-d grant_type=authorization_code -d client_id=acme \
-d redirect_uri=http://example.com -d code=jYWioI
{"access_token":"2219199c-966e-4466-8b7e-12bb9038c9bb","token_type":"bearer","refresh_token":"d193caf4-5643-4988-9a4a-1c03c9d657aa","expires_in":43199,"scope":"openid"}
억세스 토큰은 서버의 메모리상주 토큰 저장소에 의해 만들어진 UUID ("2219199c…")이다. 우리는 또한 현재의 토큰의 유효기간이 끝날때 새로운 억세스토큰을 받는데 사용하는 리프레시 토큰을 받는다.
우리가 "acme" 클라이언트에 "password" 승인을 허용하였으므로, curl과 인증코드 대신 user credential을 사용하여 토큰 endpoint로부터 직접 토큰을 얻을 수 있다. 이는 테스트용으로는 유용하지만 브라우저 기반의 클라이언트에서 적합하지않다.
위의 링크를 따라가보면, 스프링 OAuth가 제공하는 whitelabel UI를 볼 수 있다. 우리가 이것을 사용하려면 self-contained서버를 위해 곧 두번째 섹션에서 했던 것을 다시 보강해보자
스프링 기본 시큐리티 설정을 사용하는 우리 서버는 HTTP Basic 인증에 의해 보호되는 첫번째 섹션의 서버와 같다. authorization code token grant 를 초기화 하기위해, 당신은 인증endpoint를 거쳐야한다. 이를테면 http://localhost:9999/uaa/oauth/authorize? response_type=code&client_id=acme&redirect_uri=http://example.com. 일단 인증을 받았으면 인증코드가 첨부되어 example.com으로 리다이렉트될 것이다. 예를 들면 http://example.com/?code=jYWioI.
샘플 어플리케이션에서 등록을 위한 리다이렉트없이 "acme"클라이언트를 만든 목적은, 우리가 example.com의 리다이렉트를 얻을 수 있게 하기위함이다. 제품화수준의 어플리케이션에셔는 항상 리다이렉트를 등록해줘야한다 (HTTPS를 사용해서) |
토큰 endpoint의 "acme"클라이언트 credential을 사용하는 억세스토큰을 위해 코드를 맞교환한다:
$ curl acme:acmesecret@localhost:9999/uaa/oauth/token \
-d grant_type=authorization_code -d client_id=acme \
-d redirect_uri=http://example.com -d code=jYWioI
{"access_token":"2219199c-966e-4466-8b7e-12bb9038c9bb","token_type":"bearer","refresh_token":"d193caf4-5643-4988-9a4a-1c03c9d657aa","expires_in":43199,"scope":"openid"}
억세스 토큰은 서버의 메모리상주 토큰 저장소에 의해 만들어진 UUID ("2219199c…")이다. 우리는 또한 현재의 토큰의 유효기간이 끝날때 새로운 억세스토큰을 받는데 사용하는 리프레시 토큰을 받는다.
우리가 "acme" 클라이언트에 "password" 승인을 허용하였으므로, curl과 인증코드 대신 user credential을 사용하여 토큰 endpoint로부터 직접 토큰을 얻을 수 있다. 이는 테스트용으로는 유용하지만 브라우저 기반의 클라이언트에서 적합하지않다. |
위의 링크를 따라가보면, 스프링 OAuth가 제공하는 whitelabel UI를 볼 수 있다. 우리가 이것을 사용하려면 self-contained서버를 위해 곧 두번째 섹션에서 했던 것을 다시 보강해보자
리소스 서버 바꾸기 Changing the Resource Server
네번째 섹션에서 우리의 리소스 서버는 인증을 위해 스프링 세션을 사용했다. 우리는 이것을 스프링 OAuth으로 교체할 것이다. 또한 스프링 세션과 레디스 의존성을 제거할 필요가 있다. 다음과 같이 바꿔보자:
pom.xml<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.0.0.RC1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
위의 의존성을 아래로 바꾸자:
pom.xml<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
그 후 메인 어플리케이션 클래서에서 Filter
세션을 지우자, 이것을 스프링 클라우드 시큐리티의 @EnableOAuth2Resource
어노테이션으로 바꾼다:
ResourceApplication.groovy@SpringBootApplication
@RestController
@EnableOAuth2Resource
class ResourceApplication {
@RequestMapping('/')
def home() {
[id: UUID.randomUUID().toString(), content: 'Hello World']
}
static void main(String[] args) {
SpringApplication.run ResourceApplication, args
}
}
이 수정으로 어플리케이션은 HTTP Basic 대신 억세스 토큰을 검수하도록 준비가 되었다. 그러나 우리는 이 프로세스를 실제로 끝마치려면 설정을 수정해줘야한다. "application.properties"에 작은 외부 설정을 추가하여 리소스 서버가 사용자를 인증하고 토큰을 디코드할 수 있게 할 것이다:
application.properties
...
spring.oauth2.resource.userInfoUri: http://localhost:9999/uaa/user
이것은 서버가 "/user" endpoint에 접근하기위해 토큰을 사용하며, 인증 정보를 얻어내는데 사용할 것이라고 알려주는 것이다. (페이스북 API의 "/me" endpoint 와 유사하다). 이는 스프링 OAuth2의 ResourceServerTokenServices
인터페이스에 의해 표출됨으로서 리소스 서버가 토큰을 디코드하는 방법을 효율적으로 제공해준다.
어플리케이션을 시작하고 클라이언트의 커맨드라인에서 홈페이지를 쳐보자:
$ curl -v localhost:9000
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:9000
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
...
< WWW-Authenticate: Bearer realm="null", error="unauthorized", error_description="An Authentication object was not found in the SecurityContext"
< Content-Type: application/json;charset=UTF-8
{"error":"unauthorized","error_description":"An Authentication object was not found in the SecurityContext"}
bearer 토큰을 가르키는 "WWW-Authenticate" 헤더를 가지는 401 을 볼 수 있을 것이다.
userInfoUri
는 리소스서버가 토큰을 디코드하는 방법을 연결할 뿐 아니라 사실, 최소 공통분모의 한 종류이다 (그리고 스펙의 일부가 아니다.) 그러나 종종 (페이스북, 클라우드 파운더리, Github과 같은) OAuth2 프로바이더에서 이용가능하다. 그리고 또 다른 선택을 사용할 수 있는데 예를 들면, (JWT와 같은 예로) 사용자는 토큰의 유저인증 자체를 인코드할 수 있다. 또는 공유된 백엔드 저정소를 사용할 수도 있다. 클라우드 파운더리에는 /token_info
endpoint가 있어 사용자 정보 endpoint보다 더 자세한 정보를 제공해준다. 그러나 더 구체적인 인증을 요구한다. 또다른 (자연스러운) 옵션으로 trade-off와 다른 종유의 혜택을 제공할 수 있지만 이들에 대한 자세한 논의는 이 섹션의 범주에서 벗어난다.
네번째 섹션에서 우리의 리소스 서버는 인증을 위해 스프링 세션을 사용했다. 우리는 이것을 스프링 OAuth으로 교체할 것이다. 또한 스프링 세션과 레디스 의존성을 제거할 필요가 있다. 다음과 같이 바꿔보자:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.0.0.RC1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
위의 의존성을 아래로 바꾸자:
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
그 후 메인 어플리케이션 클래서에서 Filter
세션을 지우자, 이것을 스프링 클라우드 시큐리티의 @EnableOAuth2Resource
어노테이션으로 바꾼다:
@SpringBootApplication
@RestController
@EnableOAuth2Resource
class ResourceApplication {
@RequestMapping('/')
def home() {
[id: UUID.randomUUID().toString(), content: 'Hello World']
}
static void main(String[] args) {
SpringApplication.run ResourceApplication, args
}
}
이 수정으로 어플리케이션은 HTTP Basic 대신 억세스 토큰을 검수하도록 준비가 되었다. 그러나 우리는 이 프로세스를 실제로 끝마치려면 설정을 수정해줘야한다. "application.properties"에 작은 외부 설정을 추가하여 리소스 서버가 사용자를 인증하고 토큰을 디코드할 수 있게 할 것이다:
application.properties
...
spring.oauth2.resource.userInfoUri: http://localhost:9999/uaa/user
이것은 서버가 "/user" endpoint에 접근하기위해 토큰을 사용하며, 인증 정보를 얻어내는데 사용할 것이라고 알려주는 것이다. (페이스북 API의 "/me" endpoint 와 유사하다). 이는 스프링 OAuth2의 ResourceServerTokenServices
인터페이스에 의해 표출됨으로서 리소스 서버가 토큰을 디코드하는 방법을 효율적으로 제공해준다.
어플리케이션을 시작하고 클라이언트의 커맨드라인에서 홈페이지를 쳐보자:
$ curl -v localhost:9000
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:9000
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
...
< WWW-Authenticate: Bearer realm="null", error="unauthorized", error_description="An Authentication object was not found in the SecurityContext"
< Content-Type: application/json;charset=UTF-8
{"error":"unauthorized","error_description":"An Authentication object was not found in the SecurityContext"}
bearer 토큰을 가르키는 "WWW-Authenticate" 헤더를 가지는 401 을 볼 수 있을 것이다.
userInfoUri 는 리소스서버가 토큰을 디코드하는 방법을 연결할 뿐 아니라 사실, 최소 공통분모의 한 종류이다 (그리고 스펙의 일부가 아니다.) 그러나 종종 (페이스북, 클라우드 파운더리, Github과 같은) OAuth2 프로바이더에서 이용가능하다. 그리고 또 다른 선택을 사용할 수 있는데 예를 들면, (JWT와 같은 예로) 사용자는 토큰의 유저인증 자체를 인코드할 수 있다. 또는 공유된 백엔드 저정소를 사용할 수도 있다. 클라우드 파운더리에는 /token_info endpoint가 있어 사용자 정보 endpoint보다 더 자세한 정보를 제공해준다. 그러나 더 구체적인 인증을 요구한다. 또다른 (자연스러운) 옵션으로 trade-off와 다른 종유의 혜택을 제공할 수 있지만 이들에 대한 자세한 논의는 이 섹션의 범주에서 벗어난다. |
사용자 endpoint 구현하기 Implementing the User Endpoint
인증서버에 다음의 endpoint를 간단히 추가해보자:
AuthserverApplication.java@SpringBootApplication
@RestController
@EnableResourceServer
public class AuthserverApplication {
@RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
...
}
두번째 섹션에서 했던데로 똑같이 @RequestMapping
를 추가했다. 또한 "/oauth/* endpoint를 제외한 인증서버의 모든 곳에 기본 보안을 적용하는 스프링 OAuth의 @EnableResourceServer
어노테이션도 추가했다.
이 endpoint를 가지고 우리는 greeting 리소스를 테스트할 수 있다. 그들 둘다 이제 인증서버에 생성한 bearer 토큰을 허용하기 때문이다:
$ TOKEN=2219199c-966e-4466-8b7e-12bb9038c9bb
$ curl -H "Authorization: Bearer $TOKEN" localhost:9000
{"id":"03af8be3-2fc3-4d75-acf7-c484d9cf32b1","content":"Hello World"}
$ curl -H "Authorization: Bearer $TOKEN" localhost:9999/uaa/user
{"details":...,"principal":{"username":"user",...},"name":"user"}
(당신이 스스로 동작을 확인하기 위해 당신의 인증서버로 부터 획득한 억세스 토큰의 값으로 대체하자)
인증서버에 다음의 endpoint를 간단히 추가해보자:
@SpringBootApplication
@RestController
@EnableResourceServer
public class AuthserverApplication {
@RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
...
}
두번째 섹션에서 했던데로 똑같이 @RequestMapping
를 추가했다. 또한 "/oauth/* endpoint를 제외한 인증서버의 모든 곳에 기본 보안을 적용하는 스프링 OAuth의 @EnableResourceServer
어노테이션도 추가했다.
이 endpoint를 가지고 우리는 greeting 리소스를 테스트할 수 있다. 그들 둘다 이제 인증서버에 생성한 bearer 토큰을 허용하기 때문이다:
$ TOKEN=2219199c-966e-4466-8b7e-12bb9038c9bb
$ curl -H "Authorization: Bearer $TOKEN" localhost:9000
{"id":"03af8be3-2fc3-4d75-acf7-c484d9cf32b1","content":"Hello World"}
$ curl -H "Authorization: Bearer $TOKEN" localhost:9999/uaa/user
{"details":...,"principal":{"username":"user",...},"name":"user"}
(당신이 스스로 동작을 확인하기 위해 당신의 인증서버로 부터 획득한 억세스 토큰의 값으로 대체하자)
UI 서버 The UI Server
이 어플리케이션의 마지막 부분으로, 우리는 인증서버로 위임하고 인증 파트를 추출하는 UI서버를 완성시켜야한다. 이것을 위해 리소스 서버로부터 우리는 먼저 스프링 세션과 레디스 의존성을 제거하고 그들을 스프링 OAuth로 대체해줘야한다.
이것이 끝나면, 우리는 세션 필터와 "/user" endpoint 또한 지워준다. (@EnableOAuth2Sso
어노테이션을 사용하여) 인증서버로 리다이렉스하기 위해 어플리케이션을 설정하자:
UiApplication.java
@SpringBootApplication
@EnableZuulProxy
@EnableOAuth2Sso
public class UiApplication {
public static void main(String[] args) {
SpringApplication.run(UiApplication.class, args);
}
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
네번째 섹션을 기억하자 @EnableZuulProxy
의 혜택을 본 UI서버는 이제 API 게이트웨이로서 동작한다. 우리는 YAML에 라우트 매핑route mapping을 선언할 수 있다. 이제 "/user" endpoint는 인증서버로 프록시되어진다:
application.ymlzuul:
routes:
resource:
path: /resource/**
url: http://localhost:9000
user:
path: /user/**
url: http://localhost:9999/uaa/user
마침내 우리는WebSecurityConfigurerAdapter
를 OAuth2SsoConfigurerAdapter
로 바꿔줘야 한다. 이제부터 @EnableOAuth2Sso
에 의해 설정된 SSO filter chain이 기본값으로 바뀔것이다:
SecurityConfiguration.java
@Configuration
protected static class SecurityConfiguration extends OAuth2SsoConfigurerAdapter {
@Override
public void match(RequestMatchers matchers) {
matchers.anyRequest();
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/index.html", "/home.html", "/")
.permitAll().anyRequest().authenticated().and().csrf()
.csrfTokenRepository(csrfTokenRepository()).and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
}
... // the csrf*() methods are the same as the old WebSecurityConfigurerAdapter
}
기본 클래스 이름과 상관없이 주요한 변경사항은 자기들 스스로의 메소드로 가게 하는 matcher이다. 이제 formLogin() 은 더이상 필요없다.
또한 @EnableOAuth2Sso
어노테이션을 위한 몇개의 의무적인 외부설정 프로퍼티가 있어 올바른 인증서버에 연결하고 인증할 수 있다. 이것을 application.yml
에 넣어주자:
application.yml
spring:
oauth2:
sso:
home:
secure: false
path: /,/**/*.html
client:
accessTokenUri: http://localhost:9999/uaa/oauth/token
userAuthorizationUri: http://localhost:9999/uaa/oauth/authorize
clientId: acme
clientSecret: acmesecret
resource:
userInfoUri: http://localhost:9999/uaa/user
OAuth2 클라이언트 ("acme")와 인증서버 위치를 넣어주자. 또한 (리소스서버에 있는 것과 같은) userInfoUri
가 있어 사용자가 UI 앱 자체에서 인증받을 수 있다. "home"관련 것들은 우리의 단일 페이지 어플리케이션에서 정적 리소스에 익명의 접근을 허용한다.
이 어플리케이션의 마지막 부분으로, 우리는 인증서버로 위임하고 인증 파트를 추출하는 UI서버를 완성시켜야한다. 이것을 위해 리소스 서버로부터 우리는 먼저 스프링 세션과 레디스 의존성을 제거하고 그들을 스프링 OAuth로 대체해줘야한다.
이것이 끝나면, 우리는 세션 필터와 "/user" endpoint 또한 지워준다. (@EnableOAuth2Sso
어노테이션을 사용하여) 인증서버로 리다이렉스하기 위해 어플리케이션을 설정하자:
@SpringBootApplication
@EnableZuulProxy
@EnableOAuth2Sso
public class UiApplication {
public static void main(String[] args) {
SpringApplication.run(UiApplication.class, args);
}
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
네번째 섹션을 기억하자 @EnableZuulProxy
의 혜택을 본 UI서버는 이제 API 게이트웨이로서 동작한다. 우리는 YAML에 라우트 매핑route mapping을 선언할 수 있다. 이제 "/user" endpoint는 인증서버로 프록시되어진다:
zuul:
routes:
resource:
path: /resource/**
url: http://localhost:9000
user:
path: /user/**
url: http://localhost:9999/uaa/user
마침내 우리는WebSecurityConfigurerAdapter
를 OAuth2SsoConfigurerAdapter
로 바꿔줘야 한다. 이제부터 @EnableOAuth2Sso
에 의해 설정된 SSO filter chain이 기본값으로 바뀔것이다:
@Configuration
protected static class SecurityConfiguration extends OAuth2SsoConfigurerAdapter {
@Override
public void match(RequestMatchers matchers) {
matchers.anyRequest();
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/index.html", "/home.html", "/")
.permitAll().anyRequest().authenticated().and().csrf()
.csrfTokenRepository(csrfTokenRepository()).and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
}
... // the csrf*() methods are the same as the old WebSecurityConfigurerAdapter
}
기본 클래스 이름과 상관없이 주요한 변경사항은 자기들 스스로의 메소드로 가게 하는 matcher이다. 이제 formLogin() 은 더이상 필요없다.
또한 @EnableOAuth2Sso
어노테이션을 위한 몇개의 의무적인 외부설정 프로퍼티가 있어 올바른 인증서버에 연결하고 인증할 수 있다. 이것을 application.yml
에 넣어주자:
application.yml
spring:
oauth2:
sso:
home:
secure: false
path: /,/**/*.html
client:
accessTokenUri: http://localhost:9999/uaa/oauth/token
userAuthorizationUri: http://localhost:9999/uaa/oauth/authorize
clientId: acme
clientSecret: acmesecret
resource:
userInfoUri: http://localhost:9999/uaa/user
OAuth2 클라이언트 ("acme")와 인증서버 위치를 넣어주자. 또한 (리소스서버에 있는 것과 같은) userInfoUri
가 있어 사용자가 UI 앱 자체에서 인증받을 수 있다. "home"관련 것들은 우리의 단일 페이지 어플리케이션에서 정적 리소스에 익명의 접근을 허용한다.
클라이언트에선 In the Client
UI어플리케이션의 프론트 엔드에 약간의 수정을 해주어 우리가 여전히 인증서버로 리다이트되도록 만들어줘야한다. 먼저 앵귤러 라우트로부터 "로그인" 링크를 바꿔줘야하는 네비게이션 바의 "index.html":
index.html<div ng-controller="navigation" class="container">
<ul class="nav nav-pills" role="tablist">
...
<li><a href="#/login">login</a></li>
...
</ul>
</div>
을 그냥 평범한 HTML링크로 바꿔주자:
index.html<div ng-controller="navigation" class="container">
<ul class="nav nav-pills" role="tablist">
...
<li><a href="login">login</a></li>
...
</ul>
</div>
"/login" endpoint는 스프링 시큐리티에 의해 처리된다. 만일 사용자가 인증받지 못하면 인증서버로 리다이렉트 될 것이다.
우리는 또한 "네비게이션" 컨트롤러의 login()
함수와 앵귤러 설정에서 "/login" 라우트를 지울 것이다.
hello.js
angular.module('hello', [ 'ngRoute' ]).config(function($routeProvider) {
$routeProvider.when('/', {
templateUrl : 'home.html',
controller : 'home'
}).otherwise('/');
}). // ...
.controller('navigation',
function($rootScope, $scope, $http, $location, $route) {
$http.get('user').success(function(data) {
if (data.name) {
$rootScope.authenticated = true;
} else {
$rootScope.authenticated = false;
}
}).error(function() {
$rootScope.authenticated = false;
});
$scope.credentials = {};
$scope.logout = function() {
$http.post('logout', {}).success(function() {
$rootScope.authenticated = false;
$location.path("/");
}).error(function(data) {
$rootScope.authenticated = false;
});
}
});
UI어플리케이션의 프론트 엔드에 약간의 수정을 해주어 우리가 여전히 인증서버로 리다이트되도록 만들어줘야한다. 먼저 앵귤러 라우트로부터 "로그인" 링크를 바꿔줘야하는 네비게이션 바의 "index.html":
<div ng-controller="navigation" class="container">
<ul class="nav nav-pills" role="tablist">
...
<li><a href="#/login">login</a></li>
...
</ul>
</div>
을 그냥 평범한 HTML링크로 바꿔주자:
<div ng-controller="navigation" class="container">
<ul class="nav nav-pills" role="tablist">
...
<li><a href="login">login</a></li>
...
</ul>
</div>
"/login" endpoint는 스프링 시큐리티에 의해 처리된다. 만일 사용자가 인증받지 못하면 인증서버로 리다이렉트 될 것이다.
우리는 또한 "네비게이션" 컨트롤러의 login()
함수와 앵귤러 설정에서 "/login" 라우트를 지울 것이다.
hello.js
angular.module('hello', [ 'ngRoute' ]).config(function($routeProvider) {
$routeProvider.when('/', {
templateUrl : 'home.html',
controller : 'home'
}).otherwise('/');
}). // ...
.controller('navigation',
function($rootScope, $scope, $http, $location, $route) {
$http.get('user').success(function(data) {
if (data.name) {
$rootScope.authenticated = true;
} else {
$rootScope.authenticated = false;
}
}).error(function() {
$rootScope.authenticated = false;
});
$scope.credentials = {};
$scope.logout = function() {
$http.post('logout', {}).success(function() {
$rootScope.authenticated = false;
$location.path("/");
}).error(function(data) {
$rootScope.authenticated = false;
});
}
});
어떻게 작동하는가? How Does it Work?
이제 모든 서버를 다같이 동작시키자. 브라우저를 열고 http://localhost:8080에 방문하여 "로그인" 링크를 클릭하면 UI로부터 인증한 같은 토큰을 사용해서 OAuth2리소스 서버로부터 불러오는 greeting을 가지는 UI의 홈페이지로 리다이렉트 되기전에 ,인증(HTTP Basic 팝업)과 토큰 승인(Whitelable HTML)을 위해 인증서버로 리다이트 될것이다.
브라우저와 백엔드간의 연동은 브라우저에서 확인할 수 있다. 만일 당신이 개발자툴을 쓴다면 (보통 크롬에서 F12로 열수 있고 파이어폭스에서는 플러그인이 필요할 것이다) 여기 요약본을 보자:
Verb Path Status Response GET
/
200
index.html
GET
/css/angular-bootstrap.css
200
Twitter bootstrap CSS
GET
/js/angular-bootstrap.js
200
Bootstrap and Angular JS
GET
/js/hello.js
200
Application logic
GET
/home.html
200
HTML partial for home page
GET
/user
302
Redirect to login page
GET
/login
302
Redirect to auth server
GET
(uaa)/oauth/authorize
401
(ignored)
GET
/resource
302
Redirect to login page
GET
/login
302
Redirect to auth server
GET
(uaa)/oauth/authorize
401
(ignored)
GET
/login
302
Redirect to auth server
GET
(uaa)/oauth/authorize
200
HTTP Basic auth happens here
POST
(uaa)/oauth/authorize
302
User approves grant, redirect to /login
GET
/login
302
Redirect to home page
GET
/user
200
(Proxied) JSON authenticated user
GET
/home.html
200
HTML partial for home page
GET
/resource
200
(Proxied) JSON greeting
(uaa) 접두사가 붙은 요청들은 인증서버로 간다. "ignored"로 마크된 응답들은 우리는 그들이 던져주는 데이터를 따로 처리하지 않지 때문에 XHR호출로 앵귤러가 받게되는 응답이다. "/user"리소스의 경우 우리는 인증된 사용자를 요구한다. 첫번째 호출에 인증이 없기때문에 응답은 드랍된다.
UI의 "/trace" endpoint (마지막까지 스크롤을 내려라)에서 당신은 remote:true
설정으로 "/user"와 "/resource"의 프록시된 백엔드 요청을 볼 수 있을 것이다. 네번째 섹션에서 언급한대로 쿠키대신 bearer토큰이 인증에 사용된다. 스프링 클라우드 시큐리티는 우리를 위해 이것을 알아서 해준다: @EnableOAuth2Sso
와@EnableZuulProxy
의 설정을 인식함으로서, (기본값으로) 우리가 토큰을 프록시된 백엔드에 중계하기 원한다고 이해한다.
이전 섹션에서, 인증이 섞이는것을 막기위해 "/trace"를 다른 브라우저로 사용하라고 언급했다.
이제 모든 서버를 다같이 동작시키자. 브라우저를 열고 http://localhost:8080에 방문하여 "로그인" 링크를 클릭하면 UI로부터 인증한 같은 토큰을 사용해서 OAuth2리소스 서버로부터 불러오는 greeting을 가지는 UI의 홈페이지로 리다이렉트 되기전에 ,인증(HTTP Basic 팝업)과 토큰 승인(Whitelable HTML)을 위해 인증서버로 리다이트 될것이다.
브라우저와 백엔드간의 연동은 브라우저에서 확인할 수 있다. 만일 당신이 개발자툴을 쓴다면 (보통 크롬에서 F12로 열수 있고 파이어폭스에서는 플러그인이 필요할 것이다) 여기 요약본을 보자:
Verb | Path | Status | Response |
---|---|---|---|
GET | / | 200 | index.html |
GET | /css/angular-bootstrap.css | 200 | Twitter bootstrap CSS |
GET | /js/angular-bootstrap.js | 200 | Bootstrap and Angular JS |
GET | /js/hello.js | 200 | Application logic |
GET | /home.html | 200 | HTML partial for home page |
GET | /user | 302 | Redirect to login page |
GET | /login | 302 | Redirect to auth server |
GET | (uaa)/oauth/authorize | 401 | (ignored) |
GET | /resource | 302 | Redirect to login page |
GET | /login | 302 | Redirect to auth server |
GET | (uaa)/oauth/authorize | 401 | (ignored) |
GET | /login | 302 | Redirect to auth server |
GET | (uaa)/oauth/authorize | 200 | HTTP Basic auth happens here |
POST | (uaa)/oauth/authorize | 302 | User approves grant, redirect to /login |
GET | /login | 302 | Redirect to home page |
GET | /user | 200 | (Proxied) JSON authenticated user |
GET | /home.html | 200 | HTML partial for home page |
GET | /resource | 200 | (Proxied) JSON greeting |
(uaa) 접두사가 붙은 요청들은 인증서버로 간다. "ignored"로 마크된 응답들은 우리는 그들이 던져주는 데이터를 따로 처리하지 않지 때문에 XHR호출로 앵귤러가 받게되는 응답이다. "/user"리소스의 경우 우리는 인증된 사용자를 요구한다. 첫번째 호출에 인증이 없기때문에 응답은 드랍된다.
UI의 "/trace" endpoint (마지막까지 스크롤을 내려라)에서 당신은 remote:true
설정으로 "/user"와 "/resource"의 프록시된 백엔드 요청을 볼 수 있을 것이다. 네번째 섹션에서 언급한대로 쿠키대신 bearer토큰이 인증에 사용된다. 스프링 클라우드 시큐리티는 우리를 위해 이것을 알아서 해준다: @EnableOAuth2Sso
와@EnableZuulProxy
의 설정을 인식함으로서, (기본값으로) 우리가 토큰을 프록시된 백엔드에 중계하기 원한다고 이해한다.
이전 섹션에서, 인증이 섞이는것을 막기위해 "/trace"를 다른 브라우저로 사용하라고 언급했다. |
로그아웃 경험 The Logout Experience
로그아웃 링크를 클릭하면 홈페이지가 바뀌는 것을 볼 수 있다 (greeting이 더이상 보이지않는다). 따라서 사용자는 더이상 UI서버에 인증되어있지않다. 로그인을 다시 클릭하면 인증서버에 승인절차를 다시 거칠 필요가 없다. (왜냐하면 당신은 그곳에 로그아웃하지않았으므로). 어떤것이 올바른 사용자 경험인지 의견이 나뉠테지만, 이는 악명높은 속임수 문제가 있다.(싱글사인아웃 Single Sign Out:Science Direct article and Shibboleth docs). 이상적인 사용자 경험은 기술적으로 취약하지 않아야한다. 또한 사용자가 원하는게 무엇인가 심사숙고해야한다. "나는 '로그아웃'하길 원한다"는 말은 충분히 쉽게 들리지만 명확한 응답은 "무엇을 로그아웃할것인가?"이다. 이 싱글사인온서버에 의해 제어되는 모든 시스템에서 로그아웃하길 원하는지 아니면 그냥 "로그아웃"링크를 클릭하는 것을 원하는가? 여기는 이 주제를 포괄적으로 논의할 곳은 아니다. 그러나 주목해야할 가치가 있다. 만일 이 논제에 관심이 있다면 몇몇의 군침이 도는 아이디어를 Open ID Connect 사양에서 토론을 할 수 있다.
로그아웃 링크를 클릭하면 홈페이지가 바뀌는 것을 볼 수 있다 (greeting이 더이상 보이지않는다). 따라서 사용자는 더이상 UI서버에 인증되어있지않다. 로그인을 다시 클릭하면 인증서버에 승인절차를 다시 거칠 필요가 없다. (왜냐하면 당신은 그곳에 로그아웃하지않았으므로). 어떤것이 올바른 사용자 경험인지 의견이 나뉠테지만, 이는 악명높은 속임수 문제가 있다.(싱글사인아웃 Single Sign Out:Science Direct article and Shibboleth docs). 이상적인 사용자 경험은 기술적으로 취약하지 않아야한다. 또한 사용자가 원하는게 무엇인가 심사숙고해야한다. "나는 '로그아웃'하길 원한다"는 말은 충분히 쉽게 들리지만 명확한 응답은 "무엇을 로그아웃할것인가?"이다. 이 싱글사인온서버에 의해 제어되는 모든 시스템에서 로그아웃하길 원하는지 아니면 그냥 "로그아웃"링크를 클릭하는 것을 원하는가? 여기는 이 주제를 포괄적으로 논의할 곳은 아니다. 그러나 주목해야할 가치가 있다. 만일 이 논제에 관심이 있다면 몇몇의 군침이 도는 아이디어를 Open ID Connect 사양에서 토론을 할 수 있다.
결 론 Conclusion
이제 스프링 시큐리티와 앵귤러JS 스택을 거치는 겉핥기식 여행의 끝무렵에 다다랐다. 우리는 이제 UI/API 게이트웨이, 리소스 서버, 인증서버/토큰승인자 의 명확한 책임을 가지는 각각 3개의 분리된 컴포넌트로 구성된 멋진 아키텍쳐를 가지게 되었다. 이들은 모든 레이어에서 비지니스 로직이 아닌 코드의 양을 최소화하였고 비지니스 로직을 더욱 향상시키고 확장할 수 있도록 알아보기 쉽다. 다음 단계에서 우리는 우리의 인증서버의 UI를 깔끔하게 정리할 것이다. 아마 자바스크립트 클라이언트상의 테스트를 포함하는 약간의 테스트를 추가할 것이다. 또 하나의 흥미로은 작업은 이 표준공정(boilerplate)의 코드를 모두 추출하여 앵귤러 쪽의 네비게이션 컨트롤러를 위해 스프링 시큐리티와 스프링 세션 자동설정과 몇개의 webjars리소스를 포함하는 라이브러리 (예를 들면 "spring-security-angular")안에 넣는 것이다. 앵귤러JS나 스프링 시큐리티의 내부 동작에 대해 배우길 원하는 누구나 이 시리즈의 섹션에 아마도 실망하게 될것이다. 그러나 당신이 어떻게 모두다 함께 잘 동작하는지, 최소한의 설정으로 먼 길을 떠날 수 있는지 알기원한다면 당신은 희망적으로 좋은 경험을 얻게 될것이다. Spring Cloud 는 이제 막 릴리즈 되었고 그들을 써야할 때 스냇샷으로 가져와야한다. 그러나 Release candidate이 이용가능하고 GA가 곧 나올것이다. Github 이나 gitter.im에 피드백을 보내거나 확인할 수 있다.
이 시리즈의 다음섹션은 (인증과정에서) 접근 결정에 관한 것이다. 같은 프록시내에서 다중의 UI어플리케이션을 사용할 것이다.
이제 스프링 시큐리티와 앵귤러JS 스택을 거치는 겉핥기식 여행의 끝무렵에 다다랐다. 우리는 이제 UI/API 게이트웨이, 리소스 서버, 인증서버/토큰승인자 의 명확한 책임을 가지는 각각 3개의 분리된 컴포넌트로 구성된 멋진 아키텍쳐를 가지게 되었다. 이들은 모든 레이어에서 비지니스 로직이 아닌 코드의 양을 최소화하였고 비지니스 로직을 더욱 향상시키고 확장할 수 있도록 알아보기 쉽다. 다음 단계에서 우리는 우리의 인증서버의 UI를 깔끔하게 정리할 것이다. 아마 자바스크립트 클라이언트상의 테스트를 포함하는 약간의 테스트를 추가할 것이다. 또 하나의 흥미로은 작업은 이 표준공정(boilerplate)의 코드를 모두 추출하여 앵귤러 쪽의 네비게이션 컨트롤러를 위해 스프링 시큐리티와 스프링 세션 자동설정과 몇개의 webjars리소스를 포함하는 라이브러리 (예를 들면 "spring-security-angular")안에 넣는 것이다. 앵귤러JS나 스프링 시큐리티의 내부 동작에 대해 배우길 원하는 누구나 이 시리즈의 섹션에 아마도 실망하게 될것이다. 그러나 당신이 어떻게 모두다 함께 잘 동작하는지, 최소한의 설정으로 먼 길을 떠날 수 있는지 알기원한다면 당신은 희망적으로 좋은 경험을 얻게 될것이다. Spring Cloud 는 이제 막 릴리즈 되었고 그들을 써야할 때 스냇샷으로 가져와야한다. 그러나 Release candidate이 이용가능하고 GA가 곧 나올것이다. Github 이나 gitter.im에 피드백을 보내거나 확인할 수 있다.
이 시리즈의 다음섹션은 (인증과정에서) 접근 결정에 관한 것이다. 같은 프록시내에서 다중의 UI어플리케이션을 사용할 것이다.
부록: 인증서버를 위한 부트스트랩 UI와 JWT 토큰 Addendum: Bootstrap UI and JWT Tokens for the Authorization Server
Github의 소스코드에서 두번째 섹션에서 우리가 만들었던 로그인페이지와 유사한 방식으로 구현한 보기좋은 로그인페이지와 유저 승인 페이지를 가지는 이 어플리케이션의 또다른 버전을 찾아볼 수 있다. 또한 JWT 인코딩된 토큰을 사용한다. 그래서 "/user" endpoint를 사용하는 대신, 리소스 서버는 간단한 인증을 수행하기 위해 토큰 자체에서 충분한 정보를 뽑아올 수 있다. 브라우저 클라이언트는 UI서버를 통해 프록시되어진 이것을 사용자가 인증되었는지 결정하는데 여전히 사용한다 (실제 어플리케이션에서 리소스서버를 호출하는 숫자에 비교하면 자주 호출될 필요가 없다.)
Github의 소스코드에서 두번째 섹션에서 우리가 만들었던 로그인페이지와 유사한 방식으로 구현한 보기좋은 로그인페이지와 유저 승인 페이지를 가지는 이 어플리케이션의 또다른 버전을 찾아볼 수 있다. 또한 JWT 인코딩된 토큰을 사용한다. 그래서 "/user" endpoint를 사용하는 대신, 리소스 서버는 간단한 인증을 수행하기 위해 토큰 자체에서 충분한 정보를 뽑아올 수 있다. 브라우저 클라이언트는 UI서버를 통해 프록시되어진 이것을 사용자가 인증되었는지 결정하는데 여전히 사용한다 (실제 어플리케이션에서 리소스서버를 호출하는 숫자에 비교하면 자주 호출될 필요가 없다.)
'Tutorials' 카테고리의 다른 글
첫번째 스프링 부트 어플리케이션 개발하기 (레퍼런스 11장) (2) | 2015.12.03 |
---|---|
Spring Security and AngularJS Part VI (0) | 2015.12.03 |
Spring Security and AngularJS Part IV (0) | 2015.12.02 |
Spring Security and AngularJS Part III (0) | 2015.12.02 |
Spring Security and AngularJS Part II (0) | 2015.12.02 |