在客户端产品中有一个常见的场景,游客身份可以浏览,当进入需要登录的界面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;
}
}
}