关于单点认证的一个简单实现(结合Form认证)!

最近在做一个无敌长的项目,从五一休假做到十一休假!有一个需求就是要求单点登陆(SSO)


解决思路如下:

请求认证的网站 :用一个HttpModule 截取所有请求,判断HttpContext.User是不是Null,如为空,判断Url上是不是有签名过的认证信息, 如果有,判断签名信息是否有效,如果有效,将认证信息写入Cookie中.认证完成

认证的网站: 如果登陆页Url中有要求认证的网址,判断用户是不是已授权,如果已授权,将用户信息签名,写入Url中

二个网站都使用的Form认证

代码
请求认证网站,HttpMoudle如下

  1 None.gif using  System;
  2 None.gif using  System.Collections.Generic;
  3 None.gif using  System.Text;
  4 None.gif using  System.Web;
  5 None.gif using  System.Web.Security;
  6 None.gif using  System.Security.Principal;
  7 None.gif
  8 None.gif namespace  SSO
  9 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
 10ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
 11InBlock.gif    /// 单点认证的HttpModule
 12ExpandedSubBlockEnd.gif    /// </summary>

 13InBlock.gif    public class SSOAuthenticateHttpModule:IHttpModule
 14ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
 15InBlock.gif        public String ModuleName
 16ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 17ExpandedSubBlockStart.gifContractedSubBlock.gif            get dot.gifreturn "SSOAuthenticateHttpModule"; }
 18ExpandedSubBlockEnd.gif        }

 19InBlock.gif
 20ContractedSubBlock.gifExpandedSubBlockStart.gif        IHttpModule 成员#region IHttpModule 成员
 21InBlock.gif
 22InBlock.gif        public void Dispose()
 23ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 24InBlock.gif            
 25ExpandedSubBlockEnd.gif        }

 26InBlock.gif
 27InBlock.gif        public void Init(HttpApplication context)
 28ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 29InBlock.gif            context.AuthenticateRequest += new EventHandler(context_AuthenticateRequest);
 30InBlock.gif            context.BeginRequest += new EventHandler(context_BeginRequest);
 31ExpandedSubBlockEnd.gif        }

 32InBlock.gif
 33InBlock.gif        void context_BeginRequest(object sender, EventArgs e)
 34ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 35InBlock.gif            HttpApplication application = (HttpApplication)sender;
 36InBlock.gif
 37InBlock.gif            HttpContext context = application.Context;
 38InBlock.gif
 39InBlock.gif            HttpResponse Response = context.Response;
 40InBlock.gif            Response.AddHeader("P3P""CP=CAO PSA OUR");//加上这个,防止在Iframe的时间Cookie丢失
 41ExpandedSubBlockEnd.gif        }

 42InBlock.gif
 43InBlock.gif
 44InBlock.gif
 45InBlock.gif        void context_AuthenticateRequest(object sender, EventArgs e)
 46ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 47InBlock.gif
 48InBlock.gif
 49InBlock.gif            HttpApplication application = (HttpApplication)sender;
 50InBlock.gif
 51InBlock.gif            HttpContext context = application.Context;
 52InBlock.gif
 53InBlock.gif            HttpRequest Request = context.Request;
 54InBlock.gif            HttpResponse Response = context.Response;
 55InBlock.gif
 56InBlock.gif            //如果不是网页,就不管他了
 57InBlock.gif            if(!Request.Url.AbsolutePath.EndsWith(".aspx",StringComparison.OrdinalIgnoreCase))
 58InBlock.gif                return ;
 59InBlock.gif
 60InBlock.gif            //好像加不加都行
 61InBlock.gif            FormsAuthentication.Initialize();
 62InBlock.gif
 63InBlock.gif            //如果当前用户为空
 64InBlock.gif            if (context.User == null)
 65ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
 66InBlock.gif                //s表示签名后的信息
 67InBlock.gif                String s = Request.QueryString["s"]; 
 68InBlock.gif                //表示真正要传的信息 如果需要,可以把此部分信息也加密
 69InBlock.gif                String v =  application.Server.UrlDecode(Request.QueryString["v"]);
 70InBlock.gif
 71InBlock.gif
 72InBlock.gif                if (!String.IsNullOrEmpty(s) && !String.IsNullOrEmpty(v))
 73ExpandedSubBlockStart.gifContractedSubBlock.gif                dot.gif{
 74InBlock.gif                    //UrlDecode会把 + 号变成 '' 不知道为啥,这里再换回来,
 75InBlock.gif                    s = s.Replace(' ''+');
 76InBlock.gif
 77ContractedSubBlock.gifExpandedSubBlockStart.gif                    通过之前存的Cookie判断是不是最近的验证信息,防止别人Copy Url 地址#region 通过之前存的Cookie判断是不是最近的验证信息,防止别人Copy Url 地址
 78InBlock.gif                      HttpCookie ticksCookie = application.Request.Cookies["Ticks"];
 79InBlock.gif                    String AuthTicks = String.Empty;
 80InBlock.gif
 81InBlock.gif                    if (ticksCookie != null)
 82ExpandedSubBlockStart.gifContractedSubBlock.gif                    dot.gif{
 83InBlock.gif                        AuthTicks = ticksCookie.Value;
 84ExpandedSubBlockEnd.gif                    }

 85InBlock.gif                    //加到认证信上,签名过的信息包含此内容,只是在V中没有传过来
 86InBlock.gif                    v = v + "$" + AuthTicks;
 87ExpandedSubBlockEnd.gif                    #endregion

 88InBlock.gif                  
 89InBlock.gif                    //判断签名
 90InBlock.gif                    if (SSOClient.ValidataData(v, s))
 91ExpandedSubBlockStart.gifContractedSubBlock.gif                    dot.gif{
 92InBlock.gif                        //取出用户
 93InBlock.gif                        String userName = SSOClient.SplitUserName(v);
 94InBlock.gif                        //Token信息
 95InBlock.gif                        String tokenValue = SSOClient.SplitToken(v);
 96InBlock.gif
 97InBlock.gif                         FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
 98InBlock.gif                            1// Ticket version
 99InBlock.gif                            userName, // Username associated with ticket
100InBlock.gif                            DateTime.Now, // Date/time issued
101InBlock.gif                            DateTime.Now.AddMinutes(30), // Date/time to expire
102InBlock.gif                            false// "true" for a persistent user cookie
103InBlock.gif                            tokenValue , // User-data, in this case the roles
104InBlock.gif                            FormsAuthentication.FormsCookiePath);// Path cookie valid for
105InBlock.gif
106InBlock.gif
107InBlock.gif                        //写入自己的Cookie和用户信息
108InBlock.gif                         context.User = new GenericPrincipal(new FormsIdentity(ticket), new String[1]);
109InBlock.gif                        SSOClient.SetAuthCookie(context, userName, tokenValue, false);
110InBlock.gif    
111ExpandedSubBlockEnd.gif                    }

112InBlock.gif                    else
113ExpandedSubBlockStart.gifContractedSubBlock.gif                    dot.gif{
114InBlock.gif                        //签名无效,重定向到登陆页
115InBlock.gif                        Response.Redirect(FormsAuthentication.LoginUrl); 
116ExpandedSubBlockEnd.gif                    }

117ExpandedSubBlockEnd.gif                }

118InBlock.gif                else
119ExpandedSubBlockStart.gifContractedSubBlock.gif                dot.gif{
120InBlock.gif                    //在这里存一个Cookie信息,在验证后,由Server传回,以判断是不是自己发出的请求
121InBlock.gif                    String ticks = System.DateTime.Now.Ticks.ToString();
122InBlock.gif                    HttpCookie cookie = new HttpCookie("Ticks",ticks);
123InBlock.gif
124InBlock.gif                    application.Response.Cookies.Add(cookie);
125InBlock.gif
126InBlock.gif                    //请服务端认证
127InBlock.gif                    Response.Redirect(FormsAuthentication.LoginUrl + "?site=" + HttpUtility.UrlEncode(Request.Url.ToString()) + "&ticks=" + ticks);
128ExpandedSubBlockEnd.gif                }

129InBlock.gif
130ExpandedSubBlockEnd.gif            }

131InBlock.gif           
132ExpandedSubBlockEnd.gif        }

133InBlock.gif
134ExpandedSubBlockEnd.gif        #endregion

135ExpandedSubBlockEnd.gif    }

136ExpandedBlockEnd.gif}

137 None.gif


SSOClient 是一个助手类主要负责认证签名,设置Cookie,从Url中分离出认证信息
 1 None.gif using  System;
 2 None.gif using  System.Collections.Generic;
 3 None.gif using  System.Text;
 4 None.gif using  System.Security.Cryptography;
 5 None.gif using  System.Security.Principal;
 6 None.gif using  System.Web;
 7 None.gif using  System.Web.Security;
 8 None.gif
 9 None.gif
10 None.gif namespace  SSO
11 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
12InBlock.gif    internal class SSOClient
13ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
14InBlock.gif        private static String PublicKey = "公钥信息,我用的是RSA,你可以自己生成"
15InBlock.gif        internal static bool ValidataData(String data, String signedData)
16ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
17InBlock.gif            try
18ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
19InBlock.gif                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(512);
20InBlock.gif                RSA.FromXmlString(PublicKey);
21InBlock.gif
22InBlock.gif                UnicodeEncoding ByteConverter = new UnicodeEncoding();
23InBlock.gif
24InBlock.gif                SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider();
25InBlock.gif                return RSA.VerifyData(ByteConverter.GetBytes(data), new SHA1CryptoServiceProvider(), Convert.FromBase64String(signedData));
26ExpandedSubBlockEnd.gif            }

27InBlock.gif            catch
28ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
29InBlock.gif                return false;
30ExpandedSubBlockEnd.gif            }

31InBlock.gif
32ExpandedSubBlockEnd.gif        }

33InBlock.gif
34InBlock.gif        internal static String SplitUserName(String data)
35ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
36InBlock.gif            UnicodeEncoding ByteConverter = new UnicodeEncoding();
37InBlock.gif
38InBlock.gif            return data.Split('$')[0];
39ExpandedSubBlockEnd.gif        }

40InBlock.gif
41InBlock.gif
42InBlock.gif        internal static String SplitToken(String data)
43ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
44InBlock.gif            UnicodeEncoding ByteConverter = new UnicodeEncoding();
45InBlock.gif
46InBlock.gif
47InBlock.gif            return data.Split('$')[1];
48ExpandedSubBlockEnd.gif        }

49InBlock.gif
50InBlock.gif        internal static void SetAuthCookie(HttpContext context, String userName, String token, bool isPersistent)
51ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
52InBlock.gif
53InBlock.gif
54InBlock.gif            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
55InBlock.gif            1// Ticket version
56InBlock.gif            userName, // Username associated with ticket
57InBlock.gif            DateTime.Now, // Date/time issued
58InBlock.gif            DateTime.Now.AddMinutes(30), // Date/time to expire
59InBlock.gif            isPersistent, // "true" for a persistent user cookie
60InBlock.gif            token, // User-data, in this case the roles
61InBlock.gif            FormsAuthentication.FormsCookiePath);// Path cookie valid for
62InBlock.gif
63InBlock.gif            // Encrypt the cookie using the machine key for secure transport
64InBlock.gif            string hash = FormsAuthentication.Encrypt(ticket);
65InBlock.gif
66InBlock.gif            HttpCookie cookie = new HttpCookie(
67InBlock.gif               FormsAuthentication.FormsCookieName, // Name of auth cookie
68InBlock.gif               hash); // Hashed ticket
69InBlock.gif
70InBlock.gif            // Set the cookie's expiration time to the tickets expiration time
71InBlock.gif            if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;
72InBlock.gif
73InBlock.gif            // Add the cookie to the list for outgoing response
74InBlock.gif            context.Response.Cookies.Add(cookie);
75ExpandedSubBlockEnd.gif        }

76InBlock.gif
77InBlock.gif
78ExpandedSubBlockEnd.gif    }

79ExpandedBlockEnd.gif}

80 None.gif

被认证的网站的WebConfig文件
None.gif <? xml version="1.0" ?>
None.gif
<!--  
None.gif    注意: 除了手动编辑此文件以外,您还可以使用 
None.gif    Web 管理工具来配置应用程序的设置。可以使用 Visual Studio 中的
None.gif     “网站”->“Asp.Net 配置”选项。
None.gif    设置和注释的完整列表在 
None.gif    machine.config.comments 中,该文件通常位于 
None.gif    \Windows\Microsoft.Net\Framework\v2.x\Config 中
None.gif
-->
None.gif
< configuration >
None.gif    
< appSettings />
None.gif    
< connectionStrings />
None.gif    
< system .web >
None.gif        
<!--  
None.gif            设置 compilation debug="true" 将调试符号插入
None.gif            已编译的页面中。但由于这会 
None.gif            影响性能,因此只在开发过程中将此值 
None.gif            设置为 true。
None.gif        
-->
None.gif        
< compilation  debug ="true" >
None.gif            
< assemblies >
None.gif                
< add  assembly ="System.Security, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" /></ assemblies ></ compilation >
None.gif        
<!--
None.gif            通过 <authentication> 节可以配置 ASP.NET 使用的 
None.gif            安全身份验证模式,
None.gif            以标识传入的用户。 
None.gif        
-->
None.gif    
< authentication  mode ="Forms" >
None.gif      
< forms  loginUrl ="http://localhost/TestSSOServer/Default.aspx"  name =".ASPXFORMSAUTH"  protection ="All"  path ="/"  timeout ="30"  enableCrossAppRedirects ="true" />
None.gif    
</ authentication >
None.gif      
None.gif        
<!-- <authorization>
None.gif            <deny users="?"/>
None.gif        </authorization>
-->
None.gif            
None.gif        
None.gif        
< httpModules >
None.gif            
< add  name ="SSOAuthenticateHttpModule"  type ="SSO.SSOAuthenticateHttpModule" />
None.gif        
</ httpModules >
None.gif        
<!--
None.gif            如果在执行请求的过程中出现未处理的错误,
None.gif            则通过 <customErrors> 节可以配置相应的处理步骤。具体说来,
None.gif            开发人员通过该节可以配置
None.gif            要显示的 html 错误页
None.gif            以代替错误堆栈跟踪。
None.gif
None.gif        <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
None.gif            <error statusCode="403" redirect="NoAccess.htm" />
None.gif            <error statusCode="404" redirect="FileNotFound.htm" />
None.gif        </customErrors>
None.gif        
-->
None.gif    
</ system.web >
None.gif
</ configuration >
None.gif







认证网站,比较简单,有一个负责登陆的页面,我就在上面放了一个Button和一个TxtBox
 1 None.gif using  System;
 2 None.gif using  System.Data;
 3 None.gif using  System.Configuration;
 4 None.gif using  System.Web;
 5 None.gif using  System.Web.Security;
 6 None.gif using  System.Web.UI;
 7 None.gif using  System.Web.UI.WebControls;
 8 None.gif using  System.Web.UI.WebControls.WebParts;
 9 None.gif using  System.Web.UI.HtmlControls;
10 None.gif
11 None.gif public  partial  class  _Default : System.Web.UI.Page 
12 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
13InBlock.gif    protected void Page_Load(object sender, EventArgs e)
14ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
15InBlock.gif        if (!Page.IsPostBack)
16ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{   
17InBlock.gif            //判断是不是已认证通过--这里只是简单写了,具体你是怎么认证的,就怎么写
18InBlock.gif            if (Request.IsAuthenticated)
19ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
20InBlock.gif                //回到请求认证的网站;
21InBlock.gif                ReturnUrl();
22ExpandedSubBlockEnd.gif            }

23InBlock.gif        
24ExpandedSubBlockEnd.gif        }

25InBlock.gif        
26ExpandedSubBlockEnd.gif    }

27InBlock.gif    protected void Button1_Click(object sender, EventArgs e)
28ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
29InBlock.gif        FormsAuthentication.SetAuthCookie(TextBox1.Text,false);
30InBlock.gif
31InBlock.gif        ReturnUrl();
32InBlock.gif
33ExpandedSubBlockEnd.gif    }

34InBlock.gif
35ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
36InBlock.gif    /// 取得认证信息
37InBlock.gif    /// </summary>
38ExpandedSubBlockEnd.gif    /// <returns></returns>

39InBlock.gif    private String getAuthInfo()
40ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
41InBlock.gif        String userName = User.Identity.Name;
42InBlock.gif        String tokenValue = "这是一些附加信息,你可以写入角色什么的";//在我的应用中,我写的是一个Token
43InBlock.gif        String time = System.DateTime.Now.ToString();
44InBlock.gif
45InBlock.gif        String v = userName + "$" + tokenValue ;
46InBlock.gif
47InBlock.gif        return v;
48InBlock.gif
49InBlock.gif
50ExpandedSubBlockEnd.gif    }

51InBlock.gif
52ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
53InBlock.gif    /// 返回请求认证的网站
54ExpandedSubBlockEnd.gif    /// </summary>

55InBlock.gif    private void ReturnUrl()
56ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
57InBlock.gif     
58InBlock.gif        String strUrl = Request.QueryString["site"];
59InBlock.gif
60InBlock.gif        if (String.IsNullOrEmpty(strUrl))
61ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
62InBlock.gif            return;
63ExpandedSubBlockEnd.gif        }

64InBlock.gif
65InBlock.gif        strUrl = HttpUtility.UrlDecode(strUrl);
66InBlock.gif
67InBlock.gif        //取得认证信息
68InBlock.gif        String data = getAuthInfo();
69InBlock.gif
70ContractedSubBlock.gifExpandedSubBlockStart.gif        写入签名信息#region 写入签名信息
71InBlock.gif        if (strUrl.IndexOf('?'== -1)
72ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
73InBlock.gif
74InBlock.gif            strUrl = strUrl + "?s=" + SSO.SSOServer.SignatueData(data + "$" + Request.QueryString["ticks"]);
75InBlock.gif            strUrl = strUrl + "&v=" + Server.UrlEncode(data);
76InBlock.gif
77ExpandedSubBlockEnd.gif        }

78InBlock.gif        else
79ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
80InBlock.gif            strUrl = strUrl + "&s=" + SSO.SSOServer.SignatueData(data + "$" + Request.QueryString["ticks"]);
81InBlock.gif            strUrl = strUrl + "&v=" + Server.UrlEncode(data);
82ExpandedSubBlockEnd.gif        }

83ExpandedSubBlockEnd.gif        #endregion

84InBlock.gif
85InBlock.gif        Response.Redirect(strUrl);
86InBlock.gif  
87InBlock.gif    
88ExpandedSubBlockEnd.gif    }

89ExpandedBlockEnd.gif}

90 None.gif


认证助手类
 1 None.gif using  System;
 2 None.gif using  System.Collections.Generic;
 3 None.gif using  System.Text;
 4 None.gif using  System.Security.Cryptography;
 5 None.gif
 6 None.gif namespace  SSO
 7 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
 8InBlock.gif    public class SSOServer
 9ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
10InBlock.gif        private static String PrivateKey = "私钥信息,这个很重要,";
11InBlock.gif        
12ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
13InBlock.gif        /// 签 名用户信息
14InBlock.gif        /// </summary>
15InBlock.gif        /// <param name="data"></param>
16ExpandedSubBlockEnd.gif        /// <returns></returns>

17InBlock.gif        public static String SignatueData(String data)
18ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
19InBlock.gif            try
20ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
21InBlock.gif                //Create a UnicodeEncoder to convert between byte array and string.
22InBlock.gif                UnicodeEncoding ByteConverter = new UnicodeEncoding();
23InBlock.gif
24InBlock.gif                byte[] dataToSignatue = ByteConverter.GetBytes(data);
25InBlock.gif
26InBlock.gif                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(512);
27InBlock.gif                RSA.FromXmlString(PrivateKey);
28InBlock.gif
29InBlock.gif                String SignadString = Convert.ToBase64String(RSA.SignData(dataToSignatue, new SHA1CryptoServiceProvider()));
30InBlock.gif                return SignadString;
31InBlock.gif
32ExpandedSubBlockEnd.gif            }

33InBlock.gif            catch
34ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
35InBlock.gif                return String.Empty;
36ExpandedSubBlockEnd.gif            }

37ExpandedSubBlockEnd.gif        }

38InBlock.gif        //把一个字符串Base64编码
39InBlock.gif        public static String Base64String(String data)
40ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
41InBlock.gif            try
42ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
43InBlock.gif                //Create a UnicodeEncoder to convert between byte array and string.
44InBlock.gif                UnicodeEncoding ByteConverter = new UnicodeEncoding();
45InBlock.gif
46InBlock.gif                byte[] dataToBase = ByteConverter.GetBytes(data);
47InBlock.gif
48InBlock.gif                return Convert.ToBase64String(dataToBase);
49ExpandedSubBlockEnd.gif            }

50InBlock.gif            catch
51ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
52InBlock.gif                return String.Empty;
53ExpandedSubBlockEnd.gif            }

54InBlock.gif            
55ExpandedSubBlockEnd.gif        }

56InBlock.gif
57ExpandedSubBlockEnd.gif    }

58ExpandedBlockEnd.gif}

59 None.gif

转载于:https://www.cnblogs.com/listhome/archive/2006/10/15/529323.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值