• 再议解决arcgis portal https net::ERR_CERT_AUTHORITY_INVALID,proxy.ashx


    arcgis portal部署需要域名,默认使用https提供服务,arcgis api for js 代码访问发布的地图服务时,没有导入ssl证书的portal 就会在前端卡住了。

    第一次是这样

    测试页面是这样,看不到图的:

    图上啥也没有的。只有在新tab 中点开链接,手动点高级,继续,之后才host 顺畅浏览应用网站

    尝试使用esri 推荐的代理解决呢?

    html代码:

    <html>
      <head>
        <meta charset="utf-8" />
        <meta
          name="viewport"
          content="initial-scale=1,maximum-scale=1,user-scalable=no"
        />
        <title>
          Intro to FeatureLayer | Sample | ArcGIS API for JavaScript 4.22
        </title>
    
        <link
          rel="stylesheet"
          href="https://js.arcgis.com/4.22/esri/themes/light/main.css"
        />
        <script src="https://js.arcgis.com/4.22/"></script>
    
        <style>
          html,
          body,
          #viewDiv {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
          }
        </style>
    
        <script>
          require(["esri/config","esri/Map", "esri/views/MapView", "esri/layers/FeatureLayer"], (
            esriConfig,
            Map,
            MapView,
            FeatureLayer
          ) => {
    
            esriConfig.request.proxyUrl='http://10.xxx118:8082/proxy/proxy.ashx'
            esriConfig.request.forceProxy = true;
            
    
            const map = new Map({
            });
    
            const view = new MapView({
              container: "viewDiv",
              map: map,
              extent: {
                xmin: 86.01354144000004,
                ymin: 44.282404040000074,
                xmax: 86.08706388000007,
                ymax: 44.33425853000006,
                spatialReference: 4326
              }
            });
    
            const featureLayer = new FeatureLayer({
                url:'https://10.xxx.152:6443/arcgis/rest/services/shz/comm/MapServer/0'
            //   url: 'https://10.xxx.152:6443/arcgis/rest/services/shz/base/MapServer/48'
            });
    
            //https://poxxxm/arcgis/sharing/rest/portals/self?f=json
    
            map.add(featureLayer);
          });
        </script>
      </head>
    
      <body>
        <div id="viewDiv"></div>
      </body>
    </html>
    View Code

    修改后的proxy.ashx代码:

    <%@ WebHandler Language="C#" Class="proxy" %>
    
    /*
     * DotNet proxy client.
     *
     * Version 1.1.2
     * See https://github.com/Esri/resource-proxy for more information.
     *
     */
    
    #define TRACE
    using System;
    using System.IO;
    using System.Web;
    using System.Xml.Serialization;
    using System.Web.Caching;
    using System.Collections.Concurrent;
    using System.Diagnostics;
    using System.Text.RegularExpressions;
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    using System.Net.Security;
    
    public class proxy : IHttpHandler {
    
        private static String version = "1.1.2";
    
        class RateMeter {
            double _rate; //internal rate is stored in requests per second
            int _countCap;
            double _count = 0;
            DateTime _lastUpdate = DateTime.Now;
    
            public RateMeter(int rate_limit, int rate_limit_period) {
                _rate = (double) rate_limit / rate_limit_period / 60;
                _countCap = rate_limit;
            }
    
            //called when rate-limited endpoint is invoked
            public bool click() {
                TimeSpan ts = DateTime.Now - _lastUpdate;
                _lastUpdate = DateTime.Now;
                //assuming uniform distribution of requests over time,
                //reducing the counter according to # of seconds passed
                //since last invocation
                _count = Math.Max(0, _count - ts.TotalSeconds * _rate);
                if (_count <= _countCap) {
                    //good to proceed
                    _count++;
                    return true;
                }
                return false;
            }
    
            public bool canBeCleaned() {
                TimeSpan ts = DateTime.Now - _lastUpdate;
                return _count - ts.TotalSeconds * _rate <= 0;
            }
        }
    
        private static string PROXY_REFERER = "http://loxxxxost:8082/proxy/proxy.ashx";
        private static string DEFAULT_OAUTH = "https://www.arcgis.com/sharing/oauth2/";
        private static int CLEAN_RATEMAP_AFTER = 10000; //clean the rateMap every xxxx requests
        private static System.Net.IWebProxy SYSTEM_PROXY = System.Net.HttpWebRequest.DefaultWebProxy; // Use the default system proxy
        private static LogTraceListener logTraceListener = null;
        private static Object _rateMapLock = new Object();
    
        private bool ValidateServerCertificate(object sender, X509Certificate certificate,         
             X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true;//这里设置成true
        }
    
        public void ProcessRequest(HttpContext context) {
            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);
    
            if (logTraceListener == null)
            {
                logTraceListener = new LogTraceListener();
                Trace.Listeners.Add(logTraceListener);
            }
    
    
            HttpResponse response = context.Response;
            if (context.Request.Url.Query.Length < 1)
            {
                string errorMsg = "This proxy does not support empty parameters.";
                log(TraceLevel.Error, errorMsg);
                sendErrorResponse(context.Response, null, errorMsg, System.Net.HttpStatusCode.BadRequest);
                return;
            }
    
            string uri = context.Request.Url.Query.Substring(1);
            log(TraceLevel.Verbose, "URI requested: " + uri);
    
            //if uri is ping
            if (uri.Equals("ping", StringComparison.InvariantCultureIgnoreCase))
            {
                ProxyConfig proxyConfig = ProxyConfig.GetCurrentConfig();
    
                String checkConfig = (proxyConfig == null) ? "Not Readable" : "OK";
                String checkLog = "";
                if (checkConfig != "OK")
                {
                    checkLog = "Can not verify";
                }
                else
                {
                    String filename = proxyConfig.logFile;
                    checkLog = (filename != null && filename != "") ? "OK" : "Not Exist/Readable";
    
                    if (checkLog == "OK") {
                        log(TraceLevel.Info, "Pinged");
                    }
                }
    
                sendPingResponse(response, version, checkConfig, checkLog);
                return;
            }
    
            //if url is encoded, decode it.
            if (uri.StartsWith("http%3a%2f%2f", StringComparison.InvariantCultureIgnoreCase) || uri.StartsWith("https%3a%2f%2f", StringComparison.InvariantCultureIgnoreCase))
                uri = HttpUtility.UrlDecode(uri);
    
            ServerUrl serverUrl;
            try {
                serverUrl = getConfig().GetConfigServerUrl(uri);
    
                if (serverUrl == null) {
                    //if no serverUrl found, send error message and get out.
                    string errorMsg = "The request URL does not match with the ServerUrl in proxy.config! Please check the proxy.config!";
                    log(TraceLevel.Error, errorMsg);
                    sendErrorResponse(context.Response, null, errorMsg, System.Net.HttpStatusCode.BadRequest);
                    return;
                }
            }
            //if XML couldn't be parsed
            catch (InvalidOperationException ex) {
    
                string errorMsg = ex.InnerException.Message + " " + uri;
                log(TraceLevel.Error, errorMsg);
                sendErrorResponse(context.Response, null, errorMsg, System.Net.HttpStatusCode.InternalServerError);
                return;
            }
            //if mustMatch was set to true and URL wasn't in the list
            catch (ArgumentException ex) {
                string errorMsg = ex.Message + " " + uri;
                log(TraceLevel.Error, errorMsg);
                sendErrorResponse(context.Response, null, errorMsg, System.Net.HttpStatusCode.Forbidden);
                return;
            }
            //use actual request header instead of a placeholder, if present
            if (context.Request.Headers["referer"] != null)
                PROXY_REFERER = context.Request.Headers["referer"];
    
            //referer
            //check against the list of referers if they have been specified in the proxy.config
            String[] allowedReferersArray = ProxyConfig.GetAllowedReferersArray();
            if (allowedReferersArray != null && allowedReferersArray.Length > 0 && context.Request.Headers["referer"] != null)
            {
                PROXY_REFERER = context.Request.Headers["referer"];
                string requestReferer = context.Request.Headers["referer"];
                try
                {
                    String checkValidUri = new UriBuilder(requestReferer.StartsWith("//") ? requestReferer.Substring(requestReferer.IndexOf("//") + 2) : requestReferer).Host;
    
                }
                catch (Exception e)
                {
                    log(TraceLevel.Warning, "Proxy is being used from an invalid referer: " + context.Request.Headers["referer"]);
                    sendErrorResponse(context.Response, "Error verifying referer. ", "403 - Forbidden: Access is denied.", System.Net.HttpStatusCode.Forbidden);
                    return;
                }
    
                if (!checkReferer(allowedReferersArray, requestReferer))
                {
                    log(TraceLevel.Warning, "Proxy is being used from an unknown referer: " + context.Request.Headers["referer"]);
                    sendErrorResponse(context.Response, "Unsupported referer. ", "403 - Forbidden: Access is denied.", System.Net.HttpStatusCode.Forbidden);
                }
    
    
            }
    
            //Check to see if allowed referer list is specified and reject if referer is null
            if (context.Request.Headers["referer"] == null && allowedReferersArray != null && !allowedReferersArray[0].Equals("*"))
            {
                log(TraceLevel.Warning, "Proxy is being called by a null referer.  Access denied.");
                sendErrorResponse(response, "Current proxy configuration settings do not allow requests which do not include a referer header.", "403 - Forbidden: Access is denied.", System.Net.HttpStatusCode.Forbidden);
                return;
            }
    
            //Throttling: checking the rate limit coming from particular client IP
            if (serverUrl.RateLimit > -1) {
                lock (_rateMapLock)
                {
                    ConcurrentDictionary<string, RateMeter> ratemap = (ConcurrentDictionary<string, RateMeter>)context.Application["rateMap"];
                    if (ratemap == null)
                    {
                        ratemap = new ConcurrentDictionary<string, RateMeter>();
                        context.Application["rateMap"] = ratemap;
                        context.Application["rateMap_cleanup_counter"] = 0;
                    }
                    string key = "[" + serverUrl.Url + "]x[" + context.Request.UserHostAddress + "]";
                    RateMeter rate;
                    if (!ratemap.TryGetValue(key, out rate))
                    {
                        rate = new RateMeter(serverUrl.RateLimit, serverUrl.RateLimitPeriod);
                        ratemap.TryAdd(key, rate);
                    }
                    if (!rate.click())
                    {
                        log(TraceLevel.Warning, " Pair " + key + " is throttled to " + serverUrl.RateLimit + " requests per " + serverUrl.RateLimitPeriod + " minute(s). Come back later.");
                        sendErrorResponse(context.Response, "This is a metered resource, number of requests have exceeded the rate limit interval.", "Unable to proxy request for requested resource", (System.Net.HttpStatusCode)429);
                        return;
                    }
    
                    //making sure the rateMap gets periodically cleaned up so it does not grow uncontrollably
                    int cnt = (int)context.Application["rateMap_cleanup_counter"];
                    cnt++;
                    if (cnt >= CLEAN_RATEMAP_AFTER)
                    {
                        cnt = 0;
                        cleanUpRatemap(ratemap);
                    }
                    context.Application["rateMap_cleanup_counter"] = cnt;
                }
            }
    
            //readying body (if any) of POST request
            byte[] postBody = readRequestPostBody(context);
            string post = System.Text.Encoding.UTF8.GetString(postBody);
    
            System.Net.NetworkCredential credentials = null;
            string requestUri = uri;
            bool hasClientToken = false;
            string token = string.Empty;
            string tokenParamName = null;
    
            if ((serverUrl.HostRedirect != null) && (serverUrl.HostRedirect != string.Empty))
            {
                requestUri = serverUrl.HostRedirect + new Uri(requestUri).PathAndQuery;
            }
            if (serverUrl.UseAppPoolIdentity)
            {
                credentials=CredentialCache.DefaultNetworkCredentials;
            }
            else if (serverUrl.Domain != null)
            {
                credentials = new System.Net.NetworkCredential(serverUrl.Username, serverUrl.Password, serverUrl.Domain);
            }
            else
            {
                //if token comes with client request, it takes precedence over token or credentials stored in configuration
                hasClientToken = requestUri.Contains("?token=") || requestUri.Contains("&token=") || post.Contains("?token=") || post.Contains("&token=");
    
                if (!hasClientToken)
                {
                    // Get new token and append to the request.
                    // But first, look up in the application scope, maybe it's already there:
                    token = (String)context.Application["token_for_" + serverUrl.Url];
                    bool tokenIsInApplicationScope = !String.IsNullOrEmpty(token);
    
                    //if still no token, let's see if there is an access token or if are credentials stored in configuration which we can use to obtain new token
                    if (!tokenIsInApplicationScope)
                    {
                        token = serverUrl.AccessToken;
                        if (String.IsNullOrEmpty(token))
                            token = getNewTokenIfCredentialsAreSpecified(serverUrl, requestUri);
                    }
    
                    if (!String.IsNullOrEmpty(token) && !tokenIsInApplicationScope)
                    {
                        //storing the token in Application scope, to do not waste time on requesting new one untill it expires or the app is restarted.
                        context.Application.Lock();
                        context.Application["token_for_" + serverUrl.Url] = token;
                        context.Application.UnLock();
                    }
    
                    //name by which token parameter is passed (if url actually came from the list)
                    tokenParamName = serverUrl != null ? serverUrl.TokenParamName : null;
    
                    if (String.IsNullOrEmpty(tokenParamName))
                        tokenParamName = "token";
                }
            }
    
            //forwarding original request
            System.Net.WebResponse serverResponse = null;
            try {
                serverResponse = forwardToServer(context.Request, addTokenToUri(requestUri, token, tokenParamName), postBody, credentials);
            } catch (System.Net.WebException webExc) {
    
                string errorMsg = webExc.Message + " " + uri;
                log(TraceLevel.Error, errorMsg);
    
                if (webExc.Response != null)
                {
                    copyResponseHeaders(webExc.Response as System.Net.HttpWebResponse, context.Response);
    
                    using (Stream responseStream = webExc.Response.GetResponseStream())
                    {
                        byte[] bytes = new byte[32768];
                        int bytesRead = 0;
    
                        while ((bytesRead = responseStream.Read(bytes, 0, bytes.Length)) > 0)
                        {
                            responseStream.Write(bytes, 0, bytesRead);
                        }
    
                        context.Response.StatusCode = (int)(webExc.Response as System.Net.HttpWebResponse).StatusCode;
                        context.Response.OutputStream.Write(bytes, 0, bytes.Length);
                    }
                }
                else
                {
                    System.Net.HttpStatusCode statusCode = System.Net.HttpStatusCode.InternalServerError;
                    sendErrorResponse(context.Response, null, errorMsg, statusCode);
                }
                return;
            }
    
            if (string.IsNullOrEmpty(token) || hasClientToken)
                //if token is not required or provided by the client, just fetch the response as is:
                fetchAndPassBackToClient(serverResponse, response, true);
            else {
                //credentials for secured service have come from configuration file:
                //it means that the proxy is responsible for making sure they were properly applied:
    
                //first attempt to send the request:
                bool tokenRequired = fetchAndPassBackToClient(serverResponse, response, false);
    
    
                //checking if previously used token has expired and needs to be renewed
                if (tokenRequired) {
                    log(TraceLevel.Info, "Renewing token and trying again.");
                    //server returned error - potential cause: token has expired.
                    //we'll do second attempt to call the server with renewed token:
                    token = getNewTokenIfCredentialsAreSpecified(serverUrl, requestUri);
                    serverResponse = forwardToServer(context.Request, addTokenToUri(requestUri, token, tokenParamName), postBody);
    
                    //storing the token in Application scope, to do not waste time on requesting new one untill it expires or the app is restarted.
                    context.Application.Lock();
                    context.Application["token_for_" + serverUrl.Url] = token;
                    context.Application.UnLock();
    
                    fetchAndPassBackToClient(serverResponse, response, true);
                }
            }
    
            // Use instead of response.End() to avoid the "Exception thrown: 'System.Threading.ThreadAbortException' in mscorlib.dll" error
            // that appears in the output of Visual Studio.  response.End() appears to only really be necessary if you need to end the thread immediately
            // (i.e. no more code is processed).  Since this call is at the end of the main subroutine we can safely call ApplicationInstance.CompleteRequest()
            // and avoid unnecessary exceptions.
            // Sources:
            // http://stackoverflow.com/questions/14590812/what-is-the-difference-between-use-cases-for-using-response-endfalse-vs-appl
            // http://weblogs.asp.net/hajan/why-not-to-use-httpresponse-close-and-httpresponse-end
            // http://stackoverflow.com/questions/1087777/is-response-end-considered-harmful
    
            context.ApplicationInstance.CompleteRequest();
        }
    
        public bool IsReusable {
            get { return true; }
        }
    
    /**
    * Private
    */
        private byte[] readRequestPostBody(HttpContext context) {
            if (context.Request.InputStream.Length > 0) {
                byte[] bytes = new byte[context.Request.InputStream.Length];
                context.Request.InputStream.Read(bytes, 0, (int)context.Request.InputStream.Length);
                return bytes;
            }
            return new byte[0];
        }
    
        private void writeRequestPostBody(System.Net.HttpWebRequest req, byte[] bytes)
        {
            if (bytes != null && bytes.Length > 0)
            {
                req.ContentLength = bytes.Length;
                using (Stream outputStream = req.GetRequestStream())
                {
                    outputStream.Write(bytes, 0, bytes.Length);
                }
            }
        }
    
        private System.Net.WebResponse forwardToServer(HttpRequest req, string uri, byte[] postBody, System.Net.NetworkCredential credentials = null)
        {
            string method = postBody.Length > 0 ? "POST" : req.HttpMethod;
            System.Net.HttpWebRequest forwardReq = createHTTPRequest(uri, method, req.ContentType, credentials);
            copyRequestHeaders(req, forwardReq);
            writeRequestPostBody(forwardReq, postBody);
            return forwardReq.GetResponse();
        }
    
        /// <summary>
        /// Attempts to copy all headers from the fromResponse to the the toResponse.
        /// </summary>
        /// <param name="fromResponse">The response that we are copying the headers from</param>
        /// <param name="toResponse">The response that we are copying the headers to</param>
        private void copyResponseHeaders(System.Net.WebResponse fromResponse, HttpResponse toResponse)
        {
            foreach (var headerKey in fromResponse.Headers.AllKeys)
            {
                switch (headerKey.ToLower())
                {
                    case "content-type":
                    case "transfer-encoding":
                    case "accept-ranges":   // Prevent requests for partial content
                    case "access-control-allow-origin":
                    case "access-control-allow-credentials":
                    case "access-control-expose-headers":
                    case "access-control-max-age":
                        continue;
                    default:
                        toResponse.AddHeader(headerKey, fromResponse.Headers[headerKey]);
                        break;
                }
            }
            // Reset the content-type for OGC WMS - issue #367
            // Note: this might not be what everyone expects, but it helps some users
            // TODO: make this configurable
            if (fromResponse.ContentType.Contains("application/vnd.ogc.wms_xml")) {
                toResponse.ContentType = "text/xml";
                log(TraceLevel.Verbose, "Adjusting Content-Type for WMS OGC: " + fromResponse.ContentType );
            } else {
                toResponse.ContentType = fromResponse.ContentType;
            }
        }
    
        private void copyRequestHeaders(HttpRequest fromRequest, System.Net.HttpWebRequest toRequest)
        {
            foreach (var headerKey in fromRequest.Headers.AllKeys)
            {
                string headerValue = fromRequest.Headers[headerKey];
                string headerKeyLower = headerKey.ToLower();
    
                switch (headerKeyLower)
                {
                    case "accept-encoding":
                    case "proxy-connection":
                        continue;
                    case "range":
                        setRangeHeader(toRequest, headerValue);
                        break;
                    case "accept":
                        toRequest.Accept = headerValue;
                        break;
                    case "if-modified-since":
                        DateTime modDT;
                        if (DateTime.TryParse(headerValue, out modDT))
                            toRequest.IfModifiedSince = modDT;
                        break;
                    case "referer":
                        toRequest.Referer = headerValue;
                        break;
                    case "user-agent":
                        toRequest.UserAgent = headerValue;
                        break;
                    default:
                        // Some headers are restricted and would throw an exception:
                        // http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.headers(v=vs.100).aspx
                        // Also check for our custom list of headers that should not be sent (https://github.com/Esri/resource-proxy/issues/362)
                        if (!System.Net.WebHeaderCollection.IsRestricted(headerKey) &&
                            headerKeyLower != "accept-encoding" &&
                            headerKeyLower != "proxy-connection" &&
                            headerKeyLower != "connection" &&
                            headerKeyLower != "keep-alive" &&
                            headerKeyLower != "proxy-authenticate" &&
                            headerKeyLower != "proxy-authorization" &&
                            headerKeyLower != "transfer-encoding" &&
                            headerKeyLower != "te" &&
                            headerKeyLower != "trailer" &&
                            headerKeyLower != "upgrade" &&
                            toRequest.Headers[headerKey] == null)
                            toRequest.Headers[headerKey] = headerValue;
                        break;
                }
            }
        }
    
        private void setRangeHeader(System.Net.HttpWebRequest req, string range)
        {
            string[] specifierAndRange = range.Split('=');
            if (specifierAndRange.Length == 2)
            {
                string specifier = specifierAndRange[0];
                string[] fromAndTo = specifierAndRange[1].Split('-');
                if (fromAndTo.Length == 2)
                {
                    int from, to;
                    if (int.TryParse(fromAndTo[0], out from) && int.TryParse(fromAndTo[1], out to))
                        req.AddRange(specifier, from, to);
                }
            }
        }
    
        private bool fetchAndPassBackToClient(System.Net.WebResponse serverResponse, HttpResponse clientResponse, bool ignoreAuthenticationErrors) {
            if (serverResponse != null) {
                using (Stream byteStream = serverResponse.GetResponseStream()) {
                    // Text response
                    if (serverResponse.ContentType.Contains("text") ||
                        serverResponse.ContentType.Contains("json") ||
                        serverResponse.ContentType.Contains("xml")) {
                        using (StreamReader sr = new StreamReader(byteStream)) {
                            string strResponse = sr.ReadToEnd();
                            if (
                                !ignoreAuthenticationErrors
                                && strResponse.Contains("error")
                                && Regex.Match(strResponse, "\"code\"\\s*:\\s*49[89]").Success
                            )
                                return true;
    
                            //Copy the header info and the content to the reponse to client
                            copyResponseHeaders(serverResponse, clientResponse);
                            clientResponse.Write(strResponse);
                        }
                    } else {
                        // Binary response (image, lyr file, other binary file)
    
                        //Copy the header info to the reponse to client
                        copyResponseHeaders(serverResponse, clientResponse);
                        // Tell client not to cache the image since it's dynamic
                        clientResponse.CacheControl = "no-cache";
                        byte[] buffer = new byte[32768];
                        int read;
                        while ((read = byteStream.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            clientResponse.OutputStream.Write(buffer, 0, read);
                        }
                        clientResponse.OutputStream.Close();
                    }
                    serverResponse.Close();
                }
            }
            return false;
        }
    
        private System.Net.WebResponse doHTTPRequest(string uri, string method, System.Net.NetworkCredential credentials = null)
        {
            byte[] bytes = null;
            String contentType = null;
            log(TraceLevel.Info, "Sending " + method + " request: " + uri);
    
            if (method.Equals("POST"))
            {
                String[] uriArray = uri.Split(new char[] { '?' }, 2);
                uri = uriArray[0];
                if (uriArray.Length > 1)
                {
                    contentType = "application/x-www-form-urlencoded";
                    String queryString = uriArray[1];
    
                    bytes = System.Text.Encoding.UTF8.GetBytes(queryString);
                }
            }
    
            System.Net.HttpWebRequest req = createHTTPRequest(uri, method, contentType, credentials);
            req.Referer = PROXY_REFERER;
            writeRequestPostBody(req, bytes);
            return req.GetResponse();
        }
    
        private System.Net.HttpWebRequest createHTTPRequest(string uri, string method, string contentType, System.Net.NetworkCredential credentials = null)
        {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(uri);
            req.ServicePoint.Expect100Continue = false;
            req.Method = method;
            if (method == "POST")
                req.ContentType = string.IsNullOrEmpty(contentType) ? "application/x-www-form-urlencoded" : contentType;
    
            // Use the default system proxy
            req.Proxy = SYSTEM_PROXY;
    
            if (credentials != null)
                req.Credentials = credentials;
    
            return req;
        }
    
        private string webResponseToString(System.Net.WebResponse serverResponse) {
            using (Stream byteStream = serverResponse.GetResponseStream()) {
                using (StreamReader sr = new StreamReader(byteStream)) {
                    string strResponse = sr.ReadToEnd();
                    return strResponse;
                }
            }
        }
    
        private string getNewTokenIfCredentialsAreSpecified(ServerUrl su, string reqUrl) {
            string token = "";
            string infoUrl = "";
    
            bool isUserLogin = !String.IsNullOrEmpty(su.Username) && !String.IsNullOrEmpty(su.Password);
            bool isAppLogin = !String.IsNullOrEmpty(su.ClientId) && !String.IsNullOrEmpty(su.ClientSecret);
            if (isUserLogin || isAppLogin) {
                log(TraceLevel.Info, "Matching credentials found in configuration file. OAuth 2.0 mode: " + isAppLogin);
                if (isAppLogin) {
                    //OAuth 2.0 mode authentication
                    //"App Login" - authenticating using client_id and client_secret stored in config
                    su.OAuth2Endpoint = string.IsNullOrEmpty(su.OAuth2Endpoint) ? DEFAULT_OAUTH : su.OAuth2Endpoint;
                    if (su.OAuth2Endpoint[su.OAuth2Endpoint.Length - 1] != '/')
                        su.OAuth2Endpoint += "/";
                    log(TraceLevel.Info, "Service is secured by " + su.OAuth2Endpoint + ": getting new token...");
                    string uri = su.OAuth2Endpoint + "token?client_id=" + su.ClientId + "&client_secret=" + su.ClientSecret + "&grant_type=client_credentials&f=json";
                    string tokenResponse = webResponseToString(doHTTPRequest(uri, "POST"));
                    token = extractToken(tokenResponse, "token");
                    if (!string.IsNullOrEmpty(token))
                        token = exchangePortalTokenForServerToken(token, su);
                } else {
                    //standalone ArcGIS Server/ArcGIS Online token-based authentication
    
                    //if a request is already being made to generate a token, just let it go
                    if (reqUrl.ToLower().Contains("/generatetoken")) {
                        string tokenResponse = webResponseToString(doHTTPRequest(reqUrl, "POST"));
                        token = extractToken(tokenResponse, "token");
                        return token;
                    }
    
                    //lets look for '/rest/' in the requested URL (could be 'rest/services', 'rest/community'...)
                    if (reqUrl.ToLower().Contains("/rest/"))
                        infoUrl = reqUrl.Substring(0, reqUrl.IndexOf("/rest/", StringComparison.OrdinalIgnoreCase));
    
                    //if we don't find 'rest', lets look for the portal specific 'sharing' instead
                    else if (reqUrl.ToLower().Contains("/sharing/")) {
                        infoUrl = reqUrl.Substring(0, reqUrl.IndexOf("/sharing/", StringComparison.OrdinalIgnoreCase));
                        infoUrl = infoUrl + "/sharing";
                    }
                    else
                        throw new ApplicationException("Unable to determine the correct URL to request a token to access private resources.");
    
                    if (infoUrl != "") {
                        log(TraceLevel.Info," Querying security endpoint...");
                        infoUrl += "/rest/info?f=json";
                        //lets send a request to try and determine the URL of a token generator
                        string infoResponse = webResponseToString(doHTTPRequest(infoUrl, "GET"));
                        String tokenServiceUri = getJsonValue(infoResponse, "tokenServicesUrl");
                        if (string.IsNullOrEmpty(tokenServiceUri)) {
                            string owningSystemUrl = getJsonValue(infoResponse, "owningSystemUrl");
                            if (!string.IsNullOrEmpty(owningSystemUrl)) {
                                tokenServiceUri = owningSystemUrl + "/sharing/generateToken";
                            }
                        }
                        if (tokenServiceUri != "") {
                            log(TraceLevel.Info," Service is secured by " + tokenServiceUri + ": getting new token...");
                            string uri = tokenServiceUri + "?f=json&request=getToken&referer=" + PROXY_REFERER + "&expiration=60&username=" + su.Username + "&password=" + su.Password;
                            string tokenResponse = webResponseToString(doHTTPRequest(uri, "POST"));
                            token = extractToken(tokenResponse, "token");
                        }
                    }
    
    
                }
            }
            return token;
        }
    
        private bool checkWildcardSubdomain(String allowedReferer, String requestedReferer)
        {
            String[] allowedRefererParts = Regex.Split(allowedReferer, "(\\.)");
            String[] refererParts = Regex.Split(requestedReferer, "(\\.)");
    
            if (allowedRefererParts.Length != refererParts.Length)
            {
                return false;
            }
    
            int index = allowedRefererParts.Length - 1;
            while (index >= 0)
            {
                if (allowedRefererParts[index].Equals(refererParts[index], StringComparison.OrdinalIgnoreCase))
                {
                    index = index - 1;
                }
                else
                {
                    if (allowedRefererParts[index].Equals("*"))
                    {
                        index = index - 1;
                        continue; //next
                    }
                    return false;
                }
            }
            return true;
        }
    
        private bool pathMatched(String allowedRefererPath, String refererPath)
        {
            //If equal, return true
            if (refererPath.Equals(allowedRefererPath))
            {
                return true;
            }
    
            //If the allowedRefererPath contain a ending star and match the begining part of referer, it is proper start with.
            if (allowedRefererPath.EndsWith("*"))
            {
                String allowedRefererPathShort = allowedRefererPath.Substring(0, allowedRefererPath.Length - 1);
                if (refererPath.ToLower().StartsWith(allowedRefererPathShort.ToLower()))
                {
                    return true;
                }
            }
            return false;
        }
    
        private bool domainMatched(String allowedRefererDomain, String refererDomain)
        {
            if (allowedRefererDomain.Equals(refererDomain)){
                return true;
            }
    
            //try if the allowed referer contains wildcard for subdomain
            if (allowedRefererDomain.Contains("*")){
                if (checkWildcardSubdomain(allowedRefererDomain, refererDomain)){
                    return true;//return true if match wildcard subdomain
                }
            }
    
            return false;
        }
    
        private bool protocolMatch(String allowedRefererProtocol, String refererProtocol)
        {
            return allowedRefererProtocol.Equals(refererProtocol);
        }
    
        private String getDomainfromURL(String url, String protocol)
        {
            String domain = url.Substring(protocol.Length + 3);
    
            domain = domain.IndexOf('/') >= 0 ? domain.Substring(0, domain.IndexOf('/')) : domain;
    
            return domain;
        }
    
        private bool checkReferer(String[] allowedReferers, String referer)
        {
            if (allowedReferers != null && allowedReferers.Length > 0)
            {
                if (allowedReferers.Length == 1 && allowedReferers[0].Equals("*")) return true; //speed-up
    
                foreach (String allowedReferer in allowedReferers)
                {
    
                    //Parse the protocol, domain and path of the referer
                    String refererProtocol = referer.StartsWith("https://") ? "https" : "http";
                    String refererDomain = getDomainfromURL(referer, refererProtocol);
                    String refererPath = referer.Substring(refererProtocol.Length + 3 + refererDomain.Length);
    
    
                    String allowedRefererCannonical = null;
    
                    //since the allowedReferer can be a malformed URL, we first construct a valid one to be compared with referer
                    //if allowedReferer starts with https:// or http://, then exact match is required
                    if (allowedReferer.StartsWith("https://") || allowedReferer.StartsWith("http://"))
                    {
                        allowedRefererCannonical = allowedReferer;
    
                    }
                    else
                    {
    
                        String protocol = refererProtocol;
                        //if allowedReferer starts with "//" or no protocol, we use the one from refererURL to prefix to allowedReferer.
                        if (allowedReferer.StartsWith("//"))
                        {
                            allowedRefererCannonical = protocol + ":" + allowedReferer;
                        }
                        else
                        {
                            //if the allowedReferer looks like "example.esri.com"
                            allowedRefererCannonical = protocol + "://" + allowedReferer;
                        }
                    }
    
                    //parse the protocol, domain and the path of the allowedReferer
                    String allowedRefererProtocol = allowedRefererCannonical.StartsWith("https://") ? "https" : "http";
                    String allowedRefererDomain = getDomainfromURL(allowedRefererCannonical, allowedRefererProtocol);
                    String allowedRefererPath = allowedRefererCannonical.Substring(allowedRefererProtocol.Length + 3 + allowedRefererDomain.Length);
    
                    //Check if both domain and path match
                    if (protocolMatch(allowedRefererProtocol, refererProtocol) &&
                            domainMatched(allowedRefererDomain, refererDomain) &&
                            pathMatched(allowedRefererPath, refererPath))
                    {
                        return true;
                    }
                }
                return false;//no-match
            }
            return true;//when allowedReferer is null, then allow everything
        }
    
        private string exchangePortalTokenForServerToken(string portalToken, ServerUrl su) {
            //ideally, we should POST the token request
            log(TraceLevel.Info," Exchanging Portal token for Server-specific token for " + su.Url + "...");
            string uri = su.OAuth2Endpoint.Substring(0, su.OAuth2Endpoint.IndexOf("/oauth2/", StringComparison.OrdinalIgnoreCase)) +
                 "/generateToken?token=" + portalToken + "&serverURL=" + su.Url + "&f=json";
            string tokenResponse = webResponseToString(doHTTPRequest(uri, "GET"));
            return extractToken(tokenResponse, "token");
        }
    
    
        private static void sendPingResponse(HttpResponse response, String version, String config, String log)
        {
            response.AddHeader("Content-Type", "application/json");
            response.AddHeader("Accept-Encoding", "gzip");
            String message = "{ " +
                "\"Proxy Version\": \"" + version + "\"" +
                ", \"Configuration File\": \"" + config + "\"" +
                ", \"Log File\": \"" + log + "\"" +
                "}";
            response.StatusCode = 200;
            response.Write(message);
            response.Flush();
        }
    
        private static void sendErrorResponse(HttpResponse response, String errorDetails, String errorMessage, System.Net.HttpStatusCode errorCode)
        {
            String message = string.Format("{{\"error\": {{\"code\": {0},\"message\":\"{1}\"", (int)errorCode, errorMessage);
            if (!string.IsNullOrEmpty(errorDetails))
                message += string.Format(",\"details\":[\"message\":\"{0}\"]", errorDetails);
            message += "}}";
            response.StatusCode = (int)errorCode;
            //custom status description for when the rate limit has been exceeded
            if (response.StatusCode == 429) {
                response.StatusDescription = "Too Many Requests";
            }
            //this displays our customized error messages instead of IIS's custom errors
            response.TrySkipIisCustomErrors = true;
            response.Write(message);
            response.Flush();
        }
    
        private static string getClientIp(HttpRequest request)
        {
            if (request == null)
                return null;
            string remoteAddr = request.ServerVariables["HTTP_X_FORWARDED_FOR"];
            if (string.IsNullOrWhiteSpace(remoteAddr))
            {
                remoteAddr = request.ServerVariables["REMOTE_ADDR"];
            }
            else
            {
                // the HTTP_X_FORWARDED_FOR may contain an array of IP, this can happen if you connect through a proxy.
                string[] ipRange = remoteAddr.Split(',');
                remoteAddr = ipRange[ipRange.Length - 1];
            }
            return remoteAddr;
        }
    
        private string addTokenToUri(string uri, string token, string tokenParamName) {
            if (!String.IsNullOrEmpty(token))
                uri += uri.Contains("?")? "&" + tokenParamName + "=" + token : "?" + tokenParamName + "=" + token;
            return uri;
        }
    
        private string extractToken(string tokenResponse, string key) {
            string token = getJsonValue(tokenResponse, key);
            if (string.IsNullOrEmpty(token))
                log(TraceLevel.Error," Token cannot be obtained: " + tokenResponse);
            else
                log(TraceLevel.Info," Token obtained: " + token);
            return token;
        }
    
        private string getJsonValue(string text, string key) {
            int i = text.IndexOf(key);
            String value = "";
            if (i > -1) {
                value = text.Substring(text.IndexOf(':', i) + 1).Trim();
    
                value = value.Length > 0 && value[0] == '"' ?
                    // Get the rest of a quoted string
                    value.Substring(1, Math.Max(0, value.IndexOf('"', 1) - 1)) :
                    // Get a string up to the closest comma, bracket, or brace
                    value = value.Substring(0,
                        Math.Min(
                            value.Length,
                            Math.Min(
                                indexOf_HighFlag(value, ","),
                                Math.Min(
                                    indexOf_HighFlag(value, "]"),
                                    indexOf_HighFlag(value, "}")
                                )
                            )
                        )
                    );
            }
            return value;
        }
    
        private int indexOf_HighFlag(string text, string key) {
            int i = text.IndexOf(key);
            if (i < 0) i = Int32.MaxValue;
            return i;
        }
    
        private void cleanUpRatemap(ConcurrentDictionary<string, RateMeter> ratemap) {
            foreach (string key in ratemap.Keys){
                RateMeter rate = ratemap[key];
                if (rate.canBeCleaned())
                    ratemap.TryRemove(key, out rate);
            }
        }
    
    /**
    * Static
    */
        private static ProxyConfig getConfig() {
            ProxyConfig config = ProxyConfig.GetCurrentConfig();
            if (config != null)
                return config;
            else
                throw new ApplicationException("The proxy configuration file cannot be found, or is not readable.");
        }
    
        //writing Log file
        private static void log(TraceLevel logLevel, string msg) {
            string logMessage = string.Format("{0} {1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), msg);
    
            ProxyConfig config = ProxyConfig.GetCurrentConfig();
            TraceSwitch ts = null;
    
            if (config.logLevel != null)
            {
                ts = new TraceSwitch("TraceLevelSwitch2", "TraceSwitch in the proxy.config file", config.logLevel);
            }
            else
            {
                ts = new TraceSwitch("TraceLevelSwitch2", "TraceSwitch in the proxy.config file", "Error");
                config.logLevel = "Error";
            }
    
            Trace.WriteLineIf(logLevel <= ts.Level, logMessage);
        }
    
        private static object _lockobject = new object();
    
    }
    
    class LogTraceListener : TraceListener
    {
        private static object _lockobject = new object();
        public override void Write(string message)
        {
            //Only log messages to disk if logFile has value in configuration, otherwise log nothing.
            ProxyConfig config = ProxyConfig.GetCurrentConfig();
    
            if (config.LogFile != null)
            {
                string log = config.LogFile;
                if (!log.Contains("\\") || log.Contains(".\\"))
                {
                    if (log.Contains(".\\")) //If this type of relative pathing .\log.txt
                    {
                        log = log.Replace(".\\", "");
                    }
                    string configDirectory = HttpContext.Current.Server.MapPath("proxy.config"); //Cannot use System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath b/ config may be in a child directory
                    string path = configDirectory.Replace("proxy.config", "");
                    log = path + log;
                }
    
                lock (_lockobject)
                {
                    using (StreamWriter sw = File.AppendText(log))
                    {
                        sw.Write(message);
                    }
                }
            }
        }
    
    
        public override void WriteLine(string message)
        {
            //Only log messages to disk if logFile has value in configuration, otherwise log nothing.
            ProxyConfig config = ProxyConfig.GetCurrentConfig();
            if (config.LogFile != null)
            {
                string log = config.LogFile;
                if (!log.Contains("\\") || log.Contains(".\\"))
                {
                    if (log.Contains(".\\")) //If this type of relative pathing .\log.txt
                    {
                        log = log.Replace(".\\", "");
                    }
                    string configDirectory = HttpContext.Current.Server.MapPath("proxy.config"); //Cannot use System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath b/ config may be in a child directory
                    string path = configDirectory.Replace("proxy.config", "");
                    log = path + log;
                }
    
                lock (_lockobject)
                {
                    using (StreamWriter sw = File.AppendText(log))
                    {
                        sw.WriteLine(message);
                    }
                }
            }
        }
    
    }
    
    
    [XmlRoot("ProxyConfig")]
    public class ProxyConfig
    {
        private static object _lockobject = new object();
        public static ProxyConfig LoadProxyConfig(string fileName) {
            ProxyConfig config = null;
            lock (_lockobject) {
                if (System.IO.File.Exists(fileName)) {
                    XmlSerializer reader = new XmlSerializer(typeof(ProxyConfig));
                    using (System.IO.StreamReader file = new System.IO.StreamReader(fileName)) {
                        try {
                            config = (ProxyConfig)reader.Deserialize(file);
                        }
                        catch (Exception ex) {
                            throw ex;
                        }
                    }
                }
            }
            return config;
        }
    
        public static ProxyConfig GetCurrentConfig() {
            ProxyConfig config = HttpRuntime.Cache["proxyConfig"] as ProxyConfig;
            if (config == null) {
                string fileName = HttpContext.Current.Server.MapPath("proxy.config");
                config = LoadProxyConfig(fileName);
                if (config != null) {
                    CacheDependency dep = new CacheDependency(fileName);
                    HttpRuntime.Cache.Insert("proxyConfig", config, dep);
                }
            }
            return config;
        }
    
        //referer
        //create an array with valid referers using the allowedReferers String that is defined in the proxy.config
        public static String[] GetAllowedReferersArray()
        {
            if (allowedReferers == null)
                return null;
    
            return allowedReferers.Split(',');
        }
    
        //referer
        //check if URL starts with prefix...
        public static bool isUrlPrefixMatch(String prefix, String uri)
        {
    
            return uri.ToLower().StartsWith(prefix.ToLower()) ||
                        uri.ToLower().Replace("https://", "http://").StartsWith(prefix.ToLower()) ||
                        uri.ToLower().Substring(uri.IndexOf("//")).StartsWith(prefix.ToLower());
        }
    
        ServerUrl[] serverUrls;
        public String logFile;
        public String logLevel;
        bool mustMatch;
        //referer
        static String allowedReferers;
    
        [XmlArray("serverUrls")]
        [XmlArrayItem("serverUrl")]
        public ServerUrl[] ServerUrls {
            get { return this.serverUrls; }
            set
            {
                this.serverUrls = value;
            }
        }
        [XmlAttribute("mustMatch")]
        public bool MustMatch {
            get { return mustMatch; }
            set
            { mustMatch = value; }
        }
    
        //logFile
        [XmlAttribute("logFile")]
        public String LogFile
        {
            get { return logFile; }
            set
            { logFile = value; }
        }
    
        //logLevel
        [XmlAttribute("logLevel")]
        public String LogLevel
        {
            get { return logLevel; }
            set
            { logLevel = value; }
        }
    
    
        //referer
        [XmlAttribute("allowedReferers")]
        public string AllowedReferers
        {
            get { return allowedReferers; }
            set
            {
                allowedReferers = Regex.Replace(value, @"\s", "");
            }
        }
    
        public ServerUrl GetConfigServerUrl(string uri) {
            //split both request and proxy.config urls and compare them
            string[] uriParts = uri.Split(new char[] {'/','?'}, StringSplitOptions.RemoveEmptyEntries);
            string[] configUriParts = new string[] {};
    
            foreach (ServerUrl su in serverUrls) {
                //if a relative path is specified in the proxy.config, append what's in the request itself
                if (!su.Url.StartsWith("http"))
                    su.Url = su.Url.Insert(0, uriParts[0]);
    
                configUriParts = su.Url.Split(new char[] { '/','?' }, StringSplitOptions.RemoveEmptyEntries);
    
                //if the request has less parts than the config, don't allow
                if (configUriParts.Length > uriParts.Length) continue;
    
                int i = 0;
                for (i = 0; i < configUriParts.Length; i++) {
    
                    if (!configUriParts[i].ToLower().Equals(uriParts[i].ToLower())) break;
                }
                if (i == configUriParts.Length) {
                    //if the urls don't match exactly, and the individual matchAll tag is 'false', don't allow
                    if (configUriParts.Length == uriParts.Length || su.MatchAll)
                        return su;
                }
            }
    
            if (!mustMatch)
            {
                return new ServerUrl(uri);
            }
            else
            {
                throw new ArgumentException("Proxy has not been set up for this URL. Make sure there is a serverUrl in the configuration file that matches: " + uri);
            }
        }
    }
    
    public class ServerUrl {
        string url;
        string hostRedirect;
        bool matchAll;
        string oauth2Endpoint;
        string domain;
        bool useAppPoolIdentity;
        string username;
        string password;
        string clientId;
        string clientSecret;
        string accessToken;
        string tokenParamName;
        string rateLimit;
        string rateLimitPeriod;
    
        private ServerUrl()
        {
        }
    
        public ServerUrl(String url)
        {
            this.url = url;
        }
    
        [XmlAttribute("url")]
        public string Url {
            get { return url; }
            set { url = value; }
        }
        [XmlAttribute("hostRedirect")]
        public string HostRedirect
        {
            get { return hostRedirect; }
            set { hostRedirect = value; }
        }
        [XmlAttribute("matchAll")]
        public bool MatchAll {
            get { return matchAll; }
            set { matchAll = value; }
        }
        [XmlAttribute("oauth2Endpoint")]
        public string OAuth2Endpoint {
            get { return oauth2Endpoint; }
            set { oauth2Endpoint = value; }
        }
        [XmlAttribute("domain")]
        public string Domain
        {
            get { return domain; }
            set { domain = value; }
        }
        [XmlAttribute("useAppPoolIdentity")]
        public bool UseAppPoolIdentity
        {
            get { return useAppPoolIdentity; }
            set { useAppPoolIdentity = value; }
        }
        [XmlAttribute("username")]
        public string Username {
            get { return username; }
            set { username = value; }
        }
        [XmlAttribute("password")]
        public string Password {
            get { return password; }
            set { password = value; }
        }
        [XmlAttribute("clientId")]
        public string ClientId {
            get { return clientId; }
            set { clientId = value; }
        }
        [XmlAttribute("clientSecret")]
        public string ClientSecret {
            get { return clientSecret; }
            set { clientSecret = value; }
        }
        [XmlAttribute("accessToken")]
        public string AccessToken {
            get { return accessToken; }
            set { accessToken = value; }
        }
        [XmlAttribute("tokenParamName")]
        public string TokenParamName {
            get { return tokenParamName; }
            set { tokenParamName = value; }
        }
        [XmlAttribute("rateLimit")]
        public int RateLimit {
            get { return string.IsNullOrEmpty(rateLimit)? -1 : int.Parse(rateLimit); }
            set { rateLimit = value.ToString(); }
        }
        [XmlAttribute("rateLimitPeriod")]
        public int RateLimitPeriod {
            get { return string.IsNullOrEmpty(rateLimitPeriod)? 60 : int.Parse(rateLimitPeriod); }
            set { rateLimitPeriod = value.ToString(); }
        }
    }
    View Code

    修改后的proxy.config代码:

    <?xml version="1.0" encoding="utf-8" ?>
    <ProxyConfig allowedReferers="*"
                 mustMatch="true">
        <serverUrls>
            <!-- <serverUrl url="http://services.arcgisonline.com" matchAll="true"/> -->
            <!-- https://portal.valu.com/arcgis/sharing/rest/community/users/test01 -->
            <serverUrl url="https://10.xxx.152/arcgis/sharing/rest" 
                username='test01'
                password='xxx1'
                dynamicToken="true"
                tokenServiceUri="https://10.xxx.152:6443/arcgis/admin/generateToken"
                matchAll='true'/>
            <serverUrl url="https://10.xxx.152:6443/arcgis/sharing/rest" 
                username='test01'
                password='xxx1'
                dynamicToken="true"
                tokenServiceUri="https://10.xxx.152:6443/arcgis/admin/generateToken"
                matchAll='true'/>
            <serverUrl url="https://portal.valu.com/arcgis/sharing/rest" 
                username='test01'
                password='xxx1'
                dynamicToken="true"
                tokenServiceUri="https://10.xxx.152:6443/arcgis/admin/generateToken"
                matchAll='true'/>
            <serverUrl url='https://10.xxx.152:6443/arcgis/rest/services'
                username='test01'
                password='xxx1'
                dynamicToken="true"
                tokenServiceUri="https://10.xxx.152:6443/arcgis/admin/generateToken"
                matchAll='true'/>
        </serverUrls>
    </ProxyConfig>
    
    <!-- See https://github.com/Esri/resource-proxy for more information -->
    View Code

    当然,要是此IIS 有跨域问题,也要设置web.config文件的:

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <system.webServer>
            <handlers>
                <add name="ashx" path="*.ashx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" resourceType="File" preCondition="classicMode,runtimeVersionv4.0,bitness32" />
            </handlers>
            <httpProtocol>
                <customHeaders>
                    <add name="Access-Control-Allow-Methods" value="OPTIONS,POST,GET" />
                    <add name="Access-Control-Allow-Headers" value="x-requested-with,content-type" />
                    <add name="Access-Control-Allow-Origin" value="*" />
                </customHeaders>
            </httpProtocol>
        </system.webServer>
    </configuration>
    View Code

    最终,arcgis portal 没有导入Ssl证书,浏览器可以访问网站,不过还是有

    有谁可以指导一下?

    此记录。2022.2.24

  • 相关阅读:
    Javascript学习笔记3 Javascript与BOM简介
    Javascript学习笔记2.3 Javascript与DOM实现动态表格效果
    Javascript学习笔记2.2 Javascript与DOM选项卡(滑动门)案例详解
    javascript是做什么的
    Javascript学习笔记2.1 Javascript与DOM简介
    Javascript学习笔记1 javascript的特点
    CSS3新增的选择器和属性
    DNSlog实现Mysql注入
    [转]Firefox+Burpsuite抓包配置(可抓取https)
    爬虫初窥day3:BeautifulSoup
  • 原文地址:https://www.cnblogs.com/yansc/p/15931712.html
Copyright © 2020-2023  润新知