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>
修改后的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(); } } }
修改后的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 -->
当然,要是此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>
最终,arcgis portal 没有导入Ssl证书,浏览器可以访问网站,不过还是有
有谁可以指导一下?
此记录。2022.2.24
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律