在SSO注销之后，默认会跳转回到应用根地址，这个时候有可能由于缓存导致浏览器不会自动跳转到登录页。

这里返回一个随机状态码让浏览器不使用缓存页面。

默认状态码是当前是时间毫秒数，如果不希望增加这个状态码，可以重写这个方法返回一个空值。
336086
protected String getStateQueryParam(HttpServletRequest req, HttpServletResponse resp){
        return String.valueOf(System.currentTimeMillis());
    }
93735
private HttpResponse sendRedirectIfPosible() {
		if (this.maxRedirectCount < 1) {
			// 不重定向
			return null;
		}

		// 手动实现重定向
		if (this.httpConnection.getHttpURLConnection().getInstanceFollowRedirects()) {
			int responseCode;
			try {
				responseCode = httpConnection.responseCode();
			} catch (IOException e) {
				throw new HttpException(e);
			}
			if (responseCode != HttpURLConnection.HTTP_OK) {
				if (responseCode == HttpURLConnection.HTTP_MOVED_TEMP || responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_SEE_OTHER) {
					this.url = httpConnection.header(Header.LOCATION);
					if (redirectCount < this.maxRedirectCount) {
						redirectCount++;
						return execute();
					} else {
						StaticLog.warn("URL [{}] redirect count more than two !", this.url);
					}
				}
			}
		}
		return null;
	}
735368
private void disposeSignInResult(SignInResult result) {
        if (result == null) {
            HMSAgentLog.e("result is null");
            onSignInResult(HMSAgent.AgentResultCode.RESULT_IS_NULL, null);
            return;
        }

        Status status = result.getStatus();
        if (status == null) {
            HMSAgentLog.e("status is null");
            onSignInResult(HMSAgent.AgentResultCode.STATUS_IS_NULL, null);
            return;
        }

        int rstCode = status.getStatusCode();
        HMSAgentLog.d("status=" + status);
        // 需要重试的错误码，并且可以重试
        if ((rstCode == CommonCode.ErrorCode.SESSION_INVALID
                || rstCode == CommonCode.ErrorCode.CLIENT_API_INVALID) && retryTimes > 0) {
            retryTimes--;
            connect();
        } else {
            disposeObtainedResult(result, rstCode);
        }
    }
1085942
private static void sendRequest(String url, byte[] body, String publicKey, ICheckLoginSignHandler callback) {
        HttpURLConnection conn = null;

        OutputStream out = null;
        InputStream is = null;
        InputStreamReader isr = null;
        try {
            URL urlReq = new URL(url);
            conn = (HttpURLConnection)urlReq.openConnection();

            // 设置HttpURLConnection选项
            conn.setRequestMethod("POST");
            conn.setConnectTimeout(CONN_TIMEOUT);
            conn.setReadTimeout(READ_TIMEOUT);
            conn.setUseCaches(false);
            conn.setDoOutput(true);
            conn.setRequestProperty("Content-Type", "application/json");
            conn.setFixedLengthStreamingMode(body.length);

            // 写入body数据
            out = conn.getOutputStream();
            out.write(body);
            out.flush();

            int httpCode = conn.getResponseCode();
            if (httpCode == HttpURLConnection.HTTP_OK)
            {
                StringBuilder sb = new StringBuilder();
                char[] buffer = new char[512];
                is = conn.getInputStream();
                isr = new InputStreamReader(is, "UTF-8");
                int readLen = 0;
                while ((readLen = isr.read(buffer)) != -1) {
                    sb.append(buffer, 0, readLen);
                }

                // 资源释放
                close(out);
                out = null;
                close(is);
                is = null;
                close(isr);
                isr = null;

                // 调用子类解析函数
                String str = sb.toString();
                if (!TextUtils.isEmpty(str)) {
                    callbackResult(str, publicKey, callback);
                } else {
                    callback.onCheckResult(null, "response string is empty!", false);
                }
            } else {
                // 资源释放
                close(out);
                out = null;
                callback.onCheckResult(null, "http request code is " + httpCode, false);
            }
        }catch (Exception e) {
            // 资源释放
            close(out);
            close(is);
            close(isr);

            callback.onCheckResult(null, "http request exception:" + e.getMessage(), false);
        }
    }
383206
public void addFreeConnection(ThriftConnectionHandle<T> thriftConnectionHandle)
			throws ThriftConnectionPoolException {
		thriftConnectionHandle.setOriginatingPartition(this);
		// 更新创建的连接数 创建数 +1
		updateCreatedConnections(1);

		if (!this.freeConnections.offer(thriftConnectionHandle)) {
			// 将连接放入队列失败 创建数 - 1
			updateCreatedConnections(-1);

			// 关闭原始连接
			thriftConnectionHandle.internalClose();
		}
	}
632921
@Override
	public ThriftConnection<T> pollConnection() {
		ThriftConnection<T> result;
		if (pool.getThriftServerCount() == 0) {
			throw new IllegalStateException("当前无可用连接服务器");
		}
		int partition = (int) (Thread.currentThread().getId() % this.pool.thriftServerCount);

		ThriftConnectionPartition<T> thriftConnectionPartition = this.pool.partitions.get(partition);

		result = thriftConnectionPartition.poolFreeConnection();
		if (result == null) {
			for (int i = 0; i < this.pool.thriftServerCount; i++) {
				if (i == partition) {
					continue;
				}
				result = this.pool.partitions.get(i).poolFreeConnection();
				if (result != null) {
					thriftConnectionPartition = this.pool.partitions.get(i);
					break;
				}
			}
		}

		if (!thriftConnectionPartition.isUnableToCreateMoreTransactions()) {
			this.pool.maybeSignalForMoreConnections(thriftConnectionPartition);
		}
		return result;
	}
1177274
private void startConnect() {

        // 触发一次连接将重试次数减1
        curLeftResolveTimes--;

        HMSAgentLog.d("start thread to connect");
        ThreadUtil.INST.excute(new Runnable() {
            @Override
            public void run() {
                HuaweiApiClient client =  getApiClient();

                if (client != null) {
                    HMSAgentLog.d("connect");
                    Activity curActivity = ActivityMgr.INST.getLastActivity();

                    // 考虑到有cp后台需要调用接口，HMSSDK去掉了activity不能为空的判断。这里只是取当前activity，可能为空
                    timeoutHandler.sendEmptyMessageDelayed(APICLIENT_TIMEOUT_HANDLE_MSG, APICLIENT_CONNECT_TIMEOUT);
                    client.connect(curActivity);
                } else {
                    HMSAgentLog.d("client is generate error");
                    onConnectEnd(HMSAgent.AgentResultCode.RESULT_IS_NULL);
                }
            }
        });
    }
482874
private HttpResponse init() throws HttpException {
		try {
			this.status = httpConnection.responseCode();
			this.in = (this.status < HttpStatus.HTTP_BAD_REQUEST) ? httpConnection.getInputStream() : httpConnection.getErrorStream();
		} catch (IOException e) {
			if (e instanceof FileNotFoundException) {
				// 服务器无返回内容，忽略之
			} else {
				throw new HttpException(e);
			}
		}

		try {
			this.headers = httpConnection.headers();
		} catch (IllegalArgumentException e) {
			StaticLog.warn(e, e.getMessage());
		}
		final Charset charset = httpConnection.getCharset();
		this.charsetFromResponse = charset;
		if (null != charset) {
			this.charset = charset;
		}

		if (null == this.in) {
			// 在一些情况下，返回的流为null，此时提供状态码说明
			this.in = new ByteArrayInputStream(StrUtil.format("Error request, response status: {}", this.status).getBytes());
		} else if (isGzip() && false == (in instanceof GZIPInputStream)) {
			try {
				in = new GZIPInputStream(in);
			} catch (IOException e) {
				// 在类似于Head等方法中无body返回，此时GZIPInputStream构造会出现错误，在此忽略此错误读取普通数据
				// ignore
			}
		} else if (isDeflate() && false == (in instanceof DeflaterInputStream)) {
			//Accept-Encoding: defalte
			in = new DeflaterInputStream(in);
		}

		// 同步情况下强制同步
		return this.isAsync ? this : forceSync();
	}
834935
@RequestMapping(value = "/userLogin", method = RequestMethod.POST)
    @ResponseBody
    public JsonObject<?> login(@RequestParam(value = "username", required = true) String username,
                               @RequestParam(value = "password", required = true) String password,
                               @RequestParam(value = "remember", required = true) boolean remember,
                               HttpServletResponse response) {
        JsonObject<?> ret = JsonObject.create();
        if (LoginValidator.isValid(username, password)) {
            CookieUtils.setCookieValue(response, UserWebConstant.CookieKey.USERNAME.getValue(),
                    username, CookieExpireInHour.getExpireInHour(remember));
            CookieUtils.setCookieValue(response, UserWebConstant.CookieKey.PWDMD5.getValue(),
                    password, CookieExpireInHour.getExpireInHour(remember));
            LOG.info("Logon with username=" + username);
            ret.addData("id", UserWebConstant.USERMAP.get(username).getUserId());
            ret.addData("name", username);
        } else {
            ret.setStatus(GlobalResponseStatusMsg.LOGIN_FAILED.getCode());
            ret.addStatusInfo(GlobalResponseStatusMsg.LOGIN_FAILED.getMessage().getMessage());
        }
        return ret;
    }
805671
@Override
    public void onConnect(final int rst, final HuaweiApiClient client) {
        //需要在子线程中执行删除TOKEN操作
        ThreadUtil.INST.excute(new Runnable() {
            @Override
            public void run() {
                //调用删除TOKEN需要传入通过getToken接口获取到TOKEN，并且需要对TOKEN进行非空判断
                if (!TextUtils.isEmpty(token)){
                    if (client == null || !ApiClientMgr.INST.isConnect(client)) {
                        HMSAgentLog.e("client not connted");
                        onDeleteTokenResult(rst);
                    } else {
                        try {
                            HuaweiPush.HuaweiPushApi.deleteToken(client, token);
                            onDeleteTokenResult(HMSAgent.AgentResultCode.HMSAGENT_SUCCESS);
                        } catch (Exception e) {
                            HMSAgentLog.e("删除TOKEN失败:" + e.getMessage());
                            onDeleteTokenResult(HMSAgent.AgentResultCode.CALL_EXCEPTION);
                        }
                    }
                } else {
                    HMSAgentLog.e("删除TOKEN失败: 要删除的token为空");
                    onDeleteTokenResult(HMSAgent.AgentResultCode.EMPTY_PARAM);
                }
            }
        });
    }
732971
function handleServiceError(error) {
    console.error('handleServiceError', error)
    // data 为服务器端返回的报文
    const data = error.data || {}
    const code = +data.code

    // 401 token失效
    if (code === 401) {
        // 当前系统使用的是 HashRouter，所以这里取hash部分
        const href = window.location.hash
        const loginPath = '/#/user/login'

        // 清空token
        appStore.setToken(null)

        // 如果当前不是在登录页，即跳转到登录
        if (href.indexOf(loginPath.substring(1)) === -1) {
            window.location = loginPath
        }
        // 这里返回值会被外层捕获，并且设值为appStore.error
        // 为避免在跳转页面后，弹出错误提示，这里需要返回null
        return null
    }

    // 其它业务异常，由外边处理
    return data
}
84744
@Override
	public synchronized ThriftSocket buildThriftSocket(ClientSocketPoolParameters parameters) throws IkasoaException {
		if (!ServerUtil.checkHostAndPort(parameters.getHost(), parameters.getPort()))
			throw new IllegalArgumentException("Server host or port is null !");
		SimpleSocketPoolImpl self = selfMap.get(parameters.getKey());
		if (self == null || self.socketStatusArray == null)
			self = init(parameters);
		byte i = 0;
		for (; i < size; i++)
			if (!self.socketStatusArray[i]) {
				ThriftSocket thriftSocket = getThriftSocket(self, i, parameters);
				if (!thriftSocket.isOpen()) {
					// 如果socket未连接,则新建一个socket
					// TODO: 用isOpen方法判断是否保持连接并不准确,所以这里有可能会额外创建一些socket
					thriftSocket = parameters.buildClientThriftSocket();
					self.socketPool.put(new Byte(i), thriftSocket);
				}
				self.socketStatusArray[i] = Boolean.TRUE;
				return thriftSocket;
			}
		// 如果连接不够用,就初始化连接池.
		try {
			for (i = 0; i < size; i++) {
				ThriftSocket thriftSocket = self.socketPool.get(i);
				if (self.socketStatusArray[i] && (thriftSocket == null || thriftSocket.getSocket() == null
						|| (thriftSocket.isOpen() && thriftSocket.getSocket().isClosed()))) {
					return parameters.buildClientThriftSocket();
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		log.warn("Not enough pooled connection ! Again retry initiation pool .");
		init(parameters);
		return buildThriftSocket(parameters);
	}
911857
function callBack($from,$to,$customerSerNum,$fromSerNum,$promptTone)
	  {
        //子帐号鉴权信息验证，对必选参数进行判空。
        $auth=$this->subAuth();
        if($auth!=""){
            return $auth;
        }
        // 拼接请求包体
        if($this->BodyType=="json"){
           $body= "{'from':'$from','to':'$to','customerSerNum':'$customerSerNum','fromSerNum':'$fromSerNum','promptTone':'$promptTone'}";
        }else{
           $body= "<CallBack>
                     <from>$from</from>
                     <to>$to</to>
                     <customerSerNum>$customerSerNum</customerSerNum>
                     <fromSerNum>$fromSerNum</fromSerNum>
                     <promptTone>$promptTone</promptTone>
                   </CallBack>";
        }
        $this->showlog("request body = ".$body);
        // 大写的sig参数
        $sig =  strtoupper(md5($this->SubAccountSid . $this->SubAccountToken . $this->Batch));
        // 生成请求URL
        $url="https://$this->ServerIP:$this->ServerPort/$this->SoftVersion/SubAccounts/$this->SubAccountSid/Calls/Callback?sig=$sig";
        $this->showlog("request url = ".$url);
        // 生成授权：子帐号Id + 英文冒号 + 时间戳
        $authen=base64_encode($this->SubAccountSid . ":" . $this->Batch);
        // 生成包头
        $header = array("Accept:application/$this->BodyType","Content-Type:application/$this->BodyType;charset=utf-8","Authorization:$authen");
        // 发请求
        $result = $this->curl_post($url,$body,$header);
        $this->showlog("response body = ".$result);
        if($this->BodyType=="json"){//JSON格式
           $datas=json_decode($result);
        }else{ //xml格式
           $datas = simplexml_load_string(trim($result," \t\n\r"));
        }
      //  if($datas == FALSE){
//            $datas = new stdClass();
//            $datas->statusCode = '172003';
//            $datas->statusMsg = '返回包体错误'; 
//        }
        return $datas;
	}
455530
public boolean onLogin()
    {
        SStaticString sfStaticField = null;
        String strUserName = null;
        String strPassword = null;
        for (int i = 0; i < this.getSFieldCount(); i++)
        {
            ScreenField sField = this.getSField(i);
            if (sfStaticField == null)
                if (sField instanceof SStaticString)
                    sfStaticField = (SStaticString)sField;
            if (sField.getConverter() != null)
                if (DBParams.USER_NAME.equalsIgnoreCase(sField.getConverter().getFieldName()))
                    strUserName = sField.getConverter().getField().toString();
            if (sField.getConverter() != null)
                if (DBParams.PASSWORD.equalsIgnoreCase(sField.getConverter().getFieldName()))
                    strPassword = sField.getConverter().getField().toString();
        }
        if (strUserName == null)
            return false;
        
        App application = this.getTask().getApplication();
        int iErrorCode = application.login(this.getTask(), strUserName, strPassword, this.getProperty(DBParams.DOMAIN));
        
        if (iErrorCode == DBConstants.NORMAL_RETURN)
        {
            String strLastCommand = null;
            BasePanel parent = this.getParentScreen();
            int iUseSameWindow = ScreenConstants.USE_SAME_WINDOW | ScreenConstants.PUSH_TO_BROWSER;
            if (parent != null)
            {
                strLastCommand = parent.popHistory(1, false);
                iUseSameWindow = ScreenConstants.USE_SAME_WINDOW | ScreenConstants.DONT_PUSH_TO_BROWSER;
            }
            else
                strLastCommand = this.getScreenURL();
            
            this.handleCommand(strLastCommand, this, iUseSameWindow);     // Process the last command
        }
        else
        {
            String strError = this.getTask().getLastError(iErrorCode);
            if (strError != null)
                if (sfStaticField != null)
                    sfStaticField.setString(strError);
            try {
                Thread.sleep(3000);     // Wait 3 seconds if bad password
            } catch (InterruptedException ex) {
                ex.printStackTrace();   // Never
            }
        }

        return (iErrorCode == DBConstants.NORMAL_RETURN);    // Handled
    }
1185150
@Local
    public CompletableFuture<Integer> sendAction(final WebSocketAction action, final Serializable... userids) {
        if (userids == null || userids.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
        if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
            return this.localEngine.sendLocalAction(action, userids);
        }
        CompletableFuture<Integer> rsfuture;
        if (userids.length == 1) {
            rsfuture = sendOneUserAction(action, userids[0]);
        } else {
            String[] keys = new String[userids.length];
            final Map<String, Serializable> keyuser = new HashMap<>();
            for (int i = 0; i < userids.length; i++) {
                keys[i] = SOURCE_SNCP_USERID_PREFIX + userids[i];
                keyuser.put(keys[i], userids[i]);
            }
            tryAcquireSemaphore();
            CompletableFuture<Map<String, Collection<InetSocketAddress>>> addrsFuture = sncpNodeAddresses.getCollectionMapAsync(InetSocketAddress.class, keys);
            if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
            rsfuture = addrsFuture.thenCompose((Map<String, Collection<InetSocketAddress>> addrs) -> {
                if (addrs == null || addrs.isEmpty()) {
                    if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userids:" + JsonConvert.root().convertTo(userids) + " on any node ");
                    return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
                }
                Map<InetSocketAddress, List<Serializable>> addrUsers = new HashMap<>();
                addrs.forEach((key, as) -> {
                    for (InetSocketAddress a : as) {
                        addrUsers.computeIfAbsent(a, k -> new ArrayList<>()).add(keyuser.get(key));
                    }
                });
                if (logger.isLoggable(Level.FINEST)) {
                    logger.finest("websocket(localaddr=" + localSncpAddress + ", userids=" + JsonConvert.root().convertTo(userids) + ") found action-userid-addrs: " + addrUsers);
                }
                CompletableFuture<Integer> future = null;
                for (Map.Entry<InetSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
                    Serializable[] us = en.getValue().toArray(new Serializable[en.getValue().size()]);
                    future = future == null ? sendOneAddrAction(en.getKey(), action, us)
                        : future.thenCombine(sendOneAddrAction(en.getKey(), action, us), (a, b) -> a | b);
                }
                return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
            });
        }
        return rsfuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : rsfuture;
    }
908800
protected void initServer(TServerTransport serverTransport) {
		ThriftServerConfiguration configuration = getServerConfiguration();
		// 使用TThreadPoolServer方式启动Thrift服务器,对每个连接都会单独建立一个线程.
		TThreadPoolServer.Args args = new TThreadPoolServer.Args(serverTransport)
				.transportFactory(configuration.getTransportFactory())
				.protocolFactory(configuration.getProtocolFactory());
		// 如果不设置ExecutorService,则默认使用ThreadPoolExecutor实现.
		if (configuration.getExecutorService() != null)
			args.executorService(configuration.getExecutorService());
		server = new TThreadPoolServer(
				configuration.getServerArgsAspect().tThreadPoolServerArgsAspect(args).processor(getProcessor()));
		if (configuration.getServerEventHandler() != null)
			server.setServerEventHandler(configuration.getServerEventHandler());
	}
777325
function sequantial (target, listName) {

  /**
   * 各行に対して関数を呼び出します
   * @method forEach
   * @param  {Function} callback
   */
  target.forEach = function forEach (callback) {
    var list = [].slice.call(this[listName]);
    var len = list.length;
    for(var i = 0; i < len; i++) {
      callback(list[i], i, list);
    }
  };

  /**
   * すべての行に対して、真であるかを調査する
   * @method every
   * @param  {Function} callback
   * @return {Boolean}
   */
  target.every = function every (callback) {
    var list = [].slice.call(this[listName]);
    var len = list.length;
    for(var i = 0; i < len; i++) {
      if (!callback(list[i], i, list)) {
        return false;
      }
    }
    return true;
  };

  /**
   * いずれかの行に対して、真であるかを調査する
   * @method some
   * @param  {Function} callback
   * @return {Boolean}
   */
  target.some = function some (callback) {
    var list = [].slice.call(this[listName]);
    var len = list.length;
    for(var i = 0; i < len; i++) {
      if (callback(list[i], i, list)) {
        return true;
      }
    }
    return false;
  };

  /**
   * callbackを満たす行を配列で返す
   * @method filter
   * @param  {Function} callback
   * @return {Array}
   */
  target.filter = function filter (callback) {
    var list = [].slice.call(this[listName]);
    var len = list.length;
    var result = [];
    for(var i = 0; i < len; i++) {
      if (callback(list[i], i, list)) {
        result.push(list[i]);
      }
    }
    return result;
  };

  /**
   * 各行から新しい配列を作成します
   * @method map
   * @param  {Function} callback
   */
  target.map = function map (callback) {
    var list = [].slice.call(this[listName]);
    var len = list.length;
    var result = [];
    for(var i = 0; i < len; i++) {
      result.push(callback(list[i], i, list));
    }
    return result;
  };

  /**
   * 各行から値を計算します
   * @method reduce
   * @param  {Function} callback
   * @param  {Mixed}    initial
   * @return {Mixed}    result
   */
  target.reduce = function reduce (callback, initial) {
    var list = [].slice.call(this[listName]);
    var len = list.length;
    var value = initial;
    for(var i = 0; i < len; i++) {
      value = callback(value, list[i], i, list);
    }
    return value;
  };

  /**
   * 各行をインデックスの降順で値を計算します
   * @method reduceRight
   * @param  {Function} callback
   * @param  {Mixed}    initial
   * @return {Mixed}    result
   */
  target.reduceRight = function reduceRight (callback, initial) {
    var list = [].slice.call(this[listName]);
    var len = list.length;
    var value = initial;
    for(var i = len - 1; -1 < i; i--) {
      value = callback(value, list[i], i, list);
    }
    return value;
  };

}