DebugLZQ在这篇文章里写了webQQ登陆过程,这个过程我没有去验证,但是我却突然想到另一个问题,那QQ的密码是明文的?

 

现在我简单总结一下这个登陆过程:

1. 输入:QQ号,用户的明文密码,验证码

2. 将输入进行哈希(MD5)

3. 将QQ号、验证码、哈希值传输,因为验证码每次不一样,所以计算出来的哈希,每次都是不一样的

4. 服务器端验证,成功或者失败

 

前面3步都很正常,也很安全,就算在第3步,把QQ号、验证码、哈希值得到,还是无法解出用户的密码。 

既然我们普通人员无法解出密码,那么服务器也是从用户请求的值解出密码的。

那么,问题来了,服务器是怎么验证用户的有效性的呢?

 

1. 验证哈希值——假设服务器存储的是密码哈希的值,那么很简单,验证哈希值与服务器上存储的哈希值是否一致即可——可是,这个哈希值是每次登陆,都会变化的,也就是服务器不能存一个静态的哈希值,所以,不会是验证哈希值。

    这个方法行不通 

2. 通过在服务器端运行同样的哈希过程,然后比较哈希值——这个过程是可行的,因为大家使用同样的输入和计算过程,那么最终得到结果肯定是一样的。

    但是,这有一个前提: 那就是服务器端具有与用户同样的输入值:

    用户的输入值有:QQ号、密码、验证码

    所以,服务器端需要拿到用户的明文密码才可能重现哈希过程。

3. 其他办法验证?我暂时想不出来,还有其他办法验证用户身份——欢迎提供其他验证思路

 

所以,我的结论是:

验证服务器是可以取到用户的明文密码的——也就是说存储的要么是明文密码(虽然这种可能性不大),要么存储的是可逆加密的密码。

至于腾讯公司,在内部采用了什么办法来保证密码不会被泄露,就不得而知了。 

 

 

 

posted @ 2012-01-03 12:43 小彬 阅读(3227) 评论(23) 编辑

其实前面说的这些,支持9i啊,支持数据类型啊,支持自增长啊,或者是上网搜索就能解决,或者并不需要很高的技术含量。

但是支持分页排序的问题,可是花了我不少时间(净时间2天以上)。

 

我们知道,在oracle中是使用rownum来实现分页的,需要使用三层嵌套sql,如下

 

select t3.* from (

  select t2.*, rownum as row_num from (
     select * from t order by t.id asc
  ) t2 where rownum<=20
) t3 
where t2.row_num>11
order by t3.id asc  

如果你对SQL有疑问可以点击这里 

 

而在EFOracleProvider中,生成的sql语句是这个样子的(在调试的时候,sql语句会在输出窗口打印出来)

select t2.* from (
  select t.*, rownum as row_num from t where rownum<=20 order by ID asc
) t2 where t2.row_num>10 

order by ID asc 

 

 所以我们需要在EFOracleProvider中,加入一层嵌套。

我们在EFOracleProvider中搜索"ROWNUM",很容易定位到 SqlGenerator.cs中,Visit(DbSkipExpression e) 实现了分页sql语句。

 

我们的目标是两层嵌套的sql语句里,再加1层嵌套,我们在

select t.*, rownum as row_num from t order by ID asc

外面加一个outer,主要代码如下  

 1             SqlSelectStatement outer = new SqlSelectStatement();             

 2        outer.Select.Append("ROWNUM ");
 3             outer.Select.Append("AS ");
 4             outer.Select.Append(row_numberSymbol);
 5             outer.From.Append("( ");
 6             outer.From.Append(input);
 7             outer.From.AppendLine();
 8             outer.From.Append(") ");
 9             selectStatementStack.Push(input);
10             symbolTable.EnterScope();
11             var outerSymbol = BuildSymbol(input, e, inputColumns);
12             AddFromSymbol(outer, e.Input.VariableName, outerSymbol);
13             AddDefaultColumns(outer);
14             symbolTable.ExitScope();
15             selectStatementStack.Pop();

 其中第6行,就是将原来查询,作为outer.From子句部分。


 

修改过的EFOracleProvider下载

/Files/binblog/EFOracleProvider_dll.zip

/Files/binblog/EFOracleProvider_src.zip 

 

 

 

posted @ 2012-01-02 11:17 小彬 阅读(1234) 评论(1) 编辑
看下面这个sql语句
select t2.* from (
  select t.*, rownum as row_num from t where rownum<=20 order by ID asc

) t2 where t2.row_num>10 

order by ID asc

 

因为在查询的时候,order by 的执行是在 select 之后的,所以在第一层查询中,得到的结果可能是如下

 


ID   row_num 

1        3

8        20

20      4

21      1

...

100    8

===20条记录,其中row_num字段的值在1-20

 

 

这样的子结果集,在经过第二层过滤的时候,是得不到我们想要的结果的

 


 ID 

 11    

 12

 13

 ...

 20 

 

 所以需要用如下的sql语句实现分页排序

select t3.* from (
  select t2.*, rownum as row_num from (
     select * from t order by t.id asc
  ) t2 where rownum<=20
) t3 
where t2.row_num>11

order by t3.id asc  


 

 

posted @ 2012-01-02 10:42 小彬 阅读(44) 评论(0) 编辑

我们知道Oracle不像SqlServer那样,支持原生的自动增长型,而是通过sequence来实现类似于自增长类型的效果。

对于以项目为主的公司而言,往往需要做到数据库之间的切换。而当初引入Entity Framework,一个重要的目标就是不同数据库之间的快速切换。

现在面临的一个问题就是:自增长类型,如果做到不用修改代码,就能支持无缝切换

 

说句题外话,关于主键类型的选择

1. 自增长类型

优:简单、查找方便、空间占用少

缺:不同类型数据库的支持不同、数据同步

2. Guid 的string

优:数据同步较方便

缺:占用空间大——一般用32个字符,查找不便——没有先后次序规律

3. 自定义的生成规则

 

嗯,不管你们用什么类型的主键,反正我是需要改造一下EFOracleProvider来支持如下的功能

var entity = new XX();
//去掉设置主键值
//entity.ID = 20;
entity.Column1 = something;

//dbContext.Add(entity);

在动手前,我们需要先约定以下规则:

1. 所有表的主键名称统一叫ID

2. 为每个表建立一个序列(sequence),规则是 SEQ_表名(序列的名字长度不能超过30个字符,所以表名不要超过26个字符)

 

完成该功能,需要修改以下3个文件(共4处地方)

1. Resources\EFOracleStoreSchemaDefinition.ssdl 

2. SqlGen\DmlSqlGenerator.cs 

3. SqlGen\SqlGenerator.cs 

 

1. 修改 EFOracleStoreSchemaDefinition.ssdl

定位到第36行,将

          , 0                     "IsIdentity"

修改为:

          , case c.COLUMN_NAME when 'ID' then 1 else 0 end                     "IsIdentity" 

目的在于,告诉生成工具(edmgen2,网上搜) ,如果是ID字段,在edmx文件中,加入

<Property Name="ID" Type="number" Nullable="false" Precision="10" />

变成 

<Property Name="ID" Type="number" Nullable="false" Precision="10" StoreGeneratedPattern="Identity" />

 

2. 修改insert语句的生成规则,在DmlSqlGenerator.cs文件中,找到GenerateInsertSql方法,然后用如下代码替换

GenerateInsertSql
        internal static string GenerateInsertSql(DbInsertCommandTree tree, EFOracleProviderManifest providerManifest, EFOracleVersion sqlVersion, out List<OracleParameter> parameters)
        {
            StringBuilder commandText = new StringBuilder(s_commandTextBuilderInitialCapacity);
            ExpressionTranslator translator = new ExpressionTranslator(commandText, tree,
                null != tree.Returning, sqlVersion);

            // insert [schemaName].[tableName]
            commandText.Append("insert into ");
            tree.Target.Expression.Accept(translator);
            if (0 < tree.SetClauses.Count)
            {
                // (c1, c2, c3, ...)
                commandText.Append("(");
                bool first = true;
                var dbNewInstanceExpression = tree.Returning as DbNewInstanceExpression;//自增长ID,zhb added
                bool hasAutoIdentity = dbNewInstanceExpression != null && dbNewInstanceExpression.Arguments.Count > 0;
                if (hasAutoIdentity)
                {
                    first = false;
                    dbNewInstanceExpression.Arguments[0].Accept(translator);
                }
                foreach (DbSetClause setClause in tree.SetClauses)
                {
                    if (first) { first = false; }
                    else { commandText.Append(""); }
                    setClause.Property.Accept(translator);
                }
                commandText.AppendLine(")");

                // values c1, c2, ...
                first = true;
                commandText.Append("values (");
                if (hasAutoIdentity)
                {
                    translator.Sequence(tree.Target.Expression as DbScanExpression);
                    first = false;
                }
                foreach (DbSetClause setClause in tree.SetClauses)
                {
                    if (first) { first = false; }
                    else { commandText.Append(""); }
                    setClause.Value.Accept(translator);

                    translator.RegisterMemberValue(setClause.Property, setClause.Value);
                }
                commandText.AppendLine(")");
            }
            else
            {
                // default values
                commandText.AppendLine().AppendLine("default values");
            }
            // generate returning sql
            GenerateReturningSql(commandText, tree, translator, tree.Returning, providerManifest, sqlVersion);

            parameters = translator.Parameters;
            return commandText.ToString();

 

 这段代码的作用在于,将原本

insert into t(column1) values('value1') 

变成

insert into t(id, column1) values(seq_t.nextval, 'value1') 

 

3. 由于我们在第2步时,调用了

translator.Sequence(tree.Target.Expression as DbScanExpression);

所以我们需要修改一下ExpressionTranslator 类(在DmlSqlGenerator.cs文件中),为其添加一个Sequece的方法,如下

            internal void Sequence(DbScanExpression scanExpression)
            {
                _commandText.Append(SqlGenerator.GetSequenceSql(scanExpression.Target));

            }

 

4. 由于第3步,sequence的生成,又转到了SqlGenerator类中,于是我们在SqlGenerator.cs中,添加GetSequenceSql方法

GetSequenceSql
internal static string GetSequenceSql(EntitySetBase entitySetBase)
        {
            // ##ORACLE
            
// CONSIDER caching generated SQL here

            string definingQuery = MetadataHelpers.GetMetadataProperty<string>(entitySetBase,
                MetadataHelpers.DefiningQueryMetadata);

            if (true)
            {
                // construct escaped T-SQL referencing entity set
                StringBuilder builder = new StringBuilder(50);


                string table = MetadataHelpers.GetMetadataProperty<string>(entitySetBase,
                    MetadataHelpers.TableMetadata);

                if (!string.IsNullOrEmpty(table))
                {
                    builder.Append("seq_" + table);
                }
                else
                {
                    builder.Append("seq_" + entitySetBase.Name);
                }
                builder.Append(".nextval");
                return builder.ToString();
            }


 

至此,修改代码工作算是完成了,编译重新发布吧 

 

posted @ 2012-01-02 09:31 小彬 阅读(33) 评论(0) 编辑

SqlServer中有丰富的类型 bit, byte, short, int, long 等等

但是到了Oracle, 就是一个类型Number

 

本文需要修改的东西很少,主要是告诉大家,oracle数据类型与.net 类型如何对应转换的。

只 需要修改一个地方,就可以支持sqlserver中常见的int, smallint, tinyint, bit类型

 

在EFOracleProviderManifest.cs 中,GetEdmType方法下,实现了数据库类型到.net数据类型的转换操作

我们要修改的地方在于

case "number": 

下的代码,原本已经实现对应关系如下:

oracle----.net----sqlserver 

number(1,0)<== >bool<==>bit

number(5,0)<==>short<==>smallint

number(11,0)<==>int<==>int

 

不知道当初作者写的时候,为什么把number(11,0)而不是number(10,0)转换为int(int.MaxValue=

2,147,483,647,为10位长度)

 

 

现在,把number(3,0)<==>byte<==>tinyint加上去吧

Case "number" 

如果你还是首次使用EFOracleProvider,建议你将number(10,0) 对应 int。但对于我而言,因为之前不知道这里的对应关系,所以项目中的实体都是decimal类型,再改成int类型的话,影响的地方太多,所以还是保持number(11,0)对应int类型。

 

对了,如果你要支持char,timestamp类型的话,点击这里 

 

这篇到此就结束了 

 

 

 

posted @ 2012-01-02 09:28 小彬 阅读(74) 评论(0) 编辑

EFOracleProvider的最后发布时间是2008年,但是居然不支持Oracle9i。

用Oracle官方的组件吧,两个问题:

1. 组件好大啊,好几百兆

2. 最重要在于,一直在beta 

但是经理说要将俺们的技术升级到2010,用Entity Framework+MVC+JQuery

 

那就上吧。

第一个问题,就是支持9i的问题——声明一下,这个解决办法是在网上搜来的。写在这里只是为了汇总。

为了让EFOracleProvider支持9i,需要修改3个类:EFOracleVersion、EFOracleProviderManifest、EFOracleVersionUtils(在EFOracleVersion.cs中)

1. 在enum EFOracleVersion中添加一个enum值,如下

         /// <summary>

        /// Oracle9i
        
/// </summary>
        Oracle9i = 9,

 2. 在EFOracleProviderManifest中添加一个const值

         internal const string TokenOracle9i = "9i";

 

3. 修改EFOracleVersionUtils类,如下,

 

View Code 
    /// <summary>
    
/// This class is a simple utility class that determines the version from the 
    
/// connection
    
/// </summary>
    internal static class EFOracleVersionUtils
    {
        /// <summary>
        
/// Get the version from the connection.
        
/// </summary>
        
/// <param name="connection">current connection</param>
        
/// <returns>version for the current connection</returns>
        internal static EFOracleVersion GetStorageVersion(EFOracleConnection connection)
        {
            string serverVersion = connection.ServerVersion;
            if (serverVersion.StartsWith("9."))
            {
                return EFOracleVersion.Oracle9i;
            }
            else if (serverVersion.StartsWith("10."))
            {
                return EFOracleVersion.Oracle10g;
            }
            else if (serverVersion.StartsWith("11."))
            {
                return EFOracleVersion.Oracle11g;
            }

            throw new ArgumentException("Could not determine storage version; " +
                    "a valid storage connection or a version hint is required.");
        }

        internal static string GetVersionHint(EFOracleVersion version)
        {
            switch (version)
            {
                case EFOracleVersion.Oracle9i:
                    return EFOracleProviderManifest.TokenOracle9i;

                case EFOracleVersion.Oracle10g:
                    return EFOracleProviderManifest.TokenOracle10g;

                case EFOracleVersion.Oracle11g:
                    return EFOracleProviderManifest.TokenOracle11g;

                default:
                    throw new ArgumentException("Could not determine storage version; " +
                            "a valid storage connection or a version hint is required.");
            }
        }

        internal static EFOracleVersion GetStorageVersion(string versionHint)
        {
            if (!string.IsNullOrEmpty(versionHint))
            {
                switch (versionHint)
                {
                    case EFOracleProviderManifest.TokenOracle9i:
                        return EFOracleVersion.Oracle9i;

                    case EFOracleProviderManifest.TokenOracle10g:
                        return EFOracleVersion.Oracle10g;

                    case EFOracleProviderManifest.TokenOracle11g:
                        return EFOracleVersion.Oracle11g;
                }
            }

            throw new ArgumentException("Could not determine storage version; " +
                    "a valid storage connection or a version hint is required.");
        }

        internal static bool IsVersionX(EFOracleVersion storageVersion)
        {
            return storageVersion == EFOracleVersion.Oracle9i || storageVersion == EFOracleVersion.Oracle10g ||
                storageVersion == EFOracleVersion.Oracle11g;
        }

 


这样,EFOracleProvider就能支持Oracle9i了。

 

修改EFOracleProvider系列预告

修改EFOracleProvider——让EFOracleProvider支持自增长类型

修改EFOracleProvider——让EFOracleProvider支持int, short, byte, bit 

修改EFOracleProvider——解决分页排序问题 

 

 修改过的EFOracleProvider下载

 dll下载

源码下载 

 

posted @ 2012-01-02 08:10 小彬 阅读(866) 评论(3) 编辑

现有m个岗位需要聘用人员

每个岗位分别需要人数为: N1,N2,...Nm

现有很多人员来应聘这m个岗位

每个人岗位是否合适,用m长的数组:[1,0,...1] 来表示,1表示可以胜任岗位职责

 

从这很多人员找出N1+N2+...+Nm=S个人,求算法:

1. 这S个人,是否能满足岗位的需求?(即每个岗位的人数要求都能满足)

2. 如果不满足要求,找出哪些岗位存在空缺? 

 

谁说信息系统是增删改查啊,尼玛这算法也太复杂了,做程序员伤不起啊 

posted @ 2011-10-19 18:18 小彬 阅读(41) 评论(1) 编辑

所谓:

欲炼神功,必先自宫!——此为第一页

就算自宫,未必成功!——此为第二页

不用自宫,也能成功!——此为第三页

 

以上为冷笑话,下面进入正题。

 

 

码斗士修炼之路

 

楼主讲得津津有味,头头是道。而我是越看越觉得太过小说,太过戏剧化了。

而跟贴也是一片叫好之声,似乎都是摩拳擦掌,跃跃欲试。

 

而我想问的是:扪心自问之,你是否会成为故事的主角!

故事里的事,说是就是,说不是也是——所以称之为故事。

故事里的人,或天赋惊人,或得遇世外高人指点,还有一个被自己欺负的反面角色始终让自己的武功更进一层!

成功的奖励一般也灰常的丰厚,一般会超过一个的漂亮(但不只是漂亮)MM对男猪脚死心塌地。

朋友们,醒醒吧,这只是故事!

现实里的人,要吃饭,要睡觉,要看家里人的期望,要受领导的脸色;有缺点,有弱点;没钱,没时间,没背景,没有莫名其妙帮助你的人;做好一件事,除了自我精神上的一点安慰,奖励一般也很少;MM也不会主动爱上你。

这就是现实!

 

所以,即使如此,你依然要坚持吗?

所以,程序员做为职业是可以的,做为成功的路径,未必人人可以做到——因为不是每个人都有这样的天赋,或者每个人都觉得自己有,但《功夫》里的包租公说:“显然,他不是!”

 

所以,小说看完就算,想象自己也会成为大侠,要小心!去炼炼武术是可以的,但是永远要知道武术不会被子弹更快!

 

当然,我的本意并不是说所有人都放弃做程序员,以及放弃提升技术了——提升技术总是有那么一点优势,常常锻炼的人总会比不锻炼的人总归体力更好。

只是不要技术看成生命的全部。

 

是否可以在程序的道路有更多的成就,就得多问自己一些问题,以下为举例说明:

1. 你觉得技术吃饭的工具,还是自己的使命?或者你是否有时觉得自己就是为了做程序员而生的?

2. 你是否可以坐枯禅?连续几年坚持做一个东西?

3. 同时期的一批人中,你是否很快崭露头角,得到很多人的认可?

4. 你是否不在乎物质条件?没钱,没房,没MM都可以,但就是不能没编程?

......

 

以上问题仅为想成为大牛的人设计,如果只是做为一门职业,不在此参考内。

注:做为一门职业,除了技术的硬技能之外,软技能有时候更重要。

 

posted @ 2011-05-20 09:03 小彬 阅读(1651) 评论(13) 编辑

1、为什么使用ORM
   1)无SQL

   i.  为了证明SQL的正确性,也许我们要把SQL语句粘贴到数据库中,运行一下——当然,经历过一段时间的SQL磨练之后,肉眼能看出大多数SQL中的错误

   ii. 各个数据库,还不完全一样


   2)业务是变化的

   i.  同一个程序,也许随着时间的变化,业务也在发生着变化。以前用到的,现在不用了;现在在用的,以前没发现。

   ii. 这个软件在A公司做了,到B公司,哎,不错,给我们也做一个吧,主要就是把这个地方、这个地方和这个地方改改,应该很简单吧——旁边插不上话的程序员,在心里恨着:“说得简单!”——如果你觉得简单,说明你还没经历过,看起来几个字段的增减,却可能在N个地方都会出现问题——你会觉得这设计做得太差了吧,但实际上大多数软件,就是这么高度的耦合着

  

    3)最根本的:我添加一个字段,我不希望界面、逻辑、数据库都要仔细查找一遍——我希望能够知道,只有一个或者几个确定的地方会需要改动,怎么改动——程序员怕什么?怕自信满满的告诉项目经理,某某问题改好了。一提交测试,却告诉你,更多问题出现。

 

2、我们需要实体吗?

    答案是未必!如果一张表里的信息,我们只是简单的存储、展现一下,需要通过实体取值、赋值做什么?

    数据库的数据,是一种值键关系,展现在我们页面上的表单,也是一种值键关系。如果我们中间加一个实体转换,我们不得不:

        //从数据库取数据

        A.A1 = db.Get("A1");//或许你还需要强制转换一下

        A.A2 = db.Get("A2");

        //展示数据

        控件A1.Text = A.A1;//或许你还需要.ToString()

        控件A2.Text = A.A2;

    但如果直接是:

        FindControl("控件A1").Text = db.Get("A1");

    这时则有可能批量对控件进行赋值

        foreach(列 in db)

        {

             FindControl(列).Text = db.Get(列);

        }

 

    这个时候,加入一个实体类,对程序反而是让操作变得烦琐——如果字段上百,你就知道,原来混点工资这么简单,在visual studio不停的敲"."就可以。

    (代码生成工具?嗯,是可以的——但是在做代码生成工具之前,我希望你先把代码写得更优雅点)

 

3、计算机关心什么?

    从上面的例子我们知道, 计算机才不关心你是用实体还是其它呢,计算机关心的是正确的、全面的信息。

    所以,如果你写了个100多个属性的实体类,但这其中99个都只是赋值、取值操作(不需要根据该值做任何判断),那你用一个Hashtable,给计算机也是一样的——一个属性,取代了100个属性

 

3、我们不需要实体吗?

    if(db.Get("A1")==1) DoSth1();

    else if(db.Get("A1")==2) DoSth2();

    每当写下这样的代码的时候,我的内心都感到忐忑不安,我害怕以下几件事发生:

    1) 某天,一个维护人员看到 "A1" 这个字段名不爽,三下五去二,就把名字给换掉了

    2) 某天,客户打来电话说,直接在数据库中,把A1的状态改为2吧,我生怕 db.Get("A1")==1时,有些事情没做,结果数据库中改成状态2,做不下去

    3) 某天,程序的后期维护人员打电话找到我,弱弱的问:那个1、2分别代表什么意思。我问:什么1、2?让我看看。结果看了半天,自己也没看懂。

    ...

    最怕的是有一天,经理走过来说,把这个程序“稍微”改改。

    所以,我们还是需要实体定义,写出的代码,还是要给人看的——从事编码几年之后,才知道,一个程序员主要时间还是在修改原来的代码,而不是全新的创作。

   

4、写代码的人,究竟关心什么?

     所以,我们知道,做为给人看的代码,并不需要把实体的每个属性逐一进行赋值、取值。

     写代码的人,关心的是,哪些字段需要进行业务上的判断。

     而对于这些需要判断的字段,我希望不会随便更改。

     如果必要要更改,我也希望能在更改的时候,编译器就能告诉我哪些地方使用了这个。

    

5、如何平衡?

     办法应该不是唯一的。

     我想到的一个简易的办法就是所有实体继承自一个基类,大概是这样

          public class EntityBase

          {

                  Dictionary<string, object> values = new Dictionary<string, object>();

                  public object this[string name]{get{return values[name];}set{values[name]=value;}}                 

          }

 

          而具体的实体,可能是这样子

          public class Student:EntityBase

          {

                 public DateTime Birthday{get{return (DateTime)this["Birthday"];} set{this["Birthday"] = value;}}

          }

 

          而使用可能是这样子的

          Student stu = db.GetEntity<Student>();

          if(stu.Birthday.Day == DateTime.Now.Day) {Show("今天是您的生日");}

 

  

posted @ 2011-01-26 14:44 小彬 阅读(112) 评论(0) 编辑
摘要: 买2台电脑,一台装QQ,用作娱乐;一台装360,用于工作。OK,Perfect!阅读全文
posted @ 2010-11-04 08:46 小彬 阅读(117) 评论(2) 编辑