OAuth2.0 Practice (二)

上一篇文章中我们讲述了OAuth2.0的整体思路和运行流程, 以及其中一种授权方式: 授权码模式。在这篇文章中,我们将探索其余三种授权模式,并用我目前项目中的一部分作为Demo讲解。

简化模式

这个模式跳过了授权码这个步骤,客户端无需发送授权码给认证服务器。因此得名。

OAuth2.0 Practice (二)_第1张图片
Simplify

步骤如下:

  1. 客户端将用户导向认证服务器。
  2. 用户决定是否给于客户端授权。
  3. 用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。这里的hash部分是指URL后面"#"后的值
  4. 浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值
  5. 资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。
  6. 浏览器执行上一步获得的脚本,提取出令牌。
  7. 浏览器将令牌发给客户端。

第三步中,在服务器发回的HTTP Header中的location部分会包含hash部分,在hash中包含access token。这部分对所有访问者都可见。在开发iOS或Android App时,我们一搬用不到这种方式。过程极不安全,而且对于原生App来说,并不简便。

密码模式

在此模式中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向"服务商提供商"索要授权。

OAuth2.0 Practice (二)_第2张图片
Password

在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。当然还有另一种情况,就是认证服务器就是我们自己公司的服务器,既然客户端和服务器都是我们提供的服务,那么密码模式就要比授权模式更为简便。

步骤如下:

  1. 用户向客户端提供用户名和密码。
  2. 客户端将用户名和密码发给认证服务器,向后者请求令牌。
  3. 认证服务器确认无误后,向客户端提供访问令牌。

在发送请求时,"grant_type"类型一定要是"password"。并且整个过程客户端要做到不要保存用户密码。

客户端模式

严格来说,此模式不属于OAuth协议要解决的问题。用户直接向客户端注册,客户端以自己的名义要求"服务提供商"提供服务,其实不存在授权问题。

OAuth2.0 Practice (二)_第3张图片
Password

步骤如下:

  1. 客户端向认证服务器进行身份认证,并要求一个访问令牌。
  2. 认证服务器确认无误后,向客户端提供访问令牌。

在请求过程中,客户端需要把appid和appsecret发送给服务器即可。"grant_type"类型是"client_credential"

Demo

下面用我项目中的OAuth register和login来做一个小demo,包含一些代码片段。

首先,我们公司使用我们自己的服务器作为认证和资源服务器。

主要步骤:

  1. 客户端验证,看是不是使用我们的App进行登录或注册,用到客户端模式。
  2. OAuth Login, 使用密码模式登录,重新获取access token(与上一步的一样)和refresh token(上一步为nil)

代码片段如下:

- (void)preregistrationWithEmail:(NSString *)email
                    successBlock:(void(^)(id response))success
                 andFailureBlock:(void (^)(NSError *error))failure
{
 if (!self.token || self.token.tokenType.integerValue == TokenTypePassword) {
    [self requestClientCredentialsTokenWithSuccessBlock:^{
        [self preregistrationWithEmail:email successBlock:^(id response) {
            success(response);
        } andFailureBlock:^(NSError *error) {
            failure(error);
        }];
    } andFailureBlock:^(NSError *error) {
        failure(error);
    }];
} else {
    // check email request
    [self checkUserEmail:email withSuccessBlock:^(id response) {
        dispatch_async_in_main_queue(^{
            success(response);
        });
    } andFailureBlock:^(NSError *error) {
        dispatch_async_in_main_queue(^{
            failure(error);
        });
    }];
  }
}

这个函数其实是检测用户是否注册过我们的App,如果有,则再输入密码后进行OAuth Login步骤;若没有则输入用户名和密码后再进行OAuth Login。requestClientCredentialsTokenWithSuccessBlock 这个函数就是使用客户端模式授权的具体函数,如下:

- (void)requestClientCredentialsTokenWithSuccessBlock:(void(^)(void))success
                                      andFailureBlock:(void (^)(NSError *error))failure
{
     GFTRequestCompletionBlock requestBlock = ^(GFTBaseRequest * request){
       GFTCredentialsTokenRequest *credentialReq = (GFTCredentialsTokenRequest *)request;
    
       if (nil == request.error) {
          self.token = credentialReq.token;
        
          [[GFTRequestTaskManager sharedManager] updateCredential:self.token];
          success();
       } else {
          failure(request.error);
       }
     };

   GFTCredentialsTokenRequest *request = [[GFTCredentialsTokenRequest alloc] initWithCompletion:requestBlock];
   [request send];

}

其中用到的 GFTCredentialsTokenRequest 的HTTP Body如下:

- (NSDictionary *)reqBody
{
   NSMutableDictionary * reqBody = [NSMutableDictionary dictionary];

   reqBody[@"grant_type"]   = @"client_credentials";
   reqBody[@"client_id"]  = [GFTRequestTaskManager clientId];
   reqBody[@"client_secret"] = [GFTRequestTaskManager clientSecret];

   return reqBody;
}

大家可以看到,grant_type是"client_credentials"。

当检测完这些,就是验证用户登录了:

- (void)authenticateWithEmail:(NSString *)email
                     password:(NSString *)password
                 successBlock:(void(^)(id response))success
              andFailureBlock:(void (^)(NSError *error))failure
{
    GFTRequestCompletionBlock requestBlock = ^(GFTBaseRequest * request){
        GFTAuthenticationRequest *authRequest = (GFTAuthenticationRequest *)request;
    
        if (nil == authRequest.error) {
            self.token = authRequest.token;
            [[GFTRequestTaskManager sharedManager] updateCredential:self.token];
        
            dispatch_async_in_main_queue(^{
                success(@"success auth!");
            });
        } else {
            dispatch_async_in_main_queue(^{
                failure(request.error);
            });
        }
    };

    GFTAuthenticationRequest *authRequest = [[GFTAuthenticationRequest alloc] initWithCompletion:requestBlock];
    authRequest.userEmail = email;
    authRequest.userPassword = password;

    [authRequest send];
}

其中用到的 GFTAuthenticationRequest 的HTTP Body如下:

- (NSDictionary *)reqBody
{
    NSMutableDictionary * reqBody = [NSMutableDictionary dictionary];

    reqBody[@"username"]   = self.userEmail;
    reqBody[@"password"]   = self.userPassword;
    reqBody[@"grant_type"] = @"password";
    reqBody[@"scope"]      = @"user";
    reqBody[@"client_id"]  = [GFTRequestTaskManager clientId];
    reqBody[@"client_secret"] = [GFTRequestTaskManager clientSecret];

    return reqBody;
}

大家可以看到,grant_type是"password"。

以上就是OAuth2.0的一些简单应用。

你可能感兴趣的:(OAuth2.0 Practice (二))