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

来源:互联网 发布:知乎 怪异故事和acfun 编辑:程序博客网 时间:2024/06/03 19:21

写在前面: 本文是我看到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>  

抓取验证的代码

@Overrideprotected 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);}
@Overrideprotected 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 0
原创粉丝点击