This project is Proof of concept (aka PoC) (and code quality is not perfect), please before using in production review security concerns among other things. (See #6)
see CHANGELOG.md
Quick&dirty sample to expose how to configure AuthorizationServer (UAA) behind Zuul
This way to do may not work for all kind of configuration (I do not test without JWT and prefer-token-info: true)
Please deploy every services using docker way or maven way, then simply load http://localhost:8765/dummy on your favorite browser.
Default user/password is user/password
Start building docker images for every services, simply run following command on root directory
mvn clean package -PdockerLaunch services using docker-compose
docker-compose up -dOn each service folder run following command:
mvn spring-boot:runOpen http://localhost:8765/dummy and connect with:
- user: user
- password: password
- Avoid any absolute/hardcoded urls for
security.oauth2.client.accessTokenUri&security.oauth2.client.userAuthorizationUriin order to improve portability! AuthorizationServerdistribution for HA- Do not expose
AuthorizationServer, like other serviceAuthorizationServerwill be behindZuul
Where localhost:8765 is Zuul, as you can see AuthorizationServer is not leaked outside! Only Zuul is targeted.
ATTENTION for 2. you should manage yourself shared storage backend (unlike following sample)! Using database or something els.
I had to override OAuth2ClientContextFilter to support URI and not only URL (see DynamicOauth2ClientContextFilter).
Indeed URL does not support path like /uaa/oauth/authorize.
Why adding path support on OAuth2ClientContextFilter?
Because I want to use path on security.oauth2.client.userAuthorizationUri in order to redirect user to Zuul itself.
On this case I can't use http://localhost:${server.port}/uaa/oauth/authorize because security.oauth2.client.userAuthorizationUri is use on web redirection (header Location: ).
Flow will look like following
Browser Zuul UAA
│ /dummy │ │
├────────────────────────────────>│ │
│ Location:http://ZUUL/login │ │
│<┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┤ │
│ /login │ │
├────────────────────────────────>│ │
│ Location:/uaa/oauth/authorize │ │
│<┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┤ │
│ /uaa/oauth/authorize │ │
├────────────────────────────────>│ │
│ │ /uaa/oauth/authorize │
│ ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄>│
│ │ ├──┐
│ │ │ │ Not authorize
│ │ │<─┘
│ │ Location:http://ZUUL/uaa/login │
│ │<┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┤
│ │ │
│ Location:http://ZUUL/uaa/login │ │
│<┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┤ │
│ /uaa/login │ │
├────────────────────────────────>│ │
│ │ /uaa/login │
│ ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄>│
│ │ LOGIN FORM │
│ │<┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┤
│ LOGIN FORM │ │
│<────────────────────────────────┤ │
Take attention on second redirection, location is using path (not at browser level).
Unlike security.oauth2.client.userAuthorizationUri, security.oauth2.client.accessTokenUri is not used a browser level for redirection but used by RestTemplate.
However default RestTemplate used for accessTokenUri is not load balanced thus we can't use url like http://service-name/oauth/token.
We can simply add load balanced feature by adding such Bean
@Bean
UserInfoRestTemplateCustomizer userInfoRestTemplateCustomizer(SpringClientFactory springClientFactory) {
return template -> {
AccessTokenProviderChain accessTokenProviderChain = Stream
.of(new AuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(),
new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider())
.peek(tp -> tp.setRequestFactory(new RibbonClientHttpRequestFactory(springClientFactory)))
.collect(Collectors.collectingAndThen(Collectors.toList(), AccessTokenProviderChain::new));
template.setAccessTokenProvider(accessTokenProviderChain);
};
}An opened isse exists spring-attic/spring-security-oauth#671
Since Brixton.RC1, Zuul filters some headers (http://cloud.spring.io/spring-cloud-static/spring-cloud.html#_cookies_and_sensitive_headers).
By default it filters:
CookieSet-CookieAuthorization
But we need that AuthorizationServer could create cookies so we must clear list
zuul:
routes:
uaa-service:
sensitiveHeaders:
path: /uaa/**
stripPrefix: falseTODO Check if zuul.routes.uaa-service.sensitiveHeaders: Authorization could work?
AuthorizationServer has it own XSRF protection so we must disable at Zuul level
private RequestMatcher csrfRequestMatcher() {
return new RequestMatcher() {
// Always allow the HTTP GET method
private final Pattern allowedMethods = Pattern.compile("^(GET|HEAD|OPTIONS|TRACE)$");
// Disable CSFR protection on the following urls:
private final AntPathRequestMatcher[] requestMatchers = { new AntPathRequestMatcher("/uaa/**") };
@Override
public boolean matches(HttpServletRequest request) {
if (allowedMethods.matcher(request.getMethod()).matches()) {
return false;
}
for (AntPathRequestMatcher matcher : requestMatchers) {
if (matcher.matches(request)) {
return false;
}
}
return true;
}
};
}Ok should I really need to explain why?
http.authorizeRequests().antMatchers("/uaa/**", "/login").permitAll()ATTENTION do not use "/uaa/**" authorize only necessary API (I was to lazy)
Zuul and AuthorizationServer have to manage their own session! So both have to write two JSESSIONID cookies.
You must isolate AuthorizationServer on other context-path server.context-path = /uaa to avoid any cookies collision.
ALTERNATIVE we can check if server.session.cookie.path or server.session.cookie.name is not sufficient, I did not test it.
Does not work without. I will not explain why, please look about X-Forwarded-* headers for more information.
