Sign up/in 注册/登录

1、Sign up/in

1.1 用户登录安全原则

  • 不能在网络上传输用户隐私数据的明文。
  • 不能在本地和服务器上存储用户隐私数据的明文。

1.2 用户登录流程

  • 登录成功之后,应该跳转视图控制器到主页。
  • 如果用户上次登录成功,启动应用程序时,直接进入主页。
  • 当用户主动注销的时候,返回登录页面。

  • 在实际开发中,关于网络方面的代码执行,通常会有一个单例统一管理。涉及到网络就涉及到多线程的异步,需要控制最大并发数。

1.3 iOS 中加解密

2、明文登录

  • Objective-C

    • GET 登录

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      // 用户名
      NSString *username = self.usernameText.text;

      // 用户密码明文
      NSString *pwd = self.pwdText.text;

      NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:
      @"http://192.168.88.200/login/login.php?username=%@&password=%@", username, pwd]];
      NSURLRequest *request = [NSURLRequest requestWithURL:url];

      [[[NSURLSession sharedSession] dataTaskWithRequest:request
      completionHandler:^(NSData * _Nullable data,
      NSURLResponse * _Nullable response,
      NSError * _Nullable error) {

      if (error == nil || data != nil) {
      NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
      if ([dict[@"userId"] intValue] > 0) {
      [self saveUserLoginInfo];
      }
      }
      }] resume];
    • POST 登录

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      // 用户名
      NSString *username = self.usernameText.text;

      // 用户密码明文
      NSString *pwd = self.pwdText.text;

      NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/login/login.php"];
      NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

      request.HTTPMethod = @"POST";
      request.HTTPBody = [[NSString stringWithFormat:@"username=%@&password=%@", username, pwd]
      dataUsingEncoding:NSUTF8StringEncoding];

      [[[NSURLSession sharedSession] dataTaskWithRequest:request
      completionHandler:^(NSData * _Nullable data,
      NSURLResponse * _Nullable response,
      NSError * _Nullable error) {

      if (error == nil || data != nil) {
      NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
      if ([dict[@"userId"] intValue] > 0) {
      [self saveUserLoginInfo];
      }
      }
      }] resume];

3、Base64 编码登录

  • Base64 编码具体实现代码见 GitHub 源码 QExtension

  • Objective-C

    • NSString+Base64.m

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      // 对 ASCII 编码的字符串进行 base64 编码
      - (NSString *)base64Encode {

      NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
      return [data base64EncodedStringWithOptions:0];
      }

      // 对 base64 编码的字符串进行解码
      - (NSString *)base64Decode {

      NSData *data = [[NSData alloc] initWithBase64EncodedString:self options:0];
      return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
      }
    • POST 登录

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      // 用户名
      NSString *username = self.usernameText.text;

      // 经 base64 编码的用户密码
      NSString *pwd = [self.pwdText.text base64Encode];

      NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/login/login.php"];
      NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

      request.HTTPMethod = @"POST";
      request.HTTPBody = [[NSString stringWithFormat:@"username=%@&password=%@", username, pwd]
      dataUsingEncoding:NSUTF8StringEncoding];

      [[[NSURLSession sharedSession] dataTaskWithRequest:request
      completionHandler:^(NSData * _Nullable data,
      NSURLResponse * _Nullable response,
      NSError * _Nullable error) {

      if (error == nil || data != nil) {
      NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
      if ([dict[@"userId"] intValue] > 0) {
      [self saveUserLoginInfo];
      }
      }
      }] resume];

4、MD5 加密登录

  • MD5 加密具体实现代码见 GitHub 源码 QExtension

  • Objective-C

    • NSString+Hash.m

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      #import <CommonCrypto/CommonCrypto.h>

      // MD5 散列函数
      - (NSString *)md5String {

      const char *str = self.UTF8String;
      uint8_t buffer[CC_MD5_DIGEST_LENGTH];

      CC_MD5(str, (CC_LONG)strlen(str), buffer);

      return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
      }

      // HMAC 散列函数
      - (NSString *)hmacMD5StringWithKey:(NSString *)key {

      const char *keyData = key.UTF8String;
      const char *strData = self.UTF8String;
      uint8_t buffer[CC_MD5_DIGEST_LENGTH];

      CCHmac(kCCHmacAlgMD5, keyData, strlen(keyData), strData, strlen(strData), buffer);

      return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
      }

      // 时间戳散列函数
      - (NSString *)timeMD5StringWithKey:(NSString *)key {

      NSString *hmacKey = key.md5String;
      NSString *hmacStr = [self hmacMD5StringWithKey:hmacKey];

      NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
      fmt.dateFormat = @"yyyy-MM-ddHH:mm";
      NSString *dateStr = [fmt stringFromDate:[NSDate date]];

      hmacStr = [hmacStr stringByAppendingString:dateStr];

      return [hmacStr hmacMD5StringWithKey:hmacKey];
      }

      // 助手方法
      - (NSString *)stringFromBytes:(uint8_t *)bytes length:(int)length {

      NSMutableString *strM = [NSMutableString string];

      for (int i = 0; i < length; i++) {
      [strM appendFormat:@"%02x", bytes[i]];
      }

      return [strM copy];
      }
    • 直接 md5 加密

      1
      2
      3
      4
      5
      6
      7
      // 用户名
      NSString *username = self.usernameText.text;

      // 经 md5 加密的用户密码
      NSString *pwd = [self.pwdText.text md5String];

      NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/login/login.php"];
    • md5 + 盐 加密

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // “盐”,随机添加的字符串,要够长,够复杂
      static NSString *salt = @"FUYGIUHIJKJHVBNOIUYUGVHJK@#$%^&))(&*^%W%$^%ukjsbcjcgvbk,jmhnrhjbjknklHDYCHGNKJB";

      // 用户名
      NSString *username = self.usernameText.text;

      // 经 md5 加 盐 加密的用户密码
      NSString *pwd = [[self.pwdText.text stringByAppendingString:salt] md5String];

      NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/login/login.php"];
    • md5 + HMAC 加密

      1
      2
      3
      4
      5
      6
      7
      // 用户名
      NSString *username = self.usernameText.text;

      // 经 md5 加 HMAC 加密的用户密码
      NSString *pwd = [[self.pwdText.text hmacMD5StringWithKey:@"qianqian"] md5String];

      NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/login/login.php"];
    • md5 + HMAC + 时间戳 加密

      1
      2
      3
      4
      5
      6
      7
      // 用户名
      NSString *username = self.usernameText.text;

      // 经 md5 加 HMAC 加 时间戳 加密的用户密码
      NSString *pwd = [self.pwdText.text timeMD5StringWithKey:@"qianqian"];

      NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/login/loginhmac.php"];
    • 登录代码段

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

      request.HTTPMethod = @"POST";
      request.HTTPBody = [[NSString stringWithFormat:@"username=%@&password=%@", username, pwd]
      dataUsingEncoding:NSUTF8StringEncoding];

      [[[NSURLSession sharedSession] dataTaskWithRequest:request
      completionHandler:^(NSData * _Nullable data,
      NSURLResponse * _Nullable response,
      NSError * _Nullable error) {

      if (error == nil || data != nil) {
      NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
      if ([dict[@"userId"] intValue] > 0) {
      [self saveUserLoginInfo];
      }
      }
      }] resume];

5、完整登录

  • Objective-C

    • AppDelegate.m

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

      // 注册通知
      /*
      AppDelegate 中可以不移除
      */

      [[NSNotificationCenter defaultCenter] addObserver:self
      selector:@selector(loginSuccess)
      name:HQUserLoginSuccessedNotification
      object:nil];
      [[NSNotificationCenter defaultCenter] addObserver:self
      selector:@selector(logout)
      name:HQUserLogoutNotification
      object:nil];
      [[NSNotificationCenter defaultCenter] addObserver:self
      selector:@selector(touchIDAuthentication)
      name:HQTouchIDAuthenticationNotification
      object:nil];

      // 直接用户登录

      // 如果成功,进入主页
      [[NetworkTools sharedNetworkTools] userLoginWithTouchID:(BOOL)YES Failed:^{

      // 如果失败,进入登录页面
      [self logout];
      }];

      return YES;
      }

      /// 登录成功
      - (void)loginSuccess {

      // 显示主页面
      [self switchStroyboard:@"Home"];
      }

      /// 用户注销
      - (void)logout {

      // 显示登录页面
      [self switchStroyboard:@"Login"];
      }

      /// 指纹验证
      - (void)touchIDAuthentication {
      [self switchStroyboard:@"TouchID"];
      }

      /// 切换显示页面
      - (void)switchStroyboard:(NSString *)sbName {

      UIStoryboard *sb = [UIStoryboard storyboardWithName:sbName bundle:nil];
      self.window.rootViewController = sb.instantiateInitialViewController;
      }
    • HomeViewController.m

      1
      2
      3
      4
      5
      6
      7
      #import "NetworkTools.h"

      /// 用户注销
      - (IBAction)logout:(UIButton *)sender {

      [[NetworkTools sharedNetworkTools] userLogout];
      }
    • LoginViewController.m

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      #import "NetworkTools.h"

      @property (weak, nonatomic) IBOutlet UITextField *usernameText;
      @property (weak, nonatomic) IBOutlet UITextField *pwdText;
      @property (weak, nonatomic) IBOutlet UISwitch *savePwdSwitch;

      - (void)viewDidLoad {
      [super viewDidLoad];

      [self loadUserInfo];
      }

      /// 用户登录

      - (IBAction)postLogin {

      NetworkTools *tools = [NetworkTools sharedNetworkTools];

      tools.username = self.usernameText.text;
      tools.pwd = self.pwdText.text;
      tools.isSavePwd = self.savePwdSwitch.isOn;

      [tools userLoginWithTouchID:(BOOL)NO Failed:^{
      UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示"
      message:@"用户名或密码错误"
      preferredStyle:UIAlertControllerStyleAlert];
      [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]];
      [self presentViewController:alertController animated:YES completion:nil];
      }];
      }

      /// 加载用户信息

      - (void)loadUserInfo {

      self.usernameText.text = [NetworkTools sharedNetworkTools].username;
      self.pwdText.text = [NetworkTools sharedNetworkTools].pwd;
      }

      /// 键盘回收

      - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
      [self.view endEditing:YES];
      }
    • TouchIDViewController.m

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      #import "NetworkTools.h"

      - (void)viewDidLoad {
      [super viewDidLoad];

      [self touchIDAuthentication];
      }

      - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

      [self touchIDAuthentication];
      }

      /// 指纹验证

      - (void)touchIDAuthentication {

      NetworkTools *tools = [NetworkTools sharedNetworkTools];

      [tools touchIDAuthenticationFailed:^{
      UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示"
      message:@"指纹验证失败,请重试"
      preferredStyle:UIAlertControllerStyleAlert];
      [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]];
      [self presentViewController:alertController animated:YES completion:nil];
      }];
      }
    • NetworkTools.h

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      extern NSString * const HQUserLoginSuccessedNotification;
      extern NSString * const HQUserLogoutNotification;
      extern NSString * const HQTouchIDAuthenticationNotification;

      @interface NetworkTools : NSObject

      /// 用户名
      @property (nonatomic, copy) NSString *username;

      /// 用户口令
      @property (nonatomic, copy) NSString *pwd;

      /// 是否记住密码
      @property (nonatomic, assign) BOOL isSavePwd;

      /// 单例方法
      + (instancetype)sharedNetworkTools;

      /// 用户登录
      - (void)userLoginWithTouchID:(BOOL)touch Failed:(void (^)())failed;

      /// 用户注销
      - (void)userLogout;

      /// 用户指纹验证
      - (void)touchIDAuthenticationFailed:(void (^)())failed;

      @end
    • NetworkTools.m

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      148
      149
      150
      151
      152
      153
      154
      155
      156
      157
      158
      159
      160
      161
      162
      163
      164
      165
      166
      167
      168
      169
      170
      171
      172
      173
      174
      175
      176
      177
      178
      179
      180
      181
      182
      183
      184
      185
      186
      187
      #import "NSString+TimePwd.h"
      #import "SSKeychain.h"
      #import <LocalAuthentication/LocalAuthentication.h>

      // 定义通知字符串常量
      NSString * const HQUserLoginSuccessedNotification = @"HMUserLoginSuccessedNotification";
      NSString * const HQUserLogoutNotification = @"HMUserLogoutNotification";
      NSString * const HQTouchIDAuthenticationNotification = @"HQTouchIDAuthenticationNotification";

      @implementation NetworkTools

      /**
      单例创建中,使用 allocWithZone, copyWithZone ... 等等方法,会把所有创建第二个实例可能性全部堵死。
      在真正开发中,有的时候,会需要额外创建一个副本。
      */
      + (instancetype)sharedNetworkTools {

      static id instance;

      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
      instance = [[self alloc] init];
      });
      return instance;
      }

      - (instancetype)init {
      self = [super init];
      if (self) {

      // 第一次被实例化的时候,加载用户信息
      [self loadUserLoginInfo];
      }
      return self;
      }

      /// 用户登录

      /**
      程序启动直接执行,如果登录成功,进入主界面。如果登录失败,进入登录页面。
      */
      - (void)userLoginWithTouchID:(BOOL)touch Failed:(void (^)())failed {

      NSString *username = self.username;

      // md5 + HMAC + 时间戳 加密
      NSString *pwd = [self.pwd timeMD5StringWithKey:@"qianqian"];

      NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/login/loginhmac.php"];
      NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

      request.HTTPMethod = @"POST";
      request.HTTPBody = [[NSString stringWithFormat:@"username=%@&password=%@", username, pwd]
      dataUsingEncoding:NSUTF8StringEncoding];

      [[[NSURLSession sharedSession] dataTaskWithRequest:request
      completionHandler:^(NSData * _Nullable data,
      NSURLResponse * _Nullable response,
      NSError * _Nullable error) {

      if (error == nil || data != nil) {

      NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];

      dispatch_async(dispatch_get_main_queue(), ^{

      if ([dict[@"userId"] intValue] > 0) {

      [self saveUserLoginInfo];

      if (touch == NO || [UIDevice currentDevice].systemVersion.floatValue < 8.0) {

      // 发送登录成功通知
      [[NSNotificationCenter defaultCenter] postNotificationName:HQUserLoginSuccessedNotification object:nil];
      } else {

      // 发送指纹验证通知
      [[NSNotificationCenter defaultCenter] postNotificationName:HQTouchIDAuthenticationNotification object:nil];
      }
      } else {
      if (failed != nil) {

      // 执行失败回调
      failed();
      }
      }
      });
      }
      }] resume];
      }

      /// 用户注销

      - (void)userLogout {

      [SSKeychain deletePasswordForService:[NSBundle mainBundle].bundleIdentifier account:self.username];

      // 发送注销通知
      [[NSNotificationCenter defaultCenter] postNotificationName:HQUserLogoutNotification object:nil];
      }

      /// 指纹验证

      - (void)touchIDAuthenticationFailed:(void (^)())failed {

      // 实例化本地身份验证上下文
      LAContext *context= [[LAContext alloc] init];

      // 判断是否支持指纹识别
      if (![context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:NULL]) {

      // 发送登录成功通知
      [[NSNotificationCenter defaultCenter] postNotificationName:HQUserLoginSuccessedNotification object:nil];

      return;
      }

      [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
      localizedReason:@"请验证已有指纹"
      reply:^(BOOL success, NSError * _Nullable error) {

      dispatch_async(dispatch_get_main_queue(), ^{

      // 输入指纹开始验证,异步执行
      if (success) {

      // 发送登录成功通知
      [[NSNotificationCenter defaultCenter] postNotificationName:HQUserLoginSuccessedNotification object:nil];
      } else {
      switch (error.code) {
      case -1:
      if (failed != nil) {

      // 执行失败回调
      failed();
      }
      break;

      case -2:
      case -3:
      case -4:
      case -5:
      case -6:
      case -7:
      // 发送注销通知
      [[NSNotificationCenter defaultCenter] postNotificationName:HQUserLogoutNotification object:nil];
      break;

      default:
      break;
      }
      }
      });
      }];
      }

      /// 用户登录信息存储

      - (void)saveUserLoginInfo {

      NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

      [userDefaults setObject:self.username forKey:@"userNameKey"];
      [userDefaults setBool:self.isSavePwd forKey:@"isSavePwdKey"];
      [userDefaults synchronize];

      if (self.isSavePwd) {

      // 将密码保存到钥匙串中
      [SSKeychain setPassword:self.pwd forService:[NSBundle mainBundle].bundleIdentifier account:self.username];
      } else {
      self.pwd = nil;
      [SSKeychain deletePasswordForService:[NSBundle mainBundle].bundleIdentifier account:self.username];
      }
      }

      - (void)loadUserLoginInfo {

      NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
      self.username = [userDefaults objectForKey:@"userNameKey"];
      self.isSavePwd = [userDefaults boolForKey:@"isSavePwdKey"];

      // 将密码从钥匙串中取出
      self.pwd = [SSKeychain passwordForService:[NSBundle mainBundle].bundleIdentifier account:self.username];
      }

      @end
    • NSString+Hash.m

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      #import <CommonCrypto/CommonCrypto.h>

      /// 时间戳散列函数

      - (NSString *)timeMD5StringWithKey:(NSString *)key {
      NSString *hmacKey = key.md5String;
      NSString *hmacStr = [self hmacMD5StringWithKey:hmacKey];

      NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
      fmt.dateFormat = @"yyyy-MM-ddHH:mm";
      NSString *dateStr = [fmt stringFromDate:[NSDate date]];

      hmacStr = [hmacStr stringByAppendingString:dateStr];

      return [hmacStr hmacMD5StringWithKey:hmacKey];
      }

      /// HMAC 散列函数

      - (NSString *)hmacMD5StringWithKey:(NSString *)key {
      const char *keyData = key.UTF8String;
      const char *strData = self.UTF8String;
      uint8_t buffer[CC_MD5_DIGEST_LENGTH];

      CCHmac(kCCHmacAlgMD5, keyData, strlen(keyData), strData, strlen(strData), buffer);

      return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
      }

      /// 散列函数

      - (NSString *)md5String {
      const char *str = self.UTF8String;
      uint8_t buffer[CC_MD5_DIGEST_LENGTH];

      CC_MD5(str, (CC_LONG)strlen(str), buffer);

      return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
      }

      /// 助手方法

      - (NSString *)stringFromBytes:(uint8_t *)bytes length:(int)length {
      NSMutableString *strM = [NSMutableString string];

      for (int i = 0; i < length; i++) {
      [strM appendFormat:@"%02x", bytes[i]];
      }

      return [strM copy];
      }

6、保存用户信息

  • Objective-C

    • 明文保存

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      // 保存用户信息

      NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

      [userDefaults setObject:self.usernameText.text forKey:@"userNameKey"];
      [userDefaults setObject:self.pwdText.text forKey:@"userPwdKey"];

      [userDefaults synchronize];

      // 加载用户信息

      NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

      self.usernameText.text = [userDefaults objectForKey:@"userNameKey"];
      self.pwdText.text = [userDefaults objectForKey:@"userPwdKey"];
    • base64 编码保存

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      // NSString+Base64.m

      // 对 ASCII 编码的字符串进行 base64 编码
      - (NSString *)base64Encode {

      NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
      return [data base64EncodedStringWithOptions:0];
      }

      // 对 base64 编码的字符串进行解码
      - (NSString *)base64Decode {

      NSData *data = [[NSData alloc] initWithBase64EncodedString:self options:0];
      return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
      }

      // 保存用户信息

      NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

      [userDefaults setObject:self.usernameText.text forKey:@"userNameKey"];

      // 对密码进行 base64 编码
      [userDefaults setObject:[self.pwdText.text base64Encode] forKey:@"userPwdKey"];

      [userDefaults synchronize];

      // 加载用户信息

      NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

      self.usernameText.text = [userDefaults objectForKey:@"userNameKey"];

      // 对密码进行 base64 解码
      self.pwdText.text = [[userDefaults objectForKey:@"userPwdKey"] base64Decode];
    • 钥匙串保存

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      // 添加第三方库文件
      SSKeychain

      // 包含头文件
      #import "SSKeychain.h"

      // 保存用户信息

      NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
      [userDefaults setObject:self.usernameText.text forKey:@"userNameKey"];
      [userDefaults synchronize];

      // 将密码保存到钥匙串中
      [SSKeychain setPassword:self.pwdText.text
      forService:[NSBundle mainBundle].bundleIdentifier
      account:self.usernameText.text];

      // 加载用户信息

      NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
      self.usernameText.text = [userDefaults objectForKey:@"userNameKey"];

      // 将密码从钥匙串中取出
      self.pwdText.text = [SSKeychain passwordForService:[NSBundle mainBundle].bundleIdentifier
      account:self.usernameText.text];

7、SSKeychain/SAMKeychain 的使用

  • GitHub 网址:SAMKeychain

  • SSKeychain/SAMKeychain 使用 ARC

  • Objective-C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    // 添加第三方库文件
    SSKeychain

    // 包含头文件
    #import "SSKeychain.h"

    // 获取所有的账户信息

    // 只有同一个开发者开发的应用程序,才能够互相看到账号
    NSArray *allAccounts = [SSKeychain allAccounts];

    // 获取指定服务名的账户信息

    NSArray *accounts = [SSKeychain accountsForService:[NSBundle mainBundle].bundleIdentifier];

    // 将密码保存到钥匙串中
    /*
    + (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;

    参数:
    password :密码明文
    serviceName:服务名,可以随便写,建议使用 bundleId,应用程序的唯一标示,每一个上架的应用程序,都有一个唯一的 bundleId
    account :账户名(用户名)
    */

    // 保存 NSString 格式的密码
    [SSKeychain setPassword:self.pwdText.text
    forService:[NSBundle mainBundle].bundleIdentifier
    account:self.usernameText.text];

    NSData *pwdData = [self.pwdText.text dataUsingEncoding:NSUTF8StringEncoding];

    // 保存 NSData 格式的密码
    [SSKeychain setPasswordData:pwdData
    forService:[NSBundle mainBundle].bundleIdentifier
    account:self.usernameText.text];

    // 将密码从钥匙串中取出

    // 获取 NSString 格式的密码
    self.pwdText.text = [SSKeychain passwordForService:[NSBundle mainBundle].bundleIdentifier
    account:self.usernameText.text];

    // 获取 NSData 格式的密码
    NSData *pwdData1 = [SSKeychain passwordDataForService:[NSBundle mainBundle].bundleIdentifier
    account:self.usernameText.text];

    // 将密码从钥匙串中删除

    [SSKeychain deletePasswordForService:[NSBundle mainBundle].bundleIdentifier
    account:self.usernameText.text];
文章目录
  1. 1. 1、Sign up/in
    1. 1.1. 1.1 用户登录安全原则
    2. 1.2. 1.2 用户登录流程
    3. 1.3. 1.3 iOS 中加解密
  2. 2. 2、明文登录
  3. 3. 3、Base64 编码登录
  4. 4. 4、MD5 加密登录
  5. 5. 5、完整登录
  6. 6. 6、保存用户信息
  7. 7. 7、SSKeychain/SAMKeychain 的使用
隐藏目录