我是 PLSQL 的新手,無法弄清楚如何從 Oracle 存盤程序中恢復價值。我該如何解決這個問題?
System.Exception: '引數 'result': 沒有為可變長度資料型別設定大小:String。
我正在使用 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: 'Parameter 'result': 沒有為可變長度資料型別設定大小:String。
uj5u.com熱心網友回復:
該錯誤解釋了錯誤所在 - 您需要指定引數大小。在 Oracle 和 SQL Server 中,文本引數需要一個大小。Foruid和pass驅動程式可以假設引數大小等于值的大小(這可能導致值被截斷),但是對于輸出引數,它無法知道要使用什么大小。
你可以只傳遞大小,但是......
Cmd.Parameters.Add("result", OracleType.VarChar,20)...
一個更大的問題是不安全的密碼代碼。密碼永遠不應該以明文形式存盤,它們應該被多次加鹽和散列(至少 1000)。
并且 null 一詞與 null 值不同。想象一下,如果有人創建了一個名為 的角色會發生什么"null"。這不是吹毛求疵——系統就是這樣被破解的。
更安全的實作
可追溯到 2002 年的所有 .NET 堆疊都具有安全身份驗證和密碼存盤功能。如果可以,不要自己動手。
如果您絕對必須(為什么?)使用Rfc2898DeriveBytes類使用加密強演算法散列密碼。
這很簡單:
var keygen = new Rfc2898DeriveBytes(pass, salt,1000);
var hash=k1.GetBytes(20);
并使用該散列而不是密碼。salt可以是任何隨機值。通常這與散列一起存盤,確保每個用戶帳戶都有不同的鹽。鹽和密碼經常組合并存盤在單個欄位中。存盤新密碼時,您可以計算新的 salt 和 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(*)查詢。帳戶的鹽和哈希值從資料庫加載,根據用戶輸入計算新的哈希值并比較兩個哈希值。
資料庫代碼也可以簡化。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 可以將查詢結果映射到物件,因此Account類或記錄定義如下:
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});
uj5u.com熱心網友回復:
Panagiotis Kanavos已經解釋了錯誤,您應該將密碼存盤為加鹽哈希。您還可以改進資料庫代碼以消除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;
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/381823.html
上一篇:包含型別未實作介面“IComponentConnector”
下一篇:函式未被呼叫指定次數
