Added security over all endpoints. Now need to provide an 'oauth2-token'

parameter on each http request.
This commit is contained in:
Nikolaos Laskaris 2017-09-27 19:15:39 +03:00
parent 1abb322f59
commit fab68cf147
13 changed files with 610 additions and 12 deletions

View File

@ -7,10 +7,14 @@
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.http.version>1.19.0</project.http.version>
<project.oauth.version>1.19.0</project.oauth.version>
<dmp-backend-commons.version>0.0.1-SNAPSHOT</dmp-backend-commons.version>
<org.springframework.version>4.3.8.RELEASE</org.springframework.version>
<!-- <org.springframework.security.version>3.2.10.RELEASE</org.springframework.security.version> -->
<org.springframework.security.version>4.2.3.RELEASE</org.springframework.security.version>
<com.sun.jersey.version>1.19.1</com.sun.jersey.version>
<org.apache.tomcat.tomcat-jdbc.version>7.0.35</org.apache.tomcat.tomcat-jdbc.version>
<hibernate.version>5.2.9.Final</hibernate.version>
@ -203,6 +207,54 @@
<scope>runtime</scope>
</dependency>
<!-- Json Web Tokens -->
<!-- <dependency> -->
<!-- <groupId>io.jsonwebtoken</groupId> -->
<!-- <artifactId>jjwt</artifactId> -->
<!-- <version>0.7.0</version> -->
<!-- </dependency> -->
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-oauth2</artifactId>
<version>v2-rev75-1.19.0</version>
</dependency>
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client-jackson2</artifactId>
<version>${project.http.version}</version>
</dependency>
<dependency>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client-jetty</artifactId>
<version>${project.oauth.version}</version>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${org.springframework.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${org.springframework.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!-- Various libs -->
<dependency>
<groupId>org.apache.commons</groupId>
@ -211,8 +263,6 @@
</dependency>
</dependencies>
<build>

View File

@ -0,0 +1,11 @@
package exceptions;
public class NonValidTokenException extends Exception {
private static final long serialVersionUID = -2834659827755141154L;
public NonValidTokenException(String msg) {
super(msg);
}
}

View File

@ -76,6 +76,7 @@ public class BackendInterface {
// FETCH BY DMP(S)
@RequestMapping(method = RequestMethod.GET, value = { "/DMP" }, produces="text/plain")

View File

@ -0,0 +1,157 @@
//package rest;
//
//import java.io.BufferedReader;
//import java.io.IOException;
//import java.io.InputStreamReader;
//import java.io.Serializable;
//import java.io.UnsupportedEncodingException;
//import java.net.HttpURLConnection;
//import java.net.URL;
//import java.security.GeneralSecurityException;
//import java.util.Arrays;
//import java.util.Base64;
//import java.util.Collections;
//import java.util.List;
//import java.util.UUID;
//
//import org.springframework.http.HttpStatus;
//import org.springframework.http.ResponseEntity;
//import org.springframework.web.bind.annotation.CrossOrigin;
//import org.springframework.web.bind.annotation.RequestBody;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RequestMethod;
//import org.springframework.web.bind.annotation.RequestParam;
//import org.springframework.web.bind.annotation.ResponseBody;
//import org.springframework.web.bind.annotation.RestController;
//
//import com.fasterxml.jackson.databind.ObjectMapper;
//
//import entities.DMP;
////import io.jsonwebtoken.Claims;
////import io.jsonwebtoken.ExpiredJwtException;
////import io.jsonwebtoken.Jws;
////import io.jsonwebtoken.Jwts;
////import io.jsonwebtoken.MalformedJwtException;
////import io.jsonwebtoken.SignatureException;
////import io.jsonwebtoken.UnsupportedJwtException;
////import security.GoogleKey;
////import security.GoogleKeys;
//
//
//import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
//import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
//import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
//import com.google.api.client.http.HttpTransport;
//import com.google.api.client.http.apache.ApacheHttpTransport;
//import com.google.api.client.http.javanet.NetHttpTransport;
//import com.google.api.client.json.JsonFactory;
//import com.google.api.client.json.jackson2.JacksonFactory;
//
//
//
//@RestController
//@CrossOrigin
//public class TokenManager {
//
//
//// public static final String GOOGLE_KEYS_URL = "https://www.googleapis.com/oauth2/v3/certs";
//// public static GoogleKeys GOOGLE_KEYS;
//
// private static final JacksonFactory jacksonFactory = new JacksonFactory();
// private static final HttpTransport transport = new NetHttpTransport();
//
// private static GoogleIdTokenVerifier verifier;
//
// private static final List<String> clientIDs = Arrays.asList("1010962018903-glegmqudqtl1lub0150vacopbu06lgsg.apps.googleusercontent.com");
//
//
//
//
//
//
// static {
// verifier = new GoogleIdTokenVerifier.Builder(transport, jacksonFactory)
// .setAudience(clientIDs)
// // Or, if multiple clients access the backend:
// //.setAudience(Arrays.asList(CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3))
// .build();
// }
//
//
//
//
// @RequestMapping(method = RequestMethod.POST, value = { "/login" }, produces="text/plain")
// public @ResponseBody ResponseEntity<Object> login(
// @RequestParam("token") String token
// ){
//
// System.out.println(token);
//
// // 1. CHECK IF IT'S A VALID TOKEN
//
// GoogleIdToken idToken = null;
// try {
// idToken = verifier.verify(token);
// }
// catch(GeneralSecurityException ex) {
// return ResponseEntity.status(HttpStatus.FORBIDDEN).body("{'reason': 'Token is not valid'}");
// }
// catch(IOException ex) {
// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("{'reason': 'Could not verify token'}");
// }
//
//
// if (idToken != null) {
// Payload payload = idToken.getPayload();
//
// System.out.println("=============================");
// // Print user identifier
// String userId = payload.getSubject();
// System.out.println("User ID: " + userId);
//
// // Get profile information from payload
// String email = payload.getEmail();
// boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
// String name = (String) payload.get("name");
// String pictureUrl = (String) payload.get("picture");
// String locale = (String) payload.get("locale");
// String familyName = (String) payload.get("family_name");
// String givenName = (String) payload.get("given_name");
//
// System.out.println(email);
// System.out.println(name);
// System.out.println(familyName);
// System.out.println(locale);
// System.out.println(givenName);
// System.out.println(emailVerified);
// System.out.println(pictureUrl);
//
//
// } else {
// System.out.println("Invalid ID token.");
// }
//
//
// // 2.1 IF SO, ADD USER ON THE DATABASE
//
// // 2.2 also handle it within the session (create a c\\.ustom session)
//
//
//
// return ResponseEntity.status(HttpStatus.OK).body("{'status': 'ok'}");
//
//// try {
//// List<UUID> allIDs = dMPDao.listAllIDs();
//// return ResponseEntity.status(HttpStatus.OK).body(new ObjectMapper().writeValueAsString(allIDs));
//// }
//// catch(Exception ex) {
//// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Serialization issue: "+ex.getMessage());
//// }
//
//
// }
//
//
//
//}
//

View File

@ -0,0 +1,43 @@
package security;
import java.util.ArrayList;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.token.Token;
import org.springframework.security.core.token.TokenService;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
System.out.println("Running authentication provider's authenticate()");
if(authentication == null) {
throw new AuthenticationServiceException("Authentication failed");
}
//authentication is ok
return new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), new ArrayList<>());
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}

View File

@ -0,0 +1,77 @@
package security;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import exceptions.NonValidTokenException;
import security.entities.UserInfo;
public class GoogleTokenValidator {
private static final JacksonFactory jacksonFactory = new JacksonFactory();
private static final HttpTransport transport = new NetHttpTransport();
private static final List<String> clientIDs = Arrays.asList("1010962018903-glegmqudqtl1lub0150vacopbu06lgsg.apps.googleusercontent.com");
private GoogleIdTokenVerifier verifier = null;
public GoogleTokenValidator() {
verifier = new GoogleIdTokenVerifier.Builder(transport, jacksonFactory)
.setAudience(clientIDs)
// Or, if multiple clients access the backend:
//.setAudience(Arrays.asList(CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3))
.build();
}
public UserInfo validateToken(String token) throws NonValidTokenException {
GoogleIdToken idToken = null;
try {
idToken = verifier.verify(token);
}
catch(GeneralSecurityException ex) {
throw new NonValidTokenException("Token is not valid -> "+ex.getMessage());
}
catch(IOException ex) {
throw new NonValidTokenException("Could not verify token -> "+ex.getMessage());
}
catch(IllegalArgumentException ex) {
throw new NonValidTokenException("Could not verify token");
}
if (idToken != null) {
Payload payload = idToken.getPayload();
UserInfo userInfo = new UserInfo(payload.getSubject(), payload.getEmail(),
payload.getEmailVerified(), (String)payload.get("name"), (String)payload.get("picture"),
(String)payload.get("locale"), (String)payload.get("family_name"), (String)payload.get("given_name"));
System.out.println(userInfo.toString());
return userInfo;
} else {
throw new NonValidTokenException("Not a valid token");
}
}
}

View File

@ -0,0 +1,42 @@
//package security;
//
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.context.annotation.ComponentScan;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
//import org.springframework.security.config.annotation.web.builders.HttpSecurity;
//import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
//import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
//import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
//
////@Configuration
//@EnableWebSecurity
//public class SecurityConfig extends WebSecurityConfigurerAdapter {
//
// @Autowired
// private CustomAuthenticationProvider authProvider;
//
// @Override
// protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// System.out.println("inside SecurityConfig.configure(auth) ");
//
// auth.authenticationProvider(authProvider);
// }
//
// @Override
// protected void configure(HttpSecurity http) throws Exception {
//
// System.out.println("inside SecurityConfig.configure(http) ");
//
// http
// .authorizeRequests()
// .anyRequest()
// .authenticated()
// .and()
// .httpBasic();
//
// http.addFilterBefore(new TokenAuthenticationFilter(), BasicAuthenticationFilter.class);
//
// }
//
//}

View File

@ -0,0 +1,47 @@
package security;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
import exceptions.NonValidTokenException;
import security.entities.UserInfo;
public class TokenAuthenticationFilter extends GenericFilterBean {
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
final HttpServletRequest httpRequest = (HttpServletRequest) request;
System.out.println("in TokenAuthenticationFilter.doFilter");
// extract token from header
final String accessToken = httpRequest.getHeader("oauth2-token");
if (accessToken != null) {
// get and check whether token is valid ( from DB or file wherever you are storing the token)
GoogleTokenValidator gValidator = new GoogleTokenValidator();
UserInfo userInfo = null;
try {
userInfo = gValidator.validateToken(accessToken);
final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userInfo.getEmail(), accessToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (NonValidTokenException e) {
System.out.println("Could not validate a user by his token! Reason: "+e.getMessage());
}
}
chain.doFilter(request, response);
}
}

View File

@ -0,0 +1,95 @@
package security.entities;
import java.io.Serializable;
public class UserInfo implements Serializable{
private static final long serialVersionUID = 1225151430484658395L;
//required
String id = null;
String email = null;
//non required (could be left null)
Boolean emailIsVerified = null;
String name = null;
String pictureUrl = null;
String locale = null;
String familyName = null;
String givenName = null;
public UserInfo(String id, String email, Boolean emailIsVerified, String name, String pictureUrl, String locale, String familyName, String givenName) {
this.id = id;
this.email = email;
this.emailIsVerified = emailIsVerified;
this.name = name;
this.pictureUrl = pictureUrl;
this.locale = locale;
this.familyName = familyName;
this.givenName = givenName;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public boolean isEmailIsVerified() {
return emailIsVerified;
}
public void setEmailIsVerified(boolean emailIsVerified) {
this.emailIsVerified = emailIsVerified;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPictureUrl() {
return pictureUrl;
}
public void setPictureUrl(String pictureUrl) {
this.pictureUrl = pictureUrl;
}
public String getLocale() {
return locale;
}
public void setLocale(String locale) {
this.locale = locale;
}
public String getFamilyName() {
return familyName;
}
public void setFamilyName(String familyName) {
this.familyName = familyName;
}
public String getGivenName() {
return givenName;
}
public void setGivenName(String givenName) {
this.givenName = givenName;
}
@Override
public String toString() {
return "UserInfo [id=" + id + ", email=" + email + ", emailIsVerified=" + emailIsVerified + ", name=" + name
+ ", pictureUrl=" + pictureUrl + ", locale=" + locale + ", familyName=" + familyName + ", givenName="
+ givenName + "]";
}
}

View File

@ -29,7 +29,15 @@
<bean id="springApplicationContext" class="dao.SpringJpaDaoFactory" />
<bean id="databaseColumnType" class="typedefinition.PostgreSQLDatabaseColumnType" />
<!-- <bean id="securityConfig" class="security.SecurityConfig" /> -->
<!-- <bean id="securityWebApplicationInitializer" class="security.SecurityWebApplicationInitializer" /> -->
<bean id="tokenAuthenticationFilter" class="security.TokenAuthenticationFilter" />
<bean id="customAuthenticationProvider" class="security.CustomAuthenticationProvider" />
<bean id="emf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="DMPBackendPersistence" />
<property name="jpaProperties">

View File

@ -5,9 +5,9 @@
##########################Persistence##########################################
persistence.jdbc.driver = org.postgresql.Driver
persistence.jdbc.url = jdbc:postgresql://localhost:5432/db
persistence.dbusername = db-user
persistence.dbpassword = db-pass
persistence.jdbc.url = jdbc:postgresql://develdb1.madgik.di.uoa.gr:5432/dmptool
persistence.dbusername = dmptool
persistence.dbpassword = dmpt00lu$r
##########################/Persistence##########################################
@ -42,5 +42,4 @@ persistence.hibernate.connectionpool.c3p0.idle_connection_test_period = 3600
persistence.hibernate.connectionpool.c3p0.test_connection_on_checkin = true
persistence.hibernate.connectionpool.c3p0.test_connection_on_checkout = false
persistence.hibernate.connectionpool.c3p0.preferred_test_query = select 1
########################Persistence/Hibernate/Connection pool####################
########################Persistence/Hibernate/Connection pool####################

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">
<!--
-->
<context:component-scan base-package="security.*" />
<!--
<http auto-config='true'>
<intercept-url pattern="/**" access="ROLE_USER" />
</http>
-->
<http use-expressions="true" create-session="stateless" auto-config='true'>
<custom-filter after="BASIC_AUTH_FILTER" ref="tokenAuthenticationFilter" />
<!-- is authenticated means that they can see even if no correct creds provided -->
<!-- <intercept-url pattern="/customers/**" access="isAuthenticated()" /> -->
<intercept-url pattern="/**" access="isAuthenticated()" />
<http-basic/>
</http>
<authentication-manager>
<authentication-provider ref="customAuthenticationProvider" />
</authentication-manager>
<!--
<authentication-manager>
<authentication-provider>
<user-service>
<user name="mkyong" password="123456" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
-->
<beans:bean id="tokenFilter" class="security.TokenAuthenticationFilter"/>
</beans:beans>

View File

@ -31,11 +31,24 @@
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext.xml
</param-value>
<param-value>/WEB-INF/applicationContext.xml,/WEB-INF/spring-security.xml
</param-value>
</context-param>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<!-- THIS FILTER IS FOR SPRING SECURITY -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>