阻止用户访问类字段

本文关键字:字段 访问 用户 | 更新日期: 2023-09-27 18:35:22

我正在使用C# 4.0编写一个DLL,它将由几个基于C# .NET的桌面应用程序使用(我们称之为AppA和AppB)。要求 AppA 能够使用某些类的选定字段/属性/函数,这些字段/属性/函数甚至对 AppB 不可用。我的方法是对这些属性使用internal修饰符,并通过在 DLL 的属性中指定 AppA 的程序集名称来授予对 AppA InternalsVisibleTo访问权限。但是internal在某些属性中也需要修饰符,这些修饰符将在 DLL 的其他部分中访问,但不能由 AppA 访问。现在看来,有太多internal暴露在 AppA 中,而 AppA 不应该访问这些。

换句话说,请考虑以下属性:

class A
{
    internal int ReallyInternal {get; set;}
    internal int AppAInternal {get; set;}
}

如果我对 AppA 使用 InternalsVisibleTo 属性,则ReallyInternalAppAInternal属性都将向 AppA 公开 - ReallyInternal不应该向 AppA 公开。

如何解决这个问题?还有其他方法可以实现此方案吗?

背景

在采用InternalsVisibleTo方法之前,我们考虑了其他方法,例如使用不同的接口等。我正在编写的类库将由多个应用程序使用。我希望跨应用程序的界面相同。

请考虑TT4 DLL 中的一个类。它的属性将通过串行通信从物理设备填充。

TT4 tt4 = new TT4();
// Some code to populate tt4 object
MessageBox.Show(tt4.SerialNumber);
tt4.SerialNumber = "123";

由于对象将表示物理设备tt4因此并非所有应用程序都可以修改其所有属性。这没有意义,如果我们允许这样做,那么任何应用程序都可以更改设备的序列号。(是的,序列号可以写回设备)。

我们只有一个应用程序(例如 AppA)能够设置和更改序列号。其他应用程序不应这样做。通过将序列号的设置器设置为internal并通过InternalsVisibleTo授予对 AppA 的权限来防止它。

请注意,为库提供两个类不是解决方案。比如说,我已经为TT4实现了两个类 - TT4(不能写序列号)和TT4Super(可以写序列号)。当 DLL 将提供给客户端时,他们仍然可以看到TT4Super并使用它。

我不是其他应用程序的开发人员,也无法控制它们。

阻止用户访问类字段

一种方法是将应该向 AppA 公开的所有成员提取到抽象类(或一般的父类)并使它们protected internal。要从 AppA 访问它们,它必须从抽象类继承。例如

public abstract class ParentA
{
    internal int ReallyInternal {get; set;}
    protected internal int AppAInternal {get; set;}
}

AppA 通过以下方式访问它:

internal class AinAppA : ParentA
{
    internal AinAppA()
    {
        this.AppAInternal = 1; // can access parents protected members
        // this.ReallyInternal = 2; // but pure internal members are not visible
    }
}

作为旁注,InternalsVisibleTo并不意味着是访问修饰符。它的主要目的是使单元测试更容易,而不是启用生产程序集之间的通信。

您最担心 AppB 的设计者会对您的 DLL 执行恶意操作,还是您只是想阻止他们无意中执行某些操作?让你的成员internal并不能真正阻止某人通过反思来伤害他们,如果他们愿意的话。

您可以使用的一种(诚然不是很好)方法是通过检查调用程序集来公开这些成员,但防止除 AppA 以外的任何人使用它们:

private void VerifyCaller(Assembly a)
{
    if (a == Assembly.GetExecutingAssembly()) { return; }
    var name = a.GetName();
    if(name.Name == "AppA" && name.GetPublicKey() == appAPublicKey) { return; }
    throw new InvalidOperationException("You can't access this");
}
private string _serialNumber;
public string SerialNumber
{
    get { return _serialNumber; }
    set
    {
        VerifyCaller(Assembly.GetCallingAssembly());
        _serialNumber = value;
    }
}

这至少应该防止任何人轻易地使用反射来规避你的防御。