OAuth2.0 Practice (二)

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

简化模式

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

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来说,并不简便。

密码模式

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

Password

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

步骤如下:

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

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

客户端模式

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

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的一些简单应用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • OAuth 是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版...
    badcyc阅读 358评论 0 0
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 137,172评论 19 139
  • 1. 微服务架构介绍 1.1 什么是微服务架构? 形像一点来说,微服务架构就像搭积木,每个微服务都是一个零件,并使...
    静修佛缘阅读 6,858评论 0 39
  • 就在刚才,接到班里孩子邓海宽妈妈打来的电话。她跟我说因为家里的一些问题,夫妻俩打算到外地去发展。可是,又担心...
    微笑着生活一一秀琴阅读 527评论 0 1
  • 当你去驾校学车,有一个这样的教练时,你一定不会犯困。因为你有一个好教练,不仅技术好人还幽默。这样的教练你怎么会舍得...
    火猫HK阅读 2,436评论 41 57

友情链接更多精彩内容