移动端app要调用api,那么我要有一个api功能,我不想用之前的非spring的api功能了,不受控制且功能不全。api模块设计如下,一个token对应一个api用户,api用户的认证和授权设计成抽象的,api模块只负责处理访问api的请求,这样api模块不依赖具体的用户管理和api权限管理;另外为了支持自定义的拦截逻辑,加入一个拦截链;另外支持注解式的权限声明(尚未写实现),和Api权限类,权限是权限,api是api,api和权限不混在一起。
对外的接口有:ApiProvider,声明接口受我的模块管理,声明接口元信息;ApiTokenEventListener,token创建、销毁事件监听;ApiInterceptorChainNode,继承,以加入接口管理模块对接口请求的拦截处理;ApiPermission,接口权限实体;RequireApiPermissions,声明接口需要哪些权限(ApiPermission);AccessApiEvaluator,实现自定义的接口访问控制逻辑(比如,有A权限或有B权限就能访问某接口)。
App调用api需要用户认证、设备授权校验、api权限校验、登录状态管理,就是ares模块负责的四件事。
用户认证和设备授权认证,设计成抽象的,不依赖具体的设备管理方式和用户管理方式,交给外部实现。
Ares要管理用户登录状态,使用SessionManager,Session包含用户信息、用户所在的设备信息、用户所在的App信息,用户登录后,利用api模块创建token,和session映射好,一旦有带token的请求过来,我能找到session,找到session,用户信息、用户设备信息、用户app信息都有了。
image.png
session实际就是一个抽象,代表了一次会话(一次会话就是从登录开始,到登出结束),会话就是一个抽象(非代码层面),封装了这次会话相关的信息。有了会话的概念,这样我一个token对应一个session就行了,而不用对应一堆东西,比如对应一个token,对应一个用户、一个设备、一个app、一个登录时间等等。
只要有了session,什么设备验证啊,api权限验证啊,都可以是抽象的,想怎么实现都可以,由于有了SessionManager,可以方便获取Session,由于有了ApiInterceptorChainNode,你可以加任何的拦截、认证、校验、审计等逻辑。
讲代码读法,仍然从最具体的入手,比如com.shinow.abc.ares.api.AresAppletApiInterceptorChainNode,
就是拦截AresApi请求的代码,
@Override
protected boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 下面两行看不懂,没关系,往下看
if (!HandlerMethod.class.isAssignableFrom(handler.getClass())) {
return true;
}
// 下面这一行,获取HandlerMethod的BeanType的注解AresAppletApiProvider,AresAppletApiProvider总认识吧,写过,那HandlerMethod大概是啥,也能猜猜?
AresAppletApiProvider aresAppletApiProvider = ((HandlerMethod) handler).getBeanType().getAnnotation(AresAppletApiProvider.class);
// 看不懂,没关系,往下看
if (aresAppletApiProvider == null) {
return true;
}
Class<? extends AresApplet> appletClazz = aresAppletApiProvider.applet();
if (appletClazz.isInterface() || Modifier.isAbstract(appletClazz.getModifiers())) {
return true;
}
// session 干啥的文章上边说过了
String token = TokenUtil.fromHttpServletRequest(request);
AresSession session = aresSessionManager.fromToken(token);
// 如果session是空,返回false,并且响应app让用户登录
if (session == null) {
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(
new ObjectMapper().writeValueAsString(
ApiResponseBodyBuilder.fail().category(ApiResponseBodyBuilder.ERROR).message("请登录。").build()
));
return false;
}
// 如果用户!canUseApplet,告诉app用户没有applet权限
AresApplet applet = appletClazz.newInstance();
if (!session.getUser().canUseApplet(applet.getId())) {
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(
new ObjectMapper().writeValueAsString(
ApiResponseBodyBuilder.fail().category(ApiResponseBodyBuilder.ERROR).message("您没有使用" + applet.getDefaultName() + "(" + applet.getId() + ")的权限。").build()
));
Logger.getLogger(getClass().getName()).info("用户(" + session.getUser().getId() + ")没有使用此Applet(" + applet.getId() + ")的权限。");
return false;
}
return true;
}
其他的代码还用看吗?主流程你都知道了,还看别的干啥😝。看看对外接口就够了(AresSessionLifecycleListener,AresSessionManager,AresDeviceManager,AresAppManager)。
网友评论