介绍

关于如何使用授权和登录管理来构建应用服务器还存在一些问题,其中之一就是用户及其角色如何在在数据库中定义。该文将解释使用TkbmMWAuthorizationManager解决此问题的一种方法。有关其他的信息,可以参考前文REST easy with kbmMW #4 – Access management

首先,我们应该有一些需要登录支持的服务器。对于此示例,我选择了FishFact REST服务器。该服务器的实现可参考kbmMW #12 – Fishfact demo using HTTP.sys transport.

添加安全登录

基于该服务器,我们将TjkbmMWAuthorizationManager添加到主窗体(Unit1)。

然后我们需要确定如何从数据库中存储和访问用户信息。由于此示例已使用ORM访问数据库,因此继续使用ORM实现用户管理。

让我们添加一个描述用户的类:

  [kbmMW_Table('name:user')]
TUser = class
private
FID:kbmMWNullable<string>;
FName:kbmMWNullable<string>;
FPassword:kbmMWNullable<string>;
FRole:kbmMWNullable<string>;
public
[kbmMW_Field('name:"id", primary:true, generator:shortGuid',ftString,)]
property ID:kbmMWNullable<string> read FID write FID; [kbmMW_Field('name:"name"',ftString,)]
[kbmMW_NotNull]
property Name:kbmMWNullable<string> read FName write FName; // A secure system should never store plain text passwords, but only SHA256 hashed ones.
// In that case make room for 64 characters.
[kbmMW_Field('name:"password"',ftString,)]
property Password:kbmMWNullable<string> read FPassword write FPassword; [kbmMW_Field('name:"role"',ftString,)]
property Role:kbmMWNullable<string> read FRole write FRole;
end;

注意这里对password的警告,在这个例子中,我们在数据库中存储了明文密码,在实际的生产系统中是绝不允许的,相反,应该保存对密码加密后的结果,稍后再解释如何实现。现在,看看基于明文密码的实现方法。

在现有的Form.OnCreate事件处理程序中,我们应该让ORM确保用户表可用。此外,我们还应该定义此服务器接受的角色(role)。

在我们的示例中,只有两种类型的用户...匿名用户和使用管理员权限登录的用户。除了一个需要管理角色的REST调用外,大多数功能都可供匿名用户使用。但首先要做的事情是:

procedure TfrmMain.FormCreate(Sender: TObject);
begin
FORM:=TkbmMWORM.Create;
FORM.OpenDatabase(kbmMWSQLiteConnectionPool1);
FORM.CreateOrUpgradeTable(TUser); // Add the one single role this application server knows about except anonymous.
AuthMgr.AddRole('AdminRole'); kbmMWServer1.AutoRegisterServices;
end;

这里有趣的部分是CreateOrUpgradeTable调用,它确保数据库中有一个名为user的表,以及一个名为AdminRole的角色的定义。

在Unit1中我们还应该记得注册TUser类,以便kbmMW知道它的存在。当然,最好的地方是main form单元的初始化部分来注册TUser类:

...
initialization
TkbmMWRTTI.EnableRTTI([TUser]);
kbmMWRegisterKnownClasses([TUser]); end.

现在可以利用用户表来实现授权管理器(authorization manager)的登录过程,这的关键点在于授权管理器必须知道是谁登录,即哪个actor登录应用服务器。因此需要定义actor,如何定义呢?那就是把已知用户完整列表定义为授权管理器的actor。这项工作可以在应用服务器启动时完成,或者在需要的时候即时完成。

现在实现授权管理器(authorization manager)的OnLogin事件:

procedure TfrmMain.AuthMgrLogin(Sender: TObject; const AActorName,
ARoleName: string; var APassPhrase: string;
var AActor: TkbmMWAuthorizationActor; var ARole: TkbmMWAuthorizationRole;
var AMessage: string);
var
user:TUser;
begin
// Lookup user with given name and password.
user:=ORM.Query<TUser>(['Name','Password'],[AActorName,APassPhrase]);
if user<>nil then
try
// Check if users role is defined. If not complain.
ARole:=AuthMgr.Roles.Get(user.Role.Value);
if ARole=nil then
AMessage:='Role not supported'
else
begin
// Check if actor exists, use it, else create one.
AActor:=AuthMgr.GetActor(AActorName);
if AActor=nil then
AActor:=AuthMgr.AddActor(AActorName,APassPhrase,ARoleName);
AMessage:='User found and is allowed login';
end;
finally
user.Free;
end
else
AMessage:='User not found';
end;

上面代码,使用ORM在数据库中查找指定名称和密码的用户,如果找到一个对应用户,将进一步检查是否存在为这个用户在数据库中定义的角色(role),如果存在,则判断这个用户是否已被定义为授权管理器中的actor,如果没有,则定义一个actor,并加入到授权管理器中。

接下来的问题是,如果数据库被修改了怎么办?例如:用户更改了密码,在这种情况下,不仅要更新数据库中的密码,还要更新授权管理器中对应actor的密码。对此可以这样实现:

procedure TUnit1.UpdateUserPassword(const AUserName, ANewPassword:string);
var
user:TUser;
begin
AuthMgr.ChangeActorPassword(AUserName,ANewPassord);
user:=ORM.Query<TUser>(['Name'],[AUserName]);
if user<>nil then
try
user.Password:=ANewPassword;
ORM.Update(user);
finally
user.Free;
end;
end;

如果要删除用户,这样实现:

procedure TUnit1.RemoveUser(const AUserName:string);
begin
AuthMgr.DeleteActor(AUserName);
ORM.Delete<TUser>(['Name'],[AUserName]);
end;

最后,我们应该准确定义登录后应该保护什么。为此,打开包含REST服务的Unit2.pas,并选择一个或多个protext方法,对其进行角色(role)定义。例如:将GetSpecieByCategory定义为管理员角色可以访问的方法。

[kbmMW_Rest('method:get, path:"specieByCategory/{category}"')]
[kbmMW_Auth('role:[AdminRole], grant:true')]
function GetSpecieByCategory([kbmMW_Rest('value:"{category}"')] const ACategory:string):TBiolifeNoImage;

记得将kbmMWSecurity添加到单元的interface uses子句中。kbmMWSecurity.pas包含kbmMW_Auth属性的定义。

接下来,确保我们在数据库中定义好了具体AdminRole角色的用户及密码,然后运行应用服务器,并尝试各种REST调用,当你试到下面的调用:

http://localhost:1111/biolife/specieByCategory/Butterflyfish

浏览器将要求你登录,如果你输入了具有AdminRole角色的用户名与正确的密码,那么会显示正确的结果。否则的话,只能得到错误信息。现在,只能访问没有利用kbmMW_Auth定义的方法,一但用kbmMW_Auth设置了REST方法,则不允许匿名调用。

 Hashing passwords

前面我提到了生产环境中存储(传输)明文密码的问题,不应该这样做,因此我们应该在存储和传输之前加密密码。然而,加密通常意味着可以反加密,只要加密密钥可以被猜测或被黑客攻击,这将再次以纯文本显示密码。由于用户有时可能会在多个服务器上重复使用相同的密码,因此我们应该尽可能地让潜在的黑客回到密码的纯文本版本。

对于REST客户端,它通常是一个Web浏览器,因此由它来决定如何发送用户名和密码。典型的方法实际上是将所有加密保留给SSL和用户证书等,以确保不仅传输的登录数据被扰乱,而且还确保浏览器和服务器之间的所有其他信息流也被扰乱。这可以参考以前写过的文章REST easy with kbmMW #3 – SSL

用SSL传输数据挺好,但还有存储的问题,不应该把明文密码存储在数据库或纯文本中。所以我们使用单向加密,也称为散列(hashing),

所以我们将使用单向加密...也称为散列。它基本上计算原始密码的(复数)总和。由于它是“总和”,因此通常不可能将逆向算出原始密码,只要使用良好的安全散列算法即可。幸运的是,kbmMW为几种安全散列算法提供原生支持。最常用的一种通常被认为是安全的,称为SHA256。

现在,当我们在OnLogin事件中收到密码时,我们需要在使用它之前对其进行哈希处理。这样做非常简单。

在uses子句中包含kbmMWHashSHA256。

var
hashed:string;
begin
hashed:=TkbmMWHashSHA256.HashAsString(APassPhrase,'somesaltvalue');//HashAsString只有kbmMW 5.06支持!
...
end;

somesaltvalue在应用程序中是保秘的,也是独一无二的。它可以是任何东西,但最好是一长串随机字符乱码。

“salt”背后的想法是,它极难被暴力尝试出明文与计算出的SHA256值之间的相关性。如果您只是单独使用密码,那么攻击者可以更轻松地尝试猜测密码,因为他们可以尝试所有字符组合,并将散列结果与您散列的嗅探哈希值相匹配。添加一个“salt”,通过尝试所有组合确保攻击者无法进行暴力攻击,因为无论攻击者试图找到什么,它都将永远不会与您在数据库中存储的值相同,这要归功于“salt”。

现在我们有一个散列后的字符串,并且将其存储在数据库中,每次我们需要弄清楚用户是否输入了正确的密码时,首先需要在服务器端散列(使用正确的"salt")然后尝试在数据库中查找散列密码(和用户名)。

译者补充:在翻译这段时,怎么感觉都没有译清楚,尽力讲的明白也不行,最后,找到这篇资料做补充,才感觉满意,密码明文传输解决办法。

REST easy with kbmMW #14 – DB Controlled login的更多相关文章

  1. REST easy with kbmMW #3 – SSL

    我在前两篇文章中展示了“REST easy with kbmMW”文章,如何使用kbmMW制作REST服务器,以及如何使用该REST服务器轻松地从数据库返回和存储数据,所有这些都在不到30行的真实数据 ...

  2. REST easy with kbmMW #20 – OpenAPI and Swagger UI

    即将推出的kbmMW更新不仅是一些bug修正,同时将包含一个新的主要功能:客户端存根生成器框架. 那什么是客户端存根生成器框架呢? 他是一个基于kbmMW smart services,可以生成由各种 ...

  3. REST easy with kbmMW #17 – Database 6 – Existing databases

    kbmMW已经包含了非常精细的功能来确定和解释数据库中表的元数据. 在下一版本中,这个功能将得到进一步加强,可以导入现有数据库中的表,自动创建与表相匹配的ORM实体类. 这意味着你能够使用kbmMW的 ...

  4. REST easy with kbmMW #4 – Access management

    在前面有关如何使用kbmMW创建REST服务器的基础上,现在已经到了考虑该如何控制用户的访问.什么是访问管理?就是“允许谁做什么"的问题. 显然,这个世界中存在数据,应该保护他而不被未授权的 ...

  5. REST easy with kbmMW #15 – Handling HTTP POST

    我被问到有关如何通过基于kbmMW智能服务(Smart Service)的REST处理POST的问题. 这篇博客文章解释了典型的POST各种形式的访问,以及如何在kbmMW中处理它们. POST变种W ...

  6. REST easy with kbmMW #24 使用kbmMW实现JSON/XML/YAML转换成对象

    你想过没有,把一个给定的xml或json生成一个Delphi类,并通过这个类完成对xml或json的读写操作吗? 不管有没有,现在kbmMW为我们实现了,看下面这行代码: var s:string; ...

  7. REST easy with kbmMW #21 – Delphi client stubs

    在之前的博文中,我提到新的存根生成器框架具有生成Delphi客户端存根所需的功能,使得开发Delphi智能客户端非常容易,完全支持编译时的类型检查和IDE类/属性帮助. 我没想到会把它包含在即将发布的 ...

  8. REST easy with kbmMW #16 – Multiple servers using HTTP.sys transport

    前文写过使用HTTP.sys转输层(TkbmMWHTTPSysServerTransport),实现一个kbmMW应用服务器. 如果在一台服务器上,同时运行多个,基于TkbmMWHTTPSysServ ...

  9. REST easy with kbmMW #1

    kbmMW 5.0支持REST服务器的开发,并且非常简单,下面看看如何实作一个REST服务器. 首先我们制作一个服务器应用程序,增加一个简单的Form,并放置kbmMW组件. 在Delphi中单击Fi ...

随机推荐

  1. UVa 674 Coin Change(完全背包)

    https://vjudge.net/problem/UVA-674 题意: 计算兑换零钱的方法共有几种. 思路: 完全背包基础题. #include<iostream> #include ...

  2. UVa 11572 唯一的雪花(滑动窗口)

    https://vjudge.net/problem/UVA-11572 题意:输入一个长度为n的序列A,找到一个尽量长的连续子序列,使得该序列中没有相同的元素. 思路:很简单的题,也没啥好解释的了. ...

  3. UVa 140 带宽

    题意:给出一个n个结点的图G和一个结点的排列,定义结点的带宽为i和相邻结点在排列中的最远距离,求出让带宽最小的结点排列. 思路:用STL的next_permutation来做确实是很方便,适当剪枝一下 ...

  4. (转)Linux I/O 调度方法

    Linux I/O 调度方法 转自https://blog.csdn.net/theorytree/article/details/6259104 操作系统的调度有 CPU调度    CPU sche ...

  5. python 千位分隔符,

    >>>) >>>'1,234,567,890'

  6. 搜索:ElasticSearch OR MySQL?

    背景 我们开发一般的企业级Web应用,其实从本质上来说,都是对数据的增删查改进行各个维度的包装.所以说,不管你的程序如何开发,基本上,都离不开数据本身.那么,在开发企业级应用的过程中,很多同学一定遇到 ...

  7. js 几个重要的特性

    背景: 语法借鉴    java 函数借鉴    scheme 原型继承借鉴    self 正则表达式借鉴    Perl 1.动态语言 函数的定义和调用  形参与实参不需要一致 形参可由 argu ...

  8. 什么是SQL游标?

    1.1游标的概念 游标(Cursor)它使用户可逐行访问由SQL Server返回的结果集.使用游标(cursor)的一个主要的原因就是把集合操作转换成单个记录处理方式.用SQL语言从数据库中检索数据 ...

  9. [Java学习] Java多态和动态绑定

    在Java中,父类的变量可以引用父类的实例,也可以引用子类的实例. 请读者先看一段代码: 1. public class Demo { 2. public static void main(Strin ...

  10. 快速搭建一个简易的KMS 服务

    xu言: 之前,闹的沸沸扬扬的KMS激活工具自身都存在问题的事.让我们对以前的什么小马激活.kms激活.各种激活工具都去打了一个深深的“?”,到底哪些能用.哪些不能用.有些还注明的里面必须要关闭杀毒软 ...