类层次结构 - 类只应由另一个类调用

时间:2017-09-14 10:52:06

标签: swift class security hierarchy

我尝试实现Security类和Secret类。在我的整个项目中,Secret类只应由Security.getSecretInstance().doSomeSecretAction()

调用

所以Secret.doSomeSecretAction()应该抛出编译错误。

我需要Security.getSecretInstance()进行身份验证过程。

我正在寻找一个好的模式或其他东西,但我认为我的搜索关键字太糟糕了,或者我的要求是愚蠢的/不可能的。

目前我致电Security.getSecretInstance(),它会返回Secret的单例实例,但我也可以调用Secret.doSomeSecretAction()。没有区别。

你有一些模式,关键字或片段吗?

修改 我对awesome的定义是我有一个这样的方法:

Security.isAuthorized { secret in
   secret.doSomeSecretAction
 }, failure { 
   print("permission denied")
}

我只能通过这个.isAuthorized - 方法

来保密

3 个答案:

答案 0 :(得分:3)

我建议做的是在Secret内嵌套Security,将Secret设为私有,并在Security内创建可以访问Secret的非私有方法。像这样:

class Security {

    class func doSomeSecretAction() {
        Secret.doSomeSecretAction()
    }

    private class Secret {
        class func doSomeSecretAction(){
            print("Private method called")
        }
    }
}

Security.doSomeSecretAction()

此处,Security.doSomeSecretAction()可以从Security类外部调用,但Secret.doSomeSecretAction()只能在Security类内调用。

根据评论进行更新 一个可行的解决方案是声明Security private的初始化器,因此它只能从Security类内部调用并声明一个计算变量(现在我称之为shared),这是初始化程序的唯一访问点。此计算变量返回nil或基于Secret的{​​{1}}类的新实例。这样,每次调用Security.isAuthorized的函数时,都会检查授权状态,并且只有在状态被授权时才能调用该函数,否则Secret变量将返回shared,因此该方法未被调用。

nil

答案 1 :(得分:2)

当Dávid编辑他时,我正在研究这个答案;我没有意识到他之前发布了一个更新。我们的答案中有很多重叠,所以这只是同一种方法的另一种风格。

首先,我想清楚你所描述的内容只能实现封装,而不是“安全性”。我的意思是你可以构建一个系统,使开发人员可以很容易地正确使用它,并且很难错误地使用它。这非常简单。但是,您无法阻止开发人员提取秘密并运行他们想要的任何代码。这是他们的机器,你给他们的代码。他们总能运行它。他们有一个调试器;你不会隐藏任何东西。

但是,防止意外误用是一个很好的目标,非常简单。首先,您应该使用实例方法,而不是类方法。类方法使得所有这些都比它需要的更难。问题的解决方案看起来像这样,依赖于fileprivate来实现大多数访问控制。

class Security {
    enum Error: Swift.Error {
        case unauthorized
    }

    // This feels like it should be nested in Security, but doesn't have to be
    class Secret {
        // No one outside this file can instantiate one of these. It's likely
        // that you'll be passing some parameters here of course.
        fileprivate init() {}

        // I'm assuming you want these to be single use, so people can't store
        // a reference to them an reuse them. This is one simple way. 
        fileprivate var isAuthorized = true

        private func validate() {
            // I'm treating this kind of reuse as a programming error and
            // crashing. You could throw if you wanted, but it feels like it
            // should never happen given your design.
            guard isAuthorized else { 
                fatalError("Secrets can only be used once") 
            }
        }

        func doSomeSecretAction() {
            // Every "protected" method (which may be all of them) needs to
            // call validate() before running.
            validate()
            print("SECRET!")
        }
    }

    // Public so we can test; obviously this would at least private(set)
    var isAuthorized = false 

    func withAuthorization(execute: (Secret) -> Void) throws {
        guard isAuthorized else { throw Error.unauthorized }

        // We create a new Secret for every access and invalidate it after.
        // That prevents them from being stored and reused.
        let secret = Secret()
        execute(secret)
        secret.isAuthorized = false
    }
}

// -- Some other file

let security = Security()

security.isAuthorized = true // For testing

var stealingTheSecret: Security.Secret?

do {
    try security.withAuthorization {
        $0.doSomeSecretAction() // This is good
        stealingTheSecret = $0 // Try to steal it for later use
    }
} catch Security.Error.unauthorized {
    print("Unauthorized")
}

stealingTheSecret?.doSomeSecretAction() // Let's use it: Crash!

原则上你可以通过直接用validate()分配Secret的内存并在最后销毁它来摆脱UnsafeMutablePointer样板,但这可能比它值得更麻烦避免一行额外的代码。

(请注意,自己分配内存仍然无法保护您免受调用者保存对象的影响;他们总是可以复制内存并使用.load重新实例化;您可以做任何不安全的事情调用者也是如此。这也允许他们通过直接修改布尔值或复制对象来绕过validate()。在你使对象无效之前没有。没有技术可以防止不安全的内存访问;这就是你无法保护内部秘密的原因代码。)

答案 2 :(得分:0)

经过研究,我找到了一个好的,简单的解决方案:

class SecurityLayer {

private static var authorized: Bool = false

static func makeAuthorizeCheck() -> API2? {
    if authorized {
        return API2()
    }
    return nil
   }
}

第二类(不是子类)

class Secret {

  func test() {
    print("test")
  }

  fileprivate init() {

  }
}

实施例

  SecurityLayer.makeAuthorizeCheck()?.test() //working
  Secret() //forbidden
  Secret.test() //compiler find this method, but there are no permissions to use this one

Secret内的构造函数为private时,这将不再有效。对我来说,fileprivate的好处现在很明显。 !这些课必须在一个文件中!