Commit d9c5f3e7 by amir

1.3.0:

- add service authorization with jwt
- env param override config file
parent dd09e0a5
### Microservice Framework in JAVA ### Microservice Framework in JAVA
## 1.3.0:
- add service authorization with jwt
- env param override config file
## 1.2.5: ## 1.2.5:
- support rabbit on server side - support rabbit on server side
...@@ -26,3 +29,6 @@ ...@@ -26,3 +29,6 @@
of the dest service of the dest service
# Env for service discovery: # Env for service discovery:
service.resolver.polling.interval (milli) default is 5000 service.resolver.polling.interval (milli) default is 5000
# Env for jwt:
jwt.token.in.authorization : true
jwt.salt : "12345678901234567890123456789012"
group 'com.ipgallery.common' group 'com.ipgallery.common'
version '1.2.5' version '1.3.0'
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'maven-publish' apply plugin: 'maven-publish'
...@@ -12,9 +12,9 @@ sourceCompatibility = 1.8 ...@@ -12,9 +12,9 @@ sourceCompatibility = 1.8
repositories { repositories {
//use mavenLocal in cases you want to create this jar on your local machine //use mavenLocal in cases you want to create this jar on your local machine
//or to be able to use one //or to be able to use one
// mavenLocal()//defaults to ~/.m2/repository mavenLocal()//defaults to ~/.m2/repository
maven { url "http://172.16.1.132:8081/repository/internal" } maven { url "http://172.16.1.132:8081/repository/internal" }
maven { url "http://mandubian-mvn.googlecode.com/svn/trunk/mandubian-mvn/repository" }
} }
dependencies { dependencies {
...@@ -26,11 +26,12 @@ dependencies { ...@@ -26,11 +26,12 @@ dependencies {
compile 'com.netflix.hystrix:hystrix-metrics-event-stream:1.4.12' compile 'com.netflix.hystrix:hystrix-metrics-event-stream:1.4.12'
compile 'redis.clients:jedis:2.4.2' compile 'redis.clients:jedis:2.4.2'
compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.0.2' compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.0.2'
compile 'com.ipgallery.common:utils:1.1.3' compile 'com.ipgallery.common:utils:1.1.4'
compile ('com.ipgallery.common:rabbitmq:1.0.2') compile ('com.ipgallery.common:rabbitmq:1.0.2')
compile 'com.ecwid.consul:consul-api:1.1.9' compile 'com.ecwid.consul:consul-api:1.1.9'
compile 'com.github.davidb:metrics-influxdb:0.8.2' compile 'com.github.davidb:metrics-influxdb:0.8.2'
compile 'io.dropwizard.metrics:metrics-graphite:3.1.2' compile 'io.dropwizard.metrics:metrics-graphite:3.1.2'
compile 'io.jsonwebtoken:jjwt:0.6.0'
testCompile group: 'junit', name: 'junit', version: '4.11' testCompile group: 'junit', name: 'junit', version: '4.11'
} }
......
...@@ -71,6 +71,7 @@ public class MicroserviceApp ...@@ -71,6 +71,7 @@ public class MicroserviceApp
ILogger logger = null; ILogger logger = null;
IPubSub pubSubClient = null; IPubSub pubSubClient = null;
boolean enableMetrics = false; boolean enableMetrics = false;
boolean enableDefaultServiceAuthorization = false;
IServiceDiscovery serviceDiscovery = null; IServiceDiscovery serviceDiscovery = null;
IConfiguration configuration = null; IConfiguration configuration = null;
Map<String, microservice.MicroserviceClient> msClientMap = null; Map<String, microservice.MicroserviceClient> msClientMap = null;
...@@ -105,6 +106,11 @@ public class MicroserviceApp ...@@ -105,6 +106,11 @@ public class MicroserviceApp
} }
public static MicroserviceApp getsInstance() { return sInstance; } public static MicroserviceApp getsInstance() { return sInstance; }
public String getAppName() {
return appName;
}
/************************* /*************************
* WITH SECTION * WITH SECTION
*************************/ *************************/
...@@ -156,7 +162,12 @@ public class MicroserviceApp ...@@ -156,7 +162,12 @@ public class MicroserviceApp
this.addHandler(MON_PREFIX, optMonitorHandler.get()); this.addHandler(MON_PREFIX, optMonitorHandler.get());
return this; return this;
} }
public MicroserviceApp withDefaultServiceAuthorization() {
this.enableDefaultServiceAuthorization = true;
return this;
}
/************************************************* /*************************************************
* ADD SECTION * ADD SECTION
**************************************************/ **************************************************/
...@@ -183,6 +194,8 @@ public class MicroserviceApp ...@@ -183,6 +194,8 @@ public class MicroserviceApp
{ {
if (msMap == null) if (msMap == null)
msMap = new HashMap<String, BaseHandler>(); msMap = new HashMap<String, BaseHandler>();
if (enableDefaultServiceAuthorization && handler.getAuthType().equals(BaseHandler.EnumAuthenticationType.DEFAULT))
handler.setAuthType(BaseHandler.EnumAuthenticationType.JWT);
msMap.put(prefix, handler); msMap.put(prefix, handler);
} }
return this; return this;
......
...@@ -5,9 +5,11 @@ import java.util.Map; ...@@ -5,9 +5,11 @@ import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import microservice.defs.Enums;
import microservice.io.iface.IContainer; import microservice.io.iface.IContainer;
import microservice.io.iface.IRequest; import microservice.io.iface.IRequest;
import microservice.io.iface.IResponse; import microservice.io.iface.IResponse;
import microservice.types.UserProfile;
public class RequestContext public class RequestContext
...@@ -22,7 +24,9 @@ public class RequestContext ...@@ -22,7 +24,9 @@ public class RequestContext
public IRequest request; public IRequest request;
public ObjectMapper objMapper; public ObjectMapper objMapper;
public String rcid; // request correlation id public String rcid; // request correlation id
public UserProfile userProfile;
public Enums.EnumCrudMethod enumCrudMethod;
public String getParameter(String paramName) { public String getParameter(String paramName) {
if (queryParameters != null && queryParameters.containsKey(paramName)) if (queryParameters != null && queryParameters.containsKey(paramName))
......
...@@ -9,4 +9,11 @@ public class Constants ...@@ -9,4 +9,11 @@ public class Constants
public static final String METHOD_NOT_IMPLEMENTED = "method not implemented"; public static final String METHOD_NOT_IMPLEMENTED = "method not implemented";
public static final String FAILED_TO_GET_PARAMS = "failed to get params"; public static final String FAILED_TO_GET_PARAMS = "failed to get params";
public static final String RCID_HEADER = "X-RCID"; public static final String RCID_HEADER = "X-RCID";
public static final String JWT_TOKEN = "jwtToken";
public static final String NO_TOKEN_FOR_REQUEST = "no token for request";
public static final String TOKEN_EXPIRED = "token expired";
public static final String METHOD_NOT_AUTHORIZED = "service/method not authorized";
public static final String INVALID_REQUEST_TOKEN = "invalid request/token";
public static final String AUTHORIZATION_HEADER = "Authorization";
} }
...@@ -43,7 +43,25 @@ public class Enums ...@@ -43,7 +43,25 @@ public class Enums
E_READ, E_READ,
E_UPDATE, E_UPDATE,
E_DELETE; E_DELETE;
public static EnumCrudMethod resolveMethodFromHttp(EnumHttpMethod enumHttpMethod){
EnumCrudMethod enumCrudMethod= null;
switch (enumHttpMethod){
case E_GET:
enumCrudMethod = EnumCrudMethod.E_READ;
break;
case E_POST:
enumCrudMethod = EnumCrudMethod.E_CREATE;
break;
case E_PUT:
enumCrudMethod = EnumCrudMethod.E_UPDATE;
break;
case E_DELETE:
enumCrudMethod = EnumCrudMethod.E_DELETE;
break;
}
return enumCrudMethod;
}
} }
public enum EnumProtocol public enum EnumProtocol
......
...@@ -14,11 +14,22 @@ import microservice.io.iface.INotifyCallback; ...@@ -14,11 +14,22 @@ import microservice.io.iface.INotifyCallback;
public abstract class BaseHandler public abstract class BaseHandler
{ {
public enum EnumAuthenticationType {
DEFAULT,
NONE,
JWT,
FORM
}
protected ObjectMapper objMapper = null; protected ObjectMapper objMapper = null;
protected ILogger logger = null; protected ILogger logger = null;
protected Optional<IContainer> optContainer = Optional.empty(); protected Optional<IContainer> optContainer = Optional.empty();
protected Optional<IConfiguration> optConfiguration = Optional.empty(); protected Optional<IConfiguration> optConfiguration = Optional.empty();
protected MicroserviceApp msApp = null; protected MicroserviceApp msApp = null;
protected EnumAuthenticationType authType = EnumAuthenticationType.DEFAULT;
/* /*
* SOME HELPERS * SOME HELPERS
*/ */
...@@ -27,6 +38,12 @@ public abstract class BaseHandler ...@@ -27,6 +38,12 @@ public abstract class BaseHandler
return objMapper; return objMapper;
} }
public EnumAuthenticationType getAuthType() {
return authType;
}
public void setAuthType(EnumAuthenticationType authType) {
this.authType = authType;
}
public void setObjMapper(ObjectMapper objMapper){ public void setObjMapper(ObjectMapper objMapper){
this.objMapper = objMapper; this.objMapper = objMapper;
......
package microservice.handlers; package microservice.handlers;
import com.eaio.uuid.UUID;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.HeaderMap;
import io.undertow.util.Headers;
import io.undertow.util.HttpString;
import microservice.MicroserviceApp;
import microservice.RequestContext; import microservice.RequestContext;
import microservice.common.EncryptionUtils;
import microservice.defs.Constants; import microservice.defs.Constants;
import microservice.defs.Enums;
import microservice.defs.Enums.EnumHttpMethod; import microservice.defs.Enums.EnumHttpMethod;
import microservice.io.iface.IContainer; import microservice.io.iface.*;
import microservice.io.iface.IMetricsFactory;
import microservice.io.iface.IMetricsFactory.IMeter; import microservice.io.iface.IMetricsFactory.IMeter;
import microservice.io.iface.IMetricsFactory.ITimer; import microservice.io.iface.IMetricsFactory.ITimer;
import microservice.io.iface.INotifyCallback;
import microservice.io.iface.IPubSub;
import microservice.io.iface.IRequest;
import microservice.io.iface.IResponse;
import microservice.io.impl.IMetricsFactoryImpl; import microservice.io.impl.IMetricsFactoryImpl;
import microservice.io.impl.IRequestRestImpl; import microservice.io.impl.IRequestRestImpl;
import microservice.io.impl.IResponseRestImpl; import microservice.io.impl.IResponseRestImpl;
import com.eaio.uuid.UUID;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import microservice.types.BaseRestResponse; import microservice.types.BaseRestResponse;
import io.undertow.server.HttpHandler; import microservice.types.UserProfile;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.Headers; import static microservice.defs.Constants.*;
import io.undertow.util.HttpString;
public class RestHandler implements HttpHandler , IContainer public class RestHandler implements HttpHandler , IContainer
{ {
...@@ -39,7 +41,9 @@ public class RestHandler implements HttpHandler , IContainer ...@@ -39,7 +41,9 @@ public class RestHandler implements HttpHandler , IContainer
ITimer getTimer = null; ITimer getTimer = null;
ITimer postTimer = null; ITimer postTimer = null;
boolean enableMetrics = false; boolean enableMetrics = false;
private static boolean jwtTokenInAuthorization = Boolean.valueOf(System.getProperty("jwt.token.in.authorization","true")).booleanValue();
String appName = null;
public RestHandler(String apiContextPath,BaseHandler msHandler, IPubSub pubSub, boolean enableMetrics) public RestHandler(String apiContextPath,BaseHandler msHandler, IPubSub pubSub, boolean enableMetrics)
{ {
super(); super();
...@@ -52,6 +56,7 @@ public class RestHandler implements HttpHandler , IContainer ...@@ -52,6 +56,7 @@ public class RestHandler implements HttpHandler , IContainer
this.enableMetrics = enableMetrics; this.enableMetrics = enableMetrics;
if (this.enableMetrics) if (this.enableMetrics)
createMetrics(); createMetrics();
appName = MicroserviceApp.getsInstance().getAppName();
} }
...@@ -85,36 +90,38 @@ public class RestHandler implements HttpHandler , IContainer ...@@ -85,36 +90,38 @@ public class RestHandler implements HttpHandler , IContainer
{ {
HttpString requestMethod = exchange.getRequestMethod(); HttpString requestMethod = exchange.getRequestMethod();
EnumHttpMethod eMethod = EnumHttpMethod.resolveMethod(requestMethod.toString()); EnumHttpMethod eMethod = EnumHttpMethod.resolveMethod(requestMethod.toString());
reqContext.enumCrudMethod = Enums.EnumCrudMethod.resolveMethodFromHttp(eMethod);
//exchange. request.setCharacterEncoding(Constants.C_ENCODING_UTF8); //exchange. request.setCharacterEncoding(Constants.C_ENCODING_UTF8);
exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, Constants.CONTENT_TYPE_JSON); exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, Constants.CONTENT_TYPE_JSON);
if (eMethod != null && reqContext != null) if (eMethod != null && reqContext != null)
{ {
// pre if (validateRequest(reqContext)) {
if (enableMetrics) // pre
preHandleMetrics(eMethod); if (enableMetrics)
preHandleMetrics(eMethod);
switch (eMethod)
{ switch (eMethod) {
case E_DELETE: case E_DELETE:
doDelete(reqContext); doDelete(reqContext);
break; break;
case E_GET: case E_GET:
doGet(reqContext); doGet(reqContext);
break; break;
case E_POST: case E_POST:
doPost(reqContext); doPost(reqContext);
break; break;
case E_PUT: case E_PUT:
doPut(reqContext); doPut(reqContext);
break; break;
default: default:
break; break;
}
// post
if (enableMetrics)
postHandleMetrics(eMethod);
} }
// post
if (enableMetrics)
postHandleMetrics(eMethod);
} }
else else
{ {
...@@ -133,6 +140,76 @@ public class RestHandler implements HttpHandler , IContainer ...@@ -133,6 +140,76 @@ public class RestHandler implements HttpHandler , IContainer
//exchange.endExchange(); //exchange.endExchange();
} }
/**
* validate the request: service authorization etc.
* @param requestContext
* @return
*/
private boolean validateRequest(RequestContext requestContext)
{
boolean valid = true;
if (msHandler.getAuthType().equals(BaseHandler.EnumAuthenticationType.JWT)){
// get token
String token = getJwtToken(requestContext);
if (token != null)
{
try {
Claims claims = EncryptionUtils.parseJWT(token);
if(claims != null) {
// resolve service authorization
UserProfile up = new UserProfile(claims);
// resolve authorization
valid = up.isServiceAuthorized(appName, requestContext.enumCrudMethod);
if (valid) {
// set user profile
requestContext.userProfile = up;
} else {
sendErrorResp(requestContext.response, METHOD_NOT_AUTHORIZED);
}
}
else {
sendErrorResp(requestContext.response,Constants.INVALID_REQUEST_TOKEN);
}
} catch (ExpiredJwtException exception){
sendErrorResp(requestContext.response,TOKEN_EXPIRED);
valid = false;
}
}
else
{
MicroserviceApp.getsInstance().getLogger().error(NO_TOKEN_FOR_REQUEST);
sendErrorResp(requestContext.response,NO_TOKEN_FOR_REQUEST);
valid = false;
}
}
return valid;
}
private String getJwtToken(RequestContext requestContext) {
String jwt = null;
/*
* jwt token
*/
if (jwtTokenInAuthorization)
{
String authValue = requestContext.request.getFirstHeader(Constants.AUTHORIZATION_HEADER);
if(authValue != null)
{
// "Bearer <token>"
String[] strings = spaceSeperatorPattern.split(authValue);
if(strings.length > 1)
jwt = strings[1];
}
}
else
{
// url - param
jwt =requestContext.getParameter(JWT_TOKEN);
}
return jwt;
}
private void preHandleMetrics(EnumHttpMethod eMethod) private void preHandleMetrics(EnumHttpMethod eMethod)
{ {
switch (eMethod) switch (eMethod)
...@@ -194,7 +271,7 @@ public class RestHandler implements HttpHandler , IContainer ...@@ -194,7 +271,7 @@ public class RestHandler implements HttpHandler , IContainer
*/ */
String relativePath = exchange.getRelativePath(); String relativePath = exchange.getRelativePath();
if (relativePath != null && relativePath.length() > 1) if (relativePath != null && relativePath.length() > 1)
reqCtx.params = seperatorPattern.split(relativePath.substring(1)); reqCtx.params = slashSeperatorPattern.split(relativePath.substring(1));
String queryString = exchange.getQueryString(); String queryString = exchange.getQueryString();
if (queryString != null && !queryString.isEmpty()) if (queryString != null && !queryString.isEmpty())
{ {
...@@ -203,10 +280,11 @@ public class RestHandler implements HttpHandler , IContainer ...@@ -203,10 +280,11 @@ public class RestHandler implements HttpHandler , IContainer
/* /*
* rcid * rcid
*/ */
reqCtx.rcid = exchange.getRequestHeaders().getFirst(Constants.RCID_HEADER); HeaderMap requestHeaders = exchange.getRequestHeaders();
reqCtx.rcid = requestHeaders.getFirst(Constants.RCID_HEADER);
if (reqCtx.rcid == null) // create a new one if (reqCtx.rcid == null) // create a new one
reqCtx.rcid = new UUID().toString(); reqCtx.rcid = new UUID().toString();
return reqCtx; return reqCtx;
} }
......
...@@ -16,7 +16,8 @@ import microservice.handlers.BaseHandler; ...@@ -16,7 +16,8 @@ import microservice.handlers.BaseHandler;
*/ */
public interface IContainer public interface IContainer
{ {
public static Pattern seperatorPattern = Pattern.compile("/"); public static Pattern slashSeperatorPattern = Pattern.compile("/");
public static Pattern spaceSeperatorPattern = Pattern.compile(" ");
public void sendErrorResp(IResponse response, String error); public void sendErrorResp(IResponse response, String error);
......
package microservice.io.iface; package microservice.io.iface;
import java.io.InputStream; import java.io.InputStream;
import java.util.List;
public interface IRequest public interface IRequest
{ {
...@@ -8,4 +9,19 @@ public interface IRequest ...@@ -8,4 +9,19 @@ public interface IRequest
InputStream getInputStream(); InputStream getInputStream();
String getQueryString(); String getQueryString();
String getRelativePath(); String getRelativePath();
/**
* getting the first header value if there are more
* then the same header
* @param headerName
* @return
*/
String getFirstHeader(String headerName);
/**
* getting all the header values
* @param headerName
* @return list of the header values
*/
List<String> getHeader(String headerName);
} }
...@@ -2,6 +2,7 @@ package microservice.io.impl; ...@@ -2,6 +2,7 @@ package microservice.io.impl;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.util.List;
//import com.sun.xml.internal.ws.api.message.stream.InputStreamMessage; //import com.sun.xml.internal.ws.api.message.stream.InputStreamMessage;
...@@ -47,6 +48,16 @@ public class IRequestMBIImpl implements IRequest ...@@ -47,6 +48,16 @@ public class IRequestMBIImpl implements IRequest
{ {
return rmqRequest.getParameterByName(RMQRestRequest.PARAMS); return rmqRequest.getParameterByName(RMQRestRequest.PARAMS);
} }
@Override
public String getFirstHeader(String headerName) {
return null;
}
@Override
public List<String> getHeader(String headerName) {
return null;
}
} }
package microservice.io.impl; package microservice.io.impl;
import java.io.InputStream; import java.io.InputStream;
import java.util.List;
import io.undertow.server.HttpServerExchange; import io.undertow.server.HttpServerExchange;
import microservice.io.iface.IRequest; import microservice.io.iface.IRequest;
...@@ -36,6 +37,16 @@ public class IRequestRestImpl implements IRequest ...@@ -36,6 +37,16 @@ public class IRequestRestImpl implements IRequest
{ {
return exchange.getRelativePath(); return exchange.getRelativePath();
} }
@Override
public String getFirstHeader(String headerName) {
return exchange.getRequestHeaders().getFirst(headerName);
}
@Override
public List<String> getHeader(String headerName) {
return exchange.getRequestHeaders().get(headerName);
}
} }
...@@ -12,6 +12,7 @@ public class TestConfiguration { ...@@ -12,6 +12,7 @@ public class TestConfiguration {
@Test @Test
public void testConfProviderConsulImpl() public void testConfProviderConsulImpl()
{ {
System.setProperty("configFile.location","/opt/mcx/config/config.properties");
IServiceDiscoveryConsulImpl iServiceDiscovery = new IServiceDiscoveryConsulImpl("localhost",8500); IServiceDiscoveryConsulImpl iServiceDiscovery = new IServiceDiscoveryConsulImpl("localhost",8500);
iServiceDiscovery.registerService("myService", "2", "1.2.3.4", 32002); iServiceDiscovery.registerService("myService", "2", "1.2.3.4", 32002);
IConfiguration config = new IConfigurationConfigPropImpl(); IConfiguration config = new IConfigurationConfigPropImpl();
...@@ -20,4 +21,5 @@ public class TestConfiguration { ...@@ -20,4 +21,5 @@ public class TestConfiguration {
Object value1 = config.getString("key1",null); Object value1 = config.getString("key1",null);
System.out.println(value1); System.out.println(value1);
} }
} }
...@@ -30,11 +30,11 @@ public class TestMicroserviceApp { ...@@ -30,11 +30,11 @@ public class TestMicroserviceApp {
//ILogger logger = new ILogger4jImpl(appName); //ILogger logger = new ILogger4jImpl(appName);
microservice.MicroserviceApp msApp = new microservice.MicroserviceApp(appName); microservice.MicroserviceApp msApp = new microservice.MicroserviceApp(appName);
msApp.withMetrics() msApp.withMetrics()
//.withLogger(logger) .withDefaultServiceAuthorization()
.withMonitoring() .withPubSub(new microservice.io.impl.IPubSubMQTTImpl("tcp://localhost",0,null,0))
.withPubSub(new microservice.io.impl.IPubSubMQTTImpl("tcp://localhost",0,null,0)) .withServiceDiscovery(serDisco)
//.withServiceDiscovery(serDisco) .withMonitoring()
.addHandler("/test",new TestMicroserviceHandler()) // .addHandler("/test",new TestMicroserviceHandler())
.addMicroserviceClient(new MicroserviceClient(cmdClient,clientParams)) .addMicroserviceClient(new MicroserviceClient(cmdClient,clientParams))
//.addMicroserviceClient("rabbit-service",new MicroserviceClient(EnumRestClientType.E_RABBITMQ,clientParams)) //.addMicroserviceClient("rabbit-service",new MicroserviceClient(EnumRestClientType.E_RABBITMQ,clientParams))
.addRestServer(new IRestServerUndertowImpl(new RestServerParams(32000, "localhost", 2))) .addRestServer(new IRestServerUndertowImpl(new RestServerParams(32000, "localhost", 2)))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment