簡體   English   中英

獲取 Oracle 存儲過程 C# .NET 的參數大小錯誤

[英]Getting Parameter Size error for Oracle stored procedure C# .NET

我是 PLSQL 的新手,不知道如何從 Oracle 存儲過程中獲取值。 我該如何解決這個問題?

System.Exception:'參數'結果':沒有為可變長度數據類型設置大小:字符串。'

我正在使用 Iracle 10g

程序:

PROCEDURE LoginAuthentication(uid in users.userid%type,pass in users.password%type,result out users.role%type)
    is
    rl users.role%type;
    c number;
    begin
    
    select role into rl from users where userid=uid and password=pass and status=1;
    select count(*) into c from users where userid=uid and password=pass and status=1;
    if(c=1) then
    result:=rl;
    else
    result:='null';
    end if;
    exception
     when no_data_found then
     result:='null';
    end LoginAuthentication;

代碼:

public string Login(String id, string password)
{
    using (OracleConnection oCon = new OracleConnection("DATA SOURCE = localhost:1521;USER ID = PROJECTDB;Password=pass"))
    {
        OracleCommand Cmd = new OracleCommand();
            
        try 
        {
            Cmd.Connection = oCon;
            Cmd.CommandText = " user_package.LoginAuthentication";
            Cmd.CommandType = CommandType.StoredProcedure;

            Cmd.Parameters.Add("uid", OracleType.VarChar).Value = id;
            Cmd.Parameters.Add("pass", OracleType.VarChar).Value = password;
            Cmd.Parameters.Add("result", OracleType.VarChar).Direction = ParameterDirection.Output;

            oCon.Open();
            Cmd.ExecuteNonQuery();

            string result = Cmd.Parameters["result"].Value.ToString();

            return result;
        }
        catch (Exception ex)
        {
            throw ex;
        }
        finally 
        {
            oCon.Close();
        }
    }
}

錯誤:

System.Exception:'參數'結果':沒有為可變長度數據類型設置大小:字符串。

錯誤說明了問題所在 - 您需要指定參數大小。 在 Oracle 和 SQL 服務器文本參數中需要大小。 對於uidpass ,驅動程序可以假設參數大小等於值的大小(這可能導致值被截斷),但是使用 output 參數,它無法知道使用什么大小。

你可以通過大小但是....

Cmd.Parameters.Add("result", OracleType.VarChar,20)...

一個更大的問題是不安全的密碼代碼。 密碼永遠不應該以明文形式存儲,它們應該被多次加鹽和散列(至少 1000 次)。

null 這個詞與 null 的值不同。 想象一下如果有人創建了一個名為"null"的角色會發生什么。 這不是吹毛求疵——系統就是以這種方式被破解的。

更安全的實施

可追溯到 2002 年的所有 .NET 堆棧都具有安全身份驗證和密碼存儲功能。 如果可以的話,不要自己動手。

如果您絕對必須(為什么? )使用Rfc2898DeriveBytes class 到 hash 密碼使用加密強算法。

它很簡單:

var keygen = new Rfc2898DeriveBytes(pass, salt,1000);
var hash=k1.GetBytes(20);

並使用 hash 而不是密碼。 salt可以是任何隨機值。 通常這與 hash 一起存儲,確保每個用戶帳戶都有不同的鹽。 很多時候,鹽和密碼組合在一起並存儲在一個字段中。 存儲新密碼時,您可以計算新鹽和 hash 並將它們一起存儲在例如binary(28)字段中:

byte[] salt = new byte[8];
using (var rngCsp = new
RNGCryptoServiceProvider())
{
    rngCsp.GetBytes(salt);
}
var keygen = new Rfc2898DeriveBytes(pass, salt,1000);
var hash=keygen.GetBytes(20);

var finalHash=new byte[28];
Array.Copy(salt,finalHash,8);
Array.Copy(hash,finalHash,8,20);
...
//Store the hash

密碼檢查代碼也很少使用COUNT(*)查詢。 從數據庫加載帳戶的鹽和 hash,根據用戶輸入計算新的 hash,並比較兩個哈希值。

數據庫代碼也可以簡化。 usingfinally更安全,因為finally可以在某些災難性異常中被跳過,而using則不會。 您也可以使用像Dapper這樣的庫來消除樣板代碼。

您的代碼可以簡化為:

using (var oCon = new OracleConnection("DATA SOURCE = localhost:1521;USER ID = PROJECTDB;Password=pass"))
{
    var sql="select hash where userid=:id and status=1";
    var accountHash=oCon.SingleOrDefault<byte[]>(sql,new {id=id});


    if(accountHash == null)
    {
        //No such user
    }
    var salt=accountHash[0..8];
    var storedHash=accountHash[8..];

    var keygen = new Rfc2898DeriveBytes(pass, salt,1000);
    var inputHash=keygen.GetBytes(20);

    if(!inputHash.SequenceEqual(storedHash))
    {
        //Bad password
    }
}

不過,使用堆棧的內置密碼存儲功能會更好。

也可以修改查詢以返回角色。 Dapper 可以 map 查詢結果到對象,所以一個Account class 或記錄定義如下:

record Account(byte[] Hash,string Role);

可以在查詢中使用:

var sql="select Hash,Role where userid=:id and status=1";
var account=oCon.SingleOrDefault<Account>(sql,new {id=id});

這解決了我的問題,

Cmd.Parameters.Add("result", OracleType.VarChar,20).Direction = ParameterDirection.Output;

Panagiotis Kanavos解釋了錯誤,您應該將密碼存儲為鹽漬 hash。 您還可以改進您的數據庫代碼,以消除將COUNT行作為您的第一個查詢的冗余查詢:

select role
into   rl
from   users
where userid=uid and password=pass and status=1;

如果沒有匹配的行,將失敗並出現NO_DATA_FOUND異常,如果有重復的行,則會出現TOO_MANY_ROWS異常,因此您的第二個查詢:

select count(*)
into   c
from   users
where  userid=uid and password=pass and status=1;

只會返回值1

因此,您的代碼可以簡化為:

PROCEDURE LoginAuthentication(
  uid    IN  users.userid%type,
  pass   IN  users.password%type,
  result OUT users.role%type
)
IS
BEGIN
  SELECT role
  INTO   result
  FROM   users
  WHERE  userid   = uid
  AND    password = pass
  AND    status   = 1;
EXCEPTION
  WHEN no_data_found THEN
    result := 'null';
END LoginAuthentication;

此外,您的錯誤處理塊會將result設置為字符串文字'null' 您可能不想使用字符串文字,而只想使用NULL值。

EXCEPTION
  WHEN no_data_found THEN
    result := NULL;
END LoginAuthentication;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM