在这篇文章中,我们将为您详细介绍asp.net-mvc–为什么Asp.netMVC4不能使用SQLServer会话状态存储的cookieless的内容。此外,我们还会涉及一些关于ASP.NETMVC4
在这篇文章中,我们将为您详细介绍asp.net-mvc – 为什么Asp.net MVC4不能使用SQL Server会话状态存储的cookieless的内容。此外,我们还会涉及一些关于ASP.NET MVC4会话状态存储用户名、asp.net mvc4开启SqlServer 会话共享模式、asp.net-core-mvc – 如何在Asp.net Core MVC(又名Asp.Net 5 RC1)中检查响应cookie?、asp.net-mvc – .NET MVC4 Facebook,Google和Twitter登录的知识,以帮助您更全面地了解这个主题。
本文目录一览:- asp.net-mvc – 为什么Asp.net MVC4不能使用SQL Server会话状态存储的cookieless
- ASP.NET MVC4会话状态存储用户名
- asp.net mvc4开启SqlServer 会话共享模式
- asp.net-core-mvc – 如何在Asp.net Core MVC(又名Asp.Net 5 RC1)中检查响应cookie?
- asp.net-mvc – .NET MVC4 Facebook,Google和Twitter登录
asp.net-mvc – 为什么Asp.net MVC4不能使用SQL Server会话状态存储的cookieless
<configuration> <configSections> <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup,System.Web.WebPages.Razor,Version=2.0.0.0,Culture=neutral,PublicKeyToken=31BF3856AD364E35"> <section name="webAssets" type="Telerik.Web.Mvc.Configuration.WebAssetConfigurationSection,Telerik.Web.Mvc" requirePermission="false" /> <!-- For more information on Entity Framework configuration,visit http://go.microsoft.com/fwlink/?LinkID=237468 --> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection,EntityFramework,Version=4.4.0.0,PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </sectionGroup> </configSections> <connectionStrings> <add name="..." connectionString="..." providerName="System.Data.sqlClient" /> </connectionStrings> <appSettings> <add key="webpages:Version" value="2.0.0.0" /> <add key="webpages:Enabled" value="true" /> <add key="PreserveLoginUrl" value="true" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" /> </appSettings> <system.web> <customErrors mode="Off" /> <httpRuntime executionTimeout="14400" maxRequestLength="716800" /> <compilation debug="true" targetFramework="4.0" /> <authentication mode="Forms"> <forms loginUrl="~/Account/Login" timeout="2880" /> </authentication> <pages> <namespaces> <add namespace="System.Web.Helpers" /> <add namespace="System.Web.Mvc" /> <add namespace="System.Web.Mvc.Ajax" /> <add namespace="System.Web.Mvc.Html" /> <add namespace="System.Web.Routing" /> <add namespace="System.Web.WebPages" /> <add namespace="Telerik.Web.Mvc.UI" /> </namespaces> </pages> <profile defaultProvider="DefaultProfileProvider"> <providers> <add name="DefaultProfileProvider" type="System.Web.Providers.DefaultProfileProvider,System.Web.Providers,Version=1.0.0.0,PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" /> </providers> </profile> <membership defaultProvider="DefaultMembershipProvider"> <providers> <add name="DefaultMembershipProvider" type="System.Web.Providers.DefaultMembershipProvider,PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minrequiredPasswordLength="6" minrequiredNonalphanumericCharacters="0" passwordAttemptwindow="10" applicationName="/" /> </providers> </membership> <roleManager defaultProvider="DefaultRoleProvider"> <providers> <add name="DefaultRoleProvider" type="System.Web.Providers.DefaultRoleProvider,PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" /> </providers> </roleManager> <!-- <sessionState mode="InProc" timeout="30" customProvider="DefaultSessionProvider"> <providers> <add name="DefaultSessionProvider" type="System.Web.Providers.DefaultSessionStateProvider,PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" /> </providers> </sessionState> --> <sessionState mode="sqlServer" cookieless="true" sqlConnectionString="data source=xxxx; User ID=xxxx; password=xxxx" timeout="300" sqlCommandTimeout="10" /> <httpHandlers> <add verb="GET,HEAD" path="asset.axd" validate="false" type="Telerik.Web.Mvc.WebAssetHttpHandler,Telerik.Web.Mvc" /> </httpHandlers> </system.web> <system.webServer> <security> <requestfiltering> <requestLimits maxAllowedContentLength="3000000000" /> </requestfiltering> </security> <validation validateIntegratedModeConfiguration="false" /> <modules runAllManagedModulesForAllRequests="true" /> <directorybrowse enabled="true" /> <handlers> <remove name="asset" /> <add name="asset" preCondition="integratedMode" verb="GET,HEAD" path="asset.axd" type="Telerik.Web.Mvc.WebAssetHttpHandler,Telerik.Web.Mvc" /> </handlers> </system.webServer> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" /> <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" /> <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" /> <bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Microsoft.WindowsAzure.StorageClient" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-1.7.0.0" newVersion="1.7.0.0" /> </dependentAssembly> </assemblyBinding> </runtime> </configuration>
解决方法
<form action="/(S(kkt0zgbnuaoxad23ew33iod4))/home/index" method="post">
它产生:
<form action="/home/index" method="post">
当您发布到/ home / index时,ASP.NET会自动对/(S(kkt0zgbnuaoxad23ew33iod4))进行重定向.重定向意味着GET请求=>你的POST动作永远不会被击中.
作为一种解决方法,您可以编写自定义的Html.BeginForm帮助程序来修复该错误:
public static class FormExtensions { public static Idisposable MyBeginForm(this HtmlHelper htmlHelper) { var rawUrl = htmlHelper.ViewContext.HttpContext.Request.RawUrl; var formAction = htmlHelper.ViewContext.HttpContext.Response.ApplyAppPathModifier("~/") + rawUrl; var builder = new TagBuilder("form"); builder.MergeAttributes(new RouteValueDictionary()); builder.MergeAttribute("action",formAction); builder.MergeAttribute("method",HtmlHelper.GetFormMethodString(FormMethod.Post),true); htmlHelper.ViewContext.Writer.Write(builder.ToString(TagRenderMode.StartTag)); var form = new MvcForm(htmlHelper.ViewContext); return form; } }
然后使用:
@using (Html.MyBeginForm()) { ... }
就BeginForm帮助器的其他重载而言,它们应该正常工作并生成包含会话ID的适当操作.
ASP.NET MVC4会话状态存储用户名
用户登录/注册后,我将其用户名存储在会话中.我从会话中读取此属性以确定用户是否已登录.
这是一种不好的做法吗?我读了很多关于RESTful和无状态webapps的内容.我觉得我不应该在会话中保存任何东西.
例如
@if (string.IsNullOrEmpty(Session["User"] as string)) { <dl> <dt><a href="/Account/Register">Register</a></dt> <dt><a href="/Account/Login">Login</a></dt> </dl> } else { <dl> <dt><a href="/Account/ShowAccount/@Session["User"]">@Session["User"]</a></dt> <dt><a href="/Account/logout">Log out</a></dt> </dl> }
Q1:这是一个不好的做法吗?
Q2:这是“黑客安全”吗?这样,是否容易破解当前会话并在Session [“User”]中存储值以绕过登录?
解决方法
1)一般来说,使用会话状态并不是一种不好的做法,只要您需要它用于您的应用程序并且您了解它对性能和可伸缩性的影响.但是,在您的情况下,如果您需要存储的只是用户的名称,那么您真的不需要它,如果您的应用程序使用的是ASP.Net成员资格提供程序,那么此信息可在MVCController的User属性中找到基类:
var username = User.Identity.Name
可以存储会话数据的三种方式:“InProc”,它存储在应用程序进程中,“StateServer”,它存储在单独服务器上的输出进程,以及“sqlServer”,它存储在一个单独的服务器上sql Server数据库.您应该使用哪一个取决于您是否正在使用服务器场,如果您的会话需要持久(即在机器重启后仍然存在),以及您的应用程序的性能要求是什么(StateServer和sqlServer的性能不如InProc).更多信息可以在here找到
2)您应该使用SSL来保护会话数据.通过SSL(HTTPS)发送的数据是完全加密的,包括标头(因此是cookie).关于如何防止会话劫持攻击的一个很好的讨论是found here.
asp.net mvc4开启SqlServer 会话共享模式
应用部署结构(精简):
站点部署在
Nginx
后面,以Nginx
作为反向代理,不希望在Nginx
上设置ip_hash
,实现比较真实的负载均衡效果。
这时考虑到需要让site1
和site2
同时共享会话信息,进行如下的配置:
默认情况下asp.net站点的会话模式是采用Inproc模式,这种模式在站点因IIS重启会导致丢失用户会话(已经登陆的用户会自动重定向到登陆页面),在本次实践中使用SqlServer模式
创建会话数据库(独立于应用):
aspnet_regsql.exe -S server_ip -E -ssadd -sstype c -d dbname
更多参数可以参考命令的帮助aspnet_regsql.exe /?,通常路径在**C:\Windows\Microsoft.NET\Framework64\{.net_version}**下
配置如下:
<sessionState mode="SQLServer" sqlConnectionString="server=server_ip;database=dbname;uid=user;pwd=pwd;" allowCustomSqlDatabase="True" cookieless="false" timeout="20" />
会话数据库的表ASPStateTempApplications中存放应用的信息(appId和AppName),每一个站点一条记录,我两个站点分别使用该数据库所以有两条记录,如图:
通过以上的配置, 貌似完成了会话的共享,这时候通过Nginx
的IP
地址发送两个请求到服务器(一个登陆,一个获取用户考试列表)。通过Nginx
的日志发现两次请求被分发到两台不同的后端服务器:
从图中可以看出第一次请求是192.168.2.2:80803
这个站点处理,第二个请求被192.168.2.5:8083
这个站点处理。明明已经设置了会话共享为什么第二次请求被提示为未登录。检查是否登陆的代码如下:
public class WebApiCheckLoginFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
LoginService service = new LoginService();
string controllerName = actionContext.ActionDescriptor.ControllerDescriptor.ControllerName;
string actionName = actionContext.ActionDescriptor.ActionName;
bool isAuthAction = controllerName.Equals("ExamAPI") && actionName.Equals("Login");
if (!isAuthAction)
{
HttpSessionState Session = System.Web.HttpContext.Current.Session;
if (Session["user"] == null)
{
throw new HttpException((int)HttpStatusCode.NonAuthoritativeInformation, "未登录用户或已在其他设备中登录");
}
if (!service.IsOnline(Model.DeviceType.Phone, Session.SessionID))
{
throw new HttpException((int)HttpStatusCode.NonAuthoritativeInformation, "未登录用户或已在其他设备中登录");
}
}
base.OnActionExecuting(actionContext);
}
}
上述代码是为了检查如果session[''user'']==null,就会提示是未登陆。已经配置了会话共享为什么session[''user'']还是获取不到用户对象?通过各种尝试发现asp.net在处理session的时候会根据站点的AppId加上浏览器上的SessionId在数据库表([ASPStateTempSessions])中创建会话记录,SessionId的值如下:
0kf0zoq4to3h0z0b1eztw43s28d8c075
- 28d8c075 是
AppId
685293685的16进制(对应的AppId) - 0kf0zoq4to3h0z0b1eztw43s是客户端
cookie
中存放的sessionid
通过查看存储过程[dbo].[TempGetAppID]
的代码,发现asp.net会通过每个站点的AppName(见表ASPStateTempApplications
)去获取AppId
,然后将SessionId
+AppId
(16进制)作为主键插入到表ASPStateTempSessions
中,这样的处理逻辑是保证一个会话数据库会被N个站点共同使用,不用每个站点创建自己的会话数据库。隐藏的问题是如果站点的AppName
不一样会导致获取的AppId
不一样达不到会话共享的目的。
验证过程如下:向Nginx
发送两个请求(一个登陆,一个获取考试列表)会在ASPStateTempSessions表中产生两条记录,正常情况应该两个请求也应该只产生一条会话记录。
为了保证只产生一条会话记录可以通过让site1
和site2
的AppName保持一致,每次通过存储过程 [dbo].[TempGetAppID]
获取的AppId
都是同一个。 修改方式为:
修改完成后,再次发送两个请求(一个登陆,一个获取考试列表),两个请求经过Nginx
分发到两个站点后在ASPStateTempSessions
只产生一条会话记录,这样才真正的实现会话的一致性。
配置machineKey
,在做asp.net站点集群的时候如果站点中使用到cookie
或viewstate
的话需要配置machineKey
来确保多台机器共享验证和ViewState
.每个节点上的machineKey
配置必须一致
按照MSDN的标准说法:“对密钥进行配置,以便将其用于对 Forms 身份验证 Cookie 数据和视图状态数据进行加密和解密,并将其用于对进程外会话状态标识进行验证.加密和解密使用的就是
machineKey
生成machineKey的代码
public static string CreateMachineKey(int length)
{
byte[] random = new byte[length / 2];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetBytes(random);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < random.Length; i++)
{
builder.Append(string.Format("{0:X2}", random[i]));
}
return builder.ToString();
}
string decryptionKey= CreateMachineKey(48);
string validationKey = CreateMachineKey(128);
web.config
配置内容
<system.web>
<compilation targetFramework="4.0" />
<machineKey validationKey="validationKey" decryptionKey="decryptionKey"/>
...other...
</system.web>
小结
asp.net 站点的会话模式如果选用
SqlServer
的话,需要保证站点群(可能有N个站点)通过[dbo].[TempGetAppID]
存储过程获取AppId
后的结果是相同的。可以通过修改存储过程,也可以通过修改AppName。
asp.net-core-mvc – 如何在Asp.net Core MVC(又名Asp.Net 5 RC1)中检查响应cookie?
for(int i = 0; i < cookies.Count; i++) { if(cookies[i].Name == cookieName) { return cookies[i]; } }
但是在asp.net core mvc中访问响应cookie的界面如下所示:
基于此接口,我没有看到检查响应cookie是否存在并获取其属性的方法.但是还有一些方法可以做到吗?
在一个动作方法中,我尝试在响应对象上设置两个cookie,然后立即尝试访问它们.但intellisense没有显示允许我访问它们的任何方法,属性或索引器:
有一会儿,我想也许我可以使用Response.Cookies.ToString();并且只是解析信息以找到我的cookie信息,但是,ToString()调用返回“Microsoft.AspNet.Http.Internal.ResponseCookies”,因为该对象不会覆盖默认实现.
只是为了好玩,我还检查了GitHub的当前dev分支,看看自RC1以来接口是否已经改变,但它没有.所以给定这个接口,我如何检查响应cookie的存在并获取它的属性?我曾经考虑过通过响应头集合进行攻击,但这看起来很蹩脚.
解决方法
string GetCookieValueFromresponse(HttpResponse response,string cookieName) { foreach (var headers in response.Headers.Values) foreach (var header in headers) if (header.StartsWith($"{cookieName}=")) { var p1 = header.IndexOf('='); var p2 = header.IndexOf(';'); return header.Substring(p1 + 1,p2 - p1 - 1); } return null; }
asp.net-mvc – .NET MVC4 Facebook,Google和Twitter登录
我下载了DotNetopenAuth 4.1,但我在网上找不到任何关于如何将它与我的应用程序集成的示例,并在我的视图中有一个提供程序的登录表单.
谢谢
解决方法
只需生成一个新的MVC4应用程序,选择互联网模板,运行它并使用您的Facebook ID登录.
我们今天的关于asp.net-mvc – 为什么Asp.net MVC4不能使用SQL Server会话状态存储的cookieless的分享已经告一段落,感谢您的关注,如果您想了解更多关于ASP.NET MVC4会话状态存储用户名、asp.net mvc4开启SqlServer 会话共享模式、asp.net-core-mvc – 如何在Asp.net Core MVC(又名Asp.Net 5 RC1)中检查响应cookie?、asp.net-mvc – .NET MVC4 Facebook,Google和Twitter登录的相关信息,请在本站查询。
本文标签: