天天看点

Android客户端中权限的控制

在客户端产品中有一个常见的场景,游客身份可以浏览,当进入需要登录的界面A时,弹出登录框,用户输入用户名密码,客户端跳进界面A。

简单的实现方案是,在点击按钮上实现当前用户身份的判断,如果用户未登录时,跳到登录界面,否则进入目标界面。

但是当越来越多的界面都需要做验证时,这种方案就变得很难维护。

怎样通过不侵入当前代码的方式实现身份的过滤呢?

首先,既然是基础功能,肯定要在BaseActivity中实现。当用户startActivity的时候判断要进入的界面是不是需要登录的,如果需要登录并且当前未登录就进入登录界面并提示用户。

在startActivity中获取当前要启动的界面的代码是:

Class clz = Class.forName(intent.getComponent().getClassName());
           

通过覆盖Activity的startActivity方法,判断当前的界面是否需要登录,如果需要登录就跳转到登录界面,并且将当前的Intent作为参数传递到Login界面,当登录成功后销毁登录界面并跳转到目标界面,这样一个简单的权限过滤就实现了。

接下来,就是怎样设置当前界面是否是需要登录的界面呢?用Annotation是比较简单的方法了,定义一个界面类型的Annotation类如下:

package cn.flyrise.support.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by Lenovo on 2015/9/25.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewType {
    boolean needLogin() default false;
}
           

这样如果一个Activity是需要登录的话只需要加上一行代码即可:

@ViewType(needLogin = true)
public class ActJoinActivity extends BaseActivity {

}
           

上面只是介绍了startAcitivity的实现,如果有的界面启动时通过startAcitivityForResult的呢?由于startAcitivityForResult需要返回结果,所以startAcitivityForResult必须在当前界面来调用,不能在登录界面来调用。

实现思路,就是在Activity中保存当前的Intent信息,如果该界面重新显示时是登录成功并且Intent信息不为空,就调用startAcitivityForResult来请求目标界面。

最后还有一种情况就是有些界面在有时是可以游客浏览的,但有时也是要登录用户才能浏览的。比如一个Activity实现帖子列表功能,查看所有的帖子是不需要登录的,但是查看自己的帖子是需要登录,这两个功能都是调用一个Activity,只是参数传递不同而已。

这种情况,可以通过在Intent中传入一个值作为是否绕过权限过滤的标记,如果该值为ture的话就不进行验证。这样一个完整的验证就实现了。

最后看看BaseActivity中完整的代码:

public class BaseActivity extends AppCompatActivity  {
    public static final String REDIRECT_KEY = "Redirect";
    public static final String IS_FILTER_KEY = "isFilter";//是否需要过滤

    private Intent redirectIntent;
    private int redirectRequestCode;
    private boolean isStartForResult;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public void startActivity(Intent intent) {
        if(AuthenticationUtils.isNeedLogin(intent)){
            Toast.makeText(this, R.string.error_no_login,Toast.LENGTH_SHORT).show();
            Intent loginIntent = LoginForVisitorActivity.newIntent(this);
            loginIntent.putExtra(REDIRECT_KEY,intent);
            startActivity(loginIntent);
        }else{
            super.startActivity(intent);
        }
    }

    @Override
    public void startActivityForResult(Intent intent,int requestCode) {
        if(AuthenticationUtils.isNeedLogin(intent)){
            Toast.makeText(this, R.string.error_no_login,Toast.LENGTH_SHORT).show();
            Intent loginIntent = LoginForVisitorActivity.newIntent(this);
            saveRedirectForResult(intent,requestCode);
            startActivity(loginIntent);
        }else{
            super.startActivityForResult(intent, requestCode);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.e("Test","["+this.getClass().getSimpleName()+"] isStartForResult=="+isStartForResult+" redirectIntent=="+redirectIntent);
        if(isStartForResult
                && redirectIntent != null
                && !UserVO.VISITOR
                    .equals(UserVOHelper.getInstance().getCurrUserVO().getUserType())){//已经登录成功并且需要重定向
            super.startActivityForResult(redirectIntent, redirectRequestCode);
        }
        resetRedirect();
    }

    private void saveRedirectForResult(Intent intent,int requestCode){
        redirectIntent = intent;
        isStartForResult = true;
        redirectRequestCode = requestCode;
    }

    private void resetRedirect(){
        redirectIntent = null;
        isStartForResult = false;
    }
}
           

LoginForVisitorAcitivty实现了登录成功后跳转到目标Activity的功能,代码如下:

public class LoginForVisitorActivity extends BaseActivity{
  

    public static Intent newIntent(Context context){
        Intent intent = new Intent(context,LoginForVisitorActivity.class);
        return intent;
    }

    @Override
	protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
    }

    public void login(View view){
        request();
    }

  

  
    @Override
    public void onResponse(Request request,Response rsp) {
        Intent intent = getIntent().getParcelableExtra(REDIRECT_KEY);
        if(intent != null){
            startActivity(intent);
        }
        this.finish();
    }
}
           

AuthenticatonUtils工具类代码如下:

public class AuthenticationUtils {

    /**
     * 根据Intent判断要进入的界面是否需要登录
     * @param intent
     * @return
     */
    public static boolean isNeedLogin(Intent intent){
        try {
            //某些页面可能需要登录才能看,但是不登录也能看
            //此时将页面设置为needLogin,在不需要登录也能看时,需要将IS_FILTER_KEY设置为false
            boolean isFilter = intent.getBooleanExtra(BaseActivity.IS_FILTER_KEY,true);
            Class clz = Class.forName(intent.getComponent().getClassName());
            ViewType viewType = (ViewType)clz.getAnnotation(ViewType.class);
            return viewType != null
                    && viewType.needLogin()
                    && UserVO.VISITOR
                        .equals(UserVOHelper.getInstance().getCurrUserVO().getUserType())
                    && isFilter;//需要登录,并且是游客,并且该Intent指定需要过滤
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return false;
        }
    }
}