[英]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 服務器文本參數中需要大小。 對於uid
和pass
,驅動程序可以假設參數大小等於值的大小(這可能導致值被截斷),但是使用 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,並比較兩個哈希值。
數據庫代碼也可以簡化。 using
比finally
更安全,因為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.