拥有授权登录用户的应用程序设计
2013-11-03 08:36
204 查看
今天实在太忙, 去公司加班回来之后,继续帮朋友实现网站,今晚的任务是设计一套框架以兼容上一篇文章所说的QQ, 新浪微博授权登录和之后的其他操作, 这套框架的设计目标是有有良好的扩展性(以方便以后可以实现更多公司的第三方登录), 和模块独立性(如果第三方提供的API变动,不会太影响自己的网站逻辑)。
定好需求之后第一个想到就是使用桥梁模式。 以下是第一个设计版本。
抽象类TPAgent 是模块的门户,对外它是主要提供访问接口。 这里我只列出了一个示例方法 createTPAccountBinding(), 这个方法将返回 TPAccountBinding 类实例, 而这个类包含了用户在第三方网站里面的一些信息。 桥梁模式的另一头是接口RemoteResourceProxy. (图有误,实在懒得换了) , 所有TPAgent 的子类都应该持有这个接口,并且要通过这个接口完成所需要的逻辑。 而RemoteResourceProxy 接口的继承类可以使用第三方NATIVE
API了,例如SinaweiboReousceProxy 使用Sinaweibo的JAVA API, QQResourceProxy 类里面使用腾讯的API。
通过这种设计我相信应该可以的满足扩展性和模块独立性的需求了。 例如,如果我们以后要加入人人网的第三方登录功能,我们就可以实现一个例如RenRenResouceProxy的类并且使它实现RemoteResourceProxy接口, 然后在枚举类TPType里增加新的 一项。 而且由于TPAgent 面向的是RemoteResourceProxy接口,不直接面对各个NATIVE API, 所有就算是我们升级NATIVE API, 或者加入新的RemoteResourceProxy接口实现类, TPAent的子类逻辑也不会跟着做变动,
我们只要调整相应的RemoteResourceProxy接口实现类就可以了。
看似设计就这么简单的结束了,其实不然, 不多想象的话以上设计就是纸上谈兵。 因为有两个问题没有考虑到。 1 RemoteResourceProxy接口实现类一般都需要一些参数,例如OAUTH 的 这些 ResourceProxy 一定需要为了得到ACCESS TOKEN 的 code。 如何优雅的传入参数是一个问题。 2 我们的TPAgent 有一个静态的Build 方法,我们应该如何根据TPType的值来构建相应的TPAgent 子类。
对于第一个问题我首先想到的是用ThreadLocal,这样我们可以在的STRUTS Action 中直接用ThreadLocal.set()设置参数,而我们的RemoteResourceProxy接口实现类可以简单的通过ThreadLocal.get() 得到ACTION 的值。 这种想法有点象STRUTs2里面的 ActionContext的使用. 但是用 ThreadLocal 有一些弊端, 例如如果我希望开一个额外的线程来完成微博分享操作,那么ThreadLocal马上就变得失效。 所以最后我还是设计了一个TPAgentBuildingParam
类, 用户在调用TPAgent类的build方法的时候需要传入这个类的实例。
对于第二个问题, 我想到BUILDER 模式, 我可以设计的一个Builder 类, 然后让这个类根据传入的TPType, 和TPAgentBuildingParam 创建对应的TPAgent 子类实例。而TPAgent 的静态方法Build()将会使用这个Builder 类。 但是一想到要在这个类里写一大堆IF ELSE. 我就放弃了。因为每次我加新的RemoteResourceProxy接口实现类都会在这个类加新的IF ELSE 条件语句, 这样破坏了开闭原则。 我最后想到的方法是借用TPType
这个枚举类。 因为每次加入新的RemoteResourceProxy接口实现类的时候,我都要在这个枚举类里面添加新的项, 如果我把一些如何构建TPAgent 子类的信息放入这个项里面,那就会变得方便很多。 于是把一开始想的Builder 类设计成一个接口,而对于每个第三方登录的源,我都实现一个对应的Builder接口实现类
这么设计的好处是如果我们添加新的RemoteResourceProxy接口实现类的时候, 我要做的就是 1. 写一个新的Builder实现类,2. 在枚举类添加新的项然后把新的Builder实现类的Class 作为实例传入。
最后整体的设计变为
设计终于完成了, 不过现在太晚了,脑子不做主了,说不定还有什么遗漏的东西,不管了以后边做边改吧。
定好需求之后第一个想到就是使用桥梁模式。 以下是第一个设计版本。
抽象类TPAgent 是模块的门户,对外它是主要提供访问接口。 这里我只列出了一个示例方法 createTPAccountBinding(), 这个方法将返回 TPAccountBinding 类实例, 而这个类包含了用户在第三方网站里面的一些信息。 桥梁模式的另一头是接口RemoteResourceProxy. (图有误,实在懒得换了) , 所有TPAgent 的子类都应该持有这个接口,并且要通过这个接口完成所需要的逻辑。 而RemoteResourceProxy 接口的继承类可以使用第三方NATIVE
API了,例如SinaweiboReousceProxy 使用Sinaweibo的JAVA API, QQResourceProxy 类里面使用腾讯的API。
通过这种设计我相信应该可以的满足扩展性和模块独立性的需求了。 例如,如果我们以后要加入人人网的第三方登录功能,我们就可以实现一个例如RenRenResouceProxy的类并且使它实现RemoteResourceProxy接口, 然后在枚举类TPType里增加新的 一项。 而且由于TPAgent 面向的是RemoteResourceProxy接口,不直接面对各个NATIVE API, 所有就算是我们升级NATIVE API, 或者加入新的RemoteResourceProxy接口实现类, TPAent的子类逻辑也不会跟着做变动,
我们只要调整相应的RemoteResourceProxy接口实现类就可以了。
看似设计就这么简单的结束了,其实不然, 不多想象的话以上设计就是纸上谈兵。 因为有两个问题没有考虑到。 1 RemoteResourceProxy接口实现类一般都需要一些参数,例如OAUTH 的 这些 ResourceProxy 一定需要为了得到ACCESS TOKEN 的 code。 如何优雅的传入参数是一个问题。 2 我们的TPAgent 有一个静态的Build 方法,我们应该如何根据TPType的值来构建相应的TPAgent 子类。
对于第一个问题我首先想到的是用ThreadLocal,这样我们可以在的STRUTS Action 中直接用ThreadLocal.set()设置参数,而我们的RemoteResourceProxy接口实现类可以简单的通过ThreadLocal.get() 得到ACTION 的值。 这种想法有点象STRUTs2里面的 ActionContext的使用. 但是用 ThreadLocal 有一些弊端, 例如如果我希望开一个额外的线程来完成微博分享操作,那么ThreadLocal马上就变得失效。 所以最后我还是设计了一个TPAgentBuildingParam
类, 用户在调用TPAgent类的build方法的时候需要传入这个类的实例。
public class TPAgentBuildingParam { public static int OAUTH_CODE = 1; private Map<Integer, Object> paramMap = new HashMap<Integer, Object>(); public void setOAuthCodeForAccessToken(String code){ paramMap.put(OAUTH_CODE, code); } public String getOAuthCode()
}
对于第二个问题, 我想到BUILDER 模式, 我可以设计的一个Builder 类, 然后让这个类根据传入的TPType, 和TPAgentBuildingParam 创建对应的TPAgent 子类实例。而TPAgent 的静态方法Build()将会使用这个Builder 类。 但是一想到要在这个类里写一大堆IF ELSE. 我就放弃了。因为每次我加新的RemoteResourceProxy接口实现类都会在这个类加新的IF ELSE 条件语句, 这样破坏了开闭原则。 我最后想到的方法是借用TPType
这个枚举类。 因为每次加入新的RemoteResourceProxy接口实现类的时候,我都要在这个枚举类里面添加新的项, 如果我把一些如何构建TPAgent 子类的信息放入这个项里面,那就会变得方便很多。 于是把一开始想的Builder 类设计成一个接口,而对于每个第三方登录的源,我都实现一个对应的Builder接口实现类
interface TPAgentBuilder { TPAgent build( TPAgentBuildingParam param); }
class QQresourceAgentBuilder implements TPAgentBuilder { @Override public TPAgent build(TPAgentBuildingParam param) { if(param.getParam(TPAgentBuildingParam.OAUTH_CODE)==null) throw new IllegalArgumentException("oauth code is empty in TPAgentBuildingParam instance"); QQResourceProxy proxy = new QQResourceProxy(param.getOAuthCode()); return new OAuthTPAgent(proxy); } }
class SinaweiboResourceAgentBuilder implements TPAgentBuilder { @Override public TPAgent build(TPAgentBuildingParam param) { if(param.getParam(TPAgentBuildingParam.OAUTH_CODE)==null) throw new IllegalArgumentException("oauth code is empty in TPAgentBuildingParam instance"); QQResourceProxy proxy = new QQResourceProxy(param.getOAuthCode()); return new OAuthTPAgent(proxy); } }
public enum TPType { SINAWEIBO(TPAgentBuildingParam.OAUTH_CODE,SinaweiboResourceAgentBuilder.class), QQ(TPAgentBuildingParam.OAUTH_CODE, QQresourceAgentBuilder.class ); int paramField; Class<? extends TPAgentBuilder> agentBuilerClass; private TPType(int i, Class<? extends TPAgentBuilder> agentBuilerClass) { paramField= i; this.agentBuilerClass = agentBuilerClass; } }
public abstract class TPAgent { public abstract TPAccountBinding createTPAccountBinding(); public static TPAgent build(TPType type, TPAgentBuildingParam param) throws TPAgentException { try { TPAgentBuilder builder = (TPAgentBuilder)type.agentBuilerClass.newInstance(); return builder.build(param); } catch (Exception e) { throw new TPAgentException(e); } } }
这么设计的好处是如果我们添加新的RemoteResourceProxy接口实现类的时候, 我要做的就是 1. 写一个新的Builder实现类,2. 在枚举类添加新的项然后把新的Builder实现类的Class 作为实例传入。
最后整体的设计变为
设计终于完成了, 不过现在太晚了,脑子不做主了,说不定还有什么遗漏的东西,不管了以后边做边改吧。
相关文章推荐
- 某海量用户网站,用户拥有积分,积分可能会在使用过程中随时更新。现在要为该网站设计一种算法,在每次用户登录时显示其当前积分排名。用户最大规模为2亿;积分为非负整数,且小于100万。
- 【用户授权设计】java第三方登录(微博,QQ)详细代码
- Win Vista服务中指定已登录用户会话来启动应用程序
- Java面向对象基础--类的设计及分析问题的方法---用户登录例子
- ASP.NET MVC下判断用户登录和授权状态方法
- 网站用户登录系统设计——jsGen实现版
- IIS管理器里添加虚拟目录,提示: “/”应用程序中的服务器错误。用户 'NT AUTHORITY\IUSR' 登录失败
- 修改Linux远程登录用户并授权,禁止root远程登录,修改Linux登录端口
- 如何设计出用户体验良好的登录/注册页面
- 用户具有多重角色,角色拥有可重复权限,确定用户具有权限的数据库设计方案(关系型数据库)
- Sqlserver中 登录用户只能看到自己拥有权限的库
- C#检测应用程序重复启动----函数检测(可以在多用户登录情况下检测)
- ubuntu下如何查看用户登录及系统授权相关信息
- 外界用户对支付宝登录密码认知度较低,只知道支付密码,因此设计如此
- 如何设计一个用户为中心的Web应用程序
- Oracle中管理用户(创建用户,用户加锁,用户解锁,修改用户密码,授权登录权限,撤销登录权限,授权连接权限,conn命令,创建角色,并为角色赋权限,将角色赋给指定用户)
- 关于远程桌面登陆提示“连接被拒绝,因为没有授权此用户账号进行远程登录”
- 连接被拒绝 因为没有授权此用户账户进行远程登录
- 微信小程序处理用户拒绝授权情况及微信登录,登录保存等系列解决方案