Skip to content
Prev Previous commit
Next Next commit
Make proxy sample work with Boot 2
  • Loading branch information
Dave Syer committed Jul 11, 2018
commit a08e72435bd733e018ab619e1612a5507a509c09
29 changes: 15 additions & 14 deletions proxy/README.adoc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[[_the_api_gateway_pattern_angular_js_and_spring_security_part_iv]]
`[[_the_api_gateway_pattern_angular_js_and_spring_security_part_iv]]
= The API Gateway

In this section we continue <<_the_resource_server_angular_js_and_spring_security_part_iii,our discussion>> of how to use http://projects.spring.io/spring-security[Spring Security] with http://angular.io[Angular] in a "single page application". Here we show how to build an API Gateway to control the authentication and access to the backend resources using http://projects.spring.io/spring-cloud/[Spring Cloud]. This is the fourth in a series of sections, and you can catch up on the basic building blocks of the application or build it from scratch by reading the <<_spring_and_angular_js_a_secure_single_page_application,first section>>, or you can just go straight to the https://github.com/spring-guides/tut-spring-security-and-angular-js/tree/master/proxy[source code in Github]. In the <<_the_resource_server_angular_js_and_spring_security_part_iii,last section>> we built a simple distributed application that used https://github.com/spring-projects/spring-session/[Spring Session] to authenticate the backend resources. In this one we make the UI server into a reverse proxy to the backend resource server, fixing the issues with the last implementation (technical complexity introduced by custom token authentication), and giving us a lot of new options for controlling access from the browser client.
Expand Down Expand Up @@ -33,8 +33,9 @@ and in an external configuration file we need to map a local resource in the UI
.application.yml
[source,yaml]
----
security:
...
spring:
security:
...
zuul:
routes:
resource:
Expand All @@ -54,7 +55,7 @@ All we need to make this work is the right stuff on the classpath. For that purp
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR4</version>
<version>Finchley.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand All @@ -64,13 +65,13 @@ All we need to make this work is the right stuff on the classpath. For that purp
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
...
</dependencies>
----

Note the use of the "spring-cloud-starter-zuul" - it's a starter POM just like the Spring Boot ones, but it governs the dependencies we need for this Zuul proxy. We are also using `&lt;dependencyManagement&gt;` because we want to be able to depend on all the versions of transitive dependencies being correct.
Note the use of the "spring-cloud-starter-netflix-zuul" - it's a starter POM just like the Spring Boot ones, but it governs the dependencies we need for this Zuul proxy. We are also using `&lt;dependencyManagement&gt;` because we want to be able to depend on all the versions of transitive dependencies being correct.

=== Consuming the Proxy in the Client

Expand Down Expand Up @@ -123,22 +124,22 @@ That's enough to get us a secure resource server, but it won't get us a working

== Sharing Authentication State

We can use the same mechanism to share authentication (and CSRF) state as we did in the last, i.e. https://github.com/spring-projects/spring-session/[Spring Session]. We add the dependency to both servers as before:
We can use Spring Session to share authentication (and CSRF) state. We add the dependency to both servers:

.pom.xml
[source,xml]
----
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
----

but this time the configuration is much simpler because we can just add the same `Filter` declaration to both. First the UI server, declaring explicitly that we want all headers to be forwarded (i.e. none are "sensitive"):
There are some minor tweaks to the two applications. First the UI server, declaring explicitly that we want all headers to be forwarded (i.e. none are "sensitive"):

.application.yml
[source,properties]
Expand Down Expand Up @@ -174,15 +175,15 @@ ____
Aside: an alternative, which would also prevent the authentication dialog, would be to keep HTTP Basic but change the 401 challenge to something other than "Basic". You can do that with a one-line implementation of `AuthenticationEntryPoint` in the `HttpSecurity` configuration callback.
____

and the other is to explicitly ask for a non-stateless session creation policy in `application.properties`:
and the other is to explicitly ask for a non-stateless session creation policy (in the same method):

.application.properties
[source,properties]
.ResourceApplication.java
[source,java]
----
security.sessions: NEVER
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
----

As long as redis is still running in the background (use the https://github.com/spring-guides/tut-spring-security-and-angular-js/tree/master/proxy/docker-compose.yml[`docker-compose.yml`] if you like to start it) then the system will work. Load the homepage for the UI at http://localhost:8080[http://localhost:8080] and login and you will see the message from the backend rendered on the homepage.
As long as redis is running in the background (use the https://github.com/spring-guides/tut-spring-security-and-angular-js/tree/master/proxy/docker-compose.yml[`docker-compose.yml`] if you like to start it) then the system will work. Load the homepage for the UI at http://localhost:8080[http://localhost:8080] and login and you will see the message from the backend rendered on the homepage.

== How Does it Work?

Expand Down
4 changes: 2 additions & 2 deletions proxy/resource/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
<version>2.0.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>

Expand All @@ -29,7 +29,7 @@
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
2 changes: 2 additions & 0 deletions proxy/resource/src/main/java/demo/ResourceApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -22,6 +23,7 @@ public Message home() {
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable();
http.authorizeRequests().anyRequest().authenticated();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
}

public static void main(String[] args) {
Expand Down
3 changes: 1 addition & 2 deletions proxy/resource/src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
server.port: 9000
server.address: 127.0.0.1
security.sessions: NEVER
spring.session.store-type: redis
logging.level.org.springframework.security=debug
10 changes: 4 additions & 6 deletions proxy/resource/src/test/java/demo/ApplicationTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.context.embedded.LocalServerPort;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
Expand All @@ -16,14 +16,12 @@
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class ApplicationTests {

@LocalServerPort
private int port;

private TestRestTemplate template = new TestRestTemplate();
@Autowired
private TestRestTemplate template = new TestRestTemplate();

@Test
public void resourceLoads() {
ResponseEntity<String> response = template.getForEntity("http://localhost:{port}/", String.class, port);
ResponseEntity<String> response = template.getForEntity("/", String.class);
assertEquals(HttpStatus.FORBIDDEN, response.getStatusCode());
}
}
Expand Down
8 changes: 4 additions & 4 deletions proxy/ui/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
<version>2.0.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>

Expand All @@ -30,7 +30,7 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR2</version>
<version>Finchley.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand All @@ -40,7 +40,7 @@
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
Expand All @@ -56,7 +56,7 @@
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
12 changes: 9 additions & 3 deletions proxy/ui/src/main/java/demo/UiApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.stereotype.Controller;
Expand Down Expand Up @@ -36,16 +37,21 @@ public static void main(String[] args) {
}

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override

@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/*.bundle.*", "/favicon.ico");
}

@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic().and()
.logout().and()
.authorizeRequests()
.antMatchers("/index.html", "/", "/home", "/login").permitAll()
.antMatchers("/index.html", "/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.csrf()
Expand Down
10 changes: 3 additions & 7 deletions proxy/ui/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
security:
user:
password: password
ignored:
- "*.bundle.*"
spring:
session:
store-type: redis
security:
user:
password: password
zuul:
routes:
resource:
Expand Down
2 changes: 1 addition & 1 deletion proxy/ui/src/test/java/demo/ApplicationTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.context.embedded.LocalServerPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
Expand Down