欢迎您来到 数字平台。 您尚未登录。[登录] [注册新用户]
当前位置: 论坛首页 / 栏目 产品与服务 / 文章 680

render过程抛出异常: 由于超时时间已过,该操作返回。 (异常来自 HRESULT:0x800705B4)  

点击:77325[回复顶层] [树状] [详细]
[回复留言] [回复(需要先登录)] [引用(需要先登录)]精品第 1 楼
文章id: 679
批量修改读者密码

作者: 精灵


    最近我们馆升级的DP2管理系统,升级后OPAC具有了书评、荐书等功能。这些功能没有问题一直很稳定,不过我总是感觉有些隐忧,因为很多读者证没有设定密码,如果别有用心的人利用这一点登陆系统在书评中乱加评论,就算我们可以追究读者密码保管不利的问题。但是,学校恐怕也会追究我们馆,监管不严的责任。为了杜绝这一点点的隐忧,我想对所有没有密码的读者证添加一个密码。这样,出了问题也好有个交代。

    为了解决这个问题,我想到了“添加或删除读者证”这个方案,这个方案原本是修改读者证状态或者是删除读者数据用的。我开始考虑的比较简单,我想这个方案既然可以删除读者证数据,或者修改读者证状态那么也应该可以修改读者证的密码。打开方案后,仔细看了一下,修改了里面的“string strState = DomUtil.GetElementText(this.ReaderDom.DocumentElement, "state");”语句,另外注掉了里面的一个判断,结果一测试没有效果,跟踪了里面的返回值返现返回值正常。后来,我又仔细看了一下系统的参考手册,发现里面还有一个“ChangeReaderPassword”函数专门用来修改密码的,我自己试着使用了一下,感觉还是不行。最后,实在没有办法,只好求助江老师,下面是江老师给我发回来的他与谢老师的聊天记录片段。

API SetReaderInfo()是不能修改读者的密码的。道理很简单,工作人员不应随意修改读者的密码,而API SetReaderInfo()正好是拿给读者窗使用的。如果要修改密码,可以使用API ChangeReaderPassword()。

long lRet = this.ReaderStatisForm.Channel.ChangeReaderPassword(

                    null, // stop,

                    this.textBox_reader_barcode.Text,      // 读者证条码号

                    this.textBox_reader_oldPassword.Text,        // 旧密码

                    this.textBox_reader_newPassword.Text,       // 新密码

                    out strError);

函数返回1为成功。0和-1都是失败。

如果是工作人员身份调用这个API,则旧密码参数无意义,也就是说,工作人员可以强制修改读者密码(需要changereaderpassword权限)。如果是读者身份调用这个API,则需要提供旧密码。

    下面是谢老师编写的关于修改读者密码的一个统计窗的统计方案main.cs中的内容:

// 测试修改读者密码

// 创建日期:2011/11/1

using System;

using System.Windows.Forms;

using System.IO;

using System.Text;

using System.Xml;

using System.Drawing; // Size

using DigitalPlatform;

using DigitalPlatform.Xml;

using DigitalPlatform.Text;

using DigitalPlatform.dp2.Statis;

using DigitalPlatform.CirculationClient;

using DigitalPlatform.CirculationClient.localhost;

using dp2Circulation;

public class MyStatis : ReaderStatis

{

    Table table = new Table(4);

    string strOutputErrorFilename = ""; // 输出的错误信息文件名

    StreamWriter sw_error = null;

    int nErrorCount = 0; // 发生错误的读者记录数

    public override void OnBegin(object sender, StatisEventArgs e)

    {

        this.ClearConsoleForPureTextOutputing();

        strOutputErrorFilename = this.ProjectDir + "\\error.txt";

        sw_error = new StreamWriter(strOutputErrorFilename,

                    false, // append

                    Encoding.GetEncoding(936)); // gb2312

        sw_error.Write("<pre>\r\n");

    }

    public override void OnRecord(object sender, StatisEventArgs e)

    {

        string strError = "";

        int nRet = 0;

        // 读者条码

        string strReaderBarcode = DomUtil.GetElementText(this.ReaderDom.DocumentElement, "barcode");

        this.WriteTextToConsole(this.CurrentRecordIndex.ToString() + " : " + strReaderBarcode + "\r\n");

        long lRet = this.ReaderStatisForm.Channel.ChangeReaderPassword(

                null, // stop,

                strReaderBarcode,      // 读者证条码号

                null,        // 旧密码

                "test1",       // 新密码

                out strError);

        if (lRet != 1)

        {

            nErrorCount++;

            sw_error.Write("读者记录 " + this.CurrentRecPath + " 修改密码时发生错误: " + strError + "\r\n");

        }

        return;

    ERROR1:

        MessageBox.Show(this.ReaderStatisForm, strError);

        e.Continue = ContinueType.SkipAll;

    }

    public override void FreeResources()

    {

        if (sw_error != null)

        {

            sw_error.Close();

            sw_error = null;

        }

    }

    public override void OnEnd(object sender, StatisEventArgs e)

    {

        if (sw_error != null)

        {

            sw_error.Close();

            sw_error = null;

        }

        string strText = "";

        if (nErrorCount > 0)

        {

            strText += "\r\n\r\n错误信息 " + nErrorCount.ToString() + " 条,请看文件 " + strOutputErrorFilename + " (在读者统计窗的打印结果里面可以看到)";

            // 写入打印输出文件

            string strPrintFile = this.NewOutputFileName();

            File.Copy(this.strOutputErrorFilename, strPrintFile, true);

        }

        MessageBox.Show(this.ReaderStatisForm, strText);

    }

}

    下面是我参考谢老师编写的方案后,自己修改出来的

// 测试修改读者密码

// 创建日期:2011/11/3

using System;

using System.Windows.Forms;

using System.IO;

using System.Text;

using System.Xml;

using System.Drawing;    // Size

using DigitalPlatform;

using DigitalPlatform.Xml;

using DigitalPlatform.Text;

using DigitalPlatform.dp2.Statis;

using DigitalPlatform.CirculationClient;

using DigitalPlatform.CirculationClient.localhost;

using dp2Circulation;

public class MyStatis : ReaderStatis

{

    Table table = new Table(4);

    string strOutputErrorFilename = "";    // 输出的错误信息文件名

    StreamWriter sw_error = null;

    int nErrorCount = 0;    // 发生错误的读者记录数

    public override void OnBegin(object sender, StatisEventArgs e)

    {

        this.ClearConsoleForPureTextOutputing();

        strOutputErrorFilename = this.ProjectDir + "\\error.txt";

        sw_error = new StreamWriter(strOutputErrorFilename,

                    false,    // append

                    Encoding.GetEncoding(936));    // gb2312

        sw_error.Write("<pre>\r\n");

    }

    public override void OnRecord(object sender, StatisEventArgs e)

    {

        if (DomUtil.GetElementText(this.ReaderDom.DocumentElement, "password") == "")

        {

            string strError = "";

            int nRet = 0;

            // 读者条码

            string strReaderBarcode = DomUtil.GetElementText(this.ReaderDom.DocumentElement, "barcode");

            // 读者证号

            string strcardNumber = DomUtil.GetElementText(this.ReaderDom.DocumentElement, "cardNumber");

            // 读者类型

            string strreaderType = DomUtil.GetElementText(this.ReaderDom.DocumentElement, "readerType");

            if (strreaderType == "中文专业" || strreaderType == "本科" || strreaderType == "专科")

            {

                if (strcardNumber == "")

                {

                    strcardNumber = Guid.NewGuid().ToString();

                }

            }

            else if (strreaderType == "教参借阅" || strreaderType == "教师" || strreaderType == "工人" || strreaderType == "特殊借阅")

            {

                strcardNumber = strReaderBarcode;

            }

            this.WriteTextToConsole(this.CurrentRecordIndex.ToString() + " : " + strReaderBarcode + "\r\n");

            //if (strcardNumber == "") { strcardNumber = "4fd56s4fs65f46s"; }

            long lRet = this.ReaderStatisForm.Channel.ChangeReaderPassword(

                    null,    // stop,

                    strReaderBarcode,      // 读者证条码号

                    null,        // 旧密码

                    strcardNumber,       // 新密码

                    out strError);

            if (lRet != 1)

            {

                nErrorCount++;

                sw_error.Write("读者记录 " + this.CurrentRecPath + " 修改密码时发生错误: " + strError + "\r\n");

            }

            return;

        ERROR1:

            MessageBox.Show(this.ReaderStatisForm, strError);

            e.Continue = ContinueType.SkipAll;

        }

    }

    public override void FreeResources()

    {

        if (sw_error != null)

        {

            sw_error.Close();

            sw_error = null;

        }

    }

    public override void OnEnd(object sender, StatisEventArgs e)

    {

        if (sw_error != null)

        {

            sw_error.Close();

            sw_error = null;

        }

        string strText = "";

        if (nErrorCount > 0)

        {

            strText += "\r\n\r\n错误信息 " + nErrorCount.ToString() + " 条,请看文件 " + strOutputErrorFilename + " (在读者统计窗的打印结果里面可以看到)";

            // 写入打印输出文件

            string strPrintFile = this.NewOutputFileName();

            File.Copy(this.strOutputErrorFilename, strPrintFile, true);

        }

        MessageBox.Show(this.ReaderStatisForm, strText);

    }

}

    我上面的方案是在谢老师写的方案的基础上,首先判断此读者是否有密码,如果,这个读者已经设置密码则跳过,如果没有设置密码,则想根据读者类型区分出老师与学生这两类读者,对于学生这类没有设置密码的读者首先判断这个读者是否有证号(证号这个字段中存放的是学生的学号),如果有证号这个字段,就用证号作为这个读者的密码。因为我们馆不是所有的学生数据在入库时都填写了证号,所以,对于没有证号的这类读者,我就采取了一个比较极端的处理方式,用GUID函数直接生成一个不可重复的字符串作为密码写入库中,这样,可以强制这类读者来馆修改密码。对于老师这类读者,因为人数少,所以就用他自己的借阅证号作为密码写入库中。

    以上就是这件事情的经过,以及我自己参考谢老师的方案写出的处理方法。在这里首先对谢老师以及江老师的指导表示感谢。呵呵。。谢谢二位的指导,另外,也想请二位老师,以及其他馆的老师,一起讨论一下,看看我这么做是否得当。。。是不是我把这个问题想的太严重了。同时,如果有跟我需求相同的同行们,也可以参考一下谢老师写的方案。方便大家处理这类问题。



发表时间: 2011-11-03 15:59:53
最后修改时间: 2011-11-03 16:08:24
[回复留言] [回复(需要先登录)] [引用(需要先登录)]普通文章第 2 楼
文章id: 680
关于修改密码的一些补充意见

作者: xietao


谢谢精灵把自己的心得体会写出来,与大家分享。

关于读者帐户没有密码而可能导致隐私泄露问题,绝不是空穴来风。我们对隐私和安全问题应当抱有严肃的态度。江汇泉曾经提出过这个问题,并且给出了一定的解决办法建议就是读者记录创建的时候其初始密码可以采用姓名拼音或者生日或者几个字段的内容的组合。

当时我觉得解决办法还不太成熟,也想听听用户单位的意见;另外我们的系统以灵活和可以配置为傲,我一直在想是不是可以用一个二次开发脚本函数来定制这个缺省密码的功能,但又觉得动用脚本函数是不是有些牛刀了,也要考虑大部分用户单位对于管理的简便性的要求 ---- 于是在纠结和试图平衡的过程中时间就流逝了。

我想,是不是还是尽早把这个缺省密码的功能,也可以做出来再改进,不过那样就有可能带来用户的一些颠簸了 --- 可能要跟着把配置文件改来改去。

即便是解决了缺省密码的问题,但是那些以前创建的没有密码的读者记录怎么办呢?怎么给它们加上密码?那么精灵编写的这个统计方案还是有现实意义的。可以认为是缺省密码机制的一个补充。

但,目前这个统计方案还需要通用化以后,才能作为公司推荐的统计方案放在网站上,提供给所有用户单位使用。我建议可以设定一些预定义的动作类型,比如采用生日字符串,采用姓名拼音,采用其他什么字段来填入密码。

(顺便悄悄问一句:有拼音字段么,读者记录中?)

这样使用这个统计方案的操作者就可以选用预定义的动作来执行了。

~~~

我前两天还在琢磨,精灵同志将怎样在程序中判断一个读者记录有没有密码呢?

看了今天的代码,我不得不解释一下了。精灵所采用的判断<password>元素的值是否为空的办法,是可以用的,但是理论上不严密。

为什么呢?因为读者记录中<password>这个元素中存储的是读者密码的hash码,而“空”密码hash以后可不是空哟!当然恰好现在的dp2library服务器程序在创建读者记录的时候让<password>元素内容是空,这是一个巧合。理论上说,<password>元素内容是空,或者是“空”的hash码,两种情况都算空。

那么这个“空”的hash码是什么呢?这,就是天机不可泄漏了。hash码的用意就是不要你知道,故意扰乱的。我们就不要沿着这个方向去走了。

可以改换方向思考。在dp2circulation的出纳窗中,有一种校验读者密码的方式,那么肯定dp2circulation前端采用了什么API调用来验证读者密码的。我们可以使用这个API来,验证一下读者的密码是不是空。这个方法最好,因为这个API是系统提供的,有足够的可靠性和稳定性,我们作为二次开发者就不必操心那些hash码之类的事情了。

 

这个校验读者密码的API的类型是:

long VerifyReaderPassword(Stop stop, string strReaderBarcode, string strPassword, out strError)

返回-1是出错,返回0是校验不匹配,返回1是匹配。

调用这个API,需要当前用户具有verifyreaderpassword的权限。如果是读者类型的帐户,则只能校验自己的密码,不能校验其他人的密码。

希望精灵可以试一试用这个函数判断读者当前是否有了密码。

当然,这个API和其他API一样,是在Channel里面的一个函数,调用时形态如下:

long lRet = this.ReaderStatisForm.Channel.VerifyReaderPassword(

null, // stop,

strReaderBarcode, // 读者证条码号

"", // 要检测的密码

out strError);



发表时间: 2011-11-03 16:22:36
最后修改时间: 2011-11-03 16:33:30



[回复留言] [回复(需要先登录)] [引用(需要先登录)]普通文章第 3 楼
文章id: 681

作者: xietao


精灵你说:

> 后来,我又仔细看了一下系统的参考手册,发现里面还有一个“ChangeReaderPassword”函数专门用来修改密码的,我自己试着使用了一下,感觉还是不行...

现在我想知道,同样是用了这个API,你第一次为什么“不行”呢?具体出现了什么问题?我想了解这些细节的目的是,想看看我们的函数本身或者文档或者其他环境因素有没有妨碍二次开发的地方,如果有,我们好想办法克服。



发表时间: 2011-11-03 16:37:52



在线用户
访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客 (我自己)   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客   访客访客
当前栏目在线用户数 210, 总在线用户数 229