Could not open connection to WebSocket.

3,076 views
Skip to first unread message

nilesh...@citruspay.com

unread,
Jun 5, 2016, 5:08:50 PM6/5/16
to Swagger
Hi ,

I have a project which is using spring security for Oauth implementation. There are multiple calls to token validation API. I am working to make this API call as websocket using Swagger socket.

Things done at server side.

Added following code in web.xml

 <!-- Swagger socket servelet -->
  <servlet>
        <description>SwaggerSocketServlet</description>
        <servlet-name>SwaggerSocketServlet</servlet-name>
        <servlet-class>com.wordnik.swaggersocket.server.SwaggerSocketServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        
         <init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>com.citruspay.adminsite.subscription.api</param-value>
        </init-param>
    </servlet>


package com.citruspay.adminsite.subscription.api;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Service;

import com.citruspay.adminsite.subscription.response.bean.MemberInfoResponse;
import com.citruspay.adminsite.subscription.service.TokenValidatorExtendedProcess;

@Service
@Path("/v2/")
public class TokenValidatorService {

private final TokenStore tStore;
@Autowired
private ResourceServerTokenServices tokenServices;

private static final Logger logger = LoggerFactory.getLogger(TokenValidatorService.class);

private final TokenValidatorExtendedProcess tokenValidatorExtendedProcess;

@Autowired
public TokenValidatorService(TokenStore tStore, TokenValidatorExtendedProcess tokenValidatorExtendedProcess) {
this.tStore = tStore;
this.tokenValidatorExtendedProcess = tokenValidatorExtendedProcess;
}

@GET
@Path("token/validate")
@Produces(MediaType.APPLICATION_JSON)
public Response accessible(@HeaderParam("OwnerAuthorization") String auth,
@HeaderParam("OwnerScope") String scope) {
if (StringUtils.isBlank(auth) || StringUtils.isBlank(scope) || !StringUtils.startsWith(auth, "Bearer ")) {
return unauthorized(Response.Status.BAD_REQUEST, "either token or scope is null");
}
OAuth2AccessToken accessToken = tStore.readAccessToken(auth.substring(7));
if (null == accessToken || null == accessToken.getScope() || accessToken.isExpired()) {
return unauthorized(Response.Status.UNAUTHORIZED, "Access Denied, Invalid Token");
}
if (accessToken.getScope().contains(scope)) {
return Response.ok("{\"expiration\": \"" + accessToken.getExpiration() + "\"}", MediaType.APPLICATION_JSON)
.build();
}
return unauthorized(Response.Status.UNAUTHORIZED, "Access Denied");
}

@Context
private HttpServletRequest serveletRequest;

@GET
@Path("token/validate/extended")
@Produces(MediaType.APPLICATION_JSON)
public Response validate(@HeaderParam("OwnerAuthorization") String auth, @HeaderParam("OwnerScope") String scopecsv,
@HeaderParam("OwnerRole") String role, @HeaderParam("httpMethod") String httpMethod,
@HeaderParam("attributeCSV") String attributeCSV) {
logger.info("token/validate/extended validation started for token with scope : {}, role : {}", new Object[] { scopecsv, role });
logger.info("***************************************************");
Enumeration<String> headerNames = serveletRequest.getHeaderNames();
while(headerNames.hasMoreElements()) {
 String headerName = (String)headerNames.nextElement();
 logger.info("-->"+headerName+": "+serveletRequest.getHeader(headerName));
 
}
if (StringUtils.isBlank(auth) || StringUtils.isEmpty(scopecsv)
|| !StringUtils.startsWithIgnoreCase(auth, "Bearer ")) {
logger.error("OwnerAuthorization is not valid or OwnerScope not valid for token : {}",
new Object[] { auth });
return unauthorized(Response.Status.BAD_REQUEST, "either token or scope is null");
}
OAuth2Authentication authentication = null;
String accessTokenVal = (auth.replaceAll("\\s+", " ").split(" "))[1];
OAuth2AccessToken accessToken = null;
try {
accessToken = tStore.readAccessToken(accessTokenVal);
authentication = tokenServices.loadAuthentication(accessTokenVal);
String clientId = authentication.getOAuth2Request().getClientId();
logger.info("client id for token is {}.", new Object[] { clientId });
} catch (Exception e) {
logger.error("AccessToken not Authenticated due to : {}", new Object[] { e });
return unauthorized(Response.Status.UNAUTHORIZED, "Access Denied, Invalid Token");
}
try {
String[] scopeArray = scopecsv.split(",");
if (isAccessGranted(authentication, scopeArray, role, attributeCSV)) {
String principal = authentication.getName();
logger.info("principal found while validating token .Principal is {}",
new Object[] { principal });

String clientId = authentication.getOAuth2Request().getClientId();
MemberInfoResponse memberInfoResponse = tokenValidatorExtendedProcess
.getMemberInfoResponse(accessToken.getExpiresIn(), principal, clientId,scopecsv);

if (memberInfoResponse == null) {
logger.info("Member info not found for principal : {}", new Object[] { principal });
logger.info("Scope is {} for token.", new Object[] { scopecsv });
String grant_type=authentication.getOAuth2Request().getRequestParameters().get("grant_type");
boolean isImplicitorAdminToken="implicit".equals(grant_type);
return Response.ok(new MemberInfoResponse(clientId,scopecsv,accessToken.getExpiresIn(),isImplicitorAdminToken,""), MediaType.APPLICATION_JSON).build();
} else {
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
String roles="";
for (GrantedAuthority authority : authorities) {
if (authority.getAuthority().contains("ROLE")) {
roles+=authority.getAuthority();
}
}
boolean isAdminToken= StringUtils.contains(roles, "ROLE_CITRUSPAY_MERCHANT_SUPERVISOR");
memberInfoResponse.setImplicitType(isAdminToken);
return Response.ok(memberInfoResponse, MediaType.APPLICATION_JSON).build();
}

} else {
throw new AccessDeniedException("Access Denied");
}
} catch (Exception e) {
logger.error("Resource not accessible for Access Token {} due to : {}", new Object[] { auth }, e);
return unauthorized(Response.Status.UNAUTHORIZED, "Access Denied, Invalid Authorities");
}
}

private boolean isAccessGranted(OAuth2Authentication authentication, String[] scopeArray, String role,
String attributeCSV) {
Set<String> scopeSet = authentication.getOAuth2Request().getScope();
// String role=authentication.getDetails();
int grant = 0;
int denied = 0;
boolean scopeMatched = false;
outer_loop: for (String s : scopeArray) {
for (String t : scopeSet) {
if (s.equals(t.trim())) {
scopeMatched = true;
break outer_loop;
}
}
}
if (scopeMatched) {
grant++;
} else {
logger.error("Scope Required Not Present in Access Token expected : {} , got : {}", new Object[] { scopeArray, scopeSet });
denied++;
}

Collection<GrantedAuthority> authorities = authentication.getAuthorities();
if (!StringUtils.isBlank(role)) {
boolean rolePresent = false;
for (GrantedAuthority authority : authorities) {
if (authority.getAuthority().equalsIgnoreCase(role)) {
grant++;
rolePresent = true;
break;
}
}
if (!rolePresent) {
logger.error("Role {} Not Present in Access Token",role);
denied++;
}
}
if (attributeCSV != null) {
String[] attributeArray = attributeCSV.split(",");
List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
if (attributeArray.length > 0 && !StringUtils.isEmpty(attributeCSV)) {
for (String attr : attributeArray) {
attributes.add(new SecurityConfig(attr));
AuthenticatedVoter authenticationVoter = new AuthenticatedVoter();
int result = authenticationVoter.vote(authentication, null, attributes);
if (result == 1) {
grant++;
} else {
logger.error("Authentication Voter failed the authentication");
denied++;
}
}
}
}
if (grant > denied) {
return true;
}
return false;

}

private Response unauthorized(Response.Status status, String message) {
return Response.status(status).entity("{\"error\": \"" + message + "\"}").type(MediaType.APPLICATION_JSON)
.build();
}

}


Client code is :
package com.citruspay.um.poc;

import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.bouncycastle.jcajce.provider.asymmetric.dsa.DSASigner.stdDSA;
import org.jfree.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import com.citruspay.common.rest.RestTemplateClient;
import com.citruspay.um.poc.client.JSR356SwaggerSocketClient;
import com.citruspay.um.poc.client.impl.JSR356SwaggerSocketClientImpl;


import com.wordnik.swaggersocket.protocol.*;
import com.wordnik.swaggersocket.protocol.Request.Builder;

@Service
@Path("/um/poc")
public class TokenValidationAndMemberInfoServiceImpl {

private Logger logger = LoggerFactory.getLogger(TokenValidationAndMemberInfoServiceImpl.class);

@Path("/validate/webSocket")
@GET
@Produces(MediaType.APPLICATION_JSON)
public MemberInfoResponse validateTokenAndGetMemberInfoWebSocket()
throws InvalidTokenException {
RestTemplate restTemplate = RestTemplateClient.getRestTemplate();

MemberInfoResponse memberInfoResponse = null;
Long startTime=System.currentTimeMillis();
try {
/*
header.add("Authorization", "Bearer " + "1db38bfc-8f91-4d5d-a129-cbbd12b6ac06"); //local
header.add("OwnerAuthorization", "Bearer " + "c1c1c52f-582f-4931-8348-b2e716dfa46a");//local
header.add("OwnerRole", "");
header.add("OwnerScope", "OwnerScope");
* */
JSR356SwaggerSocketClientImpl client = new JSR356SwaggerSocketClientImpl();
//client.onOpen(session);
//client.open(request);
com.wordnik.swaggersocket.protocol.Request.Builder b=new Builder();
Request request= b.build();
request.setMethod("GET");
List<Header> headers=new ArrayList<Header>();
//headers.add(new Header("Authorization", "Bearer " + "1db38bfc-8f91-4d5d-a129-cbbd12b6ac06"));
headers.add(new Header("OwnerAuthorization", "Bearer " + "c1c1c52f-582f-4931-8348-b2e716dfa46a"));
headers.add(new Header("OwnerRole", ""));
headers.add(new Header("OwnerScope", "OwnerScope"));
request.setHeaders(headers);
client.open(request);
MemberInfoResponse response=client.send(request, MemberInfoResponse.class);
logger.info("MemberInfoResponse: "+response);
/*HttpHeaders header = new HttpHeaders();
header.setContentType(org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED);
//header.add("Authorization", "Bearer " + "55202e3a-9529-4e15-a546-6e2c8972e1ac");//stg7
//header.add("OwnerAuthorization", "Bearer " + "e3c29834-e51f-4fa7-baf9-c9603648652b");//stg7
header.add("Authorization", "Bearer " + "1db38bfc-8f91-4d5d-a129-cbbd12b6ac06"); //local
header.add("OwnerAuthorization", "Bearer " + "c1c1c52f-582f-4931-8348-b2e716dfa46a");//local
header.add("OwnerRole", "");
header.add("OwnerScope", "subscription");

// header.setConnection(HTTP.CONN_KEEP_ALIVE);
// header.set(HTTP.CONN_KEEP_ALIVE,"timeout=15,max=100");
//String validateUrl = "https://stgadmin7.citruspay.com" + "/service/v2/token/validate/extended";//stg7
String validateUrl = "http://127.0.0.1:8090/admin-site" + "/service/v2/token/validate/extended";//local

HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<MultiValueMap<String, Object>>(null,
header);
ResponseEntity<MemberInfoResponse> responseEntity = restTemplate.exchange(validateUrl, HttpMethod.GET,
httpEntity, MemberInfoResponse.class);

if (responseEntity.getStatusCode() == HttpStatus.OK)
memberInfoResponse = responseEntity.getBody();
else
throw new InvalidTokenException(responseEntity.getBody().getError());
*/
} catch (Exception e) {
logger.error("Error while validating token in PrepaidSecurityContextFilter {} : {}",
new Object[] { e.getMessage(), e });
throw new InvalidTokenException("Token Validation Failed", e);
} /*finally {
restTemplate = null;
}*/
Long endTime=System.currentTimeMillis();
Long duration=(endTime-startTime);
logger.info("-----------Call Duration: "+duration.toString()+"-----------------");
return memberInfoResponse;

}

}


Whenever I do client.open, I get the following errors:

Caused by: com.citruspay.um.poc.client.exception.JSR356SwaggerSocketException: An Unexpected Error Occurred Establishing the Swagger Socket Connection
at com.citruspay.um.poc.client.impl.JSR356SwaggerSocketClientImpl.open(JSR356SwaggerSocketClientImpl.java:186)
at com.citruspay.um.poc.client.impl.JSR356SwaggerSocketClientImpl.open(JSR356SwaggerSocketClientImpl.java:148)
at com.citruspay.um.poc.TokenValidationAndMemberInfoServiceImpl.validateTokenAndGetMemberInfoWebSocket(TokenValidationAndMemberInfoServiceImpl.java:113)
... 82 more
Caused by: javax.websocket.DeploymentException: The HTTP response from the server [400] did not permit the HTTP upgrade to WebSocket
at org.apache.tomcat.websocket.WsWebSocketContainer.connectToServer(WsWebSocketContainer.java:386)
at org.apache.tomcat.websocket.WsWebSocketContainer.connectToServer(WsWebSocketContainer.java:184)
at com.citruspay.um.poc.client.impl.JSR356SwaggerSocketClientImpl.open(JSR356SwaggerSocketClientImpl.java:172)
... 84 more

My connection request is not getting upgraded to WebSocket.
I am not able to figure out what I am missing.


Thanks,
Nilesh Gupta

Aki Yoshida

unread,
Jun 6, 2016, 7:01:04 PM6/6/16
to swagger-sw...@googlegroups.com
I am not sure if I see the websocket endpoint really hosted at
ws://127.0.0.1:8090/admin-site/service/v2/token/validate/extended/websocket,
which is the target url that you are sending the initial upgrade
request to. Should it just be
ws://127.0.0.1:8090/admin-site/service/v2? And subsequent requests
should be sent to the relative path.

From your description alone, i can't tell whether there is some issue
with the hosted service or there is some issue in the way how your
client is sending requests, and whether a simple swaggersocket is
working in your setup or not. What is your server container and which
swaggersocket/atmosphere versions you are using?

regards, aki
> --
> You received this message because you are subscribed to the Google Groups
> "Swagger" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to swagger-swaggers...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages