基于分布式文件系统 FastDFS,利用 Zuul 网关实现滑块验证登录

14 篇文章 1 订阅
10 篇文章 0 订阅

点击上方蓝色“架构荟萃”关注我们,输入1024,你懂的

需求分析

现在滑块验证码在许多网站逐步流行起来,比如今日头条搜新闻时会提示滑块验证。

一方面,滑块验证对用户体验来说,比较新颖,操作简单。另一方面其安全性相对于图形验证码来说,并没有降低多少。当然没有绝对的安全验证,只是在不断增加攻击者的穿透成本。另外,还可以起到宣传企业愿景和重大历史事件的作用。

最终效果图

关键技术栈

  1. SpringCloud

  2. FastDFS:分布式文件系统

  3. Redis:利用 Go 语言编写的 Codis 作为分布式缓存系统

  4. Mysql

  5. Docker/K8s 容器平台

设计要点

1、如何生成滑块临时大图以及小图

2、前后端分离,脱离传统 Session 会话机制,如何存储用户会话滑块图片参数

3、最佳滑块验证方式:统一入口网关验证

滑动验证流程分析

1、服务端随机生成小图和带有小图阴影的背景大图,服务端保存随机抠图位置坐标;

2、前端实现滑动交互,将小图拼在大图阴影之上,同时获取用户滑动距离值(一般语序误差范围在 3-5 像素);

3、前端将用户滑动距离值传入服务端,服务端校验误差是否在容许范围内;验证OK,返回登录验证码

4、服务网关(Zuul)登录验证:通过客户端传值验证码与 redis 缓存验证码比较匹配,决定是否登录成功

数据库设计

滑块主表

CREATE TABLE `us_slide` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`rdm_len` int(11) NOT NULL DEFAULT '5' COMMENT '随机图片总数',
`img_prefix` varchar(20) DEFAULT 'login_slide_img_' COMMENT '随机图片默认编号前缀:slide_img_',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

滑块-随机图片表-从表

CREATE TABLE `us_slide_image` (
`img_no` varchar(20) NOT NULL COMMENT '图片编号',
`img_url` varchar(200) NOT NULL COMMENT '图片地址',
`sid` bigint(20) NOT NULL COMMENT '滑块外键ID',
`expire` char(1) DEFAULT '1' COMMENT '1是过期',
PRIMARY KEY (`img_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

滑块-临时图片记录表:用于通过 fastdfs 删除历史临时滑块图片

CREATE TABLE `us_slide_image_record` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`his_img_url` varchar(200) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=540 DEFAULT CHARSET=utf8;

分布式文件系统设计:FastDFS

FastDFS

是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等。

Maven 依赖

<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
</dependency>

参数配置

fdfs:
  soTimeout: 30000
  connectTimeout: 20000
  thumbImage:
  width: 150
  height: 150
  trackerList:
    - 127.0.0.1:22122

FastDFSClient 工具类设计 

滑块验证相关接口设计

初始化用户登录大小图片接口设计

/slide/v1/opi/initial-image

RecordRunnar 线程

由于早8点-9点用户登录频次最高,为了提高程序并发请求性能,可以利用 Java 线程池异步多线程方式,异步保存临时图片存储记录,然后用于临时图片清理工具(imgcleaner)单独清理。

class RecordRunnar implements Runnable {
  private String tempImageUrl;
  
  public RecordRunnar(String tempImageUrl) {
      this.tempImageUrl = tempImageUrl;
  }
  
  @Override
  public void run() {
      SlideImageRecordEntity record = new SlideImageRecordEntity();
      record.setCreateTime(new Date());
      record.setHisImgUrl(this.tempImageUrl);
      baseMapper.insertImageRecord(record);
  }
}

实体设计

@Data
public class SlideEntity implements Serializable {
  private Long id;
  /**
  * 随机图片大小
  */
  private Integer rdmLen;
  /**
  * 默认图片名称默认前缀:slide_img_
  */
  private String imgPrefix;
  /**
  * 图片集合
  */
  private List<SlideImageEntity> imgList;
}
@Data
public class SlideImageRecordEntity implements Serializable {
  private long id;
  // 历史图片地址
  private String hisImgUrl;
  private Date createTime;
}

生成滑块小图和带阴影的大图关键代码

随机图片宽度参数说明

String xwCode = java.util.UUID.randomUUID().toString();
redisClient.setex(SiteConst.Cache.PORTAL_LOGIN_IMAGE_XWIDTH + ":" + xwCode, 5 * 60, widthRandom + "");

每次用户登录时,后台通过 redis设置临时随机图片宽度 xwidth 到缓存(类似与session 会话,保存用户会话参数),用于与前端用户滑块移动宽度比对验证。

同时设置临时随机图片宽度参数:xwidth,过期时间为 5 分钟。

随机小图说明

随机生成的小图 BufferedImage 对象通过 base64 算法加密成一个密码串,

/**
* 图片转BASE64
*
* @param image
* @return
* @throws IOException
*/
public static String getImageBASE64(BufferedImage image) throws IOException {
    byte[] imagedata = null;
    ByteArrayOutputStream bao = new ByteArrayOutputStream();
    ImageIO.write(image, "png", bao);
    imagedata = bao.toByteArray();
    Base64 encoder = new Base64();
    String BASE64IMAGE = encoder.encodeAsString(imagedata).trim();
    // String BASE64IMAGE=encoder.encodeBuffer(imagedata).trim();
    BASE64IMAGE = BASE64IMAGE.replaceAll("\r|\n", "");
    return BASE64IMAGE;
}

用于前端页面通过 img 网页标签把 base64 的加密串渲染出来

base64 串渲染格式

<img src="data:image/png;base64, 加密串/>

滑块图片用户拼装宽度校验接口设计

/slide/v1/opi/check-width/{moveLength}/{xwc}

登录验证码

在前端滑块验证通过后,在用户触发登录接口时,需要把请求参数 vc 传入服务网关进行验证码验证。

//生成验证码
String verfiyCode = java.util.UUID.randomUUID().toString();
redisClient.setex(SiteConst.Cache.PORTAL_VERIFY_CODE + ":" + verfiyCode, 5 * 60, verfiyCode);
Map<String, Object> dataMap = new HashMap<>();
dataMap.put("vc", verfiyCode); // 登录验证码
dataMap.put("xw", xWidth); // 实际图片距离

同时设置登录验证码:verfiyCode,过期时间为 5 分钟。

Docker/K8s

一般微服务基于 docker/k8s 容器化部署,所以考虑单个微服务多节点实例部署,需要考虑数据共享问题,所以不管是随机图片 xwidth 以及登录验证码采用redis 替代 session 会话方式改动成本最小。

Zuul 网关验证:利用网关登录接口方法

在校验登录前,先判断滑块登录验证码 vc 参数是否合法。

验证规则是利用客户端传值验证码与缓存验证码值比较,如果不相等,直接返回验证码校验错误,登录失败。

@ResponseBody
@RequestMapping(value = "/login", method = RequestMethod.POST)
public R login(HttpServletRequest request) {
    //图形验证码
    String vc = request.getParameter("vc");
    if (StringUtils.isNotEmpty(vc)) {
        //portal:vc
        String vcKey = "portal:vc:" + vc;
        boolean captcha = authenticationService.captcha(vcKey, vc);
        if (!captcha) {
            return R.error(ResponseCode.VALIDATION_ERROR);
        }
    }
}

推荐阅读

 

Java 技术经理一枚,头条付费专栏《Spring Cloud Alibaba微服务实战match》作者,擅长微服务&分布式、SpringCloud&SpringBoot、工作流。

                     

后台回复 1024 免费领取微服务、微信小程序、面试等视频资料,扫描上图微信二维码(WooolaDuang)进微信群

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彬禹随笔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值