Skip to content

Commit 5391b29

Browse files
committed
Integrated SSO with GitHub sample project from Spring Boot repository
1 parent 4969ac2 commit 5391b29

File tree

8 files changed

+376
-0
lines changed

8 files changed

+376
-0
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ Session handling demo project using plain Java. Uses plain Java to create and up
5151
##sql-injection
5252
Spring Boot based web application to experiment with normal (vulnerable) statements, statements with escaped input, and prepared statements. After launching, open the web application in your browser at **http://localhost:8080**.
5353

54+
#sso-with-github
55+
Contains a Spring Boot demo application with GitHub login. Requires to setup an application in your GitHub account and to provide `github.client.clientId` and `github.client.clientSecret` as runtime parameters. After launching, open the web application in your browser at **http://localhost:8080**.
56+
5457
##xss
5558
Cross-Site Scripting (XSS) demo project preventing XSS in a JavaServer Pages (JSP) web application by utilizing input validation, output escaping with [OWASP Java Encoder](https://www.owasp.org/index.php/OWASP_Java_Encoder_Project) and the [Content Security Policy (CSP)](http://www.w3.org/TR/CSP). After launching, open the web application in your browser at **http://localhost:8080/xss**.
5659

pom.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,16 @@
117117
<artifactId>bootstrap</artifactId>
118118
<version>3.3.7-1</version>
119119
</dependency>
120+
<dependency>
121+
<groupId>org.webjars</groupId>
122+
<artifactId>angularjs</artifactId>
123+
<version>1.6.2</version>
124+
</dependency>
125+
<dependency>
126+
<groupId>org.webjars</groupId>
127+
<artifactId>jquery</artifactId>
128+
<version>2.2.4</version>
129+
</dependency>
120130
</dependencies>
121131
</dependencyManagement>
122132

@@ -230,6 +240,7 @@
230240
<module>session-handling</module>
231241
<module>session-handling-spring-security</module>
232242
<module>sql-injection</module>
243+
<module>sso-with-github</module>
233244
<module>xss</module>
234245
</modules>
235246
</project>

sso-with-github/pom.xml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<parent>
5+
<groupId>de.dominikschadow.javasecurity</groupId>
6+
<artifactId>javasecurity</artifactId>
7+
<version>2.0.0</version>
8+
</parent>
9+
<modelVersion>4.0.0</modelVersion>
10+
<artifactId>sso-with-github</artifactId>
11+
<packaging>jar</packaging>
12+
<name>SSO with Spring Boot</name>
13+
14+
<description>SSO with GitHub sample project. Start via the main method in the Application class. After launching,
15+
open the web application in your browser at http://localhost:8080.</description>
16+
17+
<properties>
18+
<start-class>de.dominikschadow.javasecurity.SsoWithGitHubApplication</start-class>
19+
</properties>
20+
21+
<dependencies>
22+
<dependency>
23+
<groupId>org.springframework.boot</groupId>
24+
<artifactId>spring-boot-starter-security</artifactId>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.springframework.security.oauth</groupId>
28+
<artifactId>spring-security-oauth2</artifactId>
29+
</dependency>
30+
<dependency>
31+
<groupId>org.springframework.boot</groupId>
32+
<artifactId>spring-boot-starter-web</artifactId>
33+
</dependency>
34+
35+
<dependency>
36+
<groupId>org.webjars</groupId>
37+
<artifactId>angularjs</artifactId>
38+
</dependency>
39+
<dependency>
40+
<groupId>org.webjars</groupId>
41+
<artifactId>jquery</artifactId>
42+
</dependency>
43+
<dependency>
44+
<groupId>org.webjars</groupId>
45+
<artifactId>bootstrap</artifactId>
46+
</dependency>
47+
<dependency>
48+
<groupId>org.webjars</groupId>
49+
<artifactId>webjars-locator</artifactId>
50+
</dependency>
51+
52+
<dependency>
53+
<groupId>org.springframework.boot</groupId>
54+
<artifactId>spring-boot-starter-test</artifactId>
55+
<scope>test</scope>
56+
</dependency>
57+
</dependencies>
58+
59+
<build>
60+
<finalName>${project.artifactId}</finalName>
61+
<defaultGoal>spring-boot:run</defaultGoal>
62+
<plugins>
63+
<plugin>
64+
<groupId>org.springframework.boot</groupId>
65+
<artifactId>spring-boot-maven-plugin</artifactId>
66+
<executions>
67+
<execution>
68+
<goals>
69+
<goal>build-info</goal>
70+
</goals>
71+
<configuration>
72+
<additionalProperties>
73+
<versions.spring-boot>${project.parent.parent.version}</versions.spring-boot>
74+
</additionalProperties>
75+
</configuration>
76+
</execution>
77+
</executions>
78+
</plugin>
79+
<plugin>
80+
<groupId>com.spotify</groupId>
81+
<artifactId>docker-maven-plugin</artifactId>
82+
<configuration>
83+
<imageName>${docker.image.prefix}/${project.artifactId}</imageName>
84+
<dockerDirectory>src/main/docker</dockerDirectory>
85+
<resources>
86+
<resource>
87+
<targetPath>/</targetPath>
88+
<directory>${project.build.directory}</directory>
89+
<include>${project.build.finalName}.jar</include>
90+
</resource>
91+
</resources>
92+
</configuration>
93+
</plugin>
94+
</plugins>
95+
</build>
96+
</project>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM openjdk:8-jre-alpine
2+
MAINTAINER Dominik Schadow <dominikschadow@gmail.com>
3+
4+
VOLUME /tmp
5+
6+
ADD sso-with-github.jar app.jar
7+
8+
RUN sh -c 'touch /app.jar'
9+
10+
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package de.dominikschadow.javasecurity;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.boot.SpringApplication;
5+
import org.springframework.boot.autoconfigure.SpringBootApplication;
6+
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
7+
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices;
8+
import org.springframework.boot.context.properties.ConfigurationProperties;
9+
import org.springframework.boot.web.servlet.FilterRegistrationBean;
10+
import org.springframework.context.annotation.Bean;
11+
import org.springframework.core.annotation.Order;
12+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
13+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
14+
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
15+
import org.springframework.security.oauth2.client.OAuth2ClientContext;
16+
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
17+
import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter;
18+
import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter;
19+
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
20+
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
21+
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
22+
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
23+
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
24+
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
25+
import org.springframework.security.web.csrf.CsrfFilter;
26+
import org.springframework.security.web.csrf.CsrfToken;
27+
import org.springframework.security.web.csrf.CsrfTokenRepository;
28+
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
29+
import org.springframework.web.bind.annotation.RequestMapping;
30+
import org.springframework.web.bind.annotation.RestController;
31+
import org.springframework.web.filter.CompositeFilter;
32+
import org.springframework.web.filter.OncePerRequestFilter;
33+
import org.springframework.web.util.WebUtils;
34+
35+
import javax.servlet.Filter;
36+
import javax.servlet.FilterChain;
37+
import javax.servlet.ServletException;
38+
import javax.servlet.http.Cookie;
39+
import javax.servlet.http.HttpServletRequest;
40+
import javax.servlet.http.HttpServletResponse;
41+
import java.io.IOException;
42+
import java.security.Principal;
43+
import java.util.ArrayList;
44+
import java.util.LinkedHashMap;
45+
import java.util.List;
46+
import java.util.Map;
47+
48+
/**
49+
* Requires the configuration of @code{github.client.clientId} and @code{github.client.clientSecret} as runtime parameter.
50+
* This project is based on the Spring Boot and OAuth2 tutorial available at https://spring.io/guides/tutorials/spring-boot-oauth2
51+
*/
52+
@SpringBootApplication
53+
@EnableWebSecurity
54+
@RestController
55+
@EnableOAuth2Client
56+
@EnableAuthorizationServer
57+
@Order(6)
58+
public class SsoWithGitHubApplication extends WebSecurityConfigurerAdapter {
59+
@Autowired
60+
private OAuth2ClientContext oAuth2ClientContext;
61+
62+
public static void main(String[] args) {
63+
SpringApplication.run(SsoWithGitHubApplication.class, args);
64+
}
65+
66+
@RequestMapping({"/user"})
67+
public Map<String, String> user(Principal principal) {
68+
Map<String, String> map = new LinkedHashMap<>();
69+
map.put("name", principal.getName());
70+
return map;
71+
}
72+
73+
@Override
74+
protected void configure(HttpSecurity http) throws Exception {
75+
// @formatter:off
76+
http.antMatcher("/**")
77+
.authorizeRequests()
78+
.antMatchers("/", "/login**", "/webjars/**").permitAll()
79+
.anyRequest().authenticated()
80+
.and().exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/"))
81+
.and().logout().logoutSuccessUrl("/").permitAll()
82+
.and().csrf().csrfTokenRepository(csrfTokenRepository())
83+
.and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
84+
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
85+
// @formatter:on
86+
}
87+
88+
@Bean
89+
public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
90+
FilterRegistrationBean registration = new FilterRegistrationBean();
91+
registration.setFilter(filter);
92+
registration.setOrder(-100);
93+
return registration;
94+
}
95+
96+
@Bean
97+
@ConfigurationProperties("github")
98+
ClientResources github() {
99+
return new ClientResources();
100+
}
101+
102+
private Filter ssoFilter() {
103+
CompositeFilter filter = new CompositeFilter();
104+
List<Filter> filters = new ArrayList<>();
105+
filters.add(ssoFilter(github(), "/login/github"));
106+
filter.setFilters(filters);
107+
return filter;
108+
}
109+
110+
private Filter ssoFilter(ClientResources client, String path) {
111+
OAuth2ClientAuthenticationProcessingFilter oAuth2ClientAuthenticationFilter =
112+
new OAuth2ClientAuthenticationProcessingFilter(path);
113+
OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(client.getClient(),
114+
oAuth2ClientContext);
115+
oAuth2ClientAuthenticationFilter.setRestTemplate(oAuth2RestTemplate);
116+
UserInfoTokenServices tokenServices = new UserInfoTokenServices(
117+
client.getResource().getUserInfoUri(), client.getClient().getClientId());
118+
tokenServices.setRestTemplate(oAuth2RestTemplate);
119+
oAuth2ClientAuthenticationFilter.setTokenServices(tokenServices);
120+
return oAuth2ClientAuthenticationFilter;
121+
}
122+
123+
private Filter csrfHeaderFilter() {
124+
return new OncePerRequestFilter() {
125+
@Override
126+
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
127+
throws ServletException, IOException {
128+
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
129+
if (csrf != null) {
130+
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
131+
String token = csrf.getToken();
132+
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
133+
cookie = new Cookie("XSRF-TOKEN", token);
134+
cookie.setPath("/");
135+
response.addCookie(cookie);
136+
}
137+
}
138+
filterChain.doFilter(request, response);
139+
}
140+
};
141+
}
142+
143+
private CsrfTokenRepository csrfTokenRepository() {
144+
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
145+
repository.setHeaderName("X-XSRF-TOKEN");
146+
return repository;
147+
}
148+
149+
class ClientResources {
150+
private OAuth2ProtectedResourceDetails client = new AuthorizationCodeResourceDetails();
151+
private ResourceServerProperties resource = new ResourceServerProperties();
152+
153+
public OAuth2ProtectedResourceDetails getClient() {
154+
return client;
155+
}
156+
157+
public ResourceServerProperties getResource() {
158+
return resource;
159+
}
160+
}
161+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
spring:
2+
resources:
3+
chain:
4+
enabled: true
5+
6+
github:
7+
client:
8+
accessTokenUri: https://github.com/login/oauth/access_token
9+
userAuthorizationUri: https://github.com/login/oauth/authorize
10+
clientAuthenticationScheme: form
11+
resource:
12+
userInfoUri: https://api.github.com/user
13+
14+
logging:
15+
level:
16+
org.springframework.security: DEBUG
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6+
<title>SSO with GitHub</title>
7+
<meta name="viewport" content="width=device-width" />
8+
<base href="/" />
9+
<link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css" />
10+
<script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script>
11+
<script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script>
12+
</head>
13+
<body ng-app="app" ng-controller="home as home">
14+
15+
<h1>SSO with GitHub</h1>
16+
17+
<div class="container" ng-show="!home.authenticated">
18+
<div>
19+
<a href="/login/github">Log in</a> with GitHub
20+
</div>
21+
</div>
22+
23+
<div class="container" ng-show="home.authenticated">
24+
Logged in as <span ng-bind="home.user"></span>
25+
<div>
26+
<button ng-click="home.logout()" class="btn btn-primary">Logout</button>
27+
</div>
28+
</div>
29+
30+
<script type="text/javascript" src="/webjars/angularjs/angular.min.js"></script>
31+
<script type="text/javascript">
32+
angular
33+
.module("app", [])
34+
.config(
35+
function($httpProvider) {
36+
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
37+
}).controller("home", function($http, $location) {
38+
var self = this;
39+
$http.get("/user").success(function(data) {
40+
if (data.name) {
41+
self.user = data.name;
42+
self.authenticated = true;
43+
} else {
44+
self.user = "N/A";
45+
self.authenticated = false;
46+
}
47+
}).error(function() {
48+
self.user = "N/A";
49+
self.authenticated = false;
50+
});
51+
self.logout = function() {
52+
$http.post('logout', {}).success(function() {
53+
self.authenticated = false;
54+
$location.path("/");
55+
}).error(function(data) {
56+
console.log("Logout failed")
57+
self.authenticated = false;
58+
});
59+
};
60+
});
61+
</script>
62+
</body>
63+
</html>

0 commit comments

Comments
 (0)