上一篇文章中我们讲述了OAuth2.0的整体思路和运行流程, 以及其中一种授权方式: 授权码模式。在这篇文章中,我们将探索其余三种授权模式,并用我目前项目中的一部分作为Demo讲解。
简化模式
这个模式跳过了授权码这个步骤,客户端无需发送授权码给认证服务器。因此得名。
步骤如下:
- 客户端将用户导向认证服务器。
- 用户决定是否给于客户端授权。
- 用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。这里的hash部分是指URL后面"#"后的值
- 浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值
- 资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。
- 浏览器执行上一步获得的脚本,提取出令牌。
- 浏览器将令牌发给客户端。
第三步中,在服务器发回的HTTP Header中的location部分会包含hash部分,在hash中包含access token。这部分对所有访问者都可见。在开发iOS或Android App时,我们一搬用不到这种方式。过程极不安全,而且对于原生App来说,并不简便。
密码模式
在此模式中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向"服务商提供商"索要授权。
在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。当然还有另一种情况,就是认证服务器就是我们自己公司的服务器,既然客户端和服务器都是我们提供的服务,那么密码模式就要比授权模式更为简便。
步骤如下:
- 用户向客户端提供用户名和密码。
- 客户端将用户名和密码发给认证服务器,向后者请求令牌。
- 认证服务器确认无误后,向客户端提供访问令牌。
在发送请求时,"grant_type"类型一定要是"password"。并且整个过程客户端要做到不要保存用户密码。
客户端模式
严格来说,此模式不属于OAuth协议要解决的问题。用户直接向客户端注册,客户端以自己的名义要求"服务提供商"提供服务,其实不存在授权问题。
步骤如下:
- 客户端向认证服务器进行身份认证,并要求一个访问令牌。
- 认证服务器确认无误后,向客户端提供访问令牌。
在请求过程中,客户端需要把appid和appsecret发送给服务器即可。"grant_type"类型是"client_credential"
Demo
下面用我项目中的OAuth register和login来做一个小demo,包含一些代码片段。
首先,我们公司使用我们自己的服务器作为认证和资源服务器。
主要步骤:
- 客户端验证,看是不是使用我们的App进行登录或注册,用到客户端模式。
- 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的一些简单应用。