解读某OAuth 2.0的开源示例android-oauth-app

本文详细解读了开源项目android-oauth-app,这是一个Android OAuth 2.0演示应用。文章分析了配置文件、Manifest、StartActivity、AuthenticationDbService、SchemeCaptureActivity等关键组件,探讨了OAuth认证流程,包括网络权限设置、数据存储、令牌获取和刷新等操作。
摘要由CSDN通过智能技术生成

解读某OAuth 2.0的开源示例android-oauth-app

  • OpenContextApps的android-oauth-app是学习OAuth认证时候找到的一个开源代码. 是4年前的一个Android OAuth 2.0 Demo Application. 认证的地址都已经失效了, 但是还是值得一看的.

  • 项目的github地址: https://github.com/OpenConextApps/android-oauth-app

简介:
This mobile application is able to connect to web service secured with OAuth 2.0.
这个app能够使用OAuth 2.0进行web service安全认证.

OAuth Properties 关于OAuth属性的配置文件

The properties file located at res/raw/demo.properties contains the OAuth configuration parameters: 在res/raw/demo.properties中有OAuth的配置参数.

authorize_url=https://frko.surfnetlabs.nl/workshop/php-oauth/authorize.php
authorize_response_type=code
authorize_grant_type=authorization_code
authorize_client_id=oauth-mobile-app
authorize_scope=grades
authorize_redirect_uri=oauth-mobile-app://callback
token_url=https://frko.surfnetlabs.nl/workshop/php-oauth/token.php
token_grant_type=authorization_code
webservice_url=https://frko.surfnetlabs.nl/workshop/php-oauth-grades-rs/api.php/grades/@me

You can modify them for instance to use your own environment. 你可以根据你自己的情况修改它们.

下面开始分析过程:

分析manifest文件

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".StartActivity"
            android:label="@string/title_activity_start" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".SchemeCaptureActivity"
            android:exported="true"
            android:label="@string/title_activity_scheme_capture"
            android:permission="android.permission.INTERNET" >
            <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:scheme="oauth-mobile-app" />
            </intent-filter>
        </activity>
    </application>

只有两个activity. 注意到SchemeCaptureActivity中可以单独为Activity设置网络访问权限. 另外注意到intent-filter的设置

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" /> //指定该Activity能被浏览器安全调用
    <data android:scheme="oauth-mobile-app" />
</intent-filter>
  • 这里表示android:scheme指定接受Uri的Scheme为oauth-mobile-app, BROWSABLE的意思就是浏览器在特定条件下可以打开你的activity.
  • 整个流程是StartActivity打开浏览器, 然后浏览器在特定的条件下打开SchemeCaptureActivity.

分析StartActivity类

StartActivity的布局

/**
 * The Home Activity to start the application with. The current settings can be
 * shown here. With the start button the Authentication flow will be started.
 * The scheme of the redirect_url will be catch by The other Activity
 * (SchemeCaptureActivity).
 * 程序的主Activity. 将显示当前设置. 点击开始按钮将开始认证流程. redirect_url将会被SchemeCaptureActivity捕获.
 *
 * @author jknoops @ iprofs.nl
 */
public class StartActivity extends Activity {
   
    private AuthenticationDbService service;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.v("demo.SActivity", "Starting Demo Application");
        setContentView(R.layout.activity_start);
        Log.d("demo.SActivity", "Loading properties");
        service = AuthenticationDbService.getInstance(this); //加载属性值
        Log.d("demo.SActivity", "Initialiing the screen.");
        EditText editTextUrl = (EditText) findViewById(R.id.editText_autorize_url);
        editTextUrl.setText(service.getAuthorize_url()); //设置认证地址
        EditText editTextResponseType = (EditText) findViewById(R.id.editText_response_type);
        editTextResponseType.setText(service.getAuthorize_response_type()); //是指响应类型
        EditText editTextClientId = (EditText) findViewById(R.id.editText_client_id);
        editTextClientId.setText(service.getAuthorize_client_id());//设置认证客户端id
        EditText editTextRedirectUri = (EditText) findViewById(R.id.editText_redirect_uri);
        editTextRedirectUri.setText(service.getAuthorize_redirect_uri()); //设置重定向uri
        Button startButton = (Button) findViewById(R.id.button_start);
        startButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                //拼接OAuth认证地址
                StringBuilder sb = new StringBuilder();
                // basic authorize
                sb.append(service.getAuthorize_url());
                // response type
                sb.append("?");
                sb.append("response_type=");
                sb.append(service.getAuthorize_response_type());
                // client_id
                sb.append("&");
                sb.append("client_id=");
                sb.append(service.getAuthorize_client_id());
                // scope
                sb.append("&");
                sb.append("scope=");
                sb.append(service.getAuthorize_scope());
                // redirect
                sb.append("&");
                sb.append("redirect_uri=");
                sb.append(service.getAuthorize_redirect_uri());
                String url = sb.toString();
                Log.d("demo.SActivity", "Starting (Starting class) with url = " + url);
                //拼接之后的url为https://frko.surfnetlabs.nl/workshop/php-oauth/authorize.php?response_type=code&client_id=oauth-mobile-app&scope=grades&redirect_uri=oauth-mobile-app://callback
                Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                startActivity(i);
            }
        });
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_start, menu);
        return true;
    }
}
  • StartActivity从AuthenticationDbService对象中获得了很多数据. 这些数据是从properties中或则从网络请求中获得的数据, 然后保存在SP文件中.

分析AuthenticationDbService类

/**
 * A Helper class for storing/retrieving some data. The tokens are stored in a
 * Preferences file. The properties are loaded from the properties file.
 * 存储/获取一些数据的帮助类. token值被存储在sp文件中. 属性值保存在properties文件中.
 */
public class AuthenticationDbService {
   
    /**
     * the local AuthenticationDbService.
     * 本地验证数据服务对象
     */
    private static AuthenticationDbService _instance;
    private static Context _context;
    //声明Oauth认证中要使用的参数
    private static final String ACCESS_TOKEN = "access_token"; //访问的token
    private static final String REFRESH_TOKEN = "refresh_token"; //刷新的token
    private static final String TOKEN_TYPE = "token_type"; //token类型
    private static final String EXPIRES_IN = "expires_in"; //过期时长
    private static final String SCOPE = "scope"; //范围
    private static final String EXPIRES_IN_LONG = "expires_in_long"; //过期时间的毫秒值
    public static final String RESPONSE_TYPE_TOKEN = "token"; //响应类型token
    public static final String RESPONSE_TYPE_CODE = "code"; //响应类型code
    /**
     * The properties from demo.properties are loaded inside this Properties.
     * 从demo.properties中获得属性值
     */
    private Properties demoProperties;
    /**
     * The values for the tokens are stored inside the preferences file.
     * 这些token值保存在sp中
     */
    private SharedPreferences mPrefs;
    /**
     * Static getInstance() method for Singleton pattern.
     * 单例模式
     *
     * @return the AuthenticationDbService
     */
    public static AuthenticationDbService getInstance(Context context) {
        if (_instance == null) {
            _context = context;
            _instance = new AuthenticationDbService();
        }
        return _instance;
    }
    /**
     * Private constructor
     * 私有化构造方法,从sp中读取配置信息
     */
    private AuthenticationDbService() {
        mPrefs = _context.getSharedPreferences("OpenConext.demo",
                Context.MODE_PRIVATE);
        loadProperties(); //加载属性值
    }
    /**
     * Loading properties from the file system.
     * 从属性文件中获得属性
     */
    private void loadProperties() {
        Resources resources = _context.getResources();
        // Read from the /res/raw directory
        //从 /res/raw目录读取配置信息
        try {
            //openRawResource()读取raw中文件为流的形式
            InputStream rawResource = resources.openRawResource(R.raw.demo);
            demoProperties = new Properties();
            demoProperties.load(rawResource); //将值到流中
            Log.d("demo.DbService", "The properties are now loaded");
            Log.v("demo.DbService", "properties: " + demoProperties);
        } catch (NotFoundException e) {
            Log.e("demo.DbService.error", "Did not find raw resource: " + e);
        } catch (IOException e) {
            Log.e("demo.DbService.error", "Failed to open demo property file");
        }
    }
    /**
     * Retrieving the refresh token from the local storage on the device.
     * 获得refresh_token
     */
    public String getRefreshToken() {
        return mPrefs.getString(REFRESH_TOKEN, null);
    }
    /**
     * Storing the refresh token into the local storage on the device.
     * 将refresh_token保存到sp中
     */
    public void setRefreshToken(final String token) {
        Editor editor = mPrefs.edit();
        editor.putString(REFRESH_TOKEN, token);
        editor.commit();
    }
    /**
     * Retrieving the access token from the local storage on the device. If
     * there is a value "expires in" is stored, there will be checked if the
     * access token is still valid. When there is no "expires in" or the access
     * token is still valid, the access token will be returned. Otherwise an
     * empty access token will be returned.
     * <p/>
     * 从本地获得access_token. 如果存储了过期时间信息, 那么将要检查access_token的有效性.
     * 当没有过期,或者access_token仍然有效, 将会返回access_token. 否则将会返回空值.
     */
    public String getAccessToken() {
        //获得过期时间
        long expires_in_long = mPrefs.getLong(EXPIRES_IN_LONG, -1);
        //如果sp不包含expires_in键值, 则直接获得access_token
        if (!mPrefs.contains(EXPIRES_IN)) {
            return mPrefs.getString(ACCESS_TOKEN, null);
        }
        //如果没有获得过期时间则access_token为空
        if (expires_in_long == -1) {
            return "";
        }
        //获得当前时间的毫秒值
        long now_long = new Date().getTime();
        //判断过期时间是否大于当前时间
        if (expires_in_long > now_long) {
            Log.v("access_token", "Expires in " + (expires_in_long - now_long)
                    + " milliseconds");
            //没有过期则返回access_token
            return mPrefs.getString(ACCESS_TOKEN, null);
        }
        Log.v("access_token", "Overtime " + (now_long - expires_in_long)
                + " milliseconds");
        return "";
    }
    /**
     * 设置access_token
     */
    public void setAccessToken(final String token) {
        Editor editor = mPrefs.edit();
        editor.putString(ACCESS_TOKEN, token);
        editor.commit();
    }
    /**
     * 获得token_type
     */
    public String getTokenType() {
        return mPrefs.getString(TOKEN_TYPE, null);
    }
    /**
     * 设置token_type
     */
    public void setTokenType(final String type) {
        Editor editor = mPrefs.edit();
        editor.putString(TOKEN_TYPE, type);
        editor.commit();
    }
    /**
     * 获得expires_in过期时长
     */
    public Integer getExpiresIn() {
        return mPrefs.getInt(EXPIRES_IN, -1);
    }
    /**
     * 设置expires_in过期时长,和expires_in_long过期时间的毫秒值
     * 注意:expires_in和expires_in_long总是一起保存的或修改的
     */
    public void setExpiresIn(final int expiresIn) {
        //获得当期时间的毫秒值
        Date nowDate = new Date();
        long nowLong = nowDate.getTime();
        //计算expires值
        long expiresLong = nowLong + (1000l * expiresIn);
        //写入sp中
        Editor editor = mPrefs.edit();
        editor.putLong(EXPIRES_IN_LONG, expiresLong);
        editor.putInt(EXPIRES_IN, expiresIn);
        editor.commit();
    }
    /**
     * 获取scope
     */
    public String getScope() {
        return mPrefs.getString(SCOPE, null);
    }
    /**
     * 设置scope
     */
    public void setScope(final String scope) {
        Editor editor = mPrefs.edit();
        editor.putString(SCOPE, scope);
        editor.commit();
    }

    public String getAuthorize_client_secret() {
        return demoProperties.getProperty("authorize_client_secret", null);
    }
    /**
     * 获得认证地址
     */
    public String getAuthorize_url() {
        return demoPropertie
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值