大继的基础级业务实战设计记录(四),谷歌令牌
2018-07-21 本文已影响0人
大继
场景描述
- 为提高系统安全,多数情况我们使用手机验证码,但是如果操作频率太高,手机验证码经常会丢失,发送太频繁,太多引起验证码丢失问题。所以选用google token来满足。
- 场景一,后台对积分管理
- 场景二,订单支付操作等...
满足
- 用户锁定google令牌
- 可以用google令牌验证,敏感类型操作。
- 现非聚群系统也可以使用
- 多服务聚合使用(保证多个服务集合使用是uri不冲突)
- 默认原则,能默认就不用定义
- 资源化restful
- 防止暴力破解
主要实现
- 发行一个用户密钥
- 通过旧密钥更新密钥 http://google/authenticates GoogleAuthenticateUserRefreshRequest
- 用户向 http://google/authenticates GoogleAuthenticateUserRequest 验证动态码是否正确
- 强行更新密钥 PUT http://google/authenticates/xxx GoogleAuthenticate (用于后台管理,或通过手机验证码等更高权重的验证进行刷新或修改)
范围
一个用户只能绑定一个证明 authenticate.
restful设计
- 绑定google令牌码 POST http://google/authenticates GoogleAuthenticate
- POST http://google/authenticates GoogleAuthenticateUserRequest
TOTP 实现
发行
public GoogleAuthenticate release(Long userId, String account) {
if(googleAuthenticateRepository.findByUserId(userId) == null){
GoogleAuthenticate googleAuthenticate = new GoogleAuthenticate();
googleAuthenticate.setId(UUID.randomUUID().toString());
googleAuthenticate.setCreateTime(new Date());
googleAuthenticate.setEnable(true);
googleAuthenticate.setIssuer(ISSUER);
googleAuthenticate.setAccount(account);
googleAuthenticate.setKey(randomSecretKey());
googleAuthenticate.setUserId(userId);
googleAuthenticateRepository.save(googleAuthenticate);
return googleAuthenticate;
}else {
throw new RuntimeException("User had already released authenticate.");
}
}
验证
public Boolean validate(Long userId, String code) {
Boolean isAuthenticate = false;
//连续错误次数 6/1 (次/天) 锁定用户
GoogleAuthenticateWrongDaliyLog googleAuthenticateWrongDaliyLog =
googleAuthenticateWrongDailyLogRepository.findByUserIdAndDay(userId,
new java.sql.Date(System.currentTimeMillis()));
if(googleAuthenticateWrongDaliyLog != null && googleAuthenticateWrongDaliyLog.getWrongTimes() > 6){
return false;
}
GoogleAuthenticate googleAuthenticate = googleAuthenticateRepository.findByUserId(userId);
if(googleAuthenticate != null){
//check
if(code.equals(getTOTPCode(googleAuthenticate.getKey()))){
isAuthenticate = true;
}
}
if(!isAuthenticate){
if(googleAuthenticateWrongDaliyLog == null){
googleAuthenticateWrongDaliyLog = new GoogleAuthenticateWrongDaliyLog();
googleAuthenticateWrongDaliyLog.setDay(new java.sql.Date(System.currentTimeMillis()));
googleAuthenticateWrongDaliyLog.setUserId(userId);
googleAuthenticateWrongDaliyLog.setWrongTimes(0);
}
googleAuthenticateWrongDaliyLog.setWrongTimes(googleAuthenticateWrongDaliyLog.getWrongTimes() + 1);
googleAuthenticateWrongDailyLogRepository.save(googleAuthenticateWrongDaliyLog);
}
return isAuthenticate;
}
实体设计
@Entity
@Table(name = "google_authenticate",
indexes = {@Index(columnList = "userId",unique = true)})
public class GoogleAuthenticate {
@Id
@Column(length = 37)
private String id;
@Column
private String account;
@Column(length = 64)
private String key;
@Column
private String issuer;
@Column
private Long userId;
/**
* 用于锁定用户,如果过于频繁,验证失败。
*/
@Column
private Boolean enable;
@Column
private Date updateTime;
@Column
private Date createTime;
}
验证
@Test
public void releaseTest(){
GoogleAuthenticate googleAuthenticate = googleAuthenticateService.release(0L,"daji@qq.com");
//把输出放,草料生成二维码,导入到 google authenticator
System.out.println(googleAuthenticateService.produceQrCode(googleAuthenticate));
}
输出
otpauth://totp/hitstone.cn%3Adaji%40qq.com?secret=3HOKVR3YIM3YXCCOB3WSU34COBFA5MBH&issuer=hitstone.cn
//导入到 google authenticator
image.png
@Test
public void checkCodeTest(){
String code = "047345"; //查看google authenticator 填入,并认证。
System.out.println(googleAuthenticateService.validate(0L,code));
}
输出
true
由于写的比较冲忙,只对思路进行描述。