代码详见: github.com/zjbao123/PatternDesign
需求来源
为了保证接口调用的安全性,我们希望设计实现一个接口调用鉴权功能,只有经过认证之后的系统才能调用我们的接口,没有认证过的系统调用我们的接口会被拒绝。我希望由你来负责这个任务的开发,争取尽快上线。
需求分析
阶段一:
通过 userId和password进行认证,如果不一致,则鉴权失败,如果一致则成功。
缺点:密码明文存在安全问题
阶段二:
将password进行加密后进行认证。
缺点:将会导致重放攻击。
阶段三:
增加随机数,例如时间戳来生成token进行认证,设定超时间隔(一分钟),超时则认为token过期。
缺点:一分钟内还是会有重放攻击。
需求拆分
- 将URL,userId,password和时间戳拼接成字符串
- 通过加密算法将字符串生成token
- 将token和userId,时间戳拼在URL上发送至服务端
- 解析URL,拆分出token,userId,时间戳
- 比较时间戳,判断token是否超时
- 从数据库通过userId取得对应的password
- 生成token比较是否一致。
设计
- 划分职责进而识别出有哪些类
- 定义类以及属性和方法
- 定义类与类之间的交互关系
- 将类组装起来并提供执行入口
划分职责进而识别有哪些类
1,2,5,7 与token的生成,验证,比较有关
3,4与URL有关,负责处理URL的拼接和拆分
6操作userId和password,从存储中读取userId和password
因此,初步可以设置成三个类,分别为AuthToken,ApiRequest,CredentialStorage
定义类以及属性和方法
AuthToken
属性
token,createTime,expiredTimeInterval
构造函数
AuthToken(String token, long createTime);
AuthToken(String token, long createTime, long expiredTimeInterval);
方法
createToken(String url, long createTime, Map<String,String> params);
getToken();
isExpired();
match(AuthToken authToken);
ApiRequest
属性
token, baseUrl, userId,timestamp
构造方法
ApiRequest(String token, String baseUrl, String userId);
方法
get;
ApiRequest getRequestFromURL(String url);
CredentialStorage
接口
String getPasswordFromUserId(String userId);
定义类与类之间的交互关系
UML中主要分为 泛化,实现,组合,聚合, 关联, 依赖。
泛化就是继承,实现就是接口和实现类之间的关系
聚合是包含关系,A类对象包含B类对象,B的生命周期不依赖A的生命周期,类似课程和学生的关系
组合也是包含关系,不同点在于B的生命周期依赖A的生命周期,类似翅膀和鸟类的关系。
关联包含聚合和组合两种关系。
依赖时一种比关联关系更加弱的关系,包含关联关系,只要B类对象时A类对象的参数,返回值,或者局部变量,任何使用关系,都称之为依赖。
简化后,将组合关系替代UML中的组合,聚合,关联三个概念,都称之为组合,因此也就有了
“多用组合少用继承”的设计原则。
将类组装起来并提供执行入口
ApiAuthenticator接口
void auth(String url);
void auth(ApiRequest apiRequest);
ApiAuthenticator实现
属性
private CredentialStorage credentialStorage;
构造函数
ApiAuthenticator();
ApiAuthenticator(ApiAuthenticator apiAuthenticator);
函数
void auth(String url);
void auth(ApiRequest apiRequest);