我尝试实现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
- 方法
答案 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
的好处现在很明显。
!这些课必须在一个文件中!