Secured feature layer access is denied sometimes, says unauthorized access to a secure service

2199
7
05-24-2017 04:59 AM
BatbayarBazarragchaa
New Contributor III

I'm using Arcgis Java SDK 10.2.4 and developing desktop application.

During development we're using feature services without any authentication and now we're secured them with username and password.

But secured feature layer access is denied sometimes, says "Unauthorized access to a secure service" even though password is correct. We're able to login through web with the same username and password and query features works.

We're creating user credentials following code:

UserCredentials userCredentials = new UserCredentials();
userCredentials.setUserAccount(defaultArcgisUsername, defaultArcgisPassword);

final GeodatabaseFeatureServiceTable fsTable = new GeodatabaseFeatureServiceTable(layerData.getFullUrl(), userCredentials, layerData.getLayerIndex());

Our feature service is running with self signed certificate and we've set custom trust store which has the feature service certificate.

Any things to check?

0 Kudos
7 Replies
nita14
by
Occasional Contributor III

Hi,

you might need to double check username which can be case sensitive. I suggest you use fiddler to debug the rest requests. Also, check this useful resource from Android SDK (Workflow 2) Working with secure ArcGIS services—ArcGIS Runtime SDK for Android | ArcGIS for Developers 

Hope this helps,

Adam

BatbayarBazarragchaa
New Contributor III

I've double checked the username and password on the feature service web interface (https://x.x.x.x:6443/arcgis/rest/services/xxx/xxx/xxx/33)

It works just fine.

0 Kudos
BatbayarBazarragchaa
New Contributor III

Maybe I should use token based authentication, generate token that has long expiry time for my needs.

And if token expired, acquire new token.

Acquiring ArcGIS tokens—Documentation (10.3 and 10.3.1) | ArcGIS for Server 

0 Kudos
nita14
by
Occasional Contributor III

Maybe I should use token based authentication, generate token that has long expiry time for my needs.

And if token expired, acquire new token.

This is exactly what UserCredentials does by default.

Adam

0 Kudos
BatbayarBazarragchaa
New Contributor III

I've write small code to generate tokens and use that token instead of username and password and it seems working better.

I generate tokens for 8 hours and store in database. When next time user logs in I check the current token and if expired I generate new one.

public class FeatureServiceTokenService {

    private static final Logger LOGGER = Logger.getLogger(ArcgisTokenService.class.getName());

    private final RestTemplate restTemplate;

    private final ObjectMapper objectMapper = new ObjectMapper();

    public FeatureServiceTokenService() {
        List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
        messageConverters.add(new StringHttpMessageConverter());
        messageConverters.add(new FormHttpMessageConverter());
        //messageConverters.add(new MappingJackson2HttpMessageConverter());

        restTemplate = new RestTemplate();
        restTemplate.setMessageConverters(messageConverters);
    }

    public TokenData generateToken(String username, String password, int expireMinutes) {
        LOGGER.log(Level.INFO, "Generating token for {0} minutes", expireMinutes);

        TokenData tokenData = null;
        try {
            // post params
            MultiValueMap<String, String> postParams = new LinkedMultiValueMap<>();
            postParams.add("username", username);
            postParams.add("password", password);
            postParams.add("client", RequestConstants.CLIENT_TYPE_REQUEST_IP);
            postParams.add("expiration", "" + expireMinutes);
            postParams.add("f", RequestConstants.FORMAT_JSON);

            ResponseEntity<String> response = restTemplate.postForEntity("http://xxx.xxx.xxx.xxx:6080/arcgis/tokens/", postParams, String.class);
            LOGGER.log(Level.INFO, "Token response: {0}", response);
            if (response.getStatusCode() == HttpStatus.OK) {
                tokenData = objectMapper.readValue(response.getBody(), TokenData.class);
            }
        } catch (IOException | RestClientException e) {
            LOGGER.log(Level.SEVERE, null, e);
        }

        return tokenData;
    }
}
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

And supply the token by:

userCredentials = new UserCredentials();
userCredentials.setTokenServiceUrl(astConfig.ARCGIS_SERVICE_TOKEN_SERVICE_URL);
userCredentials.setAuthenticationType(UserCredentials.AuthenticationType.TOKEN);
userCredentials.setUserToken(tokenData.getToken(), tokenData.getExpires(), "http://xxx.xxx.xxx.xxx:6080/arcgis/tokens/");‍‍‍‍
0 Kudos
BatbayarBazarragchaa
New Contributor III

I've found out another thing that If we configure tokenServiceUrl for userCredentials and when the token expires it tries to get another token. But I don't know what url to use for tokenServiceUrl.

It throws following exception when I configure url: http://xxx.xxx.xxx.xxx:6080/arcgis/tokens/

SEVERE: null
com.esri.core.io.EsriSecurityException: Invalid request <br>Usage: http://xxx.xxx.xxx.xxx:6080/arcgis/tokens?request=gettoken&username=username&password=password&<br>Usage: http://xxx.xxx.xxx.xxx:6080/arcgis/tokens/generateToken?username=username&password=password&<br>Usage: http://xxx.xxx.xxx.xxx:6080/arcgis/tokens/gettoken.html<br>
     at com.esri.core.internal.io.handler.c.c(Unknown Source)
     at com.esri.core.internal.io.handler.c.a(Unknown Source)
     at com.esri.core.internal.io.handler.c.a(Unknown Source)
     at com.esri.core.internal.io.handler.a.a(Unknown Source)
     at com.esri.core.internal.io.handler.a.b(Unknown Source)
     at com.esri.core.internal.tasks.ags.p.a(Unknown Source)
     at com.esri.core.geodatabase.GeodatabaseFeatureServiceTable.a(Unknown Source)
     at com.esri.core.geodatabase.GeodatabaseFeatureServiceTable.a(Unknown Source)
     at com.esri.core.geodatabase.GeodatabaseFeatureServiceTable$8.a(Unknown Source)
     at com.esri.core.geodatabase.GeodatabaseFeatureServiceTable$8.call(Unknown Source)
     at java.util.concurrent.FutureTask.run(FutureTask.java:266)
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
     at java.lang.Thread.run(Thread.java:745)
0 Kudos
BatbayarBazarragchaa
New Contributor III

Is there any appropriate way to check if token is valid?

0 Kudos