Skip to content

Commit 9480d86

Browse files
committed
Merge pull request rohitghatol#1 from rohitghatol/master
Match with changes in rohitghatol's repo
2 parents 95bd6b8 + 8fb3c4e commit 9480d86

File tree

76 files changed

+2897
-152
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+2897
-152
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ build
1515
bin
1616
.settings
1717
.gradle
18+
.DS_Store
19+
.classpath

README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
This repository is an example of how to get Microservices going using Spring Boot, Spring Cloud, Spring OAuth 2 and Netflix OSS frameworks.
33

44
# Table of Content
5+
* [Contributors](#contributors)
6+
* [Using the application](#using-application)
57
* [Microservices Overview](#microservices-overview)
68
* [Netflix OSS](#netflix-oss)
79
* [Spring Boot Overview](#spring-boot-overview)
@@ -11,6 +13,29 @@ This repository is an example of how to get Microservices going using Spring Boo
1113
* [OAuth 2.0 Overview](#oauth-2.0-overview)
1214
* [Spring OAuth 2.0 Overview](#spring-oauth-2.0-overview)
1315

16+
## <a name="contributors"></a>Contributors
17+
18+
* [Rohit Ghatol](https://www.linkedin.com/in/rohitghatol)
19+
* [Anil Allewar](https://www.linkedin.com/pub/anil-allewar/18/378/393)
20+
21+
## <a name="using-application"></a>Using the application
22+
23+
The application consists of 7 different services
24+
25+
* [config server](config-server/README.md) - setup external configuration
26+
* [webservice-registry](webservice-registry/README.md) - Eureka server
27+
* [auth-server](auth-server/README.md) - Oauth2 authorization server
28+
* [user-webservice](user-webservice/README.md) - User micro-service
29+
* [task-webservice](task-webservice/README.md) - Task micro-service
30+
* [comments-webservice](comments-webservice/README.md) - Comments for task micro-service
31+
* [api-gateway](api-gateway/README.md) - API gateway that proxies all the micro-services
32+
* [web-portal](web-portal/README.md) - Single Page Application that provides the UI
33+
34+
Please refer to the individual readme files on instructions of how to run the services. For demo, you can run the applications in the same order listed above.
35+
36+
Note:
37+
* If the gradle wrapper doesn't work, then install gradle and run `gradle wrapper` before using `gradlew`.
38+
* If you need to setup the classpath correctly, then run `./gradlew clean build eclipse` which would setup the `.classpath` accordingly.
1439

1540
## <a name="microservices-overview"></a>Microservices Overview
1641

@@ -129,13 +154,44 @@ You can read in detail about Spring Boot here - https://spring.io/guides/gs/spri
129154

130155
## <a name="spring-cloud-overview"></a>Spring Cloud Overview
131156

157+
Spring Cloud provides tools for developers to quickly build some of the common patterns in distributed systems (e.g. configuration management, service discovery, circuit breakers, intelligent routing, micro-proxy, control bus, one-time tokens, global locks, leadership election, distributed sessions, cluster state)
132158

159+
You can read in detail about Spring Cloud here - http://projects.spring.io/spring-cloud/
133160

134161
## <a name="spring-cloud-config-overview"></a>Spring Cloud Config Overview
135162

163+
Spring Cloud config provides support for externalizing configuration in distributed systems. With the Config Server you have a central place to manage external properties for applications across all environments.
164+
165+
You can read in detail about Spring Cloud config here - http://cloud.spring.io/spring-cloud-config/
166+
136167
## <a name="spring-cloud-netflix-overview"></a>Spring Cloud Netflix Overview
137168

169+
Spring Cloud Netflix provides Netflix OSS integrations for Spring Boot apps through autoconfiguration and binding to the Spring Environment and other Spring programming model idioms.
170+
171+
You can read in detail about Spring Cloud Netflix here - http://cloud.spring.io/spring-cloud-netflix/
172+
138173
## <a name="oauth-2.0-overview"></a>OAuth 2.0 Overview
139174

175+
OAuth2 is an authorization framework that specifies different ways a third-party application can obtain limited access to determined set of resources.
176+
177+
![OAuth2 abstract protocol](/images/OAuth2 abstract protocol flow.png)
178+
179+
OAuth defines four roles:
180+
181+
**resource owner:**
182+
An entity capable of granting access to a protected resource. When the resource owner is a person, it is referred to as an end-user.
183+
184+
**resource server:**
185+
The server hosting the protected resources, capable of accepting and responding to protected resource requests using access tokens.
186+
187+
**client:**
188+
An application making protected resource requests on behalf of the resource owner and with its authorization. The term "client" does not imply any particular implementation characteristics (e.g., whether the application executes on a server, a desktop, or other devices).
189+
190+
**authorizationserver:**
191+
The server issuing access tokens to the client after successfully authenticating the resource owner and obtaining authorization.
192+
193+
To get more details of how differnt authorizations work in OAuth2, please refer to the readme at **[auth-server](auth-server/README.md)**
194+
140195
## <a name="spring-oauth-2.0-overview"></a>Spring OAuth2 Overview
141196

197+
Spring provides nice integration between Spring security and OAuth2 providers including the ability to setup your own authorization server. Please see [Spring security with OAuth2](http://projects.spring.io/spring-security-oauth/docs/oauth2.html) for more details.

api-gateway/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#Overview
2+
3+
The api-gateway application acts the router and authentication and authorization endpoint.
4+
5+
The Zuul api gateway solves a very common use case where a UI application wants to proxy calls to one or more back end services. This feature is useful for a user interface to proxy to the backend services it requires, avoiding the need to manage CORS and authentication concerns independently for all the backends.For example in our application `/api/user/**` endpoint is mapped to the `user-webservice`.
6+
7+
It also knows how to invoke the authorization server in case the user is not authenticated. Once the authentication is complete, it relays the OAuth2 token to the respective services so that they can find the authenticated user and provide services.
8+
9+
##Pre-requisites
10+
11+
### Projects that need to be started before
12+
* [config server](/../../blob/master/config-server/README.md) - For pulling the configuration information
13+
* [webserver-registry](/../../blob/master/webservice-registry/README.md) - For starting the Eureka server since the authorization server also is a micro-service that needs to be registered with Eureka server.
14+
15+
### Running the application
16+
* Build the application by running the `./gradlew clean build` gradle command at the "task-webservice" project root folder on the terminal.
17+
* If you want to run the application as jar file, then run `java -jar build/libs/sample-api-gateway-0.0.1.jar` command at the terminal.
18+
19+
## External Configuration
20+
Please refer to [user webservice](/../../blob/master/user-webservice/README.md) for details on how the external configuration works. Note that there is separate configuration file for each Spring application; the application should refer to it's own .yml file for configuration.

api-gateway/application.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
logging:
2+
level:
3+
org:
4+
springframework:
5+
security: DEBUG

api-gateway/bootstrap.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
spring:
22
application:
33
name: api-gateway
4-
cloud:
5-
config:
6-
uri: http://localhost:8888
4+
cloud:
5+
config:
6+
uri: http://localhost:8888

api-gateway/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ dependencies {
3939
compile("org.springframework.cloud:spring-cloud-config-client:${project.cloudVersion}")
4040
compile("org.springframework.cloud:spring-cloud-starter-eureka:${project.cloudVersion}")
4141
compile("org.springframework.cloud:spring-cloud-starter-zuul:${project.cloudVersion}")
42+
compile("org.springframework.cloud:spring-cloud-starter-security:${project.cloudVersion}")
4243

4344
compile("org.springframework.boot:spring-boot-starter-security:${project.bootVersion}")
4445
compile("org.springframework.security.oauth:spring-security-oauth2:${project.seurityVersion}")

api-gateway/src/main/java/com/rohitghatol/microservice/gateway/Application.java

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,52 @@
55

66
import org.springframework.boot.SpringApplication;
77
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
8-
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
8+
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
99
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
10+
import org.springframework.cloud.security.oauth2.resource.EnableOAuth2Resource;
11+
import org.springframework.cloud.security.oauth2.sso.EnableOAuth2Sso;
1012
import org.springframework.context.annotation.ComponentScan;
1113
import org.springframework.context.annotation.Configuration;
1214

1315

1416
/**
15-
* The Main Spring Boot Application class.
16-
*
17+
* The Main Spring Boot Application class which does the following
18+
* <ol>
19+
* <li>Act as a Eureka client; this behavior is provided by the
20+
* {@link EnableEurekaClient} annotation. The Eureka server URL is provided by
21+
* the external configuration provided by the config server.</li>
22+
* <li>Act as Zuul reverse proxy; this behavior is provided by the
23+
* {@link EnableZuulProxy} annotation. Annotating the application with
24+
* {@link EnableZuulProxy} forwards local calls to the appropriate service. By
25+
* convention, a service with the Eureka ID "users", will receive requests from
26+
* the proxy located at /users (with the prefix stripped).</li>
27+
* <li>Enable OAuth2 single sign on (SSO) using the {@link EnableOAuth2Sso}
28+
* annotation.
29+
* <ol>
30+
* <li>If your app has a Spring Cloud Zuul embedded reverse proxy (using
31+
* {@link EnableZuulProxy}) then you can ask it to forward OAuth2 access tokens
32+
* downstream to the services it is proxying.</li>
33+
* <li>If you also add the {@link EnableOAuth2Sso} annotation; then it will (in
34+
* addition to loggin the user in and grabbing a token) pass the authentication
35+
* token downstream to the /proxy/* services.</li>
36+
* <li>If those services are implemented with {@link EnableOAuth2Resource} then
37+
* they will get a valid token in the correct header.</li>
38+
* </ol>
39+
* </li>
40+
* <li>Note that all these annotations work in conjunction with properties
41+
* defined in the external configuration files specified by the config server.
42+
* </li>
43+
* </ol>
44+
*
1745
* @author rohitghatol
1846
*/
1947

2048
@Configuration
2149
@ComponentScan
2250
@EnableAutoConfiguration
2351
@EnableZuulProxy
24-
@EnableDiscoveryClient
52+
@EnableEurekaClient
53+
@EnableOAuth2Sso
2554
public class Application {
2655

2756
/**
Lines changed: 106 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,129 @@
11
package com.rohitghatol.microservice.gateway.config;
22

3-
import javax.sql.DataSource;
3+
import java.io.IOException;
44

5-
import org.springframework.beans.factory.annotation.Autowired;
6-
import org.springframework.context.annotation.Bean;
5+
import javax.servlet.Filter;
6+
import javax.servlet.FilterChain;
7+
import javax.servlet.ServletException;
8+
import javax.servlet.http.Cookie;
9+
import javax.servlet.http.HttpServletRequest;
10+
import javax.servlet.http.HttpServletResponse;
11+
12+
import org.springframework.cloud.security.oauth2.sso.OAuth2SsoConfigurerAdapter;
713
import org.springframework.context.annotation.Configuration;
814
import org.springframework.http.HttpMethod;
915
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
10-
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
11-
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
12-
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
13-
import org.springframework.security.oauth2.provider.token.TokenStore;
14-
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
16+
import org.springframework.security.web.csrf.CsrfFilter;
17+
import org.springframework.security.web.csrf.CsrfToken;
18+
import org.springframework.security.web.csrf.CsrfTokenRepository;
19+
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
20+
import org.springframework.stereotype.Component;
21+
import org.springframework.web.filter.OncePerRequestFilter;
22+
import org.springframework.web.util.WebUtils;
1523

1624

1725
/**
18-
* The Class OAuthConfiguration.
26+
* The Class OAuthConfiguration that sets up the OAuth2 single sign on
27+
* configuration and the web security associated with it.
1928
*/
2029
@Configuration
21-
@EnableResourceServer
22-
public class OAuthConfiguration extends ResourceServerConfigurerAdapter {
30+
@Component
31+
public class OAuthConfiguration extends OAuth2SsoConfigurerAdapter {
32+
33+
private static final String CSRF_COOKIE_NAME = "XSRF-TOKEN";
34+
private static final String CSRF_ANGULAR_HEADER_NAME = "X-XSRF-TOKEN";
2335

24-
@Autowired
25-
private DataSource dataSource;
26-
27-
@Bean
28-
public TokenStore tokenStore() {
29-
return new JdbcTokenStore(dataSource);
30-
}
31-
32-
/* (non-Javadoc)
33-
* @see org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter#configure(org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer)
34-
*/
3536
@Override
36-
public void configure(ResourceServerSecurityConfigurer resources)
37-
throws Exception {
38-
resources.resourceId("apis").tokenStore(tokenStore());
37+
public void match(RequestMatchers matchers) {
38+
matchers.anyRequest();
3939
}
40+
41+
/**
42+
* Define the security that applies to the proxy
43+
*/
4044
@Override
4145
public void configure(HttpSecurity http) throws Exception {
4246
http
43-
.authorizeRequests()
44-
.antMatchers(HttpMethod.GET, "/api/user/**","/api/task/**").access("#oauth2.hasScope('read')")
47+
.authorizeRequests()
48+
//Allow access to all static resources without authentication
49+
.antMatchers("/","/**/*.html").permitAll()
50+
.anyRequest().authenticated()
51+
.antMatchers(HttpMethod.GET, "/api/user/**","/api/task/**").access("#oauth2.hasScope('read')")
4552
.antMatchers(HttpMethod.OPTIONS, "/api/user/**","/api/task/**").access("#oauth2.hasScope('read')")
4653
.antMatchers(HttpMethod.POST, "/api/user/**","/api/task/**").access("#oauth2.hasScope('write')")
4754
.antMatchers(HttpMethod.PUT, "/api/user/**","/api/task/**").access("#oauth2.hasScope('write')")
4855
.antMatchers(HttpMethod.PATCH, "/api/user/**","/api/task/**").access("#oauth2.hasScope('write')")
49-
.antMatchers(HttpMethod.DELETE, "/api/user/**","/api/task/**").access("#oauth2.hasScope('write')");
56+
.antMatchers(HttpMethod.DELETE, "/api/user/**","/api/task/**").access("#oauth2.hasScope('write')")
57+
.and().csrf().csrfTokenRepository(this.getCSRFTokenRepository())
58+
.and().addFilterAfter(this.createCSRFHeaderFilter(), CsrfFilter.class);
5059
}
60+
61+
/**
62+
* Spring security offers in-built protection for cross site request forgery
63+
* (CSRF) by needing a custom token in the header for any requests that are
64+
* NOT safe i.e. modify the resources from the server e.g. POST, PUT & PATCH
65+
* etc.<br>
66+
* <br>
67+
*
68+
* This protection is achieved using cookies that send a custom value (would
69+
* remain same for the session) in the first request and then the front-end
70+
* would send back the value as a custom header.<br>
71+
* <br>
72+
*
73+
* In this method we create a filter that is applied to the web security as
74+
* follows:
75+
* <ol>
76+
* <li>Spring security provides the CSRF token value as a request attribute;
77+
* so we extract it from there.</li>
78+
* <li>If we have the token, Angular wants the cookie name to be
79+
* "XSRF-TOKEN". So we add the cookie if it's not there and set the path for
80+
* the cookie to be "/" which is root. In more complicated cases, this might
81+
* have to be the context root of the api gateway.</li>
82+
* <li>We forward the request to the next filter in the chain</li>
83+
* </ol>
84+
*
85+
* The request-to-cookie filter that we add needs to be after the
86+
* <code>csrf()</code> filter so that the request attribute for CsrfToken
87+
* has been already added before we start to process it.
88+
*
89+
* @return
90+
*/
91+
private Filter createCSRFHeaderFilter() {
92+
return new OncePerRequestFilter() {
93+
@Override
94+
protected void doFilterInternal(HttpServletRequest request,
95+
HttpServletResponse response, FilterChain filterChain)
96+
throws ServletException, IOException {
97+
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
98+
.getName());
99+
if (csrf != null) {
100+
Cookie cookie = WebUtils.getCookie(request, CSRF_COOKIE_NAME);
101+
String token = csrf.getToken();
102+
if (cookie == null || token != null
103+
&& !token.equals(cookie.getValue())) {
104+
cookie = new Cookie(CSRF_COOKIE_NAME, token);
105+
cookie.setPath("/");
106+
response.addCookie(cookie);
107+
}
108+
}
109+
filterChain.doFilter(request, response);
110+
}
111+
};
112+
}
113+
114+
/**
115+
* Angular sends the CSRF token in a custom header named "X-XSRF-TOKEN"
116+
* rather than the default "X-CSRF-TOKEN" that Spring security expects.
117+
* Hence we are now telling Spring security to expect the token in the
118+
* "X-XSRF-TOKEN" header.<br><br>
119+
*
120+
* This customization is added to the <code>csrf()</code> filter.
121+
*
122+
* @return
123+
*/
124+
private CsrfTokenRepository getCSRFTokenRepository() {
125+
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
126+
repository.setHeaderName(CSRF_ANGULAR_HEADER_NAME);
127+
return repository;
128+
}
51129
}

0 commit comments

Comments
 (0)