Security is now completed... only token-based calls are now permitted.

This commit is contained in:
Nikolaos Laskaris 2017-10-13 18:08:49 +03:00
parent 55e4556167
commit 7bebf8b84b
15 changed files with 175 additions and 86 deletions

View File

@ -32,5 +32,6 @@
<nature>org.eclipse.m2e.core.maven2Nature</nature> <nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature> <nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature> <nature>org.eclipse.wst.common.project.facet.core.nature</nature>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
</natures> </natures>
</projectDescription> </projectDescription>

View File

@ -4,7 +4,7 @@
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/> <wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/> <wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/> <wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
<property name="java-output-path" value="/dmp-backend/target/classes"/>
<property name="context-root" value="dmp-backend"/> <property name="context-root" value="dmp-backend"/>
<property name="java-output-path" value="/dmp-backend/target/classes"/>
</wb-module> </wb-module>
</project-modules> </project-modules>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<faceted-project> <faceted-project>
<fixed facet="wst.jsdt.web"/> <fixed facet="wst.jsdt.web"/>
<installed facet="wst.jsdt.web" version="1.0"/>
<installed facet="java" version="1.8"/> <installed facet="java" version="1.8"/>
<installed facet="jst.web" version="3.0"/> <installed facet="jst.web" version="3.0"/>
<installed facet="jpt.jpa" version="2.1"/> <installed facet="jpt.jpa" version="2.1"/>
<installed facet="wst.jsdt.web" version="1.0"/>
</faceted-project> </faceted-project>

View File

@ -98,19 +98,19 @@
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId> <artifactId>spring-security-core</artifactId>
<version>4.2.3.RELEASE</version> <version>${org.springframework.security.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId> <artifactId>spring-security-web</artifactId>
<version>4.2.3.RELEASE</version> <version>${org.springframework.security.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId> <artifactId>spring-security-config</artifactId>
<version>4.2.3.RELEASE</version> <version>${org.springframework.security.version}</version>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -1,4 +1,4 @@
package rest.login; package login;
import java.io.Serializable; import java.io.Serializable;
import java.security.MessageDigest; import java.security.MessageDigest;
@ -71,7 +71,7 @@ public class Login {
// create a token // create a token
token = tokenSessionManager.generateRandomAlphanumeric(512); token = tokenSessionManager.generateRandomAlphanumeric(512);
// add it to the cache // add it to the cache
tokenSessionManager.set(credentials.getUsername(), token); tokenSessionManager.set(token, credentials.getUsername());
} }
//get also the additional info of the user (if he has) //get also the additional info of the user (if he has)

View File

@ -2,15 +2,22 @@ package security;
import java.util.ArrayList; import java.util.ArrayList;
import javax.naming.NameAlreadyBoundException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import dao.entities.security.UserInfoDao; import dao.entities.security.UserInfoDao;
import entities.security.UserInfo;
import exceptions.NonValidTokenException;
import security.validators.GoogleTokenValidator;
import security.validators.NativeTokenValidator;
import security.validators.TokenValidator;
@Component @Component
public class CustomAuthenticationProvider implements AuthenticationProvider { public class CustomAuthenticationProvider implements AuthenticationProvider {
@ -18,36 +25,49 @@ public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired private UserInfoDao userInfoDao; @Autowired private UserInfoDao userInfoDao;
@Autowired private GoogleTokenValidator googleTokenValidator;
@Autowired private NativeTokenValidator nativeTokenValidator;
@Override @Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException { public Authentication authenticate(Authentication authentication) throws AuthenticationException {
/*
if (authentication != null) { if (authentication != null) {
// check whether the token is valid
String token = (String)authentication.getCredentials(); String token = (String)authentication.getCredentials();
GoogleTokenValidator gValidator = new GoogleTokenValidator();
UserInfo userInfo = null; TokenValidator tokenValidator = null;
if(TokenAuthenticationFilter.HEADER_GOOGLE_TOKEN_FIELD.equals(authentication.getPrincipal()))
tokenValidator = googleTokenValidator;
else if(TokenAuthenticationFilter.HEADER_NATIVE_TOKEN_FIELD.equals(authentication.getPrincipal()))
tokenValidator = nativeTokenValidator;
else
throw new AuthenticationServiceException("The appropriate http headers have not been set. Please check!");
try { try {
userInfo = gValidator.validateToken(token); tokenValidator.validateToken(token);
} catch (NonValidTokenException e) { } catch (NonValidTokenException e) {
System.out.println("Could not validate a user by his token! Reason: "+e.getMessage()); System.out.println("Could not validate a user by his token! Reason: "+e.getMessage());
throw new AuthenticationServiceException("Token validation failed - Not a valid token"); throw new AuthenticationServiceException("Token validation failed - Not a valid token");
} }
//store to database if new
UserInfo existingUserInfo = userInfoDao.getByKey(userInfo.getId(), userInfo.getEmail());
if(existingUserInfo == null)
userInfoDao.create(userInfo);
// if reached this point, authentication is ok //store to database if new
// UserInfo existingUserInfo = userInfoDao.getByKey(userInfo.getId(), userInfo.getEmail());
// if(existingUserInfo == null)
// userInfoDao.create(userInfo);
// if reached this point, authentication is ok, so return just an instance with whatever.
return new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), new ArrayList<>()); return new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), new ArrayList<>());
} }
else else
throw new AuthenticationServiceException("Authentication failed"); throw new AuthenticationServiceException("Authentication failed");
*/
//DELETE THIS, USE THE ABOVE
return new UsernamePasswordAuthenticationToken("", "", new ArrayList<>()); // //DELETE THIS, USE THE ABOVE
// return new UsernamePasswordAuthenticationToken("", "", new ArrayList<>());
} }

View File

@ -15,8 +15,10 @@ import org.springframework.web.filter.GenericFilterBean;
public class TokenAuthenticationFilter extends GenericFilterBean { public class TokenAuthenticationFilter extends GenericFilterBean {
private static final String HEADER_NATIVE_TOKEN_FIELD = "native-token"; public static final String HEADER_NATIVE_TOKEN_FIELD = "native-token";
private static final String HEADER_GOOGLE_TOKEN_FIELD = "google-token"; public static final String HEADER_GOOGLE_TOKEN_FIELD = "google-token";
public static final char HEADERNAME_USERNAME_DELIMITER = 0x1e; //specially crafted delimiter
@Override @Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException { public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
@ -26,17 +28,17 @@ public class TokenAuthenticationFilter extends GenericFilterBean {
String nativeToken = httpRequest.getHeader(HEADER_NATIVE_TOKEN_FIELD); String nativeToken = httpRequest.getHeader(HEADER_NATIVE_TOKEN_FIELD);
String googleToken = httpRequest.getHeader(HEADER_GOOGLE_TOKEN_FIELD); String googleToken = httpRequest.getHeader(HEADER_GOOGLE_TOKEN_FIELD);
//just pass the token into the credentials object of the UsernamePasswordAuthenticationToken class //just pass the header, the username and the token into the credentials object of the UsernamePasswordAuthenticationToken class
UsernamePasswordAuthenticationToken authentication = null; UsernamePasswordAuthenticationToken authentication = null;
if(nativeToken != null) if(nativeToken != null)
authentication = new UsernamePasswordAuthenticationToken("native-user", nativeToken); authentication = new UsernamePasswordAuthenticationToken(HEADER_NATIVE_TOKEN_FIELD, nativeToken);
if(googleToken != null) if(googleToken != null)
authentication = new UsernamePasswordAuthenticationToken("google-user", nativeToken); authentication = new UsernamePasswordAuthenticationToken(HEADER_GOOGLE_TOKEN_FIELD, googleToken);
if(authentication != null) {
SecurityContextHolder.getContext().setAuthentication(authentication); SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response); chain.doFilter(request, response);
}
} }

View File

@ -34,16 +34,14 @@ public class TokenSessionManager {
.build(); .build();
} }
public String getUser(String token) {
public String get(String key) { return cache.getIfPresent(token);
return cache.getIfPresent(key);
} }
public void set(String key, String value) { public void set(String token, String user) {
cache.put(key, value); cache.put(token, user);
} }
public String generateRandomAlphanumeric(int length) { public String generateRandomAlphanumeric(int length) {
SecureRandom random = new SecureRandom(); SecureRandom random = new SecureRandom();
byte bytes[] = new byte[length]; byte bytes[] = new byte[length];

View File

@ -15,7 +15,7 @@ import com.google.api.client.json.jackson2.JacksonFactory;
import entities.security.UserInfo; import entities.security.UserInfo;
import exceptions.NonValidTokenException; import exceptions.NonValidTokenException;
public class GoogleTokenValidator { public class GoogleTokenValidator implements TokenValidator {
private static final JacksonFactory jacksonFactory = new JacksonFactory(); private static final JacksonFactory jacksonFactory = new JacksonFactory();
private static final HttpTransport transport = new NetHttpTransport(); private static final HttpTransport transport = new NetHttpTransport();
@ -34,7 +34,8 @@ public class GoogleTokenValidator {
} }
public UserInfo validateToken(String token) throws NonValidTokenException { @Override
public void validateToken(String token) throws NonValidTokenException {
GoogleIdToken idToken = null; GoogleIdToken idToken = null;
try { try {
@ -50,21 +51,18 @@ public class GoogleTokenValidator {
throw new NonValidTokenException("Could not verify token"); throw new NonValidTokenException("Could not verify token");
} }
if(idToken == null) {
if (idToken != null) { throw new NonValidTokenException("Not a valid token");
Payload payload = idToken.getPayload(); }
// else {
// Payload payload = idToken.getPayload();
// UserInfo userInfo = new UserInfo(payload.getSubject(), payload.getEmail(), // UserInfo userInfo = new UserInfo(payload.getSubject(), payload.getEmail(),
// payload.getEmailVerified(), (String)payload.get("name"), (String)payload.get("picture"), // payload.getEmailVerified(), (String)payload.get("name"), (String)payload.get("picture"),
// (String)payload.get("locale"), (String)payload.get("family_name"), (String)payload.get("given_name"), ""); // (String)payload.get("locale"), (String)payload.get("family_name"), (String)payload.get("given_name"), "");
// System.out.println(userInfo.toString()); // System.out.println(userInfo.toString());
// return userInfo; // return userInfo;
// }
return null;
} else {
throw new NonValidTokenException("Not a valid token");
}
} }

View File

@ -0,0 +1,22 @@
package security.validators;
import org.springframework.beans.factory.annotation.Autowired;
import exceptions.NonValidTokenException;
import security.TokenSessionManager;
public class NativeTokenValidator implements TokenValidator {
@Autowired private TokenSessionManager tokenSessionManager;
@Override
public void validateToken(String token) throws NonValidTokenException {
String tokenUser = tokenSessionManager.getUser(token);
if(tokenUser==null || tokenUser.isEmpty())
throw new NonValidTokenException("Login session has expired! Need to login again!");
}
}

View File

@ -0,0 +1,9 @@
package security.validators;
import exceptions.NonValidTokenException;
public interface TokenValidator {
public void validateToken(String token) throws NonValidTokenException;
}

View File

@ -25,8 +25,11 @@
</bean> </bean>
<bean id="tokenSessionManager" class="security.TokenSessionManager" factory-method="getInstance"> <bean id="tokenSessionManager" class="security.TokenSessionManager" factory-method="getInstance" />
</bean>
<bean id="googleTokenValidator" class="security.validators.GoogleTokenValidator" />
<bean id="nativeTokenValidator" class="security.validators.NativeTokenValidator" />
<bean id="proxy" class="rest.proxy.Proxy"> <bean id="proxy" class="rest.proxy.Proxy">
<constructor-arg type = "String" value = "${proxy.allowed.host}"/> <constructor-arg type = "String" value = "${proxy.allowed.host}"/>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<!-- <context:property-placeholder location="classpath*:**/dmp.properties" /> -->
<mvc:resources mapping="resources/**" location="/resources/" />
<mvc:annotation-driven />
<context:component-scan base-package="login" />
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000000" />
</bean>
<bean id="jsonConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="prefixJson" value="false" />
<property name="supportedMediaTypes" value="application/json" />
</bean>
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonConverter" />
</list>
</property>
</bean>
</beans>

View File

@ -1,5 +1,4 @@
<!--
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans <beans:beans
xmlns="http://www.springframework.org/schema/security" xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@ -32,18 +31,5 @@
<beans:bean id="tokenFilter" class="security.TokenAuthenticationFilter"/> <beans:bean id="tokenFilter" class="security.TokenAuthenticationFilter"/>
<authentication-manager>
<authentication-provider>
<password-encoder ref="encoder" />
</authentication-provider>
</authentication-manager>
<beans:bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="11" />
</beans:bean>
</beans:beans> </beans:beans>
-->

View File

@ -3,6 +3,20 @@
<display-name>dmp-backend</display-name> <display-name>dmp-backend</display-name>
<servlet>
<servlet-name>dmp-backend-login</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dmp-backend-login</servlet-name>
<url-pattern>/login/*</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet> <servlet>
<servlet-name>dmp-backend-rest</servlet-name> <servlet-name>dmp-backend-rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
@ -38,10 +52,9 @@
</listener> </listener>
<!-- ,/WEB-INF/spring-security.xml -->
<context-param> <context-param>
<param-name>contextConfigLocation</param-name> <param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml <param-value>/WEB-INF/applicationContext.xml,/WEB-INF/spring-security.xml
</param-value> </param-value>
</context-param> </context-param>
<session-config> <session-config>
@ -49,17 +62,15 @@
</session-config> </session-config>
<!-- THIS FILTER IS FOR SPRING SECURITY --> <!-- THIS FILTER IS FOR SPRING SECURITY -->
<!--
<filter> <filter>
<filter-name>springSecurityFilterChain</filter-name> <filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter> </filter>
<filter-mapping> <filter-mapping>
<filter-name>springSecurityFilterChain</filter-name> <filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern> <url-pattern>/rest/*</url-pattern>
</filter-mapping> </filter-mapping>
-->