Google验证码,扫描绑定,SpringBoot+ vue

后端

1.使用Google工具类

package com.ruoyi.common.utils.googleAuth;
import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

public class GoogleGenerator {

    // 生成的key长度( Generate secret key length)
    public static final int SECRET_SIZE = 10;

    public static final String SEED = "22150146801713967E8g";
    // Java实现随机数算法
    public static final String RANDOM_NUMBER_ALGORITHM = "SHA1PRNG";
    // 最多可偏移的时间
    int window_size = 3; // default 3 - max 17

    public static String generateSecretKey() {
        SecureRandom sr;
        try {
            sr = SecureRandom.getInstance(RANDOM_NUMBER_ALGORITHM);
            sr.setSeed(Base64.decodeBase64(SEED));
            byte[] buffer = sr.generateSeed(SECRET_SIZE);
            Base32 codec = new Base32();
            byte[] bEncodedKey = codec.encode(buffer);
            return new String(bEncodedKey);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 这个format不可以修改,身份验证器无法识别二维码
     */
    public static String getQRBarcode(String user, String secret) {
        String format = "otpauth://totp/%s?secret=%s";
        return String.format(format, user, secret);
    }

    /**
     * 根据user和secret生成二维码的密钥
     */
    public static String getQRBarcodeURL(String user, String host, String secret) {
        String format = "http://www.google.com/chart?chs=200x200&chld=M%%7C0&cht=qr&chl=otpauth://totp/%s@%s?secret=%s";
        return String.format(format, user, host, secret);
    }

    public boolean check_code(String secret, String code, long timeMsec) {
        Base32 codec = new Base32();
        byte[] decodedKey = codec.decode(secret);
        long t = (timeMsec / 1000L) / 30L;
        for (int i = -window_size; i <= window_size; ++i) {
            long hash;
            try {
                hash = verify_code(decodedKey, t + i);
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException(e.getMessage());
            }
            System.out.println("code=" + code);
            System.out.println("hash=" + hash);
            if (code.equals(addZero(hash))) {
                return true;
            }
        }
        return false;
    }

    private static int verify_code(byte[] key, long t) throws NoSuchAlgorithmException, InvalidKeyException {
        byte[] data = new byte[8];
        long value = t;
        for (int i = 8; i-- > 0; value >>>= 8) {
            data[i] = (byte) value;
        }
        SecretKeySpec signKey = new SecretKeySpec(key, "HmacSHA1");
        Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(signKey);
        byte[] hash = mac.doFinal(data);
        int offset = hash[20 - 1] & 0xF;
        long truncatedHash = 0;
        for (int i = 0; i < 4; ++i) {
            truncatedHash <<= 8;
            truncatedHash |= (hash[offset + i] & 0xFF);
        }
        truncatedHash &= 0x7FFFFFFF;
        truncatedHash %= 1000000;
        return (int) truncatedHash;
    }

    private String addZero(long code) {
        return String.format("%06d", code);
    }
}

这个 类的 verifyTest 方法可以判断扫描绑定之后的app上面验证码的准确性。

package com.ruoyi.common.utils.googleAuth;

/**
 *
 *
 * 身份认证测试
 *
 * @author yangbo
 *
 * @version 创建时间:2017年8月14日 上午11:09:23
 *
 *
 */
public class GoogleUtils {


    //@Test
    public String genSecret(String g_name) {// 生成密钥
        String secret = GoogleGenerator.generateSecretKey();
        // 把这个qrcode生成二维码,用google身份验证器扫描二维码就能添加成功
        String qrcode = GoogleGenerator.getQRBarcode(g_name, secret);
        System.out.println("qrcode:" + qrcode + ",key:" + secret);

        return secret;
    }
    /**
     * 对app的随机生成的code,输入并验证
     */

    public static boolean verifyTest(String code,String secret) {
        long t = System.currentTimeMillis();
        GoogleGenerator ga = new GoogleGenerator();
       // ga.setWindowSize(5);
        boolean r = ga.check_code(secret, code, t);
        System.out.println("检查code是否正确?" + r);
        return r;
    }

    public static void main(String [] args)
    {
        GoogleUtils gt=new GoogleUtils();

        String secret=gt.genSecret("Antpay(web3game)");
        verifyTest("Antpay(web3game)","2DYHBGQLNLQWSPZV");

    }
}


这个类通过g_user,g_code(就是谷歌验证器的secret,这个你已经插入到数据库 中)来生成相关二维码。

package com.ruoyi.common.utils.googleAuth;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import java.util.HashMap;
import java.util.Map;

/**
 * 二维码工具类
 */
public class QRCodeUtil {
    /**
     * 生成二维码
     *
     * @param content 二维码的内容
     * @return BitMatrix对象
     */
    public static BitMatrix createCode(String content) {
        //二维码的宽高
        int width = 200;
        int height = 200;

        //其他参数,如字符集编码
        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        //容错级别为H
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        //白边的宽度,可取0~4
        hints.put(EncodeHintType.MARGIN, 0);

        BitMatrix bitMatrix = null;
        try {
            //生成矩阵,因为我的业务场景传来的是编码之后的URL,所以先解码
            bitMatrix = new MultiFormatWriter().encode(content,
                    BarcodeFormat.QR_CODE, width, height, hints);

            //bitMatrix = deleteWhite(bitMatrix);
        } catch (WriterException e) {
            e.printStackTrace();
        }

        return bitMatrix;
    }

    /**
     * 删除生成的二维码周围的白边,根据审美决定是否删除
     *
     * @param matrix BitMatrix对象
     * @return BitMatrix对象
     */
    private static BitMatrix deleteWhite(BitMatrix matrix) {
        int[] rec = matrix.getEnclosingRectangle();
        int resWidth = rec[2] + 1;
        int resHeight = rec[3] + 1;

        BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
        resMatrix.clear();
        for (int i = 0; i < resWidth; i++) {
            for (int j = 0; j < resHeight; j++) {
                if (matrix.get(i + rec[0], j + rec[1]))
                    resMatrix.set(i, j);
            }
        }
        return resMatrix;
    }
}




2.用工具类自带的g_user,g_code来生成二维码

这个是SysUser数据库表的部分g_user,g_code数据。

g_userg_code
Antpay(admin)PXUPGNVY6QPWRNNQ
Antpay(payUser)LLFS2ON52UAXOIKP
Antpay(shanghu4)DYFTPDY5MS7CS3HA
Antpay(hwgame)AKZQA7ANHHHZ5TQW
Antpay(baby)5GTBWBTRPEYWCSW2
Antpay(beartech)WEPHOIBAQACJ7VNP
Antpay(gmoney)3AZTCIQJAZMV6IGK
Antpay(ml)H45DLW4C37QNVUX5
Antpay(ml_afr)4XOGTVG7AJXMPJBQ
Antpay(agent1)J6TCF3TIWYC57WWE
Antpay(10069)MKIC4KXOSIU6H2OC
Antpay(10071)7VHKY4YIWCSDBYEC
Antpay(M10068)JKBGRRXBFSQGX45Q
Antpay(M1006801)FI2TNSP2PYOWZKVX
Antpay(M1006802)OTTHGUQHFYNAHDMV

2.1通过请求来生成相关二维码,后端返回给前端

package com.ruoyi.web.controller.runscore;


import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.googleAuth.GoogleGenerator;
import com.ruoyi.common.utils.googleAuth.QRCodeUtil;
import com.ruoyi.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
@Anonymous
@RestController
@RequestMapping(value = "/googleAuth")
public class GoogleAuthController extends BaseController {
    @Autowired
    private ISysUserService sysUserService;

    //根据user和secret生成二维码的密钥
    @PostMapping(value = "/getQRBarcodeURL")
     public AjaxResult getQRBarcodeURL(String user, String host, String secret) {
        return success(GoogleGenerator.getQRBarcodeURL(user, host, secret));
    }
    //查看google 二维码信息
    @PostMapping(value = "/getQRBarcode")
     public AjaxResult getQRBarcode(String user, String secret) {
        return success(GoogleGenerator.getQRBarcode(user, secret));
    }

    /**
     * 生成二维码
     */
    @GetMapping(value = "/generateQRCode/{userId}")
     public void GenerateQRCode(String content, @PathVariable("userId") String userId, HttpServletResponse response) throws IOException {
/*        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if ("anonymousUser".equals(principal)) {
            return ;
        }
        LoginUser user = (LoginUser) principal;*/
//        UserAccountInfoVO userAccountInfo = userAccountService.getUserAccountInfo(user.getUserAccountId());

   /*     LoginUser user = SecurityUtils.getLoginUser();
        String userId = SecurityUtils.getUserId();*/
        SysUser user = sysUserService.selectUserById(userId);
        content=GoogleGenerator.getQRBarcode(user.getGoogleUser(),user.getGoogleCode());
//        content=GoogleGenerator.getQRBarcode("(gemblastmaster)","RGOEVUN2G44TTRZT");
        // 设置响应流信息
        response.setContentType("image/jpg");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        OutputStream stream = response.getOutputStream();
        //获取一个二维码图片
        BitMatrix bitMatrix = QRCodeUtil.createCode(content);
        //以流的形式输出到前端
        MatrixToImageWriter.writeToStream(bitMatrix, "jpg", stream);
    }
    //新增用户的时候生成密钥并且保存
    @GetMapping(value = "/geSecretKey")
     public AjaxResult geSecretKey() {
        return success(GoogleGenerator.generateSecretKey());
    }

    //验证code是否合法
    @PostMapping(value = "/checkValidCode")
    public AjaxResult checkGoogleValidCode(String secret, String code) {
        return success(new GoogleGenerator().check_code(secret, code, System.currentTimeMillis()));
    }

}





    private boolean isMatchMerchant(String username, String googleCode) {
        if(StringUtils.isEmpty(googleCode)){
            return false;
        }

        SysUser sysUser = userService.selectUserByUserName(username);
        if(Objects.isNull(sysUser)){
            throw new RuntimeException("不存在这个用户!");
        }
     /*   QueryWrapper<Merchant> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("relevance_account_id", sysUser.getUserId());
        Merchant merchant = merchantRepo.selectOne(queryWrapper);*/
       Merchant merchant = merchantRepo.selectByUserId(sysUser.getUserId());
        if (Objects.isNull(merchant)) {
            return false;
        }
        if(googleCode.equals(merchant.getSecretKey()) || GoogleUtils.verifyTest(googleCode, sysUser.getGoogleCode())){
            return true;
        }

        return false;
    }



3.第一次通过生成的secret_key登录,之后扫描进行绑定Google验证码,通过验证码进行登录

前端Vue

<template>
  <div class="login">
    <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
      <h3 class="title">登 录</h3>
      <el-form-item prop="username">
        <el-input
          v-model="loginForm.username"
          type="text"
          auto-complete="off"
          placeholder="账号"
        >
          <svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />
        </el-input>
      </el-form-item>
      <el-form-item prop="password">
        <el-input
          v-model="loginForm.password"
          type="password"
          auto-complete="off"
          placeholder="密码"
          @keyup.enter.native="handleLogin"
        >
          <svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" />
        </el-input>
      </el-form-item>
      <el-form-item prop="code">
        <el-input
          v-model="loginForm.code"
          type="text"
          placeholder="Google验证码"
          @keyup.enter.native="handleLogin"
        >
          <!-- <svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" /> -->
        </el-input>
      </el-form-item>
      <!-- <el-form-item prop="code" v-if="captchaEnabled">
        <el-input
          v-model="loginForm.code"
          auto-complete="off"
          placeholder="验证码"
          style="width: 63%"
          @keyup.enter.native="handleLogin"
        >
          <svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" />
        </el-input>
        <div class="login-code">
          <img :src="codeUrl" @click="getCode" class="login-code-img"/>
        </div>
      </el-form-item> -->
      <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox> <br>
      <el-button type="text" @click="showCode" >扫码关联Google验证器 </el-button>
      <el-form-item style="width:100%;">
        <el-button
          :loading="loading"
          size="medium"
          type="primary"
          style="width:100%;"
          @click.native.prevent="handleLogin"
        >
          <span v-if="!loading">登 录</span>
          <span v-else>登 录 中...</span>
        </el-button>
        <div style="float: right;" v-if="register">
          <router-link class="link-type" :to="'/register'">立即注册</router-link>
        </div>
      </el-form-item>
    </el-form>
    <!--  底部  -->
    <div class="el-login-footer">
      <span>Copyright © 2018-2024 antcash.vip All Rights Reserved.</span>
    </div>
  </div>
</template>

<script>
import { getCodeImg, login, showQRCode} from "@/api/login";
import Cookies from "js-cookie";
import { encrypt, decrypt } from '@/utils/jsencrypt'

export default {
  name: "Login",
  data() {
    return {

      codeUrl: "",
      loginForm: {
        username: "admin",
        password: "admin123",
        rememberMe: false,
        googleCode: "",
        code: "",
        uuid: ""
      },
      loginRules: {
        username: [
          { required: true, trigger: "blur", message: "请输入您的账号" }
        ],
        password: [
          { required: true, trigger: "blur", message: "请输入您的密码" }
        ],
        // code: [{ required: true, trigger: "change", message: "请输入验证码" }],
        code: [{ required: true, trigger: "blur", message: "请输入google验证码" }],
      },
      loading: false,
      // 验证码开关
      captchaEnabled: false,
      // 注册开关
      register: false,
      redirect: undefined
    };
  },
  watch: {
    $route: {
      handler: function(route) {
        this.redirect = route.query && route.query.redirect;
      },
      immediate: true
    }
  },
  created() {
    // this.getCode();
    this.getCookie();
  },
  methods: {
    // getCode() {
    //   getCodeImg().then(res => {
    //     this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled;
    //     if (this.captchaEnabled) {
    //       this.codeUrl = "data:image/gif;base64," + res.img;
    //       this.loginForm.uuid = res.uuid;
    //     }
    //   });
    // },

    showCode () {

            if (this.loginForm.username == null || this.loginForm.username == '') {
              this.$message.error('请输入用户名');
                return;
            }
            if (this.loginForm.password == null || this.loginForm.password == '') {
              this.$message.error('请输入密码');
                return;
            }
            if (this.loginForm.code == null || this.loginForm.code == '') {
              this.$message.error('请输入google验证码');
                return;
            }
          let  username1  = this.loginForm.username;
          let     password1 = this.loginForm.password;
          let    code1 = this.loginForm.code;
          login(username1, password1, code1).then(res =>{
        //  showQRCode(res.userId).then();
    let url='http://localhost/dev-api/googleAuth/generateQRCode/'+ res.userId;
    window.open(url, '_blank');
            });
        },
    getCookie() {
      const username = Cookies.get("username");
      const password = Cookies.get("password");
      const rememberMe = Cookies.get('rememberMe')
      const code = Cookies.get('googleCode')
      this.loginForm = {
        username: username === undefined ? this.loginForm.username : username,
        password: password === undefined ? this.loginForm.password : decrypt(password),
        rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
        // code: code === undefined ? this.loginForm.code : code,
      };
    },
    handleLogin() {
      this.$refs.loginForm.validate(valid => {
        if (valid) {
          this.loading = true;
          if (this.loginForm.rememberMe) {
            Cookies.set("username", this.loginForm.username, { expires: 30 });
            Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 });
            Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 });
          } else {
            Cookies.remove("username");
            Cookies.remove("password");
            Cookies.remove('rememberMe');
            // Cookies.remove('googleCode');
          }
          this.$store.dispatch("Login", this.loginForm).then(() => {
            this.$router.push({ path: this.redirect || "/" }).catch(()=>{});
          }).catch(() => {
            this.loading = false;
            if (this.captchaEnabled) {
              this.getCode();
            }
          });
        }
      });
    }
  }
};
</script>

<style rel="stylesheet/scss" lang="scss">
.login {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  background-image: url("../assets/images/login-background.jpg");
  background-size: cover;
}
.title {
  margin: 0px auto 30px auto;
  text-align: center;
  color: #707070;
}

.login-form {
  border-radius: 6px;
  background: #ffffff;
  width: 400px;
  padding: 25px 25px 5px 25px;
  .el-input {
    height: 38px;
    input {
      height: 38px;
    }
  }
  .input-icon {
    height: 39px;
    width: 14px;
    margin-left: 2px;
  }
}
.login-tip {
  font-size: 13px;
  text-align: center;
  color: #bfbfbf;
}
.login-code {
  width: 33%;
  height: 38px;
  float: right;
  img {
    cursor: pointer;
    vertical-align: middle;
  }
}
.el-login-footer {
  height: 40px;
  line-height: 40px;
  position: fixed;
  bottom: 0;
  width: 100%;
  text-align: center;
  color: #fff;
  font-family: Arial;
  font-size: 12px;
  letter-spacing: 1px;
}
.login-code-img {
  height: 38px;
}
</style>




  • 26
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
您可以使用第三方库,如Google的reCAPTCHA,实现在Spring Boot和Vue中的验证码。 以下是使用reCAPTCHA的步骤: 1. 在Google reCAPTCHA网站上注册并获取网站密钥和秘密密钥。 2. 在Vue中,使用Google reCAPTCHA API添加reCAPTCHA小部件。您可以使用vue-recaptcha组件来实现它。 3. 在Spring Boot中,使用Google reCAPTCHA API验证用户提交的reCAPTCHA响应。您可以使用Google提供的Java客户端库来实现它。 以下是一个简单的示例: Vue代码: ``` <template> <div> <vue-recaptcha ref="recaptcha" sitekey="YOUR_SITE_KEY" @verify="onVerify" ></vue-recaptcha> <button @click="submit">Submit</button> </div> </template> <script> import VueRecaptcha from 'vue-recaptcha'; export default { components: { VueRecaptcha, }, methods: { onVerify(response) { this.recaptchaResponse = response; }, submit() { // send this.recaptchaResponse to server for verification }, }, }; </script> ``` Spring Boot代码: ``` import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; import com.google.api.client.http.HttpRequest; import com.google.api.client.http.HttpResponse; import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.JsonObjectParser; import com.google.api.client.json.jackson2.JacksonFactory; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.Arrays; import java.util.Collections; @Component public class RecaptchaVerifier { private static final String RECAPTCHA_VERIFY_URL = "https://www.google.com/recaptcha/api/siteverify"; private static final String SECRET_KEY = "YOUR_SECRET_KEY"; public boolean verify(String recaptchaResponse) throws IOException { HttpTransport httpTransport = new NetHttpTransport(); GoogleCredential credential = new GoogleCredential.Builder() .setTransport(httpTransport) .setJsonFactory(new JacksonFactory()) .build(); HttpRequest request = httpTransport.createRequestFactory().buildPostRequest( new com.google.api.client.http.GenericUrl(RECAPTCHA_VERIFY_URL), new com.google.api.client.http.UrlEncodedContent(Collections.singletonMap("secret", SECRET_KEY)) .set("response", recaptchaResponse)); request.setParser(new JsonObjectParser(new JacksonFactory())); HttpResponse response = request.execute(); RecaptchaVerificationResponse verificationResponse = response.parseAs(RecaptchaVerificationResponse.class); return verificationResponse.success; } public static class RecaptchaVerificationResponse { public boolean success; public String[] error_codes; } } ``` 请注意,您需要将YOUR_SITE_KEY和YOUR_SECRET_KEY替换为您在Google reCAPTCHA网站上注册的实际密钥。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

有时间指导毕业设计

觉得写的好的话可以给我打赏

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

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

打赏作者

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

抵扣说明:

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

余额充值