🔏密码学理论与技术要点分析

  • 对用户口令进行加密存储

    • Spring Security 支持多种不同的数据源,这些不同的数据源最终都将被封装成 UserDetailsService 的实例,我们是自己来创建一个类实现 UserDetailsService 接口,除了自己封装,我们也可以使用系统默认提供的 UserDetailsService 实例
    • 我们来看下 UserDetailsService 都有哪些实现类:
    • img
    • 可以看到,在几个能直接使用的实现类中,除了 InMemoryUserDetailsManager 之外,还有一个 JdbcUserDetailsManager,使用 JdbcUserDetailsManager 可以让我们通过 JDBC 的方式将数据库和 Spring Security 连接起来。
    • 这里需要数据库支持,所以我们在项目中添加如下两个依赖:
    • <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jdbc</artifactId>
      </dependency>
      <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      </dependency>
    • 然后再在 application.properties 中配置一下数据库连接,配置完成后,就可以启动项目。

    • img

  • 对用户口令的强度校验和规则设定

    • Vue框架下,可以通过前端页面对于用户注册输入的口令进行强度限制和规则设定,其中输入口令强度检验在js代码的编写中设定好规则,本项目规定密码只能为阿拉伯数字、英文字母(不区分大小写)、除".-"以外的特殊符号进行设定,当用户采用其中不同组合时,密码强度的设定也为随之提高,本项目用颜色进行区分。

    • 而在特殊符号的筛选当中,使用正则表达式去进行筛选鉴别即可,确保用户的口令不会过于简单。

    • 同时在input的输入框也设置规则,包括用户的用户名、口令长度都进行限制,本项目用户名长度限制为11位的阿拉伯数字,口令长度限制为8-36

    • 实现代码如下:

    • </el-form-item>
      <el-form-item>
      <el-input
      v-model="registered.password"
      @input="checkPassLever"
      type="password"
      placeholder="请输入密码"
      onkeyup="value=value.replace(/[^\a-\z\A-\Z0-9\-\_\.]/g,'')"
      maxlength="36"
      show-word-limit
      ></el-input>
      <table
      border="0"
      align="center"
      style="width: 200px;margin-left: 0px"
      >
      <tr>
      <td width="60px">
      <el-progress
      :percentage="100"
      :color="tr1"
      :format="format"
      ></el-progress>
      </td>
      <td width="60px">
      <el-progress
      :percentage="100"
      :color="tr2"
      :format="format"
      ></el-progress>
      </td>
      <td width="60px">
      <el-progress
      :percentage="100"
      :color="tr3"
      :format="format"
      ></el-progress>
      </td>
      <td width="20px">
      <div
      class="strength"
      :style="{ color: fontColor }"
      v-if="
      registered.password !== '' &&
      registered.password !== undefined
      "
      >
      {{ strength }}
      </div>
      </td>
      </tr>
      </table>
      </el-form-item>
      export default {
      name: "Login",
      data() {
      const checkPawd = (rule, value, callback) => {
      checkPasswd(value).then((res) => {
      if (res.msg !== "notCheck") {
      this.notCheck = false;
      if (res.msg === "低") {
      this.fontColor = "red";
      this.strength = this.indicator["red"];
      }
      if (res.msg === "中") {
      this.fontColor = "orange";
      this.strength = this.indicator["orange"];
      }
      if (res.msg === "高") {
      this.fontColor = "blue";
      this.strength = this.indicator["blue"];
      methods: {
      format(percentage) {
      return percentage === 100 ? "" : `${percentage}%`;
      },
      // 验证面膜等级
      checkPassLever(value) {
      // 红 蓝 绿
      if (value.length == 1) {
      this.tr2 = "#FFFFFF";
      this.tr3 = "#FFFFFF";
      }
      if (value.length == 2) {
      this.tr3 = "#FFFFFF";
      }
      if (value.length > 1) {
      var pd1 = /[._-]/;
      var pd2 = /[a-zA-Z]/;
      var pd3 = /[0-9]/;
      var x1 = pd1.test(value) && pd2.test(value);
      var x2 = pd3.test(value) && pd2.test(value);
      var x3 = pd1.test(value) && pd3.test(value);
      if (x1 || x2 || x3) {
      this.tr2 = "blue";
      } else {
      this.tr2 = "#FFFFFF";
      }
      }
      if (value.length > 2) {
      var zg2 = /^(?![^a-zA-Z]+$)(?!\D+$)(?![._-]+$)/;
      var zg3 = /[._-]/;
      if (zg2.test(value) && zg3.test(value)) {
      this.tr3 = "green";
      } else {
      this.tr3 = "#FFFFFF";
      }
      }
      },
  • 对文件路由进行加密

    • 加密需要依赖 crypto-js

    • 在创建路由的时候,添加两个方法

    • stringifyQuery: 序列化传入的query参数,方法可以接收一个对象参数

      new Router的时候传递这个属性,在序列化query参数的就执行这个方法,不会执行默认的方法,序列化后在地址栏显示序列化之后的参数

    • parseQuery: 解析地址栏参数,方法接收一个字符串参数

      new Router的时候传递这个属性,在解析query参数的时候,回执行这个方法,不会在执行默认的方法。

      注: 这个方法只解析path中的参数,或者浏览器刷新的时候的地址栏的参数,不会对在query参数对处理,如:

      this.$router.push({
      path: "foo?a=123",
      query: {
      b: 345
      }
      })
      • 在执行这段代码的时候,parseQuery方法不会对query:{b: 345}进行解析,会解析path:"foo?a=123"中的a=123的字符串

      • 序列化

        vue-router在执行createRoute的时候,获取fullPath会执行getFullPath方法

        createRouter 方法 会获取在 new VueRouter的时候传递的stringifyQuery方法,如果没有这个方法,就会在getFullPath的时候,使用默认的方法

      • 反序列化

        在调用push的时候,会执行this.router.match方法,match方法会执行normalizeLocation

        normalizeLocation通过resolveQuery方法解析path中的query,传入的三个参数(path中的?之后的参数数据字符串,使用push或replace方法传递的query参数,反序列化参数的方法)

        反序列化方法会通过router && router.options.parseQuery获取,如果在new VueRouter的时候传递了parseQuery方法,就是用该方法,如果没有就在resolveQuery方法中使用默认的方法

      • 最后用采用md5算法进行封装加密即可得到加密不可逆转的路由

      • img

  • 对文件本身支持用户自定义口令进行加密

    • 前端使用jsencrypt.js。

      后端需要一个RSA工具类,包含生成密钥对、加解密等功能。

      流程:

      1.后端生成密钥对,并把公钥传递给前端,后端保存私钥

      2.前端用公钥加密数据后,传递给后端

      3.后端用私钥解密,获取数据

      注意:

      1.后端把公钥用base64编码为字符串传输。

      import org.apache.commons.codec.binary.Base64;
      private static String encryptBASE64(byte[] bytes) {
      return Base64.encodeBase64String(bytes);
      }

      2.前端加密后的数据也是以base64编码传输的,后端需要base64解码。

  • 获取文件散列值,并进行校验文件是否遭到篡改

    • MD5的加密方式是一种哈希加密。一些主流的编程语言都已经实现了MD5的加密,所以如果你的程序或是系统涉及到在多种语言之间的校验,那么MD5可以是备选之一。不过因为MD5是采用哈希函数来进行的加密,所以它无关密钥,也就是说在确定了明文的情况下,MD5就可以加密。不过MD5是不可逆的,只能加密,不能解密。

    • public final class MD5 {
      public static String encrypt(String strSrc) {
      try {
      char hexChars[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
      '9', 'a', 'b', 'c', 'd', 'e', 'f'};
      byte[] bytes = strSrc.getBytes();
      MessageDigest md = MessageDigest.getInstance("MD5");
      md.update(bytes);
      bytes = md.digest();
      int j = bytes.length;
      char[] chars = new char[j * 2];
      int k = 0;
      for (int i = 0; i < bytes.length; i++) {
      byte b = bytes[i];
      chars[k++] = hexChars[b >>> 4 & 0xf];
      chars[k++] = hexChars[b & 0xf];
      }
      return new String(chars);
      } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
      throw new RuntimeException("MD5加密出错!!+" + e);
      }
      }
      public static void main(String[] args) {
      System.out.println(MD5.encrypt("111111"));
      }
      }