首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从NSManagedObject内部引用NSValueTransformer实体

从NSManagedObject内部引用NSValueTransformer实体
EN

Stack Overflow用户
提问于 2013-09-20 12:03:48
回答 3查看 487关注 0票数 1

我使用NSValueTranformer加密某些核心数据属性。这一切都很好,但我需要能够使用不同的加密密钥取决于NSManagedObject。无论如何,我可以从我的变压器类中访问这个实体吗?

用例是,我有多个具有不同密码的用户,可以访问不同的NSManagedObject实体。如果我对所有对象使用相同的加密密钥,就可以重新分配谁在SQL db中拥有它们,它们仍然会解密。

对这件事的最佳方式有什么想法吗?

编辑:我应该说我是用iOS来做这个的。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-09-20 13:03:31

第三次魅力?让我看看我能不能解决你的唯一转换时磁盘的要求。把这看作是另外两种方法的混合体。

代码语言:javascript
复制
@interface UserSession : NSObject

+ (UserSession*)currentSession;
+ (void)setCurrentSession: (UserSession*)session;
- (id)initWithUserName: (NSString*)username andEncryptionKey: (NSData*)key;

@property (nonatomic, readonly) NSString* userName;
@property (nonatomic, readonly) NSData* encryptionKey;

@end

@implementation UserSession

static UserSession* gCurrentSession = nil;

+ (UserSession*)currentSession
{
    @synchronized(self)
    {
        return gCurrentSession;
    }
}

+ (void)setCurrentSession: (UserSession*)userSession
{
    @synchronized(self)
    {
        gCurrentSession = userSession;
    }
}

- (id)initWithUserName: (NSString*)username andEncryptionKey: (NSData*)key
{
    if (self = [super init])
    {
        _userName = [username copy];
        _encryptionKey = [key copy];
    }
    return self;
}

- (void)dealloc
{
    _userName = nil;
    _encryptionKey = nil;
}

@end

@interface EncryptingValueTransformer : NSValueTransformer
@end

@implementation EncryptingValueTransformer

- (id)transformedValue:(id)value
{    
    UserSession* session = [UserSession currentSession];
    NSAssert(session, @"No user session! Can't decrypt!");

    NSData* key = session.encryptionKey;
    NSData* decryptedData = Decrypt(value, key);
    return decryptedData;
}

- (id)reverseTransformedValue:(id)value
{
    UserSession* session = [UserSession currentSession];
    NSAssert(session, @"No user session! Can't encrypt!");

    NSData* key = session.encryptionKey;
    NSData* encryptedData = Encrypt(value, key);
    return encryptedData;
}

@end

这里唯一棘手的部分是,您必须确保在创建托管对象上下文之前设置了当前的UserSession,并且直到上下文保存和释放之后才进行更改。

希望这能有所帮助。

票数 1
EN

Stack Overflow用户

发布于 2013-09-20 12:09:34

您可以创建具有状态(即加密密钥)的NSValueTransformer子类的自定义实例,并使用-bind:toObject:withKeyPath:options:密钥在options字典中将它们传递给NSValueTransformerBindingOption

您将无法在IB中直接设置它,因为IB按类名引用值转换器,但您可以在代码中这样做。如果您感到非常雄心勃勃,您可以在IB中设置绑定,然后在代码中用不同的选项替换它们。

它看起来可能是这样的:

代码语言:javascript
复制
@interface EncryptingValueTransformer : NSValueTransformer

@property (nonatomic,readwrite,copy) NSData* encryptionKey;

@end

@implementation EncryptingValueTransformer

- (void)dealloc
{
    _encryptionKey = nil;
}

- (id)transformedValue:(id)value
{
    if (!self.encryptionKey)
        return nil;

    // do the transformation

    return value;
}

- (id)reverseTransformedValue:(id)value
{
    if (!self.encryptionKey)
        return nil;

    // Do the reverse transformation

    return value;
}

@end


@interface MyViewController : NSViewController

@property (nonatomic, readwrite, assign) IBOutlet NSControl* controlBoundToEncryptedValue;

@end

@implementation MyViewController

// Other stuff...

- (void)loadView
{
    [super loadView];

    // Replace IB's value tansformer binding settings (which will be by class and not instance) with specific,
    // stateful instances.
    for (NSString* binding in [self.controlBoundToEncryptedValue exposedBindings])
    {
        NSDictionary* bindingInfo = [self.controlBoundToEncryptedValue infoForBinding: binding];
        NSDictionary* options = bindingInfo[NSOptionsKey];
        if ([options[NSValueTransformerNameBindingOption] isEqual: NSStringFromClass([EncryptingValueTransformer class])])
        {
            // Out with the old
            [self.controlBoundToEncryptedValue unbind: binding];

            // In with the new
            NSMutableDictionary* mutableOptions = [options mutableCopy];
            mutableOptions[NSValueTransformerNameBindingOption] = nil;
            mutableOptions[NSValueTransformerBindingOption] = [[EncryptingValueTransformer alloc] init];
            [self.controlBoundToEncryptedValue bind: binding
                                           toObject: bindingInfo[NSObservedObjectKey]
                                        withKeyPath: bindingInfo[NSObservedKeyPathKey]
                                            options: mutableOptions];
        }
    }
}

// Assuming you're using the standard representedObject pattern, this will get set every time you want
// your view to expose new model data. This is a good place to update the encryption key in the transformers'
// state...

- (void)setRepresentedObject:(id)representedObject
{
    for (NSString* binding in [self.controlBoundToEncryptedValue exposedBindings])
    {
        id transformer = [self.controlBoundToEncryptedValue infoForBinding: NSValueBinding][NSOptionsKey][NSValueTransformerBindingOption];
        EncryptingValueTransformer* encryptingTransformer = [transformer isKindOfClass: [EncryptingValueTransformer class]] ? (EncryptingValueTransformer*)transformer : nil;
        encryptingTransformer.encryptionKey = nil;
    }

    [super setRepresentedObject:representedObject];

    // Get key from model however...
    NSData* encryptionKeySpecificToThisUser = /* Whatever it is... */ nil;

    for (NSString* binding in [self.controlBoundToEncryptedValue exposedBindings])
    {
        id transformer = [self.controlBoundToEncryptedValue infoForBinding: NSValueBinding][NSOptionsKey][NSValueTransformerBindingOption];
        EncryptingValueTransformer* encryptingTransformer = [transformer isKindOfClass: [EncryptingValueTransformer class]] ? (EncryptingValueTransformer*)transformer : nil;
        encryptingTransformer.encryptionKey = encryptionKeySpecificToThisUser;
    }
}

// ...Other stuff

@end
票数 1
EN

Stack Overflow用户

发布于 2013-09-20 12:57:22

好的。这让我心烦意乱所以我再想一想.我认为最简单的方法是有某种类型的“会话”对象,然后在托管对象上有一个“派生属性”。假设您有一个名为UserData的实体,该实体具有一个名为encryptedData的属性,我编写了一些代码,这些代码可能有助于说明:

代码语言:javascript
复制
@interface UserData : NSManagedObject
@property (nonatomic, retain) NSData * unencryptedData;
@end

@interface UserData () // Private
@property (nonatomic, retain) NSData * encryptedData;
@end

// These functions defined elsewhere
NSData* Encrypt(NSData* clearData, NSData* key);
NSData* Decrypt(NSData* cipherData, NSData* key);

@interface UserSession : NSObject

+ (UserSession*)currentSession;

- (id)initWithUserName: (NSString*)username andEncryptionKey: (NSData*)key;

@property (nonatomic, readonly) NSString* userName;
@property (nonatomic, readonly) NSData* encryptionKey;

@end

@implementation UserData

@dynamic encryptedData;
@dynamic unencryptedData;

+ (NSSet*)keyPathsForValuesAffectingUnencryptedData
{
    return [NSSet setWithObject: NSStringFromSelector(@selector(encryptedData))];
}

- (NSData*)unencryptedData
{
    UserSession* session = [UserSession currentSession];
    if (nil == session)
        return nil;

    NSData* key = session.encryptionKey;
    NSData* encryptedData = self.encryptedData;
    NSData* decryptedData = Decrypt(encryptedData, key);
    return decryptedData;
}

- (void)setUnencryptedData:(NSData *)unencryptedData
{
    UserSession* session = [UserSession currentSession];
    NSAssert(session, @"No user session! Can't encrypt!");

    NSData* key = session.encryptionKey;
    NSData* encryptedData = Encrypt(unencryptedData, key);

    self.encryptedData = encryptedData;
}

@end

@implementation UserSession

static UserSession* gCurrentSession = nil;

+ (UserSession*)currentSession
{
    @synchronized(self)
    {
        return gCurrentSession;
    }
}

+ (void)setCurrentSession: (UserSession*)userSession
{
    @synchronized(self)
    {
        gCurrentSession = userSession;
    }
}

- (id)initWithUserName: (NSString*)username andEncryptionKey: (NSData*)key
{
    if (self = [super init])
    {
        _userName = [username copy];
        _encryptionKey = [key copy];
    }
    return self;
}

-(void)dealloc
{
    _userName = nil;
    _encryptionKey = nil;
}

@end

这里的想法是,当给定的用户登录时,您将创建一个新的UserSession对象并调用+[UserSession setCurrentSession: [[UserSession alloc] initWithUserName: @"foo" andEncryptionKey: <whatever>]]。派生属性(unencryptedData)访问器和变量获取当前会话,并使用键将值来回转换为“实”属性。(另外,不要跳过+keyPathsForValuesAffectingUnencryptedData方法。这将告诉运行时这两个属性之间的关系,并将有助于更无缝地工作。)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18916338

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档