Retrofit学习教程(4)-OAuth on Android

写在前面: 本文是我看到Retrofit官方推荐的一个Retrofit技术文档,感觉收益匪浅,特此想把文档翻译一下,大家一起学习。
原文地址:https://futurestud.io/tutorials/retrofit-getting-started-and-android-client

本文不会深入探讨OAuth本身。他只会展示基础的原则和必要的信息来帮助理解验证流程。

OAuth基础

OAuth是一个基于token的验证方法,它通过提供一个访问token来为用户和api进行交互。OAuth通过几个步骤和请求来获得你的访问token。

1.在api上注册一个你想要开发的App。使用你想要去开发的开发者网站的公共api。
2.在你的app中存储客户端id和客户端密钥。
3.请求从你的app中访问用户数据。
4.使用验证代码来获得访问token。
5.使用访问token和api进行交互。

注册你的App

首先,你必须为你想要开发的服务/api注册App。一旦你登入你的应用,你将会获得客户端id和客户端密匙。这两个值都将用来验证你的app。

创建工程

我们假设你已经有了一个工程,如果你没有,那么就去创建一个。当你做好之后,移至下一节,准备coding。

集成 OAuth

因为我们在上几章中,使用了ServiceGenerator,那么接下来我们依旧以此为基础进行拓展,增加一个方法来处理OAuth访问Token。下面的代码块显示所要的方法。这不意味着你应该删除先前为基础验证所创建的方法,因为你也需要他们。

public class ServiceGenerator {

    public static final String API_BASE_URL = "https://your.api-base.url";

    private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();

    private static Retrofit.Builder builder =
            new Retrofit.Builder()
                    .baseUrl(API_BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create());

    public static <S> S createService(Class<S> serviceClass) {
        return createService(serviceClass, null);
    }

    public static <S> S createService(Class<S> serviceClass, String username, String password) {
        // we shortened this part, because it’s covered in 
        // the previous post on basic authentication with Retrofit
    }

    public static <S> S createService(Class<S> serviceClass, AccessToken token) {
        if (token != null) {
            httpClient.addInterceptor(new Interceptor() {
                @Override
                public Response intercept(Interceptor.Chain chain) throws IOException {
                    Request original = chain.request();

                    Request.Builder requestBuilder = original.newBuilder()
                        .header("Accept", "application/json")
                        .header("Authorization",
                            token.getTokenType() + " " + token.getAccessToken())
                        .method(original.method(), original.body());

                    Request request = requestBuilder.build();
                    return chain.proceed(request);
                }
            });
        }

        OkHttpClient client = httpClient.build();
        Retrofit retrofit = builder.client(client).build();
        return retrofit.create(serviceClass);
    }
}

我们使用RequestInterceptor来设置HTTP请求头部的Authorization区域。这个区域包含两个部分:首先,OAuth请求的token类型,其次,访问token。
如你所见,这个方法请求一个AccessToken作为第三个参数,这个类如下所示:

public class AccessToken {

    private String accessToken;
    private String tokenType;

    public String getAccessToken() {
        return accessToken;
    }

    public String getTokenType() {
        // OAuth requires uppercase Authorization HTTP header value for token type
        if ( ! Character.isUpperCase(tokenType.charAt(0))) {
            tokenType = 
                Character
                    .toString(tokenType.charAt(0))
                    .toUpperCase() + tokenType.substring(1);
        }

        return tokenType;
    }
}

AccessToken类包含两个区域:accessToken和tokenType。因为OAuth API实现需要大写的token类型,我们首先检测类型。万一它不合适,我们将更新类型。例如,你的API返回bearer类型,那么任何这个类型的请求都会导致要么401无法验证,要么403拒绝,要么400请求失败。

当设置正确时,HTTP头部会如下例子所示。

Authorization: Bearer 12345  

在你的App中集成OAuth

首先我们我们创建一个叫做LoginActivity的新Activity。你可以使用一个只有一个button的简单的view。下面是activity的代码。

public class LoginActivity extends Activity {

    // you should either define client id and secret as constants or in string resources
    private final String clientId = "your-client-id";
    private final String clientSecret = "your-client-secret";
    private final String redirectUri = "your://redirecturi";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        Button loginButton (Button) findViewById(R.id.loginbutton);
        loginButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(
                    Intent.ACTION_VIEW,
                    Uri.parse(ServiceGenerator.API_BASE_URL + "/login" + "?client_id=" + clientId + "&redirect_uri=" + redirectUri));
                startActivity(intent);
            }
        });
    }
}

你必须协调clientID,clientSecret,redirectUri的类属性值。同时也要保证url的登入不能是有/login的,不然,则将这部分更新至正确的部分。接下来,在oncreate方法中为登入按钮设置一个点击监听事件。一旦点击事件被触发,它就会显示URI所指向的webview。重要的一点是:在这个请求中,你必须提供你的client id和cilent 密匙。因为API需要这两个参数来对你的app进行其他的操作和处理。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Login"
        android:id="@+id/loginbutton"
        android:gravity="center_vertical|center_horizontal"
        />
</RelativeLayout>  

创建一个intent删选器,它能帮助你删选你想要的响应。

<activity  
    android:name="com.futurestudio.oauthexample.LoginActivity"
    android:label="@string/app_name"
    android:configChanges="keyboard|orientation|screenSize">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:host="redirecturi"
            android:scheme="your" />
    </intent-filter>
</activity>  

抓取验证的代码

@Override
protected void onResume() {  
    super.onResume();

    // the intent filter defined in AndroidManifest will handle the return from ACTION_VIEW intent
    Uri uri = getIntent().getData();
    if (uri != null && uri.toString().startsWith(redirectUri)) {
        // use the parameter your API exposes for the code (mostly it's "code")
        String code = uri.getQueryParameter("code");
        if (code != null) {
            // get access token
            // we'll do that in a minute
        } else if (uri.getQueryParameter("error") != null) {
            // show an error message here
        }
    }
}

你的app在android的生命周期的onResume方法中进行操作,我们在这里使用了getIntent().getData()方法来检索intet的响应。
现在我们不想运行任何指针和检测值。接下来,我们从查询参数里组装处验证代码。设想一下,点击allow时,响应url如下所示。

your://redirecturi?code=1234

拒绝访问如下所示:

your://redirecturi?error=message  

获得你的访问token

接下来,我们将通过传递client id,client secrest和验证代码给API来请求访问token。我们在上文的基础验证中的LoginService的基础上进行扩展,创建一个叫做getAccessToken的方法。

public interface LoginService {  
    @FormUrlEncoded
    @POST("/token")
    Call<AccessToken> getAccessToken(
            @Field("code") String code,
            @Field("grant_type") String grantType);
}
@Override
protected void onResume() {  
    super.onResume();

    // the intent filter defined in AndroidManifest will handle the return from ACTION_VIEW intent
    Uri uri = getIntent().getData();
    if (uri != null && uri.toString().startsWith(redirectUri)) {
        // use the parameter your API exposes for the code (mostly it's "code")
        String code = uri.getQueryParameter("code");
        if (code != null) {
            // get access token
            LoginService loginService = 
                ServiceGenerator.createService(LoginService.class, clientId, clientSecret);
            Call<AccessToken> call = loginService.getAccessToken(code, "authorization_code");
            AccessToken accessToken = call.execute().body();
        } else if (uri.getQueryParameter("error") != null) {
            // show an error message here
        }
    }
}

你做完之后,你可能必须为你请求的api调整许可类型。这个需求类型是通过getAccessToken的方法的第二个参数进行传递的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值