http相关知识 - (2)状态管理 - session
1. Session原理
(1)Session用于存储特定的用户会话所需的信息。Session对象的引入是为了弥补无状态HTTP协议的不足。
(2)系统为每个用户都设立一个独立的Session,用以存储Session变量,并且各个用户的Session对象互不干扰。
(3)在服务器端有一个session池,用来存储每个用户提交session中的数据,Session对于每一个客户端(或者说浏览器实例)是“人手一份”,用户首次与Web服务器建立连接的时候,服务器会给用户分发一个SessionID作为标识。SessionID是一个由24个字符组成的随机字符串。用户每次提交页面,浏览器都会把这个SessionID包含在HTTP头中提交给Web服务器,这样Web服务器就能区分当前请求页面的是哪一个客户端,而这个SessionID是一cookie的方式保存的在客户端的内存中的,如果想要得到Session池中的数据,服务器就会根据客户端提交的唯一SessionID标识给出相应的数据返回。
(4)Session与Cookie是紧密相关的。 Session的使用要求用户浏览器必须支持Cookie,如果浏览器不支持使用Cookie,或者设置为禁用Cookie,那么将不能使用Session。Session与Cookie是紧密相关的。 Session的使用要求用户浏览器必须支持Cookie,如果浏览器不支持使用Cookie,或者设置为禁用Cookie,那么将不能使用Session。
2. Session配置
<system.web>
<sessionState mode="Off|InProc|StateServer|SQLServer"
cookieless="true|false"
timeout="number of minutes"
stateConnectionString="tcpip=server:port"
sqlConnectionString="sql connection string"
stateNetworkTimeout="number of seconds"/>
</system.web>
mode:设置将Session信息存储到哪里
a. Off:不使用Session功能;
b. InProc :将Session存储在IIS进程内,这是默认值,也最常用(优点是简单,性能最高。但是当重启IIS服务器时Session丢失。);
c. StateServer :将Session存储在ASP.NET状态服务进程中(重新启动Web应用程序时保留会话状态,并使会话状态可以用于网络中的多个Web服务器。);
d. SQLServer :将Session存储在SQL Server中(存储在内存和磁盘中,服务器挂掉重启后都还在)。
a. ture 使用Cookieless模式;这时客户端的Session信息就不再使用Cookie存储了,而是将其通过URL存储。
b. false 使用Cookie模式,这是默认值。
3. Session操作
//写入
Session["UserName"] = "Rod Chen";
//读取
var userName = Session["UserName"].ToString();
Response.Write(userName);
//遍历
IEnumerator sessionEnum = Session.Keys.GetEnumerator();
while (sessionEnum.MoveNext())
{
Response.Write(Session[sessionEnum.Current.ToString()].ToString() + " ");
}
//销毁
Session.Abandon(); //结束会话
Session.Clear();//不结束会话
4. Session问题(C#)
5. 案例(源码:https://github.com/rodchen-king/http/tree/v1.1)
(1)首先是第一个问题(这个SessionId其实对应的会返回到前端一个名为ASP.NET_SessionId的cookie): public ActionResult Index()
{
//页面初始访问就已经产生session。
var sessionId = Session.SessionID;
ViewBag.Message = "修改此模板以快速启动你的 ASP.NET MVC 应用程序。";
return View();
}
d. 上面说的是第一种方式返回这个cookie,现在说一下第二种情况。在Global.asax文件中添加session的方法,这样的话也可以返回cookie值到前端。
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
void Session_Start(object sender, EventArgs e)
{
}
void Session_End(object sender, EventArgs e)
{
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace http_study.Filter
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizationFilter: FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
//因为对于需要登录的application,其实有些界面是不需要进行验证,这里的skipAuthorization就是来判断是否需要进行验证,这里是标注如果访问的action的controller或者action本身有NoFilter 的Attribute,则不需要进行验证。
bool skipAuthorization = filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(NoFilter), true) ||
filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true);
if (!skipAuthorization)
{
string sessionCookie = filterContext.HttpContext.Request.Headers["Cookie"];
//这个地方是判断前端发的请求是否含有ASP.NET_SessionId,如果没有的话则需要跳转到系统的初始界面。
if ((null != sessionCookie) && (sessionCookie.IndexOf("ASP.NET_SessionId") <= 0))
{
if (filterContext.HttpContext.Request.IsAjaxRequest() == true)
{
//如果是ajax发过来的请求,就需要返回error,然后在前端进行页面的跳转,因为ajax的请求是不能进行重定向的。
AjaxRequestLogout(filterContext);
}
else
{
//如果不是ajax的请求,则使用重定向进行页面的跳转。
RouteValueDictionary redirectTargetDictionary = new RouteValueDictionary();
redirectTargetDictionary.Add("action", "Index");
redirectTargetDictionary.Add("controller", "Home");
redirectTargetDictionary.Add("timeout", "true");
filterContext.Result = new RedirectToRouteResult(redirectTargetDictionary);
}
}
else if (filterContext.HttpContext.Session == null || filterContext.HttpContext.Session["UserName"] == null)
{
//这地方是判断server的session有没有过期,如果过期的话进行页面的跳转,也分为两种情况,和上面的一样,这里就不在赘述了。
if (filterContext.HttpContext.Request.IsAjaxRequest() == true)
{
AjaxRequestLogout(filterContext);
}
else
{
RouteValueDictionary redirectTargetDictionary = new RouteValueDictionary();
redirectTargetDictionary.Add("action", "Index");
redirectTargetDictionary.Add("controller", "Home");
redirectTargetDictionary.Add("timeout", "true");
filterContext.Result = new RedirectToRouteResult(redirectTargetDictionary);
}
}
}
}
//如果ajax发过来的请求,需要自己构造response返回exceptino,然后在前端检测返回的error就行页面跳转。
private void AjaxRequestLogout(AuthorizationContext filterContext)
{
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
JsonResult jsonResult = new JsonResult();
jsonResult.Data = new
{
Priority = 1,
Caption = "SessionExpire",
Message = "TimeOut"
};
jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
filterContext.Result = jsonResult;
}
}
}
$(function () {
$(document).ajaxError(
function (event, xhr, options, exc) {
if (xhr.status == 500 && JSON.parse(xhr.responseText).Caption == "SessionExpire") {
window.location = '/Home/Index';
}
}
);
});
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace http_study
{
// 注意: 有关启用 IIS6 或 IIS7 经典模式的说明,
// 请访问 http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
void Session_Start(object sender, EventArgs e)
{
var sessionValue = HttpContext.Current.Session;
}
void Session_End(object sender, EventArgs e)
{
var sessionValue = HttpContext.Current.Session;
}
}
}
上面有两个方法,Session_Star, Session_End方法,字面意思大家也知道是做什么用的了吧。在介绍执行之前想给session的过期时间设置的短一点,最上面有介绍过session在web configure的配置,这里我们把过期时间改为一分钟。
<sessionState mode="InProc"
cookieless="false"
timeout="1"
stateConnectionString="tcpip=server:port"
sqlConnectionString="sql connection string"
stateNetworkTimeout="10"
/>
a. 然后启动我们项目在上面的两个方法里加上断点,我们去看一下其中的值。此时是执行到了Session_star,因为server对用户的访问产生一个唯一标识的id。
HttpContext :获取或设置与当前线程相关联的主机上下文。二个关键词:【当前线程】,【关联】。HttpContext.Current获取到与【当前请求】相关的HttpContext对象。
所以当我们的session过期的时候,这个针对当前用户这个线程都已经没有了,所以用户需要重新访问去拿新的session,用户如果是登录之后的,就需要我们写相关session过期的处理,让用户跳出整个系统。
6. cookie与session的区别
2.session其实指的就是访问者从到达某个特定主页到离开为止的那段时间。 Session其实是利用Cookie进行信息处理的,当用户首先进行了请求后,服务端就在用户浏览器上创建了一个Cookie,当这个Session结束时,其实就是意味着这个Cookie就过期了。
注:为这个用户创建的Cookie的名称是aspsessionid。这个Cookie的唯一目的就是为每一个用户提供不同的身份认证。
3.cookie 和session的区别是:cookie数据保存在客户端,session数据保存在服务器端。
4.cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗
考虑到安全应当使用session。
5.session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
考虑到减轻服务器性能方面,应当使用COOKIE。
6.单个cookie保存的数据不能超过4K(本人没有测过,有知道怎么测的可以去测一下),很多浏览器都限制一个站点最多保存20个cookie。