目录
介绍
使用代码
介绍我们已经在前面的部分讨论了如何添加验证。这部分解释了我如何向WCF服务添加身份验证和授权。
使用代码为了向我们的服务引入身份验证和授权过程,我们将使用面向方面的编程概念,如第一章所述,我们将为此使用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;
}
}
这是我们系列的第三部分,解释了将身份验证和授权过程添加到我们的服务请求中。
您可以在此处阅读下一部分(日志记录)。
https://www.codeproject.com/Articles/5326026/Improving-WCF-Service-Quality-Part-3-Authenticatio