asp.net session

什么是localstorage

前几天在老项目中发现有对cookie的操作觉得很奇怪,咨询下来是要缓存一些信息,以避免在URL上面传递参数,但没有考虑过cookie会带来什么问题:

① cookie大小限制在4k左右,不适合存业务数据
② cookie每次随HTTP事务一起发送,浪费带宽

我们是做移动项目的,所以这里真实适合使用的技术是localstorage,localstorage可以说是对cookie的优化,使用它可以方便在客户端存储数据,并且不会随着HTTP传输,但也不是没有问题:

① localstorage大小限制在500万字符左右,各个浏览器不一致
② localstorage在隐私模式下不可读取
③ localstorage本质是在读写文件,数据多的话会比较卡(firefox会一次性将数据导入内存,想想就觉得吓人啊)
④ localstorage不能被爬虫爬取,不要用它完全取代URL传参

瑕不掩瑜,以上问题皆可避免,所以我们的关注点应该放在如何使用localstorage上,并且是如何正确使用。

cookie会被用户改,怎么解决?用Session来解决
弄一个sessionId

简介编辑

session对象用于存储特定的用户会话所需的信息 。
Session对象的引入是为了弥补HTTP协议的不足,HTTP协议是一种无状态的协议。

Session中文是“会话”的意思,在ASP中代表了服务器与客户端之间的“会话”。Session的作用时间从用户到达某个特定的Web页开始,到该用户离开Web站点,或在程序中利用代码终止某个Session结束。引用Session
则可以让一个用户访问多个页面之间的切换也会保留该用户的信息。

系统为每个访问者都设立一个独立的Session对象,用以存储Session变量,并且各个访问者的Session对象互不干扰。

Session与Cookie是紧密相关的。
Session的使用要求用户浏览器必须支持Cookie,如果浏览器不支持使用Cookie,或者设置为禁用Cookie,那么将不能使用Session。

Session信息对客户来说,不同的用户用不同的Session信息来记录。当用户启用Session时,ASP自动产生一个SessionID.在新会话开始时,服务器将SessionID当做cookie存储在用户的浏览器中。

 

localstorage的使用

奥门新浦京官方网站 1

运用编辑

Session对象的集合、属性、方法、事件

名称
描述
Contents集合
包含所有通过脚本命令添加到应用程序中的数据项
SessionID属性
用来标识每一个Session对象
TimeOut属性
用来设置Session会话的超时时间(以分钟表示)
Abandon方法
强行删除当前会话的Session对象,释放系统资源
Session_OnStart事件
建立Session对象时所激发的事件
Session_OnEnd事件
结束Session对象时所激发的事件
StaticObjects集合
包含所有通过<OBJECT>标记添加到应用程序中的对象

 

Session简介丶特性

1.Session是一种Web会话中的常用状态之一。

2.Session提供了一种把信息保存在服务器内存中的方式。他能储存任何数据类型,包含自定义对象。

3.每个客户端的Seesion是独立存储的。

奥门新浦京官方网站,4.在整个会话过程中,只要SessionID的cookie不丢失,都会保存Session信息的。

5.Session不能跨进程访问,只能由该会话的用户访问。应为提取Session数据的id标识是以Cookie的方式保存到访问者浏览器的缓存里的。

6.当会话终止,或过期时,服务器就清除Session对象。

7.Session常用于保存登录用户的ID.

8.Session保存的数据是跨页面全局型的

 

 

Session原理(根据上面例子阐述)


 

一、session是怎么存储,提取的?

1.在服务器端有一个session池,用来存储每个用户提交session中的数据,Session对于每一个客户端(或者说浏览器实例)是“人手一份”,用户首次与Web服务器建立连接的时候,服务器会给用户分发一个SessionID作为标识。SessionID是一个由24个字符组成的随机字符串。用户每次提交页面,浏览器都会把这个SessionID包含在HTTP头中提交给Web服务器,这样Web服务器就能区分当前请求页面的是哪一个客户端,而这个SessionID是一cookie的方式保存的在客户端的内存中的,如果想要得到Session池中的数据,服务器就会根据客户端提交的唯一SessionID标识给出相应的数据返回。

2.输入正确的账号密码,点击登录,页面就会输出
 “admin — 点击登录”

二、Session池中每个客户端的数据是怎么存储的?

1.存储在Session池中的数据是全局型的数据,可以跨页面访问,每个SessionID中只存储唯一的数据,如:首先你这样设定:session[“userName”]=”admin”,然后你在会话还没结束的session还没过期的情况下,你又设定:session[“userName”]=”123″;这样这个SessionID没变,然而Session池中的数据则被覆盖。此时session[“userName”]的值就是“123”,而不是其它。

2.Session池中的数据不能跨进程访问。如:打开login.aspx页面写入session[“userName”]=”admin”;然后login页面不关闭,即此会话不结束,在这是你再在另外一个浏览器中打开一个login.aspx页面则session[“userName”]=null

3.输入账号密码,点击登录页面输出  “admin — 点击登录”
,如果紧接着点击获取session按钮,则页面只输出”admin—
点击获取session”,如果页面不关闭,打开另外一个浏览器,点击获取session按钮,则页面没法应。

三丶session的声明周期与销毁

1.session存储数据计时是滚动计时方式。具体是这样的,如果你打开写入session,从写入开始,此页面如果一直没有提交操作,则默认时间是20分钟,20分钟后session被服务器自动销毁,如过有提交操作,服务器会从提交后重新计时以此类推,直至设定时间内销毁。

2.可以设置session的销毁时间。上面代码有提到。

四丶session中保存的数据是在服务端的,而每个用户如进行登录操作,都要进行session数据写入,所以建议慎用session,就是少用。

基础知识

localstorage存储对象分为两种:

① sessionStrage:
session即会话的意思,在这里的session是指用户浏览某个网站时,从进入网站到关闭网站这个时间段,session对象的有效期就只有这么长。

② localStorage:
将数据保存在客户端硬件设备上,不管它是什么,意思就是下次打开计算机时候数据还在。

两者区别就是一个作为临时保存,一个长期保存。

这里来一段简单的代码说明其基本使用:

<div id="msg" style="margin: 10px 0; border: 1px solid black; padding: 10px; width: 300px;
  height: 100px;">
</div>
<input type="text" id="text" />
<select id="type">
  <option value="session">sessionStorage</option>
  <option value="local">localStorage</option>
</select>
<button onclick="save();">
  保存数据</button>
<button onclick="load();">
  读取数据</button>
<script type="text/javascript">
  var msg = document.getElementById('msg'),
            text = document.getElementById('text'),
            type = document.getElementById('type');

  function save() {
    var str = text.value;
    var t = type.value;
    if (t == 'session') {
      sessionStorage.setItem('msg', str);
    } else {
      localStorage.setItem('msg', str);
    }
  }

  function load() {
    var t = type.value;
    if (t == 'session') {
      msg.innerHTML = sessionStorage.getItem('msg');
    } else {
      msg.innerHTML = localStorage.getItem('msg');
    }
  }
</script>

image.png

真实场景

实际工作中对localstorage的使用一般有以下需求:

① 缓存一般信息,如搜索页的出发城市,达到城市,非实时定位信息

② 缓存城市列表数据,这个数据往往比较大


每条缓存信息需要可追踪,比如服务器通知城市数据更新,这个时候在最近一次访问的时候要自动设置过期

④ 每条信息具有过期日期状态,在过期外时间需要由服务器拉取数据

⑤ ……

define([], function () {

  var Storage = _.inherit({
    //默认属性
    propertys: function () {

      //代理对象,默认为localstorage
      this.sProxy = window.localStorage;

      //60 * 60 * 24 * 30 * 1000 ms ==30天
      this.defaultLifeTime = 2592000000;

      //本地缓存用以存放所有localstorage键值与过期日期的映射
      this.keyCache = 'SYSTEM_KEY_TIMEOUT_MAP';

      //当缓存容量已满,每次删除的缓存数
      this.removeNum = 5;

    },

    assert: function () {
      if (this.sProxy === null) {
        throw 'not override sProxy property';
      }
    },

    initialize: function (opts) {
      this.propertys();
      this.assert();
    },

    /*
    新增localstorage
    数据格式包括唯一键值,json字符串,过期日期,存入日期
    sign 为格式化后的请求参数,用于同一请求不同参数时候返回新数据,比如列表为北京的城市,后切换为上海,会判断tag不同而更新缓存数据,tag相当于签名
    每一键值只会缓存一条信息
    */
    set: function (key, value, timeout, sign) {
      var _d = new Date();
      //存入日期
      var indate = _d.getTime();

      //最终保存的数据
      var entity = null;

      if (!timeout) {
        _d.setTime(_d.getTime() + this.defaultLifeTime);
        timeout = _d.getTime();
      }

      //
      this.setKeyCache(key, timeout);
      entity = this.buildStorageObj(value, indate, timeout, sign);

      try {
        this.sProxy.setItem(key, JSON.stringify(entity));
        return true;
      } catch (e) {
        //localstorage写满时,全清掉
        if (e.name == 'QuotaExceededError') {
          //            this.sProxy.clear();
          //localstorage写满时,选择离过期时间最近的数据删除,这样也会有些影响,但是感觉比全清除好些,如果缓存过多,此过程比较耗时,100ms以内
          if (!this.removeLastCache()) throw '本次数据存储量过大';
          this.set(key, value, timeout, sign);
        }
        console && console.log(e);
      }
      return false;
    },

    //删除过期缓存
    removeOverdueCache: function () {
      var tmpObj = null, i, len;

      var now = new Date().getTime();
      //取出键值对
      var cacheStr = this.sProxy.getItem(this.keyCache);
      var cacheMap = [];
      var newMap = [];
      if (!cacheStr) {
        return;
      }

      cacheMap = JSON.parse(cacheStr);

      for (i = 0, len = cacheMap.length; i < len; i++) {
        tmpObj = cacheMap[i];
        if (tmpObj.timeout < now) {
          this.sProxy.removeItem(tmpObj.key);
        } else {
          newMap.push(tmpObj);
        }
      }
      this.sProxy.setItem(this.keyCache, JSON.stringify(newMap));

    },

    removeLastCache: function () {
      var i, len;
      var num = this.removeNum || 5;

      //取出键值对
      var cacheStr = this.sProxy.getItem(this.keyCache);
      var cacheMap = [];
      var delMap = [];

      //说明本次存储过大
      if (!cacheStr) return false;

      cacheMap.sort(function (a, b) {
        return a.timeout - b.timeout;
      });

      //删除了哪些数据
      delMap = cacheMap.splice(0, num);
      for (i = 0, len = delMap.length; i < len; i++) {
        this.sProxy.removeItem(delMap[i].key);
      }

      this.sProxy.setItem(this.keyCache, JSON.stringify(cacheMap));
      return true;
    },

    setKeyCache: function (key, timeout) {
      if (!key || !timeout || timeout < new Date().getTime()) return;
      var i, len, tmpObj;

      //获取当前已经缓存的键值字符串
      var oldstr = this.sProxy.getItem(this.keyCache);
      var oldMap = [];
      //当前key是否已经存在
      var flag = false;
      var obj = {};
      obj.key = key;
      obj.timeout = timeout;

      if (oldstr) {
        oldMap = JSON.parse(oldstr);
        if (!_.isArray(oldMap)) oldMap = [];
      }

      for (i = 0, len = oldMap.length; i < len; i++) {
        tmpObj = oldMap[i];
        if (tmpObj.key == key) {
          oldMap[i] = obj;
          flag = true;
          break;
        }
      }
      if (!flag) oldMap.push(obj);
      //最后将新数组放到缓存中
      this.sProxy.setItem(this.keyCache, JSON.stringify(oldMap));

    },

    buildStorageObj: function (value, indate, timeout, sign) {
      var obj = {
        value: value,
        timeout: timeout,
        sign: sign,
        indate: indate
      };
      return obj;
    },

    get: function (key, sign) {
      var result, now = new Date().getTime();
      try {
        result = this.sProxy.getItem(key);
        if (!result) return null;
        result = JSON.parse(result);

        //数据过期
        if (result.timeout < now) return null;

        //需要验证签名
        if (sign) {
          if (sign === result.sign)
            return result.value;
          return null;
        } else {
          return result.value;
        }

      } catch (e) {
        console && console.log(e);
      }
      return null;
    },

    //获取签名
    getSign: function (key) {
      var result, sign = null;
      try {
        result = this.sProxy.getItem(key);
        if (result) {
          result = JSON.parse(result);
          sign = result && result.sign
        }
      } catch (e) {
        console && console.log(e);
      }
      return sign;
    },

    remove: function (key) {
      return this.sProxy.removeItem(key);
    },

    clear: function () {
      this.sProxy.clear();
    }
  });

  Storage.getInstance = function () {
    if (this.instance) {
      return this.instance;
    } else {
      return this.instance = new this();
    }
  };

  return Storage;

});

这段代码包含了localstorage的基本操作,并且对以上问题做了处理,而真实的使用还要再抽象:

define(['AbstractStorage'], function (AbstractStorage) {

  var Store = _.inherit({
    //默认属性
    propertys: function () {

      //每个对象一定要具有存储键,并且不能重复
      this.key = null;

      //默认一条数据的生命周期,S为秒,M为分,D为天
      this.lifeTime = '30M';

      //默认返回数据
      //      this.defaultData = null;

      //代理对象,localstorage对象
      this.sProxy = new AbstractStorage();

    },

    setOption: function (options) {
      _.extend(this, options);
    },

    assert: function () {
      if (this.key === null) {
        throw 'not override key property';
      }
      if (this.sProxy === null) {
        throw 'not override sProxy property';
      }
    },

    initialize: function (opts) {
      this.propertys();
      this.setOption(opts);
      this.assert();
    },

    _getLifeTime: function () {
      var timeout = 0;
      var str = this.lifeTime;
      var unit = str.charAt(str.length - 1);
      var num = str.substring(0, str.length - 1);
      var Map = {
        D: 86400,
        H: 3600,
        M: 60,
        S: 1
      };
      if (typeof unit == 'string') {
        unit = unit.toUpperCase();
      }
      timeout = num;
      if (unit) timeout = Map[unit];

      //单位为毫秒
      return num * timeout * 1000 ;
    },

    //缓存数据
    set: function (value, sign) {
      //获取过期时间
      var timeout = new Date();
      timeout.setTime(timeout.getTime() + this._getLifeTime());
      this.sProxy.set(this.key, value, timeout.getTime(), sign);
    },

    //设置单个属性
    setAttr: function (name, value, sign) {
      var key, obj;
      if (_.isObject(name)) {
        for (key in name) {
          if (name.hasOwnProperty(key)) this.setAttr(k, name[k], value);
        }
        return;
      }

      if (!sign) sign = this.getSign();

      //获取当前对象
      obj = this.get(sign) || {};
      if (!obj) return;
      obj[name] = value;
      this.set(obj, sign);

    },

    getSign: function () {
      return this.sProxy.getSign(this.key);
    },

    remove: function () {
      this.sProxy.remove(this.key);
    },

    removeAttr: function (attrName) {
      var obj = this.get() || {};
      if (obj[attrName]) {
        delete obj[attrName];
      }
      this.set(obj);
    },

    get: function (sign) {
      var result = [], isEmpty = true, a;
      var obj = this.sProxy.get(this.key, sign);
      var type = typeof obj;
      var o = { 'string': true, 'number': true, 'boolean': true };
      if (o[type]) return obj;

      if (_.isArray(obj)) {
        for (var i = 0, len = obj.length; i < len; i++) {
          result[i] = obj[i];
        }
      } else if (_.isObject(obj)) {
        result = obj;
      }

      for (a in result) {
        isEmpty = false;
        break;
      }
      return !isEmpty ? result : null;
    },

    getAttr: function (attrName, tag) {
      var obj = this.get(tag);
      var attrVal = null;
      if (obj) {
        attrVal = obj[attrName];
      }
      return attrVal;
    }

  });

  Store.getInstance = function () {
    if (this.instance) {
      return this.instance;
    } else {
      return this.instance = new this();
    }
  };

  return Store;
});

我们真实使用的时候是使用store这个类操作localstorage,代码结束简单测试:

奥门新浦京官方网站 2

奥门新浦京官方网站 3

存储完成,以后都不会走请求,于是今天的代码基本结束 ,最后在android
Hybrid中有一后退按钮,此按钮一旦按下会回到上一个页面,这个时候里面的localstorage可能会读取失效!一个简单不靠谱的解决方案是在webapp中加入:

window.onunload = function () { };//适合单页应用,不要问我为什么,我也不知道

奥门新浦京官方网站 4

结语

localstorage是移动开发必不可少的技术点,需要深入了解,具体业务代码后续会放到git上,有兴趣的朋友可以去了解

image.png

奥门新浦京官方网站 5

image.png

奥门新浦京官方网站 6

image.png

奥门新浦京官方网站 7

image.png

这就是sessions

服务器通过cookie给用户一个sessionId
sessionId对应服务器内一小块内存,每次用户访问服务器的时候,服务器就通过sessionId去读取对应的session,然后知道用户的隐私信息

这里的这个安全是通过随机数来保证的

cookie
1、服务器通过Set-Cookie响应头设置cookie(一段字符串)
2、客户每次访问相同域名的页面时,必须带上这段字符串
3、客户端要在一段时间内保存这个cookie
4、默认在用户关闭页面的时候就失效,后台代码可以任意的设置cookie的过期时间

Session 是服务器上的hash 缺点:占内存
1、将sessionId(随机数)通过cookie发给客户端
2、客户端访问服务器时,服务器读取sessionId
3、服务器有一块内存(哈希表)保存了所有session
4、通过sessionId我们可以得到对应用户的隐私信息,如ID、email
5、这块内存(哈希表)就是服务器上的所有session

奥门新浦京官方网站 8

image.png

接下里localstorage 是HTML5技术提供的一个API

奥门新浦京官方网站 9

image.png

localstorage实质是个hash,是浏览器上的hash

奥门新浦京官方网站 10

image.png

奥门新浦京官方网站 11

image.png

奥门新浦京官方网站 12

image.png

只能存string

奥门新浦京官方网站 13

image.png

奥门新浦京官方网站 14

image.png

奥门新浦京官方网站 15

image.png

奥门新浦京官方网站 16

image.png

接下来localstorage的用法
比如我在一个页面里写了var
a=1并打印,我在控制台改下a的值为2,刷新页面,之前的a不存在了,现在是新的a,a仍然为1,那如何让一个变量在第一个页面的时候存在,到第二个页面的时候还是之前那个值呢?
通过localstorage
对于windows用户来说,localstorage存在C盘的一个文件里

奥门新浦京官方网站 17

image.png

奥门新浦京官方网站 18

image.png

奥门新浦京官方网站 19

image.png

奥门新浦京官方网站 20

image.png

过程

实际应用场景:
比如某网址要改版了,进入页面提示“我们网址改版了”,再刷新继续提示,再刷新,继续提示,这个就很不友好,我们把这个不要存在变量里,存在localstorage里

奥门新浦京官方网站 21

image.png

奥门新浦京官方网站 22

image.png

localstorage
1、localstorage与http无关
2、http不会带上localstorage的值
3、只有相同域名的页面才能互相读取localstorage(没有同源那么严格)
4、每个域名localstorage的最大存储量5Mb左右(每个浏览器不一样)
5、常用场景:记录有没有提示过用户(没有用的信息,不能记录密码)

6、localstorage永久有效(理论上),除非用户清理缓存(ctrl+shift+del,

奥门新浦京官方网站 23

image.png

)及其他网站数据就包括localstorage

sessionstorage(会话存储)
1、2、3、4同上,

5、sessionstorage在用户关闭页面(会话结束)之后就失效

奥门新浦京官方网站 24

image.png

奥门新浦京官方网站 25

image.png

一般来说,session是基于cookie实现的,session是依赖于cookie的,cookie是session的基石
cookie一般是4K,localstorage是5M

不基于cookie的session,通过查询参数和localstorage来存储id

奥门新浦京官方网站 26

image.png

到登录页

奥门新浦京官方网站 27

image.png

然后到server.js里的路由

奥门新浦京官方网站 28

image.png

然后到首页

奥门新浦京官方网站 29

image.png

奥门新浦京官方网站 30

image.png

为什么会有把cookie和localstorage放在一起比较?
因为localstorage是新的API,之前前端如何做跨页面的持久化存储呢?只能通过cookie,cookie也是存在C盘的,之前的前端经常把数据存到cookie上,用来跨页面调用,cookie上的数据都会随着请求被带到服务器上去,如果数据很大,那么请求会很慢

前端要写cookie吗?千万不要,后端写后端读

接下来是http缓存
cache-control
加2个路由

奥门新浦京官方网站 31

image.png

奥门新浦京官方网站 32

image.png

把cdn上的bootstrap.css拷贝到本地

奥门新浦京官方网站 33

image.png

来找一个比较大的js

奥门新浦京官方网站 34

image.png

奥门新浦京官方网站 35

image.png

有没有办法让其变快呢?

奥门新浦京官方网站 36

image.png

奥门新浦京官方网站 37

image.png

奥门新浦京官方网站 38

image.png

奥门新浦京官方网站 39

image.png

整个过程

奥门新浦京官方网站 40

image.png

问题:为什么不能在首页设置缓存?

奥门新浦京官方网站 41

image.png

就是路由是/的
因为如果你连首页都设置缓存(比如30天),当你的页面有更新的时候,客户是不知道你最新的页面的,客户打开的还是你缓存里的

如果max-age设置的比较长,那如果在这期间,服务器需要更新怎么办?
只需要让URL稍微的不一样即可(因为如果是同样的URL,浏览器不需要向服务器发请求,直接使用缓存)
比如我deault.css变了点,那么我URL加查询参数即可,但是还是访问的是同样的css

奥门新浦京官方网站 42

image.png

奥门新浦京官方网站 43

image.png

在cache-control之前是用Expires来加缓存
格式如下:

奥门新浦京官方网站 44

image.png

右边是格林威治时间格式,

奥门新浦京官方网站 45

image.png

Expires表示具体到什么时候过期,指的是本地时间,如果用户本地时间错乱了,那就有问题了
如果2个同时使用,则优先使用cache-control

接下来是last-modified

ETag是为了给文件一个版本号的

md5是一个摘要算法
假设你下载一个文件,下载过程中,你怎么知道自己下的对不对呢?万一少下载一个字节怎么办
md5就是解决这个的,假设这个文件算了一个MD5,你下载完了之后也给文件算一个MD5值,2个值一比较,理论应该一样

内容差异越小,md5算法算出的结果就越大
先安装md5

奥门新浦京官方网站 46

image.png

https://www.npmjs.com/package/md5
然后开始用

奥门新浦京官方网站 47

image.png

奥门新浦京官方网站 48

image.png

设置响应头

奥门新浦京官方网站 49

image.png

页面刷新下

奥门新浦京官方网站 50

image.png

请求头了多了一个if -None-Match,值和上面的响应头里的ETag一样
如果新请求的版本和之前一样,那么就不需要下载

奥门新浦京官方网站 51

image.png

奥门新浦京官方网站 52

image.png

很小,因为不需要下载

ETag是发请求,但是如果md5值一样,则不需要下载内容
cache-control是根本就不发请求,直接用缓存里的

发表评论

电子邮件地址不会被公开。 必填项已用*标注