提高 WCF 服务质量(第三部分 - 身份验证和授权)





0/5 (0投票)
如何向 WCF 服务添加身份验证和授权
引言
在上一个部分这里,我们讨论了如何添加验证。本部分将解释我如何为WCF服务添加身份验证和授权。
Using the Code
为了将身份验证和授权过程引入我们的服务,我们将使用面向切面编程(AOP)的概念,并且如第一章所述,我们将为此目的使用PostSharp。
第一步,让我们创建一个新的类库项目,并命名为WCFService.Extensions
。此项目将引用WCFLibrary.Shared
,并将被所有服务库引用。接下来,我们需要将ServiceMethodAspect
类添加到新的类库项目中。
添加ServiceMethodAspect
类后,我们将让我们的类继承自OnMethodBoundaryAspect
类,并用三个重写的方法来装饰我们的类:OnEntry
、OnExit
和OnException
。
public class ServiceMethodAspect : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
}
public override void OnExit(MethodExecutionArgs args)
{
}
public override void OnException(MethodExecutionArgs args)
{
}
}
完成此操作后,您会发现OnMethodBoundaryAspect
类无法识别。因此,请前往NuGet程序包管理器控制台,安装PostSharp
和PostSharp.Redist
库,并添加对PostSharp.Aspects
命名空间的引用。
此时,您的项目将无法成功生成,因为PostSharp在项目中使用需要有效的许可证,而我们还没有许可证。如果您符合要求,可以从此页面获取免费许可证。此外,许可证的部署描述在此页面。但最简单的方法(如果您不介意在Visual Studio中安装工具集)是访问此页面,安装扩展程序,仅此而已。许可证将自动部署,您将能够再次生成应用程序。
那么这两个方法将做什么呢?顾名思义,OnEntry
方法将处理在方法执行之前执行的操作。一旦调用您的服务方法,在执行与您的服务相关的任何内容之前,都会触发此方法。因此,这是检查身份验证和授权的好地方。但是您会发现,我们目前还没有关于请求对象的信息。这很有道理,因为我们的切面不知道哪个方法将使用哪些参数被调用。因此,我们应该找到一种方法来访问我们的输入参数。首先,让我们在OnEntry
方法的开头编写以下代码:
base.OnEntry(args);
string username = "";
string password = "";
Arguments methodArgs = args.Arguments;
foreach (var arg in methodArgs)
{
if (arg as BaseRequest!= null)
{
username = (arg as AuthenticationObject).Username;
password = (arg as AuthenticationObject).Password;
}
}
这段代码的作用是获取调用方法时传入的参数args.Arguments
,并在类型为BaseRequest
的对象中查找Username
和Password
属性以获取其值。
执行这段代码后,我们现在就拥有了输入参数中Username
和Password
属性的值。我认为这与我们在第一章所做的相符(为每个方法创建请求类作为输入参数,并让所有请求类继承自一个基类,该基类确保每个方法的请求类作为输入都具有Username
和Password
属性。在获得必要的值后,我们就可以先检查身份验证,然后检查授权,如果身份验证成功,则返回结果。
现在我们发现需要返回一个正确的响应,我们就需要弄清楚如何实现。再次参考第一章,请记住,我们将所有方法的返回类型都更改为继承BaseResponse
类的响应类,该类具有类型分别为bool
、bool
和string[]
的IsException
、IsSuccess
和Messages
属性。要访问我们的响应对象,我们需要编写以下代码:
Type returnType = ((System.Reflection.MethodInfo)args.Method).ReturnType;
object returnObj = Activator.CreateInstance(returnType);
MethodBase mb = args.Method;
这段代码做什么?首先,它通过args.Methods
获取正在执行的方法,并将其转换为System.Reflection.MethodInfo
类型,以发现方法的属性并提供对方法元数据的访问,最后通过访问ReturnType
属性获取方法的返回类型。现在我们准备好检查身份验证和授权了。因此,让我们切换回我们的WCFService.Utils
项目,添加一个名为Auth
的文件夹,并分别添加两个新类,名为AuthenticationManager
和AuthorizationManager
,并按如下方式装饰它们:
public class AuthenticationManager
{
public int? Authenticate(string username, string password, out string message)
{
message = null;
// Do your authentication work here and return UserId if authenticated, null otherwise.
}
}
public class AuthorizationManager
{
public bool IsAuthorized(int userId, string FullyQualifiedServiceName, string MethodName)
{
// Do your authorization work here according to the UserId, ServiceName and
// ServiceMethod name and return TRUE if authenticated, FALSE otherwise
}
}
Authenticate
和IsAuthorized
方法中的代码有意留空,以便用户实现自己的身份验证和授权过程。这可能使用本地数据库、云数据库、JSON文件、文本文件、注册表记录,任何您喜欢的方式。以防万一,请记住声明了一个integer
类型的标识符来保存用户数据。根据您的实现,此类型也可能根据您的选择而更改。
现在我们已经描述了所有必要的部分,让我们将所有部分组合在一起,使身份验证和授权过程能够正常工作。
public override void OnEntry(MethodExecutionArgs args)
{
base.OnEntry(args);
string username = "";
string password = "";
Arguments methodArgs = args.Arguments;
foreach (var arg in methodArgs)
{
if (arg as BaseRequest != null)
{
username = (arg as BaseRequest).Username;
password = (arg as BaseRequest).Password;
}
}
Type returnType = ((System.Reflection.MethodInfo)args.Method).ReturnType;
object returnObj = Activator.CreateInstance(returnType);
MethodBase mb = args.Method;
AuthenticationManager authenticate = new AuthenticationManager();
string authenticationMessage = null;
int? userId = authenticate.Authenticate("", "", out authenticationMessage);
if (userId.HasValue)
{ // User is authenticated, check authorization
AuthorizationManager authorize = new AuthorizationManager();
bool isAuthorized = authorize.IsAuthorized
(userId.Value, mb.DeclaringType.FullName, mb.Name);
if (isAuthorized)
{ // User is also authorized on using this services particular method
// Set base response as OK for this authentication and authorization.
// Note that this value will possibly be overwritten
// by the service method itself, so may be skipped
BaseResponse response = new BaseResponse
{
IsException = false,
IsSuccess = true,
Messages = null
};
// Set execution flow to continue to method execution
args.FlowBehavior = FlowBehavior.Continue;
// Set the return value as BaseResponse object
args.ReturnValue = response;
}
else
{ // Authorization failed, return proper response
// Get IsSuccess, IsException and Messages properties of the BaseRespone object
PropertyInfo piIsSuccess = returnType.GetProperty("IsSuccess");
PropertyInfo piIsException = returnType.GetProperty("IsException");
PropertyInfo piMessages = returnType.GetProperty("Messages");
// Set proper values on BaseReponse object
piIsSuccess.SetValue(returnObj, false);,
piIsException.SetValue(returnObj, false);
piMessages.SetValue(returnObj, new string[] { $"You are not authorized to call
this service method ({mb.DeclaringType.FullName} - {mb.Name})" });
// Set execution flow to return
args.FlowBehavior = FlowBehavior.Return;
// Set the return value as BaseResponse object
args.ReturnValue = returnObj;
}
}
else
{ // Authentication failed, return proper response
// Get IsSuccess, IsException and Messages properties of the BaseRespone object
PropertyInfo piIsSuccess = returnType.GetProperty("IsSuccess");
PropertyInfo piIsException = returnType.GetProperty("IsException");
PropertyInfo piMessages = returnType.GetProperty("Messages");
// Set proper values on BaseReponse object
piIsSuccess.SetValue(returnObj, false);
piIsException.SetValue(returnObj, false);
piMessages.SetValue(returnObj, new string[] { authenticationMessage });
// Set execution flow to return
args.FlowBehavior = FlowBehavior.Return;
// Set the return value as BaseResponse object
args.ReturnValue = returnObj;
}
}
这是我们系列文章的第三部分,介绍了如何为我们的服务请求添加身份验证和授权过程。
您可以在这里阅读下一部分(日志记录)。
历史
- 2022 年 2 月 28 日:初始版本