期末实训做的...
下载
posted @ 2008-06-28 19:50 Zhuang miao 阅读(359) | 评论 (2)编辑
     摘要: 一个VB计算器,给上学需要应付作业的人用


  阅读全文
posted @ 2007-10-19 21:29 Zhuang miao 阅读(404) | 评论 (0)编辑

在做Discuz!NT 2.0与自己的系统整合过程中,使用的是Discuz!NT 2.0的用户数据作为我的用户数据。
把Discuz!NT 2.0文件存放入bbs目录,把bbs目录设置为虚拟目录。把DNT.config拷贝到根目录。

登录很简单直接设置表单吧
<form action="bbs/login.aspx?reurl=<%= reurl %>" method="post">
<div id="Main" class="sign">
 <h2>用户登录</h2>
 <div class="meat">
  <form>
  <div>
   <label class="label_input" for="mail">用户名:</label>
   <input type="text" id="mail" class="txt" name="username" />
  </div>
  <div>
   <label class="label_input" for="password">密码:</label>
   <input type="password" id="password" class="txt" name="password" />
  </div>
  <div class="act">
   <input type="submit" value="登录" class="btn" />
   <a href="bbs/getpassword.aspx" target="_blank">忘记密码?</a>
  </div>
  </form>
 </div>
</div>
</form>


reurl 的值

    protected string reurl = string.Empty;

    protected void Page_Load(object sender, EventArgs e)
    {
        if (Request.UrlReferrer != null)
            reurl = HttpUtility.UrlEncode(Request.UrlReferrer.PathAndQuery);
        else
            reurl = HttpUtility.UrlEncode("/default.aspx");
    }



通过以下代码拿到了用户ID

        if (Request.Cookies.Count > 0)
        {
            
int uid = 0;
            
if (Request.Cookies["dnt"!= null && Request.Cookies["dnt"].Values["userid"!= null)
                
int.TryParse(Request.Cookies.Get("dnt").Values.Get("userid"), out uid);


读取一个XML文件获取人物信息。因为我在配置过程中总是有配置文件目录存放错误问题,因此,直接用了个XML文件,不用去仔细看目录怎么配置了。
XmlTextReader reader = new XmlTextReader("http://localhost/bbs/UserXML.aspx?u=" + uid + "&rd=" + rd.Next(10000).ToString());

XML文件代码是这样的
<%@ Page Language="C#" AutoEventWireup="false" EnableViewState="false" Codebehind="UserXML.aspx.cs" %>
<%@ Import namespace="System.Data" %>
<%@ Import namespace="Discuz.Common" %>
<%@ Import namespace="Discuz.Forum" %>
<%@ Import namespace="Discuz.Entity" %>

<script runat="server">
override protected void OnInit(EventArgs e)
{
    
base.OnInit(e);

    
int userid = 0;
    
int.TryParse(Request.QueryString["u"], out userid);

    StringBuilder sb 
= new StringBuilder();
    
if (userid > 0)
    
{
        UserInfo ui 
= Users.GetUserInfo(userid);

        sb.Append(
"<?xml version=\"1.0\"?>\n");
        sb.Append(
"<root>\n");
        sb.Append(
"\t<username>");
        sb.Append(ui.Username);
        sb.Append(
"\t</username>\n");
        sb.Append(
"\t<userid>");
        sb.Append(ui.Uid);
        sb.Append(
"\t</userid>\n");
        sb.Append(
"\t<userkey>");
        sb.Append(ui.Password);
        sb.Append(
"\t</userkey>\n");
        sb.Append(
"</root>\n");
    }

    
else
    
{
        sb.Append(
"<?xml version=\"1.0\"?>\n");
        sb.Append(
"<root>\n");
        sb.Append(
"\t<username>");
        sb.Append(
"游客");
        sb.Append(
"\t</username>\n");
        sb.Append(
"\t<userid>");
        sb.Append(
-1);
        sb.Append(
"\t</userid>\n");
        sb.Append(
"\t<userkey>");
        sb.Append(
7);
        sb.Append(
"\t</userkey>\n");
        sb.Append(
"</root>\n");
    }


    Response.Write(sb.ToString());    
}

</script>

做退出的时候要取userkey,我看了他的源代码
aspx/1/usercp.aspx的退出链接是这样的,在101行
templateBuilder.Append("   <a href=\"" + forumurl.ToString() + "logout.aspx?userkey=" + userkey.ToString() + "\" class=\"reg\">退出</a>\r\n");

从第一行<%@ Page language="c#" Codebehind="usercp.aspx.cs" AutoEventWireup="false" EnableViewState="false" Inherits="Discuz.ForumPage.usercp" %>看到该页使用了Dll文件里的usercp类。经查看是Discuz.Web.Dll里的usercp类,其父类是Discuz.Web.UI.Dll里的BasePage,而这个userkey正是在BasePage类定义的字段。

在BasePage的构造函数中给userkey赋值了
    this.userid = this.oluserinfo.Userid;
    this.usergroupid = this.oluserinfo.Groupid;
    this.username = this.oluserinfo.Username;
    this.password = this.oluserinfo.Password;
    if (this.password.Length > 0x10)
    {
        this.userkey = this.password.Substring(4, 8).Trim();
    }
    else
    {
        this.userkey = "";
    }

根据这样我也就可以给我使用的userkey赋值为
            while (reader.Read())
            {
                if (reader.Name.Equals("username"))
                    username = reader.ReadInnerXml();
                if (reader.Name.Equals("userid"))
                    userid = ConvertHelper.str_to_int(reader.ReadInnerXml());
                if (reader.Name.Equals("userkey"))
                {
                    string password = reader.ReadInnerXml();
                    if(password.Length > 0x10)
                        userkey = password.Substring(4, 8).Trim(); ;
                }
            }



Dll里实际退出代码是这样的
protected override void ShowPage()
{
    
base.pagetitle = "用户退出";
    
base.username = "游客";
    
int userid = base.userid;
    
base.userid = -1;
    StringBuilder builder 
= new StringBuilder();
    builder.Append(
"if (top.document.getElementById('leftmenu')){");
    builder.Append(
"\t\ttop.frames['leftmenu'].location.reload();");
    builder.Append(
"}");
    
base.AddScript(builder.ToString());
    
base.SetUrl(Utils.UrlDecode(ForumUtils.GetReUrl()));
    
base.SetMetaRefresh();
    
base.SetShowBackLink(false);
    
if (DNTRequest.GetString("userkey"== base.userkey)
    
{
        
base.AddMsgLine("已经清除了您的登录信息, 稍后您将以游客身份返回首页");
        Users.UpdateOnlineTime(userid);
        OnlineUsers.DeleteRows(
base.olid);
        ForumUtils.ClearUserCookie();
        Utils.WriteCookie(Utils.GetTemplateCookieName(), 
""-999999);
    }

    
else
    
{
        
base.AddMsgLine("无法确定您的身份, 稍后返回首页");
    }

}


 

 
posted @ 2009-06-20 00:34 Zhuang miao 阅读(12) | 评论 (0)编辑
单一登录 Web 应用程序的企业级安全系统
发布日期 : 8/17/2004 | 更新日期 : 8/17/2004

Paul D. Sheriff
PDSA, Inc.

适用范围:
Microsoft® ASP.NET

摘要:揭示能够以单一登录的方式登录多个 Web 应用程序的技术。本文还提供了示例代码,使您能够在完全使用单一登录的情况下创建强大的企业安全系统方面有一个良好的开端。

Download the code samples for this article(英文)。

本页内容

引言 引言
单一登录解决方案概述 单一登录解决方案概述
类和页面 类和页面
表
AppLauncher Web 应用程序 AppLauncher Web 应用程序
Apps 类 Apps 类
在 Web 应用程序中检索令牌 在 Web 应用程序中检索令牌
增强您的单一登录系统 增强您的单一登录系统
安装示例 安装示例
结论 结论
参考资料 参考资料

引言

如 果您的公司在运转方式上与其他绝大多数公司相同,那么您可能拥有许多用于支持公司业务的 Web 应用程序。绝大多数这种类型的应用程序需要采取安全措施,因为您肯定不希望用户随心所欲地进入任何 Web 应用程序。例如,只允许某些用户进入执行摘要决策支持系统,而其他用户则应只允许进入客户信息系统。有可能还有一些使用这些系统的外部用户,他们并不是您 的 Microsoft® Windows® 域用户。可以通过安全机制强制每个用户登录系统,从而对此进行控制。但这样做的问题在于,访问不同系统时,每个系统都会不断地要求用户登录。对于仅仅具有 五个 Web 应用程序的公司,如果某用户被允许访问其中的每一个程序,该用户会因为频繁地登录和注销而厌烦不已。所以必须找到一个更好的办法。

在阅读本文后,您将了解一种在企业中应用单一登录解决方案的方法。公司的内部用户可以使用 Windows 身份验证访问应用程序,而外部用户将被强制进行登录(参见 1)。

本文所讨论的主题

单一登录 Web 应用程序

  • 生成单一登录令牌

  • 将用户映射至应用程序

  • 基于窗体的身份验证

  • Windows 中集成的身份验证

单一登录解决方案概述

1 所示为内部用户和外部用户在登录到位于一个公司的 Intranet 上的网站时所执行的一系列步骤。在 50,000 英尺的级别上,这个系统仅有四个组件:通过 Windows 身份验证的网站、Active Directory、一个数据库服务器和一个或多个基于窗体进行身份验证的网站。

singlesignon_01

1 :涵盖内部用户和外部用户的单一登录解决方案的示例

Intranet 解决方案

1 的左上角可以看到内部用户使用浏览器浏览到特定的网站(第 1 步)。这个网站验证(第 2 步)用户的 Windows 凭据(通过 Active Directory)。如果用户是有效的 Windows 用户,则允许其进入该站点。通过身份验证后,将检索用户的标识,并调用包含指定用户能够运行的应用程序列表的数据库表(第 3 步)。这些应用程序将显示在 DataGrid 中,以供用户选择。

用户单击希望运行的应用程序后,将生成一个唯一的、 只能使用一次的令牌(第 4 步)。此令牌和用户标识将被存储在另一个数据库表中。然后此令牌被传递给用户要运行的 Web 应用程序中的一个特定页面(通过查询行)。此特殊页面从查询行读取该令牌,然后验证在数据库表中是否存在此令牌(第 5 步)。如果令牌存在,它将在数据库中检索登录 ID,然后删除存储此令牌的记录。此操作能够防止其他人再次使用此令牌并将登录 ID 发送回 Web 应用程序。

在了解了 Web 应用程序中的用户标识后,您需要生成一个 ASP.NET 窗体身份验证票据,因为在 Intranet 中,所有的 Web 应用程序都将使用基于窗体的身份验证。此票据将被正在浏览站点中所有的安全页面的用户所使用。

Extranet 解决方案

想要访问 Web 应用程序的外部用户(您所在域之外的用户)将被定向到与内部用户不同的起始页面(参见 2)。 内部用户和外部用户被定向到的 Web 应用程序采取了基于窗体的身份验证的安全措施。当外部用户试图打开此 Web 应用程序中的任何页面时,他们将被自动重定向至登录页面。此登录页面与对内部用户进行身份验证的页面不同。用户必须输入其凭据,然后系统将调用同一个数据 库,以确定该用户对此应用程序是否有效。如果有效,则将为此用户会话生成正常的基于窗体的身份验证 cookie。

类和页面

您需要创建若干类和 Web 页面以支持企业级安全系统。图 2 显示了需要为此系统编写的每个类和 Web 页。本文稍后将讨论此图所显示的每个类。在此图之后,列出了每个类的说明信息以及您的系统中的 Web 页面主要功能。

singlesignon_02

2 :设计和开发单一登录解决方案只需要几个类和页面

Apps 类

此类用于检索指定用户的应用程序列表。它还将生成一个新令牌、根据给定的令牌检索用户的标识信息以及删除令牌。

表 1:Apps 类的方法

方法名称

说明

GetAppsByLoginID

根据给定用户的域登录 ID,查找与此用户关联的应用程序,并返回应用程序的 DataSet。

CreateLoginToken

创建并返回新登录令牌。

GenerateToken

生成新令牌的方法。在本版本中,将使用一个简单的 GUID 作为令牌。

VerifyLoginToken

根据给定令牌,此方法将验证数据库中是否存在此令牌。它将创建 AppToken 类的一个实例,在写入相应的信息后将其返回。

DeleteToken

删除表中的令牌。

AppToken 类

此类包含从 Apps 类的 VerifyLoginToken 方法返回令牌信息时所需的四个属性。表 2 说明了此类包含的四个属性。

表 2:AppToken 类的属性

属性

说明

LoginID

字符串,其值表示用户的登录 ID。

AppName

字符串,其值表示与此 AppToken 记录有关的应用程序名称。

LoginKey

整数类型,其值表示 esUsers 表中用户的主键。

AppKey

整数类型,其值表示 esApps 表中应用程序的主键。

AppUserRoles 类

此类用于检索试图登录应用程序的用户的信息。其中的一个方法根据给定的登录 ID 和应用程序键值检查登录是否有效。另一个方法返回给定用户的角色集。还有一个方法根据登录 ID 和应用程序键值返回 esUser 表的主键。

AppLauncher 应用程序中的 Default.aspx

此 Web 页类将检索通过了 IIS 身份验证的 Windows 用户,并返回允许这些用户访问的应用程序的列表。它将在 DataGrid 中显示此列表(图 3),并允许用户单击其中特定的应用程序。用户单击应用程序后,此页将生成一个新令牌,并将此令牌和用户 ID 存储到 esAppToken 表,然后调用此应用程序将令牌传递给该 Web 应用程序中的 AppLogin.aspx 页。

singlesignon_03

3 :应用程序启动器显示允许登录的用户运行的应用程序的列表

每个网站中的 AppLogin.aspx

只 能从应用程序启动器调用此 Web 页类。若有任何其他的应用程序试图调用此页,它会将用户重定向至网站的 Default.aspx 页。因为每个 Web 应用程序都使用基于窗体的身份验证,所以此操作将强制 ASP.NET 将用户重定向至站点中的 Login.aspx 页。

如果使用令牌从应用程序启动器站点调用此页,那么此页将调用 Apps 类中的方法,以验证此令牌是否有效。如果令牌有效,则 Apps 类将返回一个 AppToken 对象,以便此页能够使用此对象中的信息创建基于窗体的身份验证的用户。

每个网站中的 Login.aspx

这是常规的 Web 登录页面,它要求用户输入凭据,并在数据库中检查这些凭据,以确保是有效用户;此外,如果是有效用户,则还将创建身份验证票据,并在用户进入站点时,重定向至用户请求的页面。

每个网站中的 Default.aspx

这是每个网站的主登陆页面。只有通过了 AppLogin 或 Login Web 页面的身份验证的用户才能够浏览此页以及站点中所有其他页。

您还需要在数据库中创建若干表,用来支持此单一登录企业级安全系统。本文中的表所包含的字段不多,但就所述内容而言,已经足够。 4 显示了需要在数据库中创建的每个表之间的关系。在 4 之后,会看到这些表的列表,以及本解决方案中使用的每个表的说明信息。

singlesignon_04

4 :实现单一登录系统的完整角色所需的若干表

esApps

此 表包含企业中所有 Web 应用程序的列表。除应用程序的名称以外,表中还包含应用程序的详细说明信息以及应用程序的 URL。URL 是完整形式的 URL,并且应以 AppLogin.aspx 页结尾。应用程序启动器中的 default.aspx 页将负责在重定向至 Web 应用程序前向 URL 中添加字符串 “Token=<GeneratedToken>”。

singlesignon_05thumb

5 esApps 表的示例数据

esUsers

此表列出可以使用应用程序的所有用户。您需要为所有内部用户复制您的用户域登录 ID。您可能还希望为外部用户添加密码字段。

singlesignon_06

6 esUsers 表的示例数据

esAppsUsers

此表将 esUsers 表中的用户与 esApps 表中他们能够运行的应用程序关联起来。表中的数据只有 esUsers 表和 esApps 表的外键。

esAppRoles

此表包含每个应用程序的角色集。例如,一个应用程序可能包含“Admin”和“User”角色,而其他应用程序则可能包含“User”和“Supervisor”规则。

singlesignon_07thumb

7 esAppRoles 表的示例数据

esAppUsersRoles

此表包含应用程序中每个用户的每一个角色的列表。在应用程序“HR”中,用户“Joe”可能是“Supervisor”,但在应用程序“Payroll”中,则可能是“Admin”和“User”。

esAppToken

esAppToken 包含由登录门户应用程序生成并传递给单个 Web 应用程序的令牌。这些记录在通常情况下应只存在两秒(或更短的时间),因为正被调用的 Web 应用程序在从此表中收集信息之后,会立即删除此令牌。这样可以防止他人再次使用此令牌。

singlesignon_08thumb

8 esAppToken 表的示例数据

AppLauncher Web 应用程序

应用程序启动器解决方案( 9)由通过 Windows 身份验证的站点和类库项目组成。此通过 Windows 身份验证的站点将直接读取线程中的用户域 ID,并利用此用户域 ID 在用户表中查找用户。我们看一下显示用户能够运行的应用程序的 Web 页。

singlesignon_09

9 AppLauncherxx 解决方案包含 AppLauncherxx 项目和对 AppLauncherDataxx 项目的引用

AppLauncherxx 中的 Default.aspx

应 用程序启动器网站只需要一个 Web 页:即 default.aspx。此 Web 页从页面的 User 对象中读取用户的 Windows 登录 ID,并加载数据库中为此用户定义的应用程序列表。以下是加载 default.aspx 页面时调用的 Page_Load 事件过程的代码。

// C#
private void Page_Load(object sender, System.EventArgs e)
{
// 显示用户名称
// 不带域前缀
lblLogin.Text = "Applications Available for: " +
Apps.LoginIDNoDomain(User.Identity.Name);

AppLoad();
}
' VB.NET
Private Sub Page_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
' 显示用户名称
' 不带域前缀
lblLogin.Text = "Applications Available for: " & _
Apps.LoginIDNoDomain(User.Identity.Name)

AppLoad()
End Sub

调用 Apps 类的静态方法 LoginIDNoDomain 是为了去除域前缀。如果登录 ID 为“Ken”的用户通过了名为“PDSA”的域的身份验证,那么 User.Identity.Name 属性将返回“PDSA\Ken”。而此方法则仅返回字符串“Ken”。

加载此用户能够运行的应用程序

AppLoad 方法使用 Apps 类的一个实例检索允许此用户运行的应用程序的 DataSet。本文后面的内容将显示 Apps 类中的 GetAppsByLoginID 方法。

// C#
private void AppLoad()
{
Apps app = new Apps();

try
{
// 为此用户加载应用程序
grdApps.DataSource =
app.GetAppsByLoginID(User.Identity.Name);
grdApps.DataBind();

}
catch (Exception ex)
{
lblMessage.Text = ex.Message;
}
}
' VB.NET
Private Sub AppLoad()
Dim app As New Apps

Try
' 为此用户加载应用程序
grdApps.DataSource = app.GetAppsByLoginID(User.Identity.Name)
grdApps.DataBind()

Catch ex As Exception
lblMessage.Text = ex.Message
End Try

End Sub

LinkButton 控件

default.aspx 页面的 DataGrid 控件显示指定用户的应用程序列表后(参见 3),用户就可以选择其中的应用程序。DataGrid 中用于显示超级链接以供用户单击的控件即为 LinkButton。在 Web 页面中,LinkButton 定义如下:

<asp:LinkButton id=lnkApp runat="server" 
AppID='<%# DataBinder.Eval(Container.DataItem, "iAppID") %>'
UserID='<%# DataBinder.Eval(Container.DataItem, "iUserID") %>'
CommandArgument='<%# DataBinder.Eval(Container.DataItem, "sURL") %>'
Text='<%# DataBinder.Eval(Container.DataItem, "sAppName") %>'>
</asp:LinkButton>

从上述代码可以看出,增添了一些 esApps 表的主键 (iAppID) 和 esUsers 表的主键 (iUserID) 的附加属性。这些属性以及 CommandArgument 中的 URL 提供了可存储到数据库中的足够的用户信息。稍后您会看到,我们将在 ItemCommand 事件过程中检索这些附加属性。

提示: 可以向服务器控件添加任何想要的属性。ASP.NET 将忽略这些属性,但是您可以使用服务器控件的 Attributes 属性检索它们的值。

ItemCommand 事件

用户单击 DataGrid 中的 LinkButton 控件后,将调用 ItemCommand 事件过程。在此方法中,需要创建 Apps 类的一个新实例,并检索 LinkButton 控件,以便获取属性,然后调用 Apps 类中的 CreateLoginToken 方法,以将此数据存储到数据库中的 esAppToken 表中。最后,从此方法检索出令牌后,此令牌将与 LinkButton 的 CommandArgument 属性中的 URL 连接,然后调用 Response.Redirect,以调用令牌所传递的 Web 应用程序。

// C#
private void grdApps_ItemCommand(object source,
System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
Apps app = new Apps();
bool redirect = false;
string token = String.Empty;
LinkButton lb;

try
{
lb = (LinkButton) e.Item.Cells[0].Controls[1];

// 为此用户或应用程序创建令牌
token = app.CreateLoginToken(
lb.Text,
User.Identity.Name,
Convert.ToInt32(lb.Attributes["UserID"]),
Convert.ToInt32(lb.Attributes["AppID"]));

redirect = true;
}
catch (Exception ex)
{
redirect = false;
lblMessage.Text = ex.Message;
}

if (redirect)
{
// 重定向至生成的令牌中
// 传递的 Web 应用程序

Response.Redirect(e.CommandArgument.ToString() +
"?Token=" + token, false);
}
}
' VB.NET
Private Sub grdApps_ItemCommand(ByVal source As Object, _
ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) _
Handles grdApps.ItemCommand
Dim app As New Apps
Dim boolRedirect As Boolean
Dim token As String
Dim lb As LinkButton

Try
lb = DirectCast(e.Item.Cells(0).Controls(1), LinkButton)

' 为此用户或应用程序创建令牌
token = app.CreateLoginToken(lb.Text, _
User.Identity.Name, _
Convert.ToInt32(lb.Attributes("UserID")), _
Convert.ToInt32(lb.Attributes("AppID")))

boolRedirect = True

Catch ex As Exception
boolRedirect = False
lblMessage.Text = ex.Message

End Try

If boolRedirect Then
' 重定向至生成的令牌中
' 传递的 Web 应用程序
Response.Redirect(e.CommandArgument.ToString() & _
"?Token=" & token, False)
End If
End Sub

您会注意到,在上面的代码中,检索了 e.Item 参数中的 LinkButton 控件。e.Item 用于引用您在 DataGrid 中单击的行。您可以在 LinkButton 所在的列后检索所单击的 LinkButton 控件的特定实例。在本例中,它在 Cells(0) 中。在此单元格中,可以在 Controls(1) 后取得该控件。该控件位于位置一 (1) 的原因是 DataGrid 中所使用的、允许我们将 LinkButton 置于单元格中的 ItemTemplate 被看作是元素零 (0)。

检索出 LinkButton 后,就可以使用 Attributes 属性检索在设置 LinkButton 时存储的 UserID 和 AppID。Attributes 属性是您向服务器控件添加的所有附加属性的集合,这些附加属性不是初始控件定义的组成部分。

修改 Web.Config

在 Web 应用程序对用户进行身份验证之前,必须将 Web.Config 文件中的 <authentication> 元素设置为“Windows”。此外,还必须拒绝 Web.config 中的 <authorization> 元素中的匿名用户。

<authorization>
<deny users="?" />
</authorization>

设置这两个元素可从浏览器强制服务器检索用户的 Windows 凭据。当然,只有当您在用户所登录的域中使用 Internet Explorer 时,它才会起作用。

最后一项要完成的工作是存储连接字符串,以便从数据库的表中获取。在本文的示例中,我只使用了 Web.config 中的 <appSettings> 部分存储连接字符串。

<appSettings>
<add key="eSecurityConnectString"
value="server=(local);Database=eSecurity;uid=myUserID;pwd=myPassword" />
</appSettings>

在本文中,我在 SQL Server™ 中创建了名为 eSecurity 的数据库,并创建了本文前面内容中所述的所有的表。本文的示例代码包含一个 SQL 脚本,可以运行此脚本以在 SQL Server 数据库中创建表。如果使用其他数据库系统,则需要根据您的数据库对脚本进行适当的修改。

Apps 类

现在看一下在此企业级安全系统中用于实现主要功能的 Apps 类。AppLauncherData 程序集包含的三个类中的每一个类都可负责为用户加载应用程序、加载用户角色以及操作安全令牌。较好的做法是保留与数据库进行交互的功能,以及保留在用户界 面层以外操作令牌的功能。这可以使您不必更改用户界面就可以修改令牌的创建方法,以及修改与数据库交互的方式。

Apps 类负责操作令牌以及为用户加载应用程序。我们看一下这个类的定义。

// C#
public class Apps
{
string mConnectString;

public Apps()
{
mConnectString = ConfigurationSettings.
AppSettings["eSecurityConnectString"];
}

...
}
' VB.NET
Public Class Apps
Private mConnectString As String

Public Sub New()
mConnectString = ConfigurationSettings. _
AppSettings("eSecurityConnectString")
End Sub

...
End Class

可以看到这个类首先使用从 Web.config 文件中检索出的连接字符串加载成员变量。

GetAppsByLoginID 方法

要 为特定的用户加载应用程序,应将用户的登录 ID 传递给 GetAppsByLoginID 方法。此方法负责执行联接,以检索所有相关的信息。使用 SQL 联接需要从 esApps 和 esAppsUsers 表中获取信息。此外,还需要联接 esUsers 表,因为只需要检索特定用户的应用程序,而所有我们可用的信息只有用户的 Login ID,所以必须在 esUsers 表中查找用户的主键,以便联接到其他表。

注意: 本文使用动态 SQL 只是为了显示概念。在实际的企业级安全系统中,需要对所有的 SQL 调用使用存储过程。

// C#
public DataSet GetAppsByLoginID(string loginID)
{
DataSet ds = new DataSet();
SqlCommand cmd;
SqlDataAdapter da;
string sql;

sql = "SELECT esApps.iAppID, esAppsUsers.iUserID, ";
sql += " esApps.sAppName, esApps.sDesc, esApps.sURL ";
sql += " FROM esApps";
sql += " INNER JOIN esAppsUsers ";
sql += " ON esApps.iAppID = esAppsUsers.iAppID ";
sql += " INNER JOIN esUsers ";
sql += " ON esAppsUsers.iUserID = esUsers.iUserID ";
sql += " WHERE sLoginID = @sLoginID ";
sql = String.Format(sql, Apps.LoginIDNoDomain(loginID));

try
{
cmd = new SqlCommand(sql);
cmd.Parameters.Add(new
SqlParameter("@sLoginID", SqlDbType.Char));
cmd.Parameters["@sLoginID"].Value =
Apps.LoginIDNoDomain(loginID);
cmd.Connection = new SqlConnection(mConnectString);

da = new SqlDataAdapter(cmd);

da.Fill(ds);

return ds;
}
catch (Exception ex)
{
throw ex;
}
}
' VB.NET
Public Function GetAppsByLoginID(ByVal LoginID As String) _
As DataSet
Dim ds As New DataSet
Dim cmd As SqlCommand
Dim da As SqlDataAdapter
Dim sql As String

sql = "SELECT esApps.iAppID, esAppsUsers.iUserID, "
sql &= " esApps.sAppName, esApps.sDesc, esApps.sURL "
sql &= " FROM esApps"
sql &= " INNER JOIN esAppsUsers "
sql &= " ON esApps.iAppID = esAppsUsers.iAppID "
sql &= " INNER JOIN esUsers "
sql &= " ON esAppsUsers.iUserID = esUsers.iUserID "
sql &= " WHERE sLoginID = @sLoginID "
sql = String.Format(sql, Apps.LoginIDNoDomain(LoginID))

Try
cmd = New SqlCommand(sql)
cmd.Parameters.Add(New _
SqlParameter("@sLoginID", SqlDbType.Char))
cmd.Parameters("@sLoginID").Value = _
Apps.LoginIDNoDomain(LoginID)
cmd.Connection = New SqlConnection(mConnectString)

da = New SqlDataAdapter(cmd)
da.Fill(ds)

Return ds

Catch ex As Exception
Throw ex
End Try
End Function

CreateLoginToken 方法

用户单击在 DataGrid 中的应用程序后,必须生成一个新令牌。CreateLoginToken 方法负责执行此任务。

// C#
public string CreateLoginToken(string appName,
string loginID, int userID, int appID)
{
SqlCommand cmd = new SqlCommand();
SqlParameter param;
string token;
string sql;

// 生成新令牌
token = GenerateToken();

sql = "INSERT INTO esAppToken(sToken, sAppName, ";
sql += " sLoginID, iUserID, iAppID, dtCreated) ";
sql += " VALUES(@sToken, @sAppName, @sLoginID, ";
sql += " @iUserID, @iAppID, @dtCreated) ";

param = new SqlParameter("@sToken", SqlDbType.Char);
param.Value = token;
cmd.Parameters.Add(param);

param = new SqlParameter("@sAppName", SqlDbType.Char);
param.Value = appName;
cmd.Parameters.Add(param);

param = new SqlParameter("@sLoginID", SqlDbType.Char);
param.Value = Apps.LoginIDNoDomain(loginID);
cmd.Parameters.Add(param);

param = new SqlParameter("@iUserID", SqlDbType.Int);
param.Value = userID;
cmd.Parameters.Add(param);

param = new SqlParameter("@iAppID", SqlDbType.Int);
param.Value = appID;
cmd.Parameters.Add(param);

param = new SqlParameter("@dtCreated", SqlDbType.DateTime);
param.Value = DateTime.Now;
cmd.Parameters.Add(param);

try
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = sql;

cmd.Connection = new SqlConnection(mConnectString);
cmd.Connection.Open();

cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (cmd.Connection.State != ConnectionState.Closed)
{
cmd.Connection.Close();
cmd.Connection.Dispose();
}
}

return token;
}
' VB.NET
Public Function CreateLoginToken(ByVal AppName As String, _
ByVal LoginID As String, ByVal UserID As Integer, _
ByVal AppID As Integer) As String
Dim cmd As New SqlCommand
Dim param As SqlParameter
Dim token As String
Dim sql As String

' 生成新令牌
token = GenerateToken()

sql = "INSERT INTO esAppToken(sToken, sAppName, "
sql &= " sLoginID, iUserID, iAppID, dtCreated) "
sql &= " VALUES(@sToken, @sAppName, @sLoginID, "
sql &= " @iUserID, @iAppID, @dtCreated)"
sql = String.Format(sql, token, AppName, _
Apps.LoginIDNoDomain(LoginID), UserID, AppID, _
DateTime.Now.ToString())

param = New SqlParameter("@sToken", SqlDbType.Char)
param.Value = token
cmd.Parameters.Add(param)

param = New SqlParameter("@sAppName", SqlDbType.Char)
param.Value = AppName
cmd.Parameters.Add(param)

param = New SqlParameter("@sLoginID", SqlDbType.Char)
param.Value = Apps.LoginIDNoDomain(LoginID)
cmd.Parameters.Add(param)

param = New SqlParameter("@iUserID", SqlDbType.Int)
param.Value = UserID
cmd.Parameters.Add(param)

param = New SqlParameter("@iAppID", SqlDbType.Int)
param.Value = AppID
cmd.Parameters.Add(param)

param = New SqlParameter("@dtCreated", SqlDbType.DateTime)
param.Value = DateTime.Now
cmd.Parameters.Add(param)

Try
cmd.CommandType = CommandType.Text
cmd.CommandText = sql

cmd.Connection = New SqlConnection(mConnectString)
cmd.Connection.Open()

cmd.ExecuteNonQuery()

Catch ex As Exception
Throw ex

Finally
If cmd.Connection.State <> ConnectionState.Closed Then
cmd.Connection.Close()
cmd.Connection.Dispose()
End If
End Try

Return token
End Function

要创建令牌,应调用 GenerateToken 方法。此方法独立于 CreateLoginToken 方法的原因是,它允许您更改以后要生成的令牌的类型。在本文的末尾,提出了有关的设想。此方法使用 Guid 类生成一个新的 GUID 作为令牌。

// C#
public string GenerateToken()
{
return System.Guid.NewGuid().ToString();
}
' VB.NET
Public Function GenerateToken() As String
Return System.Guid.NewGuid().ToString()
End Function

在 Web 应用程序中检索令牌

要从应用程序启动器测试启动应用程序,应创建一个可从启动器调用的用于测试的 Web 应用程序(参见 10)。这个用于测试的 Web 应用程序将使用前面所讨论的 AppLauncherDataxx 项目。

singlesignon_10

10 :每个 Web 应用程序都将使用结合了 AppLogin Login Default 页面的 AppLauncherDataxx 项目

修改 Web.Config

要 创建集成了单一登录系统的 Web 应用程序,首先需要修改 Web.config 文件,并创建 <appSettings> 区段,以存储连接字符串,从而与 eSecurity 数据库进行交互。还需要存储应用程序 ID 和应用程序名称,以供外部用户进入时使用。因为外部用户进入时,尚未创建令牌,这时需要知道用户选择的应用程序,以便在建立用户角色时加载要使用的 AppToken 对象。在本文后面的内容中,您会了解如何这样做。

<appSettings>
<add key="eSecurityConnectString"
value="server=(local);Database=
eSecurity;uid=mUserID;pwd=myPassword"></add>
<add key="eSecurityAppID" value="1"></add>
<add key="eSecurityAppName" value="Payroll"></add>
</appSettings>

您还需要设置 Web 应用程序,以使用基于窗体的身份验证。要进行此操作,请修改 <authentication> 元素,如下所示。

<authentication mode="Forms">
<forms name="AppTest" loginUrl="Login.aspx" />
</authentication>

最后,为了拒绝匿名用户访问,还需要修改 <authorization> 元素。

<authorization>
<deny users="?" />
</authorization>

AppLogin.aspx 页

请记住,从应用程序启动器调用的每个应用程序都会调用 AppLogin 页,并向此页传递生成的令牌。AppLogin 页会验证此令牌是否正确,从 esAppToken 表检索相关的信息,然后删除 esAppToken 中的记录,以使该令牌不被重复使用。

// C#
private void Page_Load(object sender, System.EventArgs e)
{
VerifyToken();
}

private void VerifyToken()
{
Apps app = new Apps();
AppToken al;

try
{
al = app.VerifyLoginToken(
Request.QueryString["Token"].ToString());

if(al.LoginID.Trim() == "")
{
// 非有效登录
// 将其重定向至默认页面
// 这将使用户转至登录页面
Response.Redirect("default.aspx");
}
else
{
// 创建窗体身份验证 Cookie
// 设置窗体身份验证变量
FormsAuthentication.Initialize();
FormsAuthentication.SetAuthCookie(
al.LoginID.ToString(), false);

// 设置应用程序令牌对象
Application["AppToken"] = al;

// 重定向至默认页面
Response.Redirect("default.aspx");
}
}
catch
{
// 通过默认页面将用户重定向至登录页面
Response.Redirect("default.aspx");
}
}
' VB.NET
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
VerifyToken()
End Sub

Private Sub VerifyToken()
Dim app As New Apps
Dim al As AppToken

Try
al = app.VerifyLoginToken( _
Request.QueryString("Token").ToString())

If al.LoginID.Trim() = "" Then
' 非有效登录
' 将其重定向至默认页面
' 这将使用户转至登录页面
Response.Redirect("default.aspx")
Else
' 创建窗体身份验证 Cookie
' 设置窗体身份验证变量
FormsAuthentication.Initialize()
FormsAuthentication.SetAuthCookie( _
al.LoginID.ToString(), False)

' 设置应用程序令牌对象
Application("AppToken") = al

' 重定向至默认页面
Response.Redirect("default.aspx")
End If

Catch
' 通过默认页面将用户重定向至登录页面
Response.Redirect("default.aspx")
End Try
End Sub

在 VerifyLogin 方法中,首先检查令牌是否有效。这一操作通过调用 Apps 类中的 VerifyLoginToken 方法完成。此方法返回 AppToken 类的一个实例。如果此类的 LoginID 属性存在值,就表明此用户是有效用户。如果没有值,则令牌无效。在令牌无效的情况下,此方法将用户重定向到网站中的 default.aspx 页面。当然,如果打开了基于窗体的身份验证,用户会被强制重定向至 Login.aspx 页面,被请求重新登录。

调用 FormsAuthentication.Initialize 和 FormsAuthentication.SetAuthCookie 方法可以向外发送窗体验证 cookie。这会将内存中的 cookie 发送到浏览器。每次用户返回此站点时,ASP.NET 运行库都会检查此 cookie。

VerifyLoginToken 方法

此方法通过查询行接受生成的令牌,并对其进行检查,以确保令牌有效。此方法会转到 esAppToken 表检查此令牌。如果在表中找到此令牌,则将表中所有的值置于 AppToken 对象的各个属性中,然后从此方法返回该 AppToken 对象。

// C#
public AppToken VerifyLoginToken(string Token)
{
AppToken al = new AppToken();
DataSet ds = new DataSet();
SqlCommand cmd;
DataRow dr;
SqlDataAdapter da;
string sql;

sql = "SELECT iAppTokenID, sAppName, sLoginID, ";
sql += " iAppID, iUserID ";
sql += " FROM esAppToken";
sql += " WHERE sToken = @sToken ";

try
{
cmd = new SqlCommand(sql);
cmd.Parameters.Add(new
SqlParameter("@sToken", SqlDbType.Char));
cmd.Parameters["@sToken"].Value = Token;
cmd.Connection = new SqlConnection(mConnectString);

da = new SqlDataAdapter(cmd);
da.Fill(ds);

if (ds.Tables[0].Rows.Count > 0)
{
dr = ds.Tables[0].Rows[0];

al.LoginID = dr["sLoginID"].ToString();
al.AppName = dr["sAppName"].ToString();
al.AppKey = Convert.ToInt32(dr["iAppID"]);
al.LoginKey = Convert.ToInt32(dr["iUserID"]);

DeleteToken(Convert.ToInt32(dr["iAppTokenID"]));
}
}
catch (Exception ex)
{
throw ex;
}

return al;
}
' VB.NET
Public Function VerifyLoginToken(ByVal Token As String) As AppToken
Dim al As New AppToken
Dim ds As New DataSet
Dim cmd As SqlCommand
Dim dr As DataRow
Dim da As SqlDataAdapter
Dim sql As String

sql = "SELECT iAppTokenID, sAppName, sLoginID, "
sql &= " iAppID, iUserID "
sql &= " FROM esAppToken"
sql &= " WHERE sToken = @sToken "

Try
cmd = New SqlCommand(sql)
cmd.Parameters.Add(New _
SqlParameter("@sToken", SqlDbType.Char))
cmd.Parameters("@sToken").Value = Token
cmd.Connection = New SqlConnection(mConnectString)

da = New SqlDataAdapter(cmd)

da.Fill(ds)

If ds.Tables(0).Rows.Count > 0 Then
dr = ds.Tables(0).Rows(0)

al.LoginID = dr("sLoginID").ToString()
al.AppName = dr("sAppName").ToString()
al.AppKey = Convert.ToInt32(dr("iAppID"))
al.LoginKey = Convert.ToInt32(dr("iUserID"))

DeleteToken(Convert.ToInt32(dr("iAppTokenID")))
End If

Catch ex As Exception
Throw ex
End Try

Return al
End Function

基于角色的安全性

用户通过 Web 应用程序的验证后,将被重定向至 default.aspx 页面。因为已经向浏览器发送了身份验证 cookie,每次都会将此 cookie 发送回来。在向用户传送请求的页面之前,将调用 global.asax 文件中的 Application_AuthenticateRequest 方法。只有在这个方法中才能够建立要与此用户关联的任何角色。我不打算在这里显示此方法的用法,因为专门有文章对其进行讨论。您可以查看示例代码中使用此 方法的例子。基于角色的安全性的代码十分简单。您完全可以改进此代码,使其能够使用缓存。它的目的只是告诉您如何着手去做。

增强您的单一登录系统

本 文介绍了创建企业级安全系统方面的很多内容。但若要介绍创建企业级安全系统的所有方面,则超出了本文的范围。例如,您还应当添加一系列的维护屏幕,以允许 管理员添加用户,并且建立用户和应用程序之间的映射关系。也需要对这组屏幕采取安全措施,以便只有特定角色内的用户才能够打开它们。

系统 另外一个需要增强的地方是自动添加不在系统中的域用户的能力。它能够帮助您向系统中添加用户,而不必手工输入用户信息,要么您也可以编写添加用户的应用程 序。您一般会将这些用户指定为默认角色,以便只允许他们运行某些应用程序而不是全部应用程序。此外,如果以这种方式添加了新用户,管理员还应当收到电子邮 件形式的通知信息。

因为这种安全系统依赖于生成的特定的令牌,而在创建令牌之后,会立即调用 Web 应用程序,所以如果应用程序未启动以响应请求,就可能出现问题。如果出现这种情况,那么令牌就会留在数据库中,从而导致潜在的安全性风险。所以还需要预先 安排一项工作,用来删除超过指定时间长度的令牌。也可以创建自己特定的令牌,该令牌由令牌和创建此令牌的时间组成。这样就只需修改 VerifyLoginToken 方法,以检查时间,确保它小于指定的时间长度。

本文中使用的令牌为 GUID 类型,它强制您回调数据库,以检索用户的配置文件信息。本系统的一个较好的增强方案是使用基于 WS-Security 增强标准的令牌加密配置文件信息,并仅将其作为令牌由应用程序启动器传递给每个 Web 应用程序。这样能够避免每次往返数据库。

当然,你可能希望更改代码中的所有动态 SQL 调用,以使用存储过程和带参数的命令对象来避免遭受任何 SQL 流量注入攻击,彻底保证数据表的安全。对于命令对象,应使用存储过程和参数。

安装示例

本 文附带两个示例应用程序。它们都是 Web 应用程序。解决方案中还包含为每个 Web 应用程序提供的类库。示例具有 Microsoft Visual Basic® .NET 和 C# 两种版本,可以选择希望使用的版本。解决方案文件中还有一个 .SQL 文件,可用来帮助您在数据库中创建所需的表。.SQL 文件用于 SQL Server,但经过简单的修改,也可用于其他数据库系统。以下是安装示例应用程序应执行的步骤。

  • 在 DBMS 中创建名为 eSecurity 的数据库。

  • 执行 .SQL 文件,在 eSecurity 数据库中创建表。

  • 将提供的 .ZIP 文件解压到文件夹中。

  • 创建虚拟目录,使其指向希望使用的每个文件夹。例如,如果使用的是 VB.NET 版本的示例,那么请创建两个名称分别为 AppLauncherVB 和 AppTestVB 的虚拟目录,并使其指向这两个文件夹。

  • 修改 .SLN 文件,使其指向您创建的虚拟目录文件夹。

结论

Web 应用程序的单一登录系统有助于节省用户时间,并减轻在企业中因需要记忆登录 ID 和密码所带来的不便。此外,它还允许外部用户使用内部 Web 应用程序,而不必将这些用户添加到域中。实现这样一个系统只需创建几个简单的表和类。但要创建一个完全使用单一登录方式的可靠的企业级安全系统,还需要做 许多工作,本文中的示例可以使您有一个良好的开端。

参考资料

ASP.NET Developer's Jumpstart(英文)

Building Secure Microsoft ASP.NET Applications(英文)

posted @ 2009-06-17 00:57 Zhuang miao 阅读(23) | 评论 (0)编辑
     摘要: 作为产品中的一大特色,模板机制一经推出,就引来了大家特别是站长们的关注。但它所饱受的风风雨雨也成了那时不少人关注的话题。而今天本人将结合在产品组中的开发经历,介绍一下模板机制在设计使用时的一些体会心得。希望借此陋文,使模板机制揭开“神秘”面纱,为大家在实际设计中提供一些有价值的参考和建议。 好了,开始今天的话题:) 首先阐述一下模板设计的目标,因为这对于它最终要实现的功能... 阅读全文
posted @ 2009-06-16 20:12 Zhuang miao 阅读(11) | 评论 (0)编辑
     摘要: Discuz整体架构如下图所示: 横向表示 同一层次中涉及的各个模块(项目) 纵向表示 不同层次之间模块的关系,某些关系是如何在各层次中传递(穿越) Discuz架构上采用了比较流行的三层架构,即表现层,业务逻辑层,数据访问层来进行设计,并结合自己的情况进行了特殊处理。 表现层: 表现层即为上图中蓝色虚线表示,主要包括:Web,Services,UI,Control。各项目主要功能为: UI 定义各种页面基类,提供Ajax访问访问接口。 Control存放Discuz用到的自定义服务器端控件。 Services提供外部访问接口。 Discuz引入了一种模板引擎的机制,来实现表现层的多样化。 主要设计思想为:针对设计人员,提供纯静态页面,并提供了一套约定的语法和标签(具体位置在:templates)。模板制作完成后,要进行模板导入,此时discuz会将静态模板进行解析将其转换成 aspx页面,然后放到aspx/1..n下。如果你打开这下面的文件,会  阅读全文
posted @ 2009-06-15 10:28 Zhuang miao 阅读(8) | 评论 (0)编辑
     摘要: C#发现之旅 --- C#开发Windows Service程序 本课程介绍 本课程说明了Windows Service程序的概念,并演示如何使用C#开发一个简单的Windows Service程序。C#工程名为MyWindowsService,编译生成的文件是MyWindowsService.exe。本课程的演示代码下载地址为http://files.cnblogs.com/xdesigner... 阅读全文
posted @ 2009-06-09 00:57 Zhuang miao 阅读(33) | 评论 (0)编辑
【全程图解】ADSL+笔记本电脑 组建WIFI网络让5800实现WIFI上网(更新完毕)

NND看了下代码模式中的图片地址,为什么莫名的在url中把域名中间字幕加了个空格!!晕!

鉴于好多朋友不懂我本文的目的,在此给大家解释一下



如果你有一台手机或是移动设备支持WIFI上网(比如nokia5800,iphone ,psp等),如果你的家中没有无线路由器,而且你得住所附近又没有WIFI热点,  但是你拥有可以通过宽带上网的笔记本电脑一台,并且想让你得5800体验一下wifi上网的快感!那么,请看此文!

本文用到的技术与以下设备无关: 蓝牙 ,无线路由器

前几天购置5800一台,得塞班论坛各位热心网友的帖子,才使得我把5800研究的入门,并且装入各种常用软件。。现在开始回馈各位,
实战讲解一下如何用笔记本电脑+家里的宽带组建WI-FI网络,让5800通关WI-FI联网

网上也有通关电脑联网的,不过操作系统都是xp,而vista和xp有很大差别

首先在桌面 网络 图标上右键 -属性 ,打开网络和共享中心



然后点击 连接到网络


选择 设置无线临时(计算机到计算机)网络


点击下一步,到达设置页面

网络名:你要将你的wifi网络起的名称,在手机连接的时候要用这个名称
安全类型:选择WEP
安全密钥/密码:自己设置,连接的时候会使用
保存连接:将此框选中



然后单击下一步完成配置
此步骤可能有时候提示无法成功配置,可能是如下原因造成:
1,电脑中有第三方无线网络配置程序,解决方案:在第三方控制程序中将控制权交回来
2,无线网络信号没打开 解决方案:打开无线信号,一般键盘上会有快捷键


然后继续回到 网络和共享中心

点击 管理无线网络

TP-LINK 和WIFI 是我以前配置的,不要理它!


右键 5800WI-FI 属性 进入 安全 选项卡
安全类型 改为 共享式
加密类型 为 WEP
然后点击确定即可


然后再次回到 网络和共享中心  点击 管理网络连接

我的电脑联网方式是路由器连接的 ,不用在电脑上拨号,所以在 本地连接 上又键 属性 (如果你是ADSL拨号的用户则请在宽带连接上右键)

把  允许其他网络用户通过此计算机的intenet连接 勾选上
然后确定


这时候会弹出如下框
点击是,这一步会将无线网卡的ip设置为 192.168.0.1



以上步骤完成了电脑端的所有设置,接下来来设置5800

进入5800

选项 --设置----连接功能--承载方式 --接入点  新建一个接入点


自动搜索可用接入点 选择否

选择接入点 选择 无线局域网

WLAN网络名称 填入 5800WIFI


网络模式/状态 选择 特殊(公开)

安全模式 选择 WEP

WLAN的WEP密钥 填入 电脑中 你设置的密钥

完成设置
然后选择 5800WIFI ,点击 选项--编辑
然后继续点击 选项-高级设置-IPV4设置

把手机IP设置为 192.168.0.2-192.168.0.254之间‘


到此设置完成,手机可以上网,然后会弹出选择什么网络,选择无线WLAN即可!


提醒:有时候会提示  无网关回应,可能是由于电脑装了杀毒软件或者防火墙,尝试关闭再连接
posted @ 2009-02-02 23:00 Zhuang miao 阅读(2717) | 评论 (0)编辑
BigInt

Int64

一个 64 位的有符号整数。

Binary

Array 类型为 Byte

二进制数据的固定长度流,范围在 1 到 8,000 个字节之间。

Bit

Boolean

无符号数值,可以是 0、1 或空引用(Visual Basic 中为 Nothing)。

Char

String

非 Unicode 字符的固定长度流,范围在 1 到 8,000 个字符之间。

DateTime

DateTime

日期和时间数据,值范围从 1753 年 1 月 1 日到 9999 年 12 月 31 日,精度为 3.33 毫秒。

Decimal

Decimal

固定精度和小数位数数值,在 -1038 -1 和 10 38 -1 之间。

Float

Double

-1.79E +308 到 1.79E +308 范围内的浮点数字。

Image

Array 类型为 Byte

二进制数据的可变长度流,范围在 0 到 231 -1(或 2,147,483,647)个字节之间。

Int

Int32

一个 32 位的有符号整数。

Money

Decimal

一个货币值,范围在 -263(即 -922,337,203,685,477.5808)到 2 63 -1(即 +922,337,203,685,477.5807)之间,精度为千分之十个货币单位。

NChar

String

Unicode 字符的固定长度流,范围在 1 到 4,000 个字符之间。

NText

String

Unicode 数据的可变长度流,最大长度为 230 - 1(或 1,073,741,823)个字符。

NVarChar

String

Unicode 字符的可变长度流,范围在 1 到 4,000 个字符之间。

注意   如果字符串大于 4,000 个字符,隐式转换会失败。在使用比 4,000 个字符更长的字符串时,请显式设置对象。
Real

Single

-3.40E +38 到 3.40E +38 范围内的浮点数字。

SmallDateTime

DateTime

日期和时间数据,值范围从 1900 年 1 月 1 日到 2079 年 6 月 6 日,精度为 1 分钟。

SmallInt

Int16

一个 16 位的有符号整数。

SmallMoney

Decimal

一个货币值,范围在 -214,748.3648 到 +214,748.3647 之间,精度为千分之十个货币单位。

Text

String

非 Unicode 数据的可变长度流,最大长度为 231 -1(或 2,147,483,647) 个字符。

Timestamp

Array 类型为 Byte

自动生成的二进制数,并保证其在数据库中唯一。timestamp 通常用作对表中各行的版本进行标记的机制。存储大小为 8 字节。

TinyInt

Byte

8 位的无符号整数。

UniqueIdentifier

Guid

全局唯一标识符(或 GUID)。

VarBinary

Array 类型为 Byte

二进制数据的可变长度流,范围在 1 到 8,000 个字节之间。

注意   如果字节数组大于 8,000 个字节,隐式转换会失败。在使用比 8,000 个字节大的字节数组时,请显式设置对象。
VarChar

String

非 Unicode 字符的可变长度流,范围在 1 到 8,000 个字符之间。

Variant

Object

特殊数据类型,可以包含数值、字符串、二进制或日期数据,以及 SQL Server 值 Empty 和 Null,后面的两个值在不声明其他类型的情况下采用。

posted @ 2008-09-12 11:51 Zhuang miao 阅读(99) | 评论 (0)编辑
     摘要:

超强精华,浏览器的豪门盛宴..各种您见过以及没见过的浏览器大收集,如果您有这里没有的,请告诉我!我给补上!谢谢!

  阅读全文
posted @ 2008-07-20 16:43 Zhuang miao 阅读(2271) | 评论 (27)编辑
     摘要:

1)如果标称是“网格”或“OGSA(开放网格服务架构)”...那么,它不是云。

2)如果需要你向厂商提供一份几十页的需求说明书...那么,它不是云。

3)如果你不能用自己的信用卡来购买...那么,它不是云。

4)如果他们想卖给你硬件设备...那么,它不是云。

5)如果没有提供API...那么,它不是云。

6)如果需要你重新构架你的系统...那么,它不是云。

  阅读全文
posted @ 2008-07-16 17:04 Zhuang miao 阅读(99) | 评论 (2)编辑