zoukankan      html  css  js  c++  java
  • asp.net 真正实现完全跨域单点登录

    单点登录Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    asp.net跨域单点登录分为:

    1、跨子域单点登录。如 blog.a.com 和 info.a.com 这2个站点同属一个主域.a.com,实现跨子域单点登录很简单,可以利用cookie,设置Domain为".a.com'即可,这里就不再赘叙。

    2、完成跨域单点登录。如 http://www.a.com/   http://www.b.com/ 这2个站点之间实现共享一个身份验证系统,只需在一处地方登录,下面主要谈下这种方式的实现方法。 

    asp.net 跨域单点登录实现原理:

    当用户第一次访问web应用系统1的时候,因为还没有登录,会被引导到认证中心进行登录;根据用户提供的登录信息,认证系统进行身份效验,如果
    通过效验,返回给用户一个认证的凭据;用户再访问别的web应用的时候就会将这个Token带上,作为自己认证的凭据,应用系统接受到请求之后会把Token送到认证中心进行效验,检查Token的合法性。如果通过效验,用户就可以在不用再次登录的情况下访问应用系统2和应用系统3了。所有应用系统共享一个身份认证系统。认证系统的主要功能是将用户的登录信息和用户信息库相比较,对用户进行登录认证;认证成功后,认证系统应该生成统一的认证标志,返还给用户。另外,认证系统还应该对Token进行效验,判断其有效性。 所有应用系统能够识别和提取Token信息要实现SSO的功能,让用户只登录一次,就必须让应用系统能够识别已经登录过的用户。应用系统应该能对Token进行识别和提取,通过与认证系统的通讯,能自动判断当前用户是否登录过,从而完成单点登录的功能。

    比如说,我现在有3个分站点和1个认证中心(总站)。当用户访问分站点的时候,分站点会发Token到验证中心进行验证。验证中心判断用户是否已经登录。如果未登录,则返回到验证中心登录入口进行登录,否之则返回Token验证到分站点,直接进入分站点。


    如图所示:

    单点登录流程图

    上面是实现单点登录的原理图,下面介绍下如何用asp.net实现跨域单点登录:

    一、新建网站 MasterSite,作为总站认证中心。配置web.config,采用form登录验证。
          配置如下:

         

    1. <authentication mode="Forms">  
    2.   <forms name=".AspxFormAuth" loginUrl="Default.aspx" defaultUrl="center.html" protection="All" path="/" timeout="120">  
    3.   </forms>  
    4. </authentication>  
    5. <authorization>  
    6.     <!--拒绝所有匿名用户-->  
    7.     <deny users="?"/>  
    8. </authorization>  

     

          添加Default.aspx页面,用来进行登录。代码如下:
       

         HTML Code:

        

    1. <%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>  
    2.   
    3. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
    4.   
    5. <html xmlns="http://www.w3.org/1999/xhtml" >  
    6. <head runat="server">  
    7.     <title>总站登录</title>  
    8. </head>  
    9. <body>  
    10.     <form id="form1" runat="server">  
    11.     <div>  
    12.         <asp:Login ID="Login1" runat="server" OnAuthenticate="Login1_Authenticate" UserName="test">  
    13.         </asp:Login>  
    14.     </div>  
    15.     </form>  
    16. </body>  
    17. </html>  


       Default.cs Code:

      

    1. using System;  
    2. using System.Data;  
    3. using System.Configuration;  
    4. using System.Web;  
    5. using System.Web.Security;  
    6. using System.Web.UI;  
    7. using System.Web.UI.WebControls;  
    8. using System.Web.UI.WebControls.WebParts;  
    9. using System.Web.UI.HtmlControls;  
    10. using System.Text;  
    11.   
    12. public partial class _Default : System.Web.UI.Page   
    13. {  
    14.     protected void Page_Load(object sender, EventArgs e)  
    15.     {  
    16.         if (!IsPostBack)  
    17.         {  
    18.             SSORequest ssoRequest = new SSORequest();  
    19.  
    20.  
    21.             #region 验证 Post 过来的参数  
    22.             //--------------------------------  
    23.             // 请求注销  
    24.             if (!string.IsNullOrEmpty(Request["Logout"]))  
    25.             {  
    26.                 Authentication.Logout();  
    27.                 return;  
    28.             }  
    29.             //--------------------------------  
    30.             // 各独立站点标识  
    31.             if (string.IsNullOrEmpty(Request["IASID"]))  
    32.             {  
    33.                 return;  
    34.             }  
    35.             else  
    36.             {  
    37.                 ssoRequest.IASID = Request["IASID"];  
    38.             }  
    39.   
    40.             //--------------------------------  
    41.             // 时间戳  
    42.             if (string.IsNullOrEmpty(Request["TimeStamp"]))  
    43.             {  
    44.                 return;  
    45.             }  
    46.             else  
    47.             {  
    48.                 ssoRequest.TimeStamp = Request["TimeStamp"];  
    49.             }  
    50.   
    51.             //--------------------------------  
    52.             // 各独立站点的访问地址  
    53.             if (string.IsNullOrEmpty(Request["AppUrl"]))  
    54.             {  
    55.                 return;  
    56.             }  
    57.             else  
    58.             {  
    59.                 ssoRequest.AppUrl = Request["AppUrl"];  
    60.             }  
    61.   
    62.             //--------------------------------  
    63.             // 各独立站点的 Token  
    64.             if (string.IsNullOrEmpty(Request["Authenticator"]))  
    65.             {  
    66.                 return;  
    67.             }  
    68.             else  
    69.             {  
    70.                 ssoRequest.Authenticator = Request["Authenticator"];  
    71.             }  
    72.   
    73.             ViewState["SSORequest"] = ssoRequest;  
    74.  
    75.             #endregion  
    76.   
    77.   
    78.             //验证从分站发过来的Token  
    79.             if (Authentication.ValidateAppToken(ssoRequest))  
    80.             {  
    81.                 string userAccount = null;  
    82.   
    83.                 // 验证用户之前是否登录过  
    84.                 //验证 EAC 认证中心的 Cookie,验证通过时获取用户登录账号  
    85.                 if (Authentication.ValidateEACCookie(out userAccount))  
    86.                 {  
    87.                     ssoRequest.UserAccount = userAccount;  
    88.   
    89.                     //创建认证中心发往各分站的 Token  
    90.                     if (Authentication.CreateEACToken(ssoRequest))  
    91.                     {  
    92.                         Post(ssoRequest);  
    93.                     }  
    94.                 }  
    95.                 else  
    96.                 {  
    97.                     return;  
    98.                 }  
    99.             }  
    100.             else  
    101.             {  
    102.                 return;  
    103.             }  
    104.         }  
    105.     }  
    106.   
    107.   
    108.     //post请求  
    109.     void Post(SSORequest ssoRequest)  
    110.     {  
    111.         PostService ps = new PostService();  
    112.   
    113.         ps.Url = ssoRequest.AppUrl;  
    114.   
    115.         ps.Add("UserAccount", ssoRequest.UserAccount);  
    116.         ps.Add("IASID", ssoRequest.IASID);  
    117.         ps.Add("TimeStamp", ssoRequest.TimeStamp);  
    118.         ps.Add("AppUrl", ssoRequest.AppUrl);  
    119.         ps.Add("Authenticator", ssoRequest.Authenticator);  
    120.   
    121.         ps.Post();  
    122.     }  
    123.   
    124.     /// <summary>  
    125.     /// 验证登录账号和密码是否正确  
    126.     /// </summary>  
    127.     /// <param name="userName">登录账号</param>  
    128.     /// <param name="userPwd">登录密码</param>  
    129.     /// <returns></returns>  
    130.     private bool ValidateUserInfo(string userName, string userPwd)  
    131.     {  
    132.         //从数据库中读取,验证登录账号和密码  
    133.         //略...  
    134.         return true;  
    135.     }  
    136.   
    137.     protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)  
    138.     {  
    139.         if (string.IsNullOrEmpty(Login1.UserName) || string.IsNullOrEmpty(Login1.Password))  
    140.         {  
    141.             Page.RegisterClientScriptBlock("Add""<mce:script lanuage=/"javascript/"><!--  
    142. alert('用户名密码不能为空!');  
    143. // --></mce:script>");  
    144.             return;  
    145.         }  
    146.         else if (ValidateUserInfo(Login1.UserName, Login1.Password) == false)  
    147.         {  
    148.             Page.RegisterClientScriptBlock("Add""<mce:script lanuage=/"javascript/"><!--  
    149. alert('用户名密码错误!');  
    150. // --></mce:script>");  
    151.             return;  
    152.         }  
    153.         else  
    154.         {  
    155.             Session["CurrUserName"] = Login1.UserName;  
    156.             Session.Timeout = 120;  
    157.   
    158.             SSORequest ssoRequest = ViewState["SSORequest"as SSORequest;  
    159.   
    160.             // 如果不是从各分站 Post 过来的请求,则默认登录主站  
    161.             if (ssoRequest == null)  
    162.             {  
    163.                 FormsAuthentication.SetAuthCookie(Login1.UserName, false);  
    164.   
    165.                 ssoRequest = new SSORequest();  
    166.                 //主站标识ID  
    167.                 ssoRequest.IASID = "00";  
    168.                 ssoRequest.AppUrl = "SiteList.aspx";  
    169.                 ssoRequest.TimeStamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm");  
    170.                 ssoRequest.Authenticator = string.Empty;  
    171.   
    172.                 Response.Redirect("SiteList.aspx");  
    173.             }  
    174.             ssoRequest.UserAccount = Login1.UserName;  
    175.   
    176.             //创建Token  
    177.             if (Authentication.CreateEACToken(ssoRequest))  
    178.             {  
    179.                 string expireTime = DateTime.Now.AddHours(3).ToString("yyyy-MM-dd HH:mm");  
    180.   
    181.                 Authentication.CreatEACCookie(ssoRequest.UserAccount, ssoRequest.TimeStamp, expireTime);  
    182.   
    183.                 Post(ssoRequest);  
    184.             }  
    185.   
    186.         }  
    187.     }  
    188.   
    189.       
    190. }  

      代码说明:验证分站post过来的Token请求,如果用户已经登录,则创建认证中心发往各分站的 Token验证,转向分站,否之则返回登录。若是直接登录主站则转向站点选择页面sitelist.aspx,选择你要登录的分站点。

    如图:

    主站登录

    选择站点

    二、新建站点1,代码如下:

    HTML Code:

    1. <%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>  
    2.   
    3. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
    4.   
    5. <html xmlns="http://www.w3.org/1999/xhtml" >  
    6. <head runat="server">  
    7.     <title> 站点一</title>  
    8. </head>  
    9. <body>  
    10.     <form id="form1" runat="server">  
    11.     <div>  
    12.         <br />  
    13.         <br />  
    14.         <asp:LinkButton ID="LinkButton1" runat="server" OnClick="LinkButton1_Click">返回主站</asp:LinkButton>  
    15.             
    16.         <asp:LinkButton ID="LinkButton2" runat="server" OnClick="LinkButton2_Click">注销登录</asp:LinkButton></div>  
    17.     </form>  
    18. </body>  
    19. </html>  

    Default.cs code:

    1. using System;  
    2. using System.Data;  
    3. using System.Configuration;  
    4. using System.Web;  
    5. using System.Web.Security;  
    6. using System.Web.UI;  
    7. using System.Web.UI.WebControls;  
    8. using System.Web.UI.WebControls.WebParts;  
    9. using System.Web.UI.HtmlControls;  
    10. using System.Text;  
    11.   
    12. public partial class _Default : System.Web.UI.Page   
    13. {  
    14.     protected void Page_Load(object sender, EventArgs e)  
    15.     {  
    16.         if (!IsPostBack)  
    17.         {  
    18.             #region SSO 部分代码  
    19.             SSORequest ssoRequest = new SSORequest();  
    20.   
    21.             if (string.IsNullOrEmpty(Request["IASID"]))  
    22.             {  
    23.                 ssoRequest.IASID = "01";  
    24.                 ssoRequest.TimeStamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm");  
    25.                 ssoRequest.AppUrl = Request.Url.ToString();  
    26.                 Authentication.CreateAppToken(ssoRequest);  
    27.   
    28.                 Post(ssoRequest);  
    29.             }  
    30.             else if (!string.IsNullOrEmpty(Request["IASID"])  
    31.                 && !string.IsNullOrEmpty(Request["TimeStamp"])  
    32.                 && !string.IsNullOrEmpty(Request["AppUrl"])  
    33.                 && !string.IsNullOrEmpty(Request["UserAccount"])  
    34.                 && !string.IsNullOrEmpty(Request["Authenticator"]))  
    35.             {  
    36.                 ssoRequest.IASID = Request["IASID"];  
    37.                 ssoRequest.TimeStamp = Request["TimeStamp"];  
    38.                 ssoRequest.AppUrl = Request["AppUrl"];  
    39.                 ssoRequest.UserAccount = Request["UserAccount"];  
    40.                 ssoRequest.Authenticator = Request["Authenticator"];  
    41.   
    42.                 if (Authentication.ValidateEACToken(ssoRequest))  
    43.                 {  
    44.                     //从数据库中获取UserId  
    45.                     Session["CurrUserName"] = Request["UserAccount"];  
    46.                     Session.Timeout = 120;  
    47.                     FormsAuthentication.SetAuthCookie(Request["UserAccount"], false);  
    48.                     Response.Write(string.Format("{0},您好!欢迎来到site1,  >> 访问<a href="/" mce_href="/""http://localhost/Site2/Default.aspx/">site2</a>",ssoRequest.UserAccount));  
    49.                 }  
    50.             }  
    51.   
    52.             ViewState["SSORequest"] = ssoRequest;  
    53.  
    54.             #endregion  
    55.         }  
    56.     }  
    57.   
    58.     void Post(SSORequest ssoRequest)  
    59.     {  
    60.         PostService ps = new PostService();  
    61.         //认证中心(主站)地址  
    62.         string EACUrl = "http://localhost/MasterSite/Default.aspx";  
    63.         ps.Url = EACUrl;  
    64.         //ps.Add("UserAccount", ssoRequest.UserAccount);  
    65.         ps.Add("IASID", ssoRequest.IASID);  
    66.         ps.Add("TimeStamp", ssoRequest.TimeStamp);  
    67.         ps.Add("AppUrl", ssoRequest.AppUrl);  
    68.         ps.Add("Authenticator", ssoRequest.Authenticator);  
    69.   
    70.         ps.Post();  
    71.     }  
    72.   
    73.   
    74.     //注销登录  
    75.     protected void LinkButton2_Click(object sender, EventArgs e)  
    76.     {  
    77.         FormsAuthentication.SignOut();  
    78.   
    79.         SSORequest ssoRequest = new SSORequest();  
    80.   
    81.         ssoRequest.IASID = "01";  
    82.         ssoRequest.TimeStamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm");  
    83.         ssoRequest.AppUrl = Request.Url.ToString();  
    84.   
    85.         Authentication.CreateAppToken(ssoRequest);  
    86.   
    87.         PostService ps = new PostService();  
    88.   
    89.         //认证中心(主站)地址  
    90.         string EACUrl = "http://localhost/MasterSite/Default.aspx";  
    91.         ps.Url = EACUrl;  
    92.   
    93.         ps.Add("IASID", ssoRequest.IASID);  
    94.         ps.Add("TimeStamp", ssoRequest.TimeStamp);  
    95.         ps.Add("AppUrl", ssoRequest.AppUrl);  
    96.         ps.Add("Authenticator", ssoRequest.Authenticator);  
    97.   
    98.         ps.Add("Logout""true");  
    99.   
    100.         ps.Post();  
    101.     }  
    102.   
    103.     //返回主站  
    104.     protected void LinkButton1_Click(object sender, EventArgs e)  
    105.     {  
    106.         if (Session["CurrUserName"] != null)  
    107.         {  
    108.             Response.Redirect("http://localhost/MasterSite/SiteList.aspx");  
    109.         }  
    110.     }  
    111. }  

    配置web.config

    1. <authentication mode="Forms">  
    2.             <forms name=".AspxFormAuth" loginUrl="Default.aspx" defaultUrl="center.html" protection="All" path="/" timeout="60">  
    3.             </forms>  
    4.         </authentication>  
    5.         <authorization>  
    6.             <!--拒绝所有匿名用户-->  
    7.             <deny users="?"/>  
    8.         </authorization>  

    三、同二一样,新建站点Site2,代码如下:

    1. using System;  
    2. using System.Data;  
    3. using System.Configuration;  
    4. using System.Web;  
    5. using System.Web.Security;  
    6. using System.Web.UI;  
    7. using System.Web.UI.WebControls;  
    8. using System.Web.UI.WebControls.WebParts;  
    9. using System.Web.UI.HtmlControls;  
    10.   
    11. public partial class _Default : System.Web.UI.Page  
    12. {  
    13.     protected void Page_Load(object sender, EventArgs e)  
    14.     {  
    15.         if (!IsPostBack)  
    16.         {  
    17.             #region SSO 部分代码  
    18.             SSORequest ssoRequest = new SSORequest();  
    19.   
    20.             if (string.IsNullOrEmpty(Request["IASID"]))  
    21.             {  
    22.                 ssoRequest.IASID = "02";  
    23.                 ssoRequest.TimeStamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm");  
    24.                 ssoRequest.AppUrl = Request.Url.ToString();  
    25.                 Authentication.CreateAppToken(ssoRequest);  
    26.   
    27.                 Post(ssoRequest);  
    28.             }  
    29.             else if (!string.IsNullOrEmpty(Request["IASID"])  
    30.                 && !string.IsNullOrEmpty(Request["TimeStamp"])  
    31.                 && !string.IsNullOrEmpty(Request["AppUrl"])  
    32.                 && !string.IsNullOrEmpty(Request["UserAccount"])  
    33.                 && !string.IsNullOrEmpty(Request["Authenticator"]))  
    34.             {  
    35.                 ssoRequest.IASID = Request["IASID"];  
    36.                 ssoRequest.TimeStamp = Request["TimeStamp"];  
    37.                 ssoRequest.AppUrl = Request["AppUrl"];  
    38.                 ssoRequest.UserAccount = Request["UserAccount"];  
    39.                 ssoRequest.Authenticator = Request["Authenticator"];  
    40.   
    41.                 if (Authentication.ValidateEACToken(ssoRequest))  
    42.                 {  
    43.                     Session["CurrUserName"] = Request["UserAccount"];  
    44.                     Session.Timeout = 120;  
    45.                     FormsAuthentication.SetAuthCookie(Request["UserAccount"], false);  
    46.                     Response.Write(string.Format("{0},您好!欢迎来到site2,  >> 访问<a href="/" mce_href="/""http://localhost/Site1/Default.aspx/">site1</a>", ssoRequest.UserAccount));  
    47.                 }  
    48.             }  
    49.   
    50.             ViewState["SSORequest"] = ssoRequest;  
    51.  
    52.             #endregion  
    53.         }  
    54.     }  
    55.   
    56.     void Post(SSORequest ssoRequest)  
    57.     {  
    58.         PostService ps = new PostService();  
    59.         //认证中心(主站)地址  
    60.         string EACUrl = "http://localhost/MasterSite/Default.aspx";  
    61.         ps.Url = EACUrl;  
    62.         //ps.Add("UserAccount", ssoRequest.UserAccount);  
    63.         ps.Add("IASID", ssoRequest.IASID);  
    64.         ps.Add("TimeStamp", ssoRequest.TimeStamp);  
    65.         ps.Add("AppUrl", ssoRequest.AppUrl);  
    66.         ps.Add("Authenticator", ssoRequest.Authenticator);  
    67.   
    68.         ps.Post();  
    69.     }  
    70.   
    71.   
    72.     //注销登录  
    73.     protected void LinkButton2_Click(object sender, EventArgs e)  
    74.     {  
    75.         FormsAuthentication.SignOut();  
    76.   
    77.         SSORequest ssoRequest = new SSORequest();  
    78.   
    79.         ssoRequest.IASID = "02";  
    80.         ssoRequest.TimeStamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm");  
    81.         ssoRequest.AppUrl = Request.Url.ToString();  
    82.   
    83.         Authentication.CreateAppToken(ssoRequest);  
    84.   
    85.         PostService ps = new PostService();  
    86.   
    87.         //认证中心(主站)地址  
    88.         string EACUrl = "http://localhost/MasterSite/Default.aspx";  
    89.         ps.Url = EACUrl;  
    90.   
    91.         ps.Add("IASID", ssoRequest.IASID);  
    92.         ps.Add("TimeStamp", ssoRequest.TimeStamp);  
    93.         ps.Add("AppUrl", ssoRequest.AppUrl);  
    94.         ps.Add("Authenticator", ssoRequest.Authenticator);  
    95.   
    96.         ps.Add("Logout""true");  
    97.   
    98.         ps.Post();  
    99.     }  
    100.   
    101.     //返回主站  
    102.     protected void LinkButton1_Click(object sender, EventArgs e)  
    103.     {  
    104.         if (Session["CurrUserName"] != null)  
    105.         {  
    106.             Response.Redirect("http://localhost/MasterSite/SiteList.aspx");  
    107.         }  
    108.     }  
    109. }  

    对于tokent请求,tokent验证,需要对它进行加密、解密。

    其它代码:

    Authentication.cs

    1. using System;  
    2. using System.Data;  
    3. using System.Configuration;  
    4. using System.Web;  
    5. using System.Web.Security;  
    6. using System.Collections.Generic;  
    7. using System.Text;  
    8.   
    9. /// <summary>  
    10. /// 安全验证类  
    11. /// </summary>  
    12. public class Authentication  
    13. {  
    14.     static readonly string cookieName = "EACToken";  
    15.     static readonly string hashSplitter = "|";  
    16.   
    17.     public Authentication()  
    18.     {  
    19.     }  
    20.   
    21.     public static string GetAppKey(int appID)  
    22.     {  
    23.         //string cmdText = @"select * from ";  
    24.         return string.Empty;  
    25.     }  
    26.   
    27.     public static string GetAppKey()  
    28.     {  
    29.         return "22362E7A9285DD53A0BBC2932F9733C505DC04EDBFE00D70";  
    30.     }  
    31.   
    32.     public static string GetAppIV()  
    33.     {  
    34.         return "1E7FA9231E7FA923";  
    35.     }  
    36.   
    37.     /// <summary>  
    38.     /// 取得加密服务  
    39.     /// </summary>  
    40.     /// <returns></returns>  
    41.     static CryptoService GetCryptoService()  
    42.     {  
    43.         string key = GetAppKey();  
    44.         string IV = GetAppIV();  
    45.   
    46.         CryptoService cs = new CryptoService(key, IV);  
    47.         return cs;  
    48.     }  
    49.   
    50.     /// <summary>  
    51.     /// 创建各分站发往认证中心的 Token  
    52.     /// </summary>  
    53.     /// <param name="ssoRequest"></param>  
    54.     /// <returns></returns>  
    55.     public static bool CreateAppToken(SSORequest ssoRequest)  
    56.     {  
    57.         string OriginalAuthenticator = ssoRequest.IASID + ssoRequest.TimeStamp + ssoRequest.AppUrl;  
    58.         string AuthenticatorDigest = CryptoHelper.ComputeHashString(OriginalAuthenticator);  
    59.         string sToEncrypt = OriginalAuthenticator + AuthenticatorDigest;  
    60.         byte[] bToEncrypt = CryptoHelper.ConvertStringToByteArray(sToEncrypt);  
    61.   
    62.         CryptoService cs = GetCryptoService();  
    63.   
    64.         byte[] encrypted;  
    65.   
    66.         if (cs.Encrypt(bToEncrypt, out encrypted))  
    67.         {  
    68.             ssoRequest.Authenticator = CryptoHelper.ToBase64String(encrypted);  
    69.   
    70.             return true;  
    71.         }  
    72.         else  
    73.         {  
    74.             return false;  
    75.         }  
    76.     }  
    77.   
    78.   
    79.     /// <summary>  
    80.     /// 验证从各分站发送过来的 Token  
    81.     /// </summary>  
    82.     /// <param name="ssoRequest"></param>  
    83.     /// <returns></returns>  
    84.     public static bool ValidateAppToken(SSORequest ssoRequest)  
    85.     {  
    86.         string Authenticator = ssoRequest.Authenticator;  
    87.   
    88.         string OriginalAuthenticator = ssoRequest.IASID + ssoRequest.TimeStamp + ssoRequest.AppUrl;  
    89.         string AuthenticatorDigest = CryptoHelper.ComputeHashString(OriginalAuthenticator);  
    90.         string sToEncrypt = OriginalAuthenticator + AuthenticatorDigest;  
    91.         byte[] bToEncrypt = CryptoHelper.ConvertStringToByteArray(sToEncrypt);  
    92.   
    93.         CryptoService cs = GetCryptoService();  
    94.         byte[] encrypted;  
    95.   
    96.         if (cs.Encrypt(bToEncrypt, out encrypted))  
    97.         {  
    98.             return Authenticator == CryptoHelper.ToBase64String(encrypted);  
    99.         }  
    100.         else  
    101.         {  
    102.             return false;  
    103.         }  
    104.     }  
    105.   
    106.   
    107.     /// <summary>  
    108.     /// 创建认证中心发往各分站的 Token  
    109.     /// </summary>  
    110.     /// <param name="ssoRequest"></param>  
    111.     /// <returns></returns>  
    112.     public static bool CreateEACToken(SSORequest ssoRequest)  
    113.     {  
    114.         string OriginalAuthenticator = ssoRequest.UserAccount + ssoRequest.IASID + ssoRequest.TimeStamp + ssoRequest.AppUrl;  
    115.         string AuthenticatorDigest = CryptoHelper.ComputeHashString(OriginalAuthenticator);  
    116.         string sToEncrypt = OriginalAuthenticator + AuthenticatorDigest;  
    117.         byte[] bToEncrypt = CryptoHelper.ConvertStringToByteArray(sToEncrypt);  
    118.   
    119.         CryptoService cs = GetCryptoService();  
    120.         byte[] encrypted;  
    121.   
    122.         if (cs.Encrypt(bToEncrypt, out encrypted))  
    123.         {  
    124.             ssoRequest.Authenticator = CryptoHelper.ToBase64String(encrypted);  
    125.   
    126.             return true;  
    127.         }  
    128.         else  
    129.         {  
    130.             return false;  
    131.         }  
    132.     }  
    133.   
    134.   
    135.     /// <summary>  
    136.     /// 验证从认证中心发送过来的 Token  
    137.     /// </summary>  
    138.     /// <param name="ssoRequest"></param>  
    139.     /// <returns></returns>  
    140.     public static bool ValidateEACToken(SSORequest ssoRequest)  
    141.     {  
    142.         string Authenticator = ssoRequest.Authenticator;  
    143.   
    144.         string OriginalAuthenticator = ssoRequest.UserAccount + ssoRequest.IASID + ssoRequest.TimeStamp + ssoRequest.AppUrl;  
    145.         string AuthenticatorDigest = CryptoHelper.ComputeHashString(OriginalAuthenticator);  
    146.         string sToEncrypt = OriginalAuthenticator + AuthenticatorDigest;  
    147.         byte[] bToEncrypt = CryptoHelper.ConvertStringToByteArray(sToEncrypt);  
    148.   
    149.         string EncryCurrentAuthenticator = string.Empty;  
    150.         CryptoService cs = GetCryptoService();  
    151.         byte[] encrypted;  
    152.   
    153.         if (cs.Encrypt(bToEncrypt, out encrypted))  
    154.         {  
    155.             EncryCurrentAuthenticator = CryptoHelper.ToBase64String(encrypted);  
    156.   
    157.             return Authenticator == EncryCurrentAuthenticator;  
    158.         }  
    159.         else  
    160.         {  
    161.             return false;  
    162.         }  
    163.     }  
    164.   
    165.   
    166.     /// <summary>  
    167.     /// 创建 EAC 认证中心的 Cookie  
    168.     /// </summary>  
    169.     /// <param name="userAccount"></param>  
    170.     /// <param name="timeStamp"></param>  
    171.     /// <param name="expireTime"></param>  
    172.     /// <param name="cookieValue"></param>  
    173.     /// <returns></returns>  
    174.     public static bool CreatEACCookie(string userAccount, string timeStamp, string expireTime)  
    175.     {  
    176.         string plainText = "UserAccount=" + userAccount + ";TimeStamp=" + timeStamp + ";ExpireTime=" + expireTime;  
    177.         plainText += hashSplitter + CryptoHelper.ComputeHashString(plainText);  
    178.   
    179.         CryptoService cs = GetCryptoService();  
    180.         byte[] encrypted;  
    181.   
    182.         if (cs.Encrypt(CryptoHelper.ConvertStringToByteArray(plainText), out encrypted))  
    183.         {  
    184.             string cookieValue = CryptoHelper.ToBase64String(encrypted);  
    185.             SetCookie(cookieValue);  
    186.   
    187.             return true;  
    188.         }  
    189.         else  
    190.         {  
    191.             return false;  
    192.         }  
    193.     }  
    194.   
    195.     /// <summary>  
    196.     /// 验证 EAC 认证中心的 Cookie,验证通过时获取用户登录账号  
    197.     /// </summary>  
    198.     /// <param name="userAccount">输出用户登录账号</param>  
    199.     /// <returns></returns>  
    200.     public static bool ValidateEACCookie(out string userAccount)  
    201.     {  
    202.         userAccount = string.Empty;  
    203.         try  
    204.         {  
    205.   
    206.             string cookieValue = GetCookie().Value;  
    207.             byte[] toDecrypt = CryptoHelper.FromBase64String(cookieValue);  
    208.             CryptoService cs = GetCryptoService();  
    209.   
    210.             string decrypted = string.Empty;  
    211.             if (cs.Decrypt(toDecrypt, out decrypted))  
    212.             {  
    213.   
    214.                 string[] arrTemp = decrypted.Split(Convert.ToChar(hashSplitter));  
    215.                 string plainText = arrTemp[0];  
    216.                 string hashedText = arrTemp[1];  
    217.   
    218.                 userAccount = plainText.Split(Convert.ToChar(";"))[0].Split(Convert.ToChar("="))[1];  
    219.   
    220.                 return hashedText.Replace("/0"string.Empty) == CryptoHelper.ComputeHashString(plainText);  
    221.   
    222.             }  
    223.             else  
    224.             {  
    225.                 return false;  
    226.             }  
    227.         }  
    228.         catch (Exception e)  
    229.         {  
    230.             return false;  
    231.         }  
    232.     }  
    233.   
    234.   
    235.     public static void Logout()  
    236.     {  
    237.         HttpContext.Current.Response.Cookies[cookieName].Expires = DateTime.Parse("1900-1-1");  
    238.         HttpContext.Current.Response.Cookies[cookieName].Path = "/";  
    239.     }  
    240.   
    241.     private static void SetCookie(string cookieValue)  
    242.     {  
    243.         HttpContext.Current.Response.Cookies[cookieName].Value = cookieValue;  
    244.         HttpContext.Current.Response.Cookies[cookieName].Expires = DateTime.Now.AddHours(24);  
    245.         HttpContext.Current.Response.Cookies[cookieName].Path = "/";  
    246.     }  
    247.   
    248.     private static HttpCookie GetCookie()  
    249.     {  
    250.         HttpCookie cookie = HttpContext.Current.Request.Cookies["EACToken"];  
    251.         return cookie;  
    252.     }  
    253. }  

    CryptoHelper.cs

    1. using System;  
    2. using System.Collections.Generic;  
    3. using System.Text;  
    4. using System.Security.Cryptography;  
    5.   
    6. public class CryptoHelper  
    7. {  
    8.     /// <summary>  
    9.     /// 复合 Hash:string --> byte[] --> hashed byte[] --> base64 string  
    10.     /// </summary>  
    11.     /// <param name="s"></param>  
    12.     /// <returns></returns>  
    13.     public static string ComputeHashString(string s)  
    14.     {  
    15.         return ToBase64String(ComputeHash(ConvertStringToByteArray(s)));  
    16.     }  
    17.   
    18.   
    19.     public static byte[] ComputeHash(byte[] buf)  
    20.     {  
    21.         //return ((HashAlgorithm)CryptoConfig.CreateFromName("SHA1")).ComputeHash(buf);  
    22.         return SHA1.Create().ComputeHash(buf);  
    23.   
    24.     }  
    25.   
    26.     /// <summary>  
    27.     /// //System.Convert.ToBase64String  
    28.     /// </summary>  
    29.     /// <param name="buf"></param>  
    30.     /// <returns></returns>  
    31.     public static string ToBase64String(byte[] buf)  
    32.     {  
    33.         return System.Convert.ToBase64String(buf);  
    34.     }  
    35.   
    36.   
    37.     public static byte[] FromBase64String(string s)  
    38.     {  
    39.         return System.Convert.FromBase64String(s);  
    40.     }  
    41.   
    42.     /// <summary>  
    43.     /// //Encoding.UTF8.GetBytes(s)  
    44.     /// </summary>  
    45.     /// <param name="s"></param>  
    46.     /// <returns></returns>  
    47.     public static byte[] ConvertStringToByteArray(String s)  
    48.     {  
    49.         return Encoding.UTF8.GetBytes(s);//gb2312  
    50.     }  
    51.   
    52.   
    53.     public static string ConvertByteArrayToString(byte[] buf)  
    54.     {  
    55.         //return System.Text.Encoding.GetEncoding("utf-8").GetString(buf);  
    56.   
    57.         return Encoding.UTF8.GetString(buf);  
    58.     }  
    59.   
    60.   
    61.     /// <summary>  
    62.     /// 字节数组转换为十六进制字符串  
    63.     /// </summary>  
    64.     /// <param name="buf"></param>  
    65.     /// <returns></returns>  
    66.     public static string ByteArrayToHexString(byte[] buf)  
    67.     {  
    68.         StringBuilder sb = new StringBuilder();  
    69.         for (int i = 0; i < buf.Length; i++)  
    70.         {  
    71.             sb.Append(buf[i].ToString("X").Length == 2 ? buf[i].ToString("X") : "0" + buf[i].ToString("X"));  
    72.         }  
    73.         return sb.ToString();  
    74.     }  
    75.   
    76.     /// <summary>  
    77.     /// 十六进制字符串转换为字节数组  
    78.     /// </summary>  
    79.     /// <param name="s"></param>  
    80.     /// <returns></returns>  
    81.     public static byte[] HexStringToByteArray(string s)  
    82.     {  
    83.         Byte[] buf = new byte[s.Length / 2];  
    84.         for (int i = 0; i < buf.Length; i++)  
    85.         {  
    86.             buf[i] = (byte)(Char2Hex(s.Substring(i * 2, 1)) * 0x10 + Char2Hex(s.Substring(i * 2 + 1, 1)));  
    87.         }  
    88.         return buf;  
    89.     }  
    90.   
    91.   
    92.     private static byte Char2Hex(string chr)  
    93.     {  
    94.         switch (chr)  
    95.         {  
    96.             case "0":  
    97.                 return 0x00;  
    98.             case "1":  
    99.                 return 0x01;  
    100.             case "2":  
    101.                 return 0x02;  
    102.             case "3":  
    103.                 return 0x03;  
    104.             case "4":  
    105.                 return 0x04;  
    106.             case "5":  
    107.                 return 0x05;  
    108.             case "6":  
    109.                 return 0x06;  
    110.             case "7":  
    111.                 return 0x07;  
    112.             case "8":  
    113.                 return 0x08;  
    114.             case "9":  
    115.                 return 0x09;  
    116.             case "A":  
    117.                 return 0x0a;  
    118.             case "B":  
    119.                 return 0x0b;  
    120.             case "C":  
    121.                 return 0x0c;  
    122.             case "D":  
    123.                 return 0x0d;  
    124.             case "E":  
    125.                 return 0x0e;  
    126.             case "F":  
    127.                 return 0x0f;  
    128.         }  
    129.         return 0x00;  
    130.     }  
    131. }  

    CryptoService.cs

    1. using System;  
    2. using System.Data;  
    3. using System.Configuration;  
    4. using System.Web;  
    5. using System.Web.Security;  
    6. using System.Web.UI;  
    7. using System.Web.UI.WebControls;  
    8. using System.Web.UI.WebControls.WebParts;  
    9. using System.Web.UI.HtmlControls;  
    10. using System.Text;  
    11. using System.Security.Cryptography;  
    12. using System.IO;  
    13.   
    14. public class CryptoService  
    15. {  
    16.     /// <summary>  
    17.     /// 加密的密钥  
    18.     /// </summary>  
    19.     string sKey = "22362E7A9285DD53A0BBC2932F9733C505DC04EDBFE00D70";  
    20.     string sIV = "1E7FA9231E7FA923";  
    21.   
    22.     byte[] byteKey;  
    23.     byte[] byteIV;  
    24.   
    25.     /// <summary>  
    26.     /// 加密向量  
    27.     /// </summary>  
    28.     static byte[] bIV ={ 1, 2, 3, 4, 5, 6, 7, 8 };  
    29.   
    30.     public CryptoService()  
    31.     { }  
    32.   
    33.     public CryptoService(string key, string IV)  
    34.     {  
    35.         sKey = key;  
    36.         sIV = IV;  
    37.   
    38.         byteKey = CryptoHelper.HexStringToByteArray(sKey);  
    39.         byteIV = CryptoHelper.HexStringToByteArray(sIV);  
    40.     }  
    41.   
    42.   
    43.   
    44.     /// <summary>  
    45.     /// 将明文加密,返回密文  
    46.     /// </summary>  
    47.     /// <param name="Data">要加密的字串</param>  
    48.     /// <returns></returns>  
    49.     public byte[] Encrypt(string Data)  
    50.     {  
    51.         try  
    52.         {  
    53.             byte[] ret;  
    54.   
    55.             using (MemoryStream mStream = new MemoryStream())  
    56.             using (CryptoStream cStream = new CryptoStream(mStream,  
    57.                 new TripleDESCryptoServiceProvider().CreateEncryptor(byteKey, byteIV),  
    58.                 CryptoStreamMode.Write))  
    59.             {  
    60.   
    61.                 byte[] toEncrypt = new ASCIIEncoding().GetBytes(Data);  
    62.   
    63.                 // Write the byte array to the crypto stream and flush it.  
    64.                 cStream.Write(toEncrypt, 0, toEncrypt.Length);  
    65.                 cStream.FlushFinalBlock();  
    66.   
    67.                 // Get an array of bytes from the   
    68.                 // MemoryStream that holds the   
    69.                 // encrypted data.  
    70.                 ret = mStream.ToArray();  
    71.   
    72.             }  
    73.   
    74.             return ret;  
    75.         }  
    76.         catch (CryptographicException e)  
    77.         {  
    78.             //Console.WriteLine("A Cryptographic error occurred: {0}", e.Message);  
    79.             return null;  
    80.         }  
    81.   
    82.     }  
    83.   
    84.   
    85.     /// <summary>  
    86.     /// 将明文加密,返回密文  
    87.     /// </summary>  
    88.     /// <param name="toEncrypt">明文</param>  
    89.     /// <param name="encrypted">密文</param>  
    90.     /// <returns></returns>  
    91.     public bool Encrypt(byte[] toEncrypt, out byte[] encrypted)  
    92.     {  
    93.         encrypted = null;  
    94.         try  
    95.         {  
    96.             // Create a new MemoryStream using the passed   
    97.             // array of encrypted data.  
    98.             // Create a CryptoStream using the MemoryStream   
    99.             // and the passed key and initialization vector (IV).  
    100.             using (MemoryStream mStream = new MemoryStream())  
    101.             using (CryptoStream cStream = new CryptoStream(mStream,  
    102.                 new TripleDESCryptoServiceProvider().CreateEncryptor(byteKey, byteIV),  
    103.                 CryptoStreamMode.Write))  
    104.             {  
    105.   
    106.                 // Write the byte array to the crypto stream and flush it.  
    107.                 cStream.Write(toEncrypt, 0, toEncrypt.Length);  
    108.                 cStream.FlushFinalBlock();  
    109.   
    110.                 // Get an array of bytes from the   
    111.                 // MemoryStream that holds the   
    112.                 // encrypted data.  
    113.                 encrypted = mStream.ToArray();  
    114.             }  
    115.   
    116.             return true;  
    117.         }  
    118.         catch (CryptographicException e)  
    119.         {  
    120.             //Console.WriteLine("A Cryptographic error occurred: {0}", e.Message);  
    121.             return false;  
    122.         }  
    123.   
    124.     }  
    125.   
    126.   
    127.   
    128.     /// <summary>  
    129.     /// 将明文加密,返回 Base64 字符串  
    130.     /// </summary>  
    131.     /// <param name="Data"></param>  
    132.     /// <returns></returns>  
    133.     public string EncryptToString(string Data)  
    134.     {  
    135.         try  
    136.         {  
    137.             string base64String = string.Empty;  
    138.   
    139.             using (MemoryStream mStream = new MemoryStream())  
    140.             using (CryptoStream cStream = new CryptoStream(mStream,  
    141.                 new TripleDESCryptoServiceProvider().CreateEncryptor(byteKey, byteIV),  
    142.                 CryptoStreamMode.Write))  
    143.             {  
    144.   
    145.                 byte[] toEncrypt = new ASCIIEncoding().GetBytes(Data);  
    146.   
    147.                 cStream.Write(toEncrypt, 0, toEncrypt.Length);  
    148.                 cStream.FlushFinalBlock();  
    149.   
    150.                 byte[] ret = mStream.ToArray();  
    151.   
    152.                 base64String = Convert.ToBase64String(ret);  
    153.             }  
    154.   
    155.             return base64String;  
    156.         }  
    157.         catch (CryptographicException e)  
    158.         {  
    159.             return null;  
    160.         }  
    161.   
    162.     }  
    163.   
    164.   
    165.     /// <summary>  
    166.     /// 将密文解密,返回明文  
    167.     /// </summary>  
    168.     /// <param name="Data">密文</param>  
    169.     /// <returns>明文</returns>  
    170.     public bool Decrypt(byte[] Data, out string decrypted)  
    171.     {  
    172.         decrypted = string.Empty;  
    173.         try  
    174.         {  
    175.   
    176.             using (MemoryStream msDecrypt = new MemoryStream(Data))  
    177.             using (CryptoStream csDecrypt = new CryptoStream(msDecrypt,  
    178.                 new TripleDESCryptoServiceProvider().CreateDecryptor(byteKey, byteIV),  
    179.                 CryptoStreamMode.Read))  
    180.             {  
    181.   
    182.                 byte[] fromEncrypt = new byte[Data.Length];  
    183.   
    184.                 // Read the decrypted data out of the crypto stream  
    185.                 // and place it into the temporary buffer.  
    186.                 csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);  
    187.   
    188.                 decrypted = Encoding.UTF8.GetString(fromEncrypt);//new ASCIIEncoding().GetString(fromEncrypt);  
    189.   
    190.                 return true;  
    191.             }  
    192.         }  
    193.         catch (CryptographicException e)  
    194.         {  
    195.             return false;  
    196.         }  
    197.     }  
    198.   
    199. }  
      

    PostService.cs

    1. using System;  
    2. using System.Collections.Generic;  
    3. using System.Text;  
    4.   
    5. public class PostService  
    6. {  
    7.     private System.Collections.Specialized.NameValueCollection Inputs = new System.Collections.Specialized.NameValueCollection();  
    8.     public string Url = "";  
    9.     public string Method = "post";  
    10.     public string FormName = "form1";  
    11.   
    12.     /// <summary>  
    13.     /// 添加需要提交的名和值  
    14.     /// </summary>  
    15.     /// <param name="name"></param>  
    16.     /// <param name="value"></param>  
    17.     public void Add(string name, string value)  
    18.     {  
    19.         Inputs.Add(name, value);  
    20.     }  
    21.   
    22.     /// <summary>  
    23.     /// 以输出Html方式POST  
    24.     /// </summary>  
    25.     public void Post()  
    26.     {  
    27.         System.Web.HttpContext.Current.Response.Clear();  
    28.   
    29.         string html = string.Empty;  
    30.   
    31.         html += ("<html><head>");  
    32.         html += (string.Format("</head><body onload=/"document.{0}.submit()/">", FormName));  
    33.         html += (string.Format("<form name=/"{0}/" method=/"{1}/" action=/"{2}/" >", FormName, Method, Url));  
    34.         try  
    35.         {  
    36.             for (int i = 0; i < Inputs.Keys.Count; i++)  
    37.             {  
    38.                 html += (string.Format("<input name=/"{0}/" type=/"hidden/" value=/"{1}/">", Inputs.Keys[i], Inputs[Inputs.Keys[i]]));  
    39.             }  
    40.             html += ("</form>");  
    41.             html += ("</body></html>");  
    42.   
    43.             System.Web.HttpContext.Current.Response.Write(html);  
    44.             System.Web.HttpContext.Current.Response.End();  
    45.         }  
    46.         catch (Exception ee)  
    47.         {  
    48.             //  
    49.         }  
    50.     }  
    51. }  

    SSORequest.cs

    1. using System;  
    2. using System.Data;  
    3. using System.Configuration;  
    4. using System.Web;  
    5. using System.Web.Security;  
    6. using System.Web.UI;  
    7. using System.Web.UI.WebControls;  
    8. using System.Web.UI.WebControls.WebParts;  
    9. using System.Web.UI.HtmlControls;  
    10.   
    11. [Serializable]  
    12. public class SSORequest : MarshalByRefObject  
    13. {  
    14.     public string IASID;         //各独立站点标识ID  
    15.     public string TimeStamp;     //时间戳  
    16.     public string AppUrl;        //各独立站点的访问地址  
    17.     public string Authenticator; //各独立站点的 Token  
    18.   
    19.     public string UserAccount;   //账号  
    20.     public string Password;      //密码  
    21.   
    22.     public string IPAddress;     //IP地址  
    23.   
    24.     //为ssresponse对象做准备  
    25.     public string ErrorDescription = "认证失败";   //用户认证通过,认证失败,包数据格式不正确,数据校验不正确  
    26.     public int Result = -1;  
    27.   
    28.     public SSORequest()  
    29.     {  
    30.   
    31.     }  
    32.   
    33.   
    34.     /// <summary>  
    35.     /// 获取当前页面上的SSORequest对象  
    36.     /// </summary>  
    37.     /// <param name="CurrentPage"></param>  
    38.     /// <returns></returns>  
    39.     public static SSORequest GetRequest(Page CurrentPage)  
    40.     {  
    41.         SSORequest request = new SSORequest();  
    42.         request.IPAddress = CurrentPage.Request.UserHostAddress;  
    43.         request.IASID = CurrentPage.Request["IASID"].ToString();// Request本身会Decode  
    44.         request.UserAccount = CurrentPage.Request["UserAccount"].ToString();//this.Text  
    45.         request.Password = CurrentPage.Request["Password"].ToString();  
    46.         request.AppUrl = CurrentPage.Request["AppUrl"].ToString();  
    47.         request.Authenticator = CurrentPage.Request["Authenticator"].ToString();  
    48.         request.TimeStamp = CurrentPage.Request["TimeStamp"].ToString();  
    49.         return request;  
    50.     }  
    51. }  
      

    配置web.config

    1. <authentication mode="Forms">  
    2.             <forms name=".AspxFormAuth" loginUrl="Default.aspx" defaultUrl="center.html" protection="All" path="/" timeout="60">  
    3.             </forms>  
    4.         </authentication>  
    5.         <authorization>  
    6.             <!--拒绝所有匿名用户-->  
    7.             <deny users="?"/>  
    8.         </authorization>  

    最后效果如下:登录总站后,各站点之间无需再登录,可以互相访问。

    另外,注销登录后,访问站点1 http://localhost/Site1/Default.aspx ,会自动跳转到主站登录页面 http://localhost/MasterSite/Default.aspx ,同样访问站点2 http://localhost/Site2/Default.aspx 也会转到主站登录页面。从主站登录后,分别访问站点1和站点2。

    在IIS配置虚拟目录MasterSite Site1 Site2,当然你也可以新建站点MasterSite Site1 Site2,修改hosts表
    127.0.0.1      http://www.mastersite.com/

    127.0.0.1      http://www.site1.com/

    127.0.0.1      http://www.site2.com/

    源代码下载:http://download.csdn.net/source/1571879 

    转载出处:http://blog.csdn.net/slimboy123/archive/2009/08/13/4442972.aspx

  • 相关阅读:
    JAVA-初步认识-第九章-抽象类-特点
    JAVA-初步认识-第九章-抽象类-概述
    JAVA-初步认识-第九章-面向对象-final关键字
    JAVA-初步认识-第九章-继承-子父类中的构造函数-子类的实例化过程-内存图解
    JAVA-初步认识-第九章-继承-子父类中的构造函数-子类的实例化过程-细节
    JAVA-初步认识-第九章-继承-子父类中的构造函数-子类的实例化过程
    JAVA-初步认识-第八章-继承-子父类中成员变量的特点-覆盖的应用
    JAVA-初步认识-第八章-继承-子父类中成员函数特点-覆盖
    JAVA-初步认识-第八章-继承-子父类中成员变量的内存图解
    JAVA-初步认识-第八章-继承-子父类中成员变量的特点
  • 原文地址:https://www.cnblogs.com/itjeff/p/4259823.html
Copyright © 2011-2022 走看看