0

Spring Integration Yoda out there

I am working on enabling REST web service calls to JavaScript widgets launched from JSF application. The implementation of the services already exists on REST easy and does not include OAuth 2.0 security. The goal is to leave the services as is while passing all JS calls through security gateway.

To achieve the goal, I started with my 1st prototype on Mule Enterprise with their OAuth augmentation of spring security and HTTP inbound/outbound endpoints. This was the Mule suggested approach.

After that, my 2nd prototype is on Spring Security/Spring integration. And is essentially the same OAuth 2.0 provider, security rules and HTTP inbound/outbound gateways. While working with spring support I was told that this would not work (reasons below). From my understanding Mule and SI are very similar and I should be able to replace one "glue" with another. This may not be the envisioned use of SI, I would like to validate if I can still use it.

Also, what is the configuration of the gateways that will essentially create a pipe and reduce the overhead?

Thank you for taking time to validate or disprove my theory.

Irena

Configuration code excerpt:

    <sec:http pattern="/mycompany/api/myws/service/v[^/]+/.*" request-matcher="regex"
    create-session="stateless" entry-point-ref="oauthAuthenticationEntryPoint"
    use-expressions="true" >
    <sec:anonymous enabled="false" />
    <sec:intercept-url pattern="/mycompany/api/myws/service/v[^/]+/gateway1/op1\?([^&amp;]+&amp;)*tenant=[0-9]+(&amp;[^&amp;]+)*"
        access="#oauth2.hasAnyScope('SCOPE1', 'SCOPE2', 'SCOPE3') and #mycompany.tenantAllowed()"
        method="GET" /> 
    <sec:intercept-url pattern="/mycompany/api/myws/service/v[^/]+/gateway2/op1\?([^&amp;]+&amp;)*tenant=[0-9]+(&amp;[^&amp;]+)*"
        access="#oauth2.hasAnyScope('SCOPE1', 'SCOPE2', 'SCOPE3') and #mycompany.tenantAllowed() and hasRole('ROLE1')"
        method="GET" /> 
    <sec:intercept-url pattern="/mycompany/api/myws/service/v[^/]+/gateway3/tenant/[0-9]+/identifier1/[0-9]+(/identifier2/[0-9]+)?(\?.+)?"
        access="#oauth2.hasAnyScope('SCOPE1', 'SCOPE2', 'SCOPE3') and #mycompany.tenantAllowed() and #mycompany.identifier1Allowed() and hasRole('ROLE1')"
        method="GET" /> 
    <sec:intercept-url pattern="/mycompany/api/myws/service/v[^/]+/gateway3/tenant/[0-9]+/identifier1/[0-9]+/op1(\?.+)?"
        access="#oauth2.hasAnyScope('SCOPE1', 'SCOPE2', 'SCOPE3') and #mycompany.tenantAllowed() and #mycompany.identifier1Allowed() and #mycompany.issuedForIdentifier1() and hasRole('ROLE1')"
        method="POST" /> 
    <sec:intercept-url pattern="/mycompany/api/myws/service/v[^/]+/gateway3/tenant/[0-9]+/identifier1/[0-9]+/op2(\?.+)?"
        access="#oauth2.hasAnyScope('SCOPE1', 'SCOPE2', 'SCOPE3') and #mycompany.tenantAllowed() and #mycompany.identifier1Allowed() and hasRole('ROLE1')"
        method="POST" /> 
    <sec:intercept-url pattern="/mycompany/api/myws/service/v[^/]+/gateway3/op3/tenant/[0-9]+/identifier1/[0-9]+/identifier3/[^/]+/identifier4/[0-9]+(\?.+)?"
        access="#oauth2.hasAnyScope('SCOPE1', 'SCOPE2', 'SCOPE3') and #mycompany.tenantAllowed() and #mycompany.identifier1Allowed() and #mycompany.issuedForIdentifier1() and hasRole('ROLE2')"
        method="POST" /> 
    <sec:intercept-url pattern="/mycompany/api/myws/service/v[^/]+/.*" access="denyAll()" />
    <sec:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
    <sec:access-denied-handler ref="oauthAccessDeniedHandler" />
    <sec:expression-handler ref="oauthWebExpressionHandler" />
</sec:http>

<int-http:inbound-gateway id="gateway1op1"      
    request-payload-type="java.lang.String"
    supported-methods="GET" 
    request-channel="mywsRequest"
    reply-channel="mywsResponse"
    path="/mycompany/api/myws/service/{version}/gateway1/op1"
    >
    <int-http:header name="outboundMethod" expression="'GET'"/>
    <int-http:header name="version" expression="#pathVariables.version"/>
    <int-http:header name="pathInfo" expression="'gateway1/op1'"/>
    <int-http:header name="queryString" expression="'?tenant=' + #requestParams['tenant']
        + '&amp;query=' +  #requestParams['query']
        + (#requestParams['format'] != null?'&amp;format=' + #requestParams['format']:'') 
        + (#requestParams['identifier0'] != null?'&amp;identifier0=' + #requestParams['identifier0']:'') "/>
</int-http:inbound-gateway> 


<int-http:inbound-gateway id="gateway2op1"      
    request-payload-type="java.lang.String"
    supported-methods="GET" 
    request-channel="mywsRequest"
    reply-channel="mywsResponse"
    path="/mycompany/api/myws/service/{version}/gateway2/op1"
    >
    <int-http:header name="outboundMethod" expression="'GET'"/>
    <int-http:header name="version" expression="#pathVariables.version"/>
    <int-http:header name="pathInfo" expression="'gateway2/op1'"/>
    <int-http:header name="queryString" expression="'?tenant=' + #requestParams['tenant']
        + (#requestParams['format'] != null?'&amp;format=' + #requestParams['format']:'') 
        + T(com.google.common.base.Joiner).on('').skipNulls().join(#requestParams['type'] .!['&amp;type=' + #this]) "/>
</int-http:inbound-gateway> 

<int-http:inbound-gateway id="gateway3op1"      
    request-payload-type="java.lang.String"
    supported-methods="GET" 
    request-channel="mywsRequest"
    reply-channel="mywsResponse"
    path="/mycompany/api/myws/service/{version}/gateway3/tenant/{tenant}/identifier1/{identifier1}"
    >
    <int-http:header name="outboundMethod" expression="'GET'"/>
    <int-http:header name="version" expression="#pathVariables.version"/>
    <int-http:header name="pathInfo" expression="'gateway3/tenant/' + #pathVariables.tenant + '/identifier1/' + #pathVariables.identifier1"/>
    <int-http:header name="queryString" expression="(#requestParams['format'] != null?'?format=' + #requestParams['format']:'') "/>
</int-http:inbound-gateway> 

<int-http:inbound-gateway id="gateway3Info"      
    request-payload-type="java.lang.String"
    supported-methods="GET" 
    request-channel="mywsRequest"
    reply-channel="mywsResponse"
    path="/mycompany/api/myws/service/{version}/gateway3/tenant/{tenant}/identifier1/{identifier1}/identifier2/{identifier2}"
    >
    <int-http:header name="outboundMethod" expression="'GET'"/>
    <int-http:header name="version" expression="#pathVariables.version"/>
    <int-http:header name="pathInfo" expression="'gateway3/tenant/' + #pathVariables.tenant + '/identifier1/' + #pathVariables.identifier1 + '/identifier2/' + #pathVariables.identifier2"/>
    <int-http:header name="queryString" expression="(#requestParams['format'] != null?'?format=' + #requestParams['format']:'') "/>
</int-http:inbound-gateway> 

<int-http:inbound-gateway id="gateway3op1op2"      
    supported-methods="POST" 
    request-channel="mywsRequest"
    reply-channel="mywsResponse"
    path="/mycompany/api/myws/service/{version}/gateway3/tenant/{tenant}/identifier1/{identifier1}/{command}"
    >
    <int-http:header name="outboundMethod" expression="'POST'"/>
    <int-http:header name="version" expression="#pathVariables.version"/>
    <int-http:header name="pathInfo" expression="'gateway3/tenant/' + #pathVariables.tenant + '/identifier1/' + #pathVariables.identifier1 + '/' + #pathVariables.command"/>
    <int-http:header name="queryString" expression="(#requestParams['format'] != null?'?format=' + #requestParams['format']:'') "/>
</int-http:inbound-gateway> 

<int-http:inbound-gateway id="gateway3op3"      
    supported-methods="POST" 
    request-channel="mywsRequest"
    reply-channel="mywsResponse"
    path="/mycompany/api/myws/service/{version}/gateway3/op3/tenant/{tenant}/identifier1/{identifier1}/identifier3/{identifier3}/identifier4/{identifier4}"
    >
    <int-http:header name="outboundMethod" expression="'POST'"/>
    <int-http:header name="version" expression="#pathVariables.version"/>
    <int-http:header name="pathInfo" expression="'gateway3/op3/tenant/' + #pathVariables.tenant + '/identifier1/' + #pathVariables.identifier1 + '/identifier3/' + #pathVariables.identifier3 + '/identifier4/' + #pathVariables.identifier4"/>
    <int-http:header name="queryString" expression="(#requestParams['format'] != null?'?format=' + #requestParams['format']:'') "/>
</int-http:inbound-gateway> 

<int:channel id="mywsRequest" />
<int:channel id="mywsResponse" />

<int-http:outbound-gateway  request-channel="mywsRequest" reply-channel="mywsResponse"
      url-expression="@accessor.mywsEndpoint + headers.pathInfo + headers.queryString"
      http-method-expression="headers.outboundMethod"
      expected-response-type="java.lang.String"
      request-factory="customRequestFactory"
      >

Support reply:

SI is probably not suitable for this kind of functionality. Here are the reasons:

  • spring integration uses "Message" type for messages. Everything that goes into a channel has to be of type "Message". This effectively means every HttpRequest needs to be converted to SI Message and vice versa. The same with HttpResponse.

  • spring integration does not directly expose HttpRequest objects. Rather they are processed implicitly internally in SI which simplifies Request processing. i.e. simple access to request parameters, header values, path variables... but in your case this is a complication. And the complication is visible in the inbound gateway configuration.

  • the more generic problem might occur later, especially in case of large requests (i.e. file upload). All such requests will be processed internally in memory in the proxy and that might cause problems

  • there might be a concurrency performance problems as every request will be processed in separate thread, however this problem is not SI specific

I gave you some reasons to reconsider using SI for proxy purposes. I'm not sure what inspired you to make this decision, have you seen any example, tutorial or blog post regarding this? Please let me know if yes, I'll look at it.

It's up to you what decision you make going forward. But at the same time I'm not giving you any idea as what to use instead as this is beyond the scope the technical support. But in case you're interested I can contact our engineering and arrange a consultancy service for you.

4

0 回答 0