项目介绍——对话式教学系统

对话式教学系统

背景

  • 该项目为团队合作开发完成的课程项目,是一个基于微信小程序的教学内容管理平台。教师在网页端编辑并发布课程内容,学生通过微信小程序进行对话式学习。对话式学习,就是学生在微信小程序端一个类似于聊天室的页面进行学习,知识点是以聊天气泡的形式展示的,每学习完一个知识点可以点击next显示下一个,该系统暂时只支持一般的陈述性知识点和简单的单选题。当学生在学习过程中遇到单选题时,需要从A、B、C、D四个选项中选择出正确的那个才能继续查看后面的知识点。

职责

  • 微信小程序的开发
  • 后端开发及服务器的部署
  • 设计数据库表结构

重点

  1. 身份认证和权限管理
    1. 使用拦截器+注解+JWT实现身份认证和权限管理:在用户登录/注册时,生成一个JWT返回给用户,用户得到token之后,在每次HTTP请求的请求头中带上这个token。创建相应的注解,添加到需要身份认证和权限认证的Controller方法上。配置拦截器,在拦截器中判断Controller方法是否有注解,如果有注解,则使用工具类判断用户身份是否合法以及是否有相应的访问权限。
    2. JWT简单介绍:JWT主要分为3个部分header、payload、signature,也就是头部、有效载荷以及签名。使用的时候首先需要设置头部,然后将用户名、权限、过期时间等信息(这些信息都可以解码获得的)放到有效载荷中,最后使用服务器中保存的密钥以及头部指定的签名算法对header、payload进行签名。
    3. JWT的优点:JWT是无状态的,不在服务器端存储任何状态,这样便降低了服务器的负载。
    4. JWT的缺点:安全性,JWT的payload是使用base64编码的,并没有加密,因此jwt中不能存储敏感数据,可以对JWT进行加密来解决此问题;一次性,JWT是一次性的,这带来了两个问题,无法废弃以及续签。如果想要解决JWT一次性的问题,那么必须添加额外的机制使得服务器保存一些状态信息,这和使用session差不多。JWT只适合那种有效期短或者只使用一次的场景,如邮件激活账户。
      1. 无法废弃:如果你在payload存储了一些信息,当信息更新时,则重新签发一个JWT,但是由于旧的JWT还没有过期,拿着这个JWT仍然可以登录。为了解决此问题,我们需要在服务器添加额外的逻辑,例如设置一个黑名单,一旦签发了新的JWT,那么旧的JWT就添加到黑名单(如使用redis实现)中,避免旧的JWT被再次使用。
      2. 续签:要改变JWT的有效时间,就需要签发新的JWT。最简单的方式就是每次请求续签JWT,便返回一个新的JWT,但是这种方式的性能很差。另外一种方式就是在redis中为每个JWT设置过期时间,每次续签便刷新JWT的过期时间。
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); // 计算过期时间
Map<String, Object> header = new HashMap<>(2); // 设置头部信息
header.put("typ", "JWT");
header.put("alg", "HS256");
Algorithm secret= Algorithm.HMAC256(TOKEN_SECRET); // 签名密钥
// 将用户名、权限、过期日期等信息放到有效载荷中
return JWT.create()
	.withHeader(header)
	.withClaim("username", username)
	.withClaim("query", true)
	.withExpiresAt(date)
	.sign(secret);
  1. 数据库中存储链表:为表增加一个字段next,一个记录的next字段指向其在链表中的下一个节点的id。以knowledge表为例,现在共包含id、next、description三个字段。以下为查询链表、增加节点、删除节点三个基本操作的概述(由于这些操作都涉及了多个查询,而且前一个查询的结果会对后一个查询产生影响,所以实现时应该使用事务。由于MySQL默认开启自动提交,所以将这些操作放到一个事务中之后还会提升一点性能,相当于锁粗化)。

    1. 查询链表:首先将数据库中所有数据查询出来,然后找到next字段为-1(next字段为-1表示链表的尾部节点)的节点并记下此节点的id,假设为2,接着找到next字段为2的节点并记下此节点的id,如此循环直到得到链表的所有节点。
    2. 插入节点:由于需要在链表的任意位置插入节点,所以需要客户端把前一个节点的id传给服务器。先把新的节点插入数据库,记作C,并从数据库中查询到要插入位置的前一个节点记作A及后一个节点记作B。将A的next字段更新为C的id,C的next字段设置成B的id,然后更新数据库中的记录。
    3. 删除节点:删除节点也需要客户端把前一个节点的id传给服务器,根据这个id从数据库中查询出前一个节点记作A,当前节点记作B,下一个节点记作C。然后从数据库中删除节点B对应的记录,将A的next字段更新为C的id。
  2. 服务器部署:租了阿里云的服务器,然后将项目后端服务部署到了Docker容器中。如何解决服务器OOM或者物理宕机等问题?

    1. OOM:嗯~当时我们并没有考虑这个问题,我能想到的原因有两个,一个是程序的逻辑有问题,另一个是高并发。程序逻辑有问题导致OOM的话,可以使用一些Java内存分析工具如jconsole来排查,然后修复bug。高并发导致OOM的话,可以在启动JVM时分配更多的内存,如果还不能解决问题的话,可能就需要创建服务器集群,最简单的服务器集群是两台云服务器+负载均衡器
    2. 物理宕机:因为服务都是部署到云服务器上的,当阿里云检测到云服务器所在的物理机宕机时,会对服务进行保护性迁移,将云服务器迁移到性能正常的宿主机上。发生宕机迁移时,服务器会被重启,所以我需要将服务设置成开机自启动。docker容器设置开机自启动,使用指令 docker update --restart=always 容器名称或者容器id

猜你喜欢

转载自blog.csdn.net/a16302010048/article/details/104579131
今日推荐