设计模式之美--鉴权功能实战

代码详见: github.com/zjbao123/PatternDesign

需求来源

为了保证接口调用的安全性,我们希望设计实现一个接口调用鉴权功能,只有经过认证之后的系统才能调用我们的接口,没有认证过的系统调用我们的接口会被拒绝。我希望由你来负责这个任务的开发,争取尽快上线。

需求分析

  • 阶段一:

    通过 userId和password进行认证,如果不一致,则鉴权失败,如果一致则成功。

    缺点:密码明文存在安全问题

  • 阶段二:

    将password进行加密后进行认证。

    缺点:将会导致重放攻击。

  • 阶段三:

    增加随机数,例如时间戳来生成token进行认证,设定超时间隔(一分钟),超时则认为token过期。

    缺点:一分钟内还是会有重放攻击。

需求拆分

  1. 将URL,userId,password和时间戳拼接成字符串
  2. 通过加密算法将字符串生成token
  3. 将token和userId,时间戳拼在URL上发送至服务端
  4. 解析URL,拆分出token,userId,时间戳
  5. 比较时间戳,判断token是否超时
  6. 从数据库通过userId取得对应的password
  7. 生成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);