xiongzhu 4 年之前
父节点
当前提交
f585e1c69a

+ 6 - 0
pom.xml

@@ -268,6 +268,12 @@
             <artifactId>easy-captcha</artifactId>
             <version>1.6.2</version>
         </dependency>
+
+        <dependency>
+            <groupId>com.warrenstrange</groupId>
+            <artifactId>googleauth</artifactId>
+            <version>1.5.0</version>
+        </dependency>
     </dependencies>
 
 </project>

+ 1 - 0
src/main/data-center-admin/package.json

@@ -9,6 +9,7 @@
     "build-theme": "npx et -o src/styles/element_theme"
   },
   "dependencies": {
+    "@chenfengyuan/vue-qrcode": "^1.0.2",
     "@download/blockies": "https://github.com/download13/blockies.git",
     "@fortawesome/fontawesome": "^1.1.8",
     "@fortawesome/fontawesome-free-solid": "^5.0.13",

+ 115 - 0
src/main/data-center-admin/src/components/TFA.vue

@@ -0,0 +1,115 @@
+<template>
+    <div>
+        <el-dialog :visible.sync="showDialog" width="600px" title="绑定两步验证">
+            <el-steps :space="300" :active="step" align-center style="margin-bottom: 60px">
+                <el-step></el-step>
+                <el-step></el-step>
+                <el-step></el-step>
+            </el-steps>
+            <div v-if="step === 1" class="step1">
+                <qrcode :value="qrcodeContent" :options="{ width: 250 }"></qrcode>
+                <el-alert type="success" :closable="false" center>
+                    <span slot="title">请用手机下载安装Google Authenticator,然后扫描上方二维码</span>
+                </el-alert>
+            </div>
+            <div v-if="step === 2" class="step2">
+                <el-form :model="tfaForm" ref="tfaForm" :rules="tfaRules" label-position="right" label-width="100px">
+                    <el-form-item prop="password" label="密码">
+                        <el-input type="password" v-model="tfaForm.password"></el-input>
+                    </el-form-item>
+                    <el-form-item prop="code" label="谷歌验证码">
+                        <el-input type="number" v-model="tfaForm.code"></el-input>
+                    </el-form-item>
+                </el-form>
+            </div>
+            <div v-if="step === 3" class="step3" v-loading="loading" element-loading-text="绑定中">
+                <i class="el-icon el-icon-success" v-if="!loading"></i>
+            </div>
+            <div slot="footer">
+                <el-button type="primary" size="mini" v-if="step === 2" @click="step = 1">上一步</el-button>
+                <el-button type="primary" size="mini" v-if="step === 1" @click="step = 2">下一步</el-button>
+                <el-button type="primary" size="mini" v-if="step === 2" @click="bind">下一步</el-button>
+                <el-button type="primary" size="mini" v-if="step === 3 && !loading" @click="showDialog = false">
+                    完成
+                </el-button>
+            </div>
+        </el-dialog>
+    </div>
+</template>
+<script>
+import qrcode from '@chenfengyuan/vue-qrcode';
+export default {
+    components: { qrcode },
+    data() {
+        return {
+            showDialog: false,
+            step: 1,
+            tfaForm: {
+                key: '',
+                password: '',
+                code: ''
+            },
+            tfaRules: {
+                password: [{ required: true, message: '请输入密码' }],
+                code: [{ required: true, message: '请输入谷歌验证码' }]
+            },
+            loading: false
+        };
+    },
+    created() {
+        this.getKey();
+        this.$http.get('/user/tfaStatus').then(res => {
+            if (res.status === false) {
+                this.showDialog = true;
+            }
+        });
+    },
+    computed: {
+        qrcodeContent() {
+            return `otpauth://totp/${this.$store.state.userInfo.id}?secret=${this.tfaForm.key}&issuer=JMRH`;
+        }
+    },
+    methods: {
+        getKey() {
+            this.$http.post('/user/getTFAKey').then(res => {
+                this.$set(this.tfaForm, 'key', res);
+            });
+        },
+        bind() {
+            this.$refs.tfaForm
+                .validate()
+                .then(res => {
+                    this.step = 3;
+                    this.loading = true;
+                    this.$http
+                        .post('/user/bindTFA', { ...this.tfaForm })
+                        .then(res => {
+                            this.loading = false;
+                        })
+                        .catch(e => {
+                            this.step = 2;
+                            this.loading = false;
+                            this.$message.error(e.error);
+                        });
+                })
+                .catch(() => {});
+        }
+    }
+};
+</script>
+<style lang="less" scoped>
+.step1 {
+    text-align: center;
+}
+.step2 {
+    width: 400px;
+    margin: auto;
+}
+.step3 {
+    text-align: center;
+    .el-icon {
+        font-size: 100px;
+        color: #67c23a;
+    }
+}
+</style>

+ 5 - 2
src/main/data-center-admin/src/views/Admin.vue

@@ -74,6 +74,7 @@
                 <el-button type="primary" @click="savePwd" :loading="pwdLoading">确认修改</el-button>
             </span>
         </el-dialog>
+        <TFA></TFA>
     </el-container>
 </template>
 
@@ -81,6 +82,7 @@
 import SysMenu from '../components/SysMenu';
 import { mapState } from 'vuex';
 import { isBefore, parse, format, addDays } from 'date-fns';
+import TFA from '../components/TFA';
 export default {
     name: 'admin',
     created() {
@@ -123,7 +125,7 @@ export default {
             .catch(e => {
                 console.log(e);
             });
-
+        console.log(this.userInfo.pwdUpdateAt);
         let pwdUpdateAt = this.userInfo.pwdUpdateAt;
         if (!pwdUpdateAt || isBefore(parse(pwdUpdateAt, 'yyyy-MM-dd HH:mm:ss', new Date()), addDays(new Date(), -30))) {
             this.$alert('您已30天未修改密码,请尽快修改密码', '提示').then(() => {
@@ -402,7 +404,8 @@ export default {
         }
     },
     components: {
-        SysMenu
+        SysMenu,
+        TFA
     }
 };
 </script>

+ 43 - 4
src/main/data-center-admin/src/views/Login.vue

@@ -190,12 +190,19 @@ export default {
                             return this.$http.get('/user/my');
                         })
                         .then(res => {
-                            this.loading = false;
+                            localStorage.removeItem('loginTry::' + this.userInfo.username);
                             this.$store.commit('updateUserInfo', res);
-                            this.$router.replace({
-                                name: this.$route.params.name || 'chart1'
+                            this.$http.get('/user/tfaStatus').then(res => {
+                                console.log(res);
+                                if (res.status === true) {
+                                    this.tfa();
+                                } else {
+                                    this.loading = false;
+                                    this.$router.replace({
+                                        name: this.$route.params.name || 'chart1'
+                                    });
+                                }
                             });
-                            localStorage.removeItem('loginTry::' + this.userInfo.username);
                         })
                         .catch(e => {
                             console.log(e);
@@ -209,6 +216,38 @@ export default {
                 }
             });
         },
+        tfa() {
+            this.$prompt('请输入谷歌验证码', '两步验证', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                inputPattern: /^\d{6}$/,
+                inputErrorMessage: '验证码格式不正确',
+                beforeClose: (action, instance, done) => {
+                    console.log(action, instance, done);
+                    if (action === 'confirm') {
+                        instance.confirmButtonLoading = true;
+                        this.$http
+                            .post('/user/verifyTFA', { code: instance.inputValue })
+                            .then(res => {
+                                instance.confirmButtonLoading = false;
+                                done();
+                                this.loading = false;
+                                this.$router.replace({
+                                    name: this.$route.params.name || 'chart1'
+                                });
+                            })
+                            .catch(() => {
+                                instance.confirmButtonLoading = false;
+                                this.$message.error('验证码错误');
+                            });
+                    } else {
+                        done();
+                    }
+                }
+            })
+                .then(() => {})
+                .catch(() => {});
+        },
         phonelogin() {
             this.$refs.form2.validate(valid => {
                 if (valid) {

+ 77 - 1
src/main/data-center-admin/yarn.lock

@@ -822,6 +822,13 @@
     lodash "^4.17.13"
     to-fast-properties "^2.0.0"
 
+"@chenfengyuan/vue-qrcode@^1.0.2":
+  version "1.0.2"
+  resolved "https://registry.nlark.com/@chenfengyuan/vue-qrcode/download/@chenfengyuan/vue-qrcode-1.0.2.tgz#37d71902e166e1ae58176bd6cb9c40905c1b0949"
+  integrity sha1-N9cZAuFm4a5YF2vWy5xAkFwbCUk=
+  dependencies:
+    qrcode "^1.4.4"
+
 "@download/blockies@https://github.com/download13/blockies.git":
   version "1.0.3"
   resolved "https://github.com/download13/blockies.git#55518d38203582cd4a7b55204b7a26f8032ea07e"
@@ -1853,6 +1860,11 @@ base64-js@^1.0.2:
   resolved "https://registry.npm.taobao.org/base64-js/download/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
   integrity sha1-WOzoy3XdB+ce0IxzarxfrE2/jfE=
 
+base64-js@^1.3.1:
+  version "1.5.1"
+  resolved "https://registry.nlark.com/base64-js/download/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+  integrity sha1-GxtEAWClv3rUC2UPCVljSBkDkwo=
+
 base@^0.11.1:
   version "0.11.2"
   resolved "https://registry.npm.taobao.org/base/download/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
@@ -2095,11 +2107,34 @@ browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.8.5:
     node-releases "^1.1.53"
     pkg-up "^2.0.0"
 
+buffer-alloc-unsafe@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.nlark.com/buffer-alloc-unsafe/download/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
+  integrity sha1-vX3CauKXLQ7aJTvgYdupkjScGfA=
+
+buffer-alloc@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.nlark.com/buffer-alloc/download/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
+  integrity sha1-iQ3ZDZI6hz4I4Q5f1RpX5bfM4Ow=
+  dependencies:
+    buffer-alloc-unsafe "^1.1.0"
+    buffer-fill "^1.0.0"
+
+buffer-fill@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.nlark.com/buffer-fill/download/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
+  integrity sha1-+PeLdniYiO858gXNY39o5wISKyw=
+
 buffer-from@^1.0.0:
   version "1.1.1"
   resolved "https://registry.npm.taobao.org/buffer-from/download/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
   integrity sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=
 
+buffer-from@^1.1.1:
+  version "1.1.2"
+  resolved "https://registry.nlark.com/buffer-from/download/buffer-from-1.1.2.tgz?cache=0&sync_timestamp=1627578710888&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fbuffer-from%2Fdownload%2Fbuffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
+  integrity sha1-KxRqb9cugLT1XSVfNe1Zo6mkG9U=
+
 buffer-indexof@^1.0.0:
   version "1.1.1"
   resolved "https://registry.npm.taobao.org/buffer-indexof/download/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c"
@@ -2124,6 +2159,14 @@ buffer@^4.3.0:
     ieee754 "^1.1.4"
     isarray "^1.0.0"
 
+buffer@^5.4.3:
+  version "5.7.1"
+  resolved "https://registry.nlark.com/buffer/download/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
+  integrity sha1-umLnwTEzBTWCGXFghRqPZI6Z7tA=
+  dependencies:
+    base64-js "^1.3.1"
+    ieee754 "^1.1.13"
+
 builtin-status-codes@^3.0.0:
   version "3.0.0"
   resolved "https://registry.npm.taobao.org/builtin-status-codes/download/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
@@ -3361,6 +3404,11 @@ diffie-hellman@^5.0.0:
     miller-rabin "^4.0.0"
     randombytes "^2.0.0"
 
+dijkstrajs@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.nlark.com/dijkstrajs/download/dijkstrajs-1.0.2.tgz#2e48c0d3b825462afe75ab4ad5e829c8ece36257"
+  integrity sha1-LkjA07glRir+datK1egpyOzjYlc=
+
 dir-glob@^2.0.0, dir-glob@^2.2.2:
   version "2.2.2"
   resolved "https://registry.npm.taobao.org/dir-glob/download/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4"
@@ -5207,6 +5255,11 @@ icss-utils@^4.0.0, icss-utils@^4.1.1:
   dependencies:
     postcss "^7.0.14"
 
+ieee754@^1.1.13:
+  version "1.2.1"
+  resolved "https://registry.nlark.com/ieee754/download/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+  integrity sha1-jrehCmP/8l0VpXsAFYbRd9Gw01I=
+
 ieee754@^1.1.4:
   version "1.1.13"
   resolved "https://registry.npm.taobao.org/ieee754/download/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
@@ -5773,6 +5826,11 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
   resolved "https://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
   integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
 
+isarray@^2.0.1:
+  version "2.0.5"
+  resolved "https://registry.nlark.com/isarray/download/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
+  integrity sha1-ivHkwSISRMxiRZ+vOJQNTmRKVyM=
+
 isexe@^2.0.0:
   version "2.0.0"
   resolved "https://registry.npm.taobao.org/isexe/download/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@@ -7691,6 +7749,11 @@ plugin-error@^0.1.2:
     arr-union "^2.0.1"
     extend-shallow "^1.1.2"
 
+pngjs@^3.3.0:
+  version "3.4.0"
+  resolved "https://registry.nlark.com/pngjs/download/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f"
+  integrity sha1-mcp9clll+2VYFOr2XzjxK72/VV8=
+
 pnp-webpack-plugin@^1.6.4:
   version "1.6.4"
   resolved "https://registry.npm.taobao.org/pnp-webpack-plugin/download/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149"
@@ -8208,6 +8271,19 @@ q@^1.1.2:
   resolved "https://registry.npm.taobao.org/q/download/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
   integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
 
+qrcode@^1.4.4:
+  version "1.4.4"
+  resolved "https://registry.nlark.com/qrcode/download/qrcode-1.4.4.tgz#f0c43568a7e7510a55efc3b88d9602f71963ea83"
+  integrity sha1-8MQ1aKfnUQpV78O4jZYC9xlj6oM=
+  dependencies:
+    buffer "^5.4.3"
+    buffer-alloc "^1.2.0"
+    buffer-from "^1.1.1"
+    dijkstrajs "^1.0.1"
+    isarray "^2.0.1"
+    pngjs "^3.3.0"
+    yargs "^13.2.4"
+
 qs@6.7.0:
   version "6.7.0"
   resolved "https://registry.npm.taobao.org/qs/download/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
@@ -10563,7 +10639,7 @@ yargs-parser@^18.1.1:
     camelcase "^5.0.0"
     decamelize "^1.2.0"
 
-yargs@^13.3.2:
+yargs@^13.2.4, yargs@^13.3.2:
   version "13.3.2"
   resolved "https://registry.npm.taobao.org/yargs/download/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
   integrity sha1-rX/+/sGqWVZayRX4Lcyzipwxot0=

+ 5 - 2
src/main/java/com/izouma/jmrh/converter/EncryptConverter.java

@@ -17,9 +17,12 @@ public class EncryptConverter implements AttributeConverter<String, String> {
     @Value("${encrypt.body.aes-key}")
     private String key;
 
+    @Value("${encrypt.enable}")
+    private boolean enableEncrypt;
+
     @Override
     public String convertToDatabaseColumn(String s) {
-        if (StringUtils.isNotBlank(s)) {
+        if (StringUtils.isNotBlank(s) && enableEncrypt) {
             try {
                 return AESEncryptUtil.encrypt(s, key);
             } catch (Exception e) {
@@ -31,7 +34,7 @@ public class EncryptConverter implements AttributeConverter<String, String> {
 
     @Override
     public String convertToEntityAttribute(String s) {
-        if (StringUtils.isNotBlank(s)) {
+        if (StringUtils.isNotBlank(s) && enableEncrypt) {
             try {
                 return AESEncryptUtil.decrypt(s, key);
             } catch (Exception e) {

+ 8 - 4
src/main/java/com/izouma/jmrh/domain/OrgInfo.java

@@ -1,5 +1,6 @@
 package com.izouma.jmrh.domain;
 
+import com.izouma.jmrh.converter.EncryptConverter;
 import com.izouma.jmrh.enums.AuditStatus;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
@@ -8,10 +9,7 @@ import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
-import javax.persistence.Entity;
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
-import javax.persistence.Transient;
+import javax.persistence.*;
 import java.time.LocalDateTime;
 
 @Data
@@ -22,6 +20,7 @@ import java.time.LocalDateTime;
 @ApiModel("企业认证")
 public class OrgInfo extends BaseEntity {
     @ApiModelProperty("单位名称")
+    @Convert(converter = EncryptConverter.class)
     private String orgName;
 
     @ApiModelProperty("单位性质")
@@ -40,6 +39,7 @@ public class OrgInfo extends BaseEntity {
     private String address;
 
     @ApiModelProperty("详细地址")
+    @Convert(converter = EncryptConverter.class)
     private String addressDetail;
 
     @ApiModelProperty("企业介绍")
@@ -55,18 +55,22 @@ public class OrgInfo extends BaseEntity {
     private String uscc;
 
     @ApiModelProperty("联系人")
+    @Convert(converter = EncryptConverter.class)
     private String contactName;
 
     @ApiModelProperty("身份证号")
+    @Convert(converter = EncryptConverter.class)
     private String contactIdNo;
 
     @ApiModelProperty("手机号")
+    @Convert(converter = EncryptConverter.class)
     private String contactPhone;
 
     @ApiModelProperty("电子邮箱")
     private String contactEmail;
 
     @ApiModelProperty("信息报送承诺书扫描件")
+    @Convert(converter = EncryptConverter.class)
     private String attach;
 
     @ApiModelProperty("用户ID")

+ 3 - 0
src/main/java/com/izouma/jmrh/domain/User.java

@@ -96,4 +96,7 @@ public class User extends BaseEntity implements Serializable {
     /*实名认证信息*/
 
     private LocalDateTime pwdUpdateAt;
+
+    @JsonIgnore
+    private String gAuthKey;
 }

+ 23 - 0
src/main/java/com/izouma/jmrh/service/UserService.java

@@ -12,6 +12,8 @@ import com.izouma.jmrh.security.JwtTokenUtil;
 import com.izouma.jmrh.security.JwtUserFactory;
 import com.izouma.jmrh.service.sms.SmsService;
 import com.izouma.jmrh.service.storage.StorageService;
+import com.izouma.jmrh.utils.SecurityUtils;
+import com.warrenstrange.googleauth.GoogleAuthenticator;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.error.WxErrorException;
@@ -218,4 +220,25 @@ public class UserService {
         }
         return setPassword(userId, password);
     }
+
+    public void bindTFA(String password, String key, String code) {
+        if (!new BCryptPasswordEncoder().matches(password, SecurityUtils.getAuthenticatedUser().getPassword())) {
+            throw new BusinessException("密码错误");
+        }
+        GoogleAuthenticator gAuth = new GoogleAuthenticator();
+        if (!gAuth.authorize(key, Integer.parseInt(code))) {
+            throw new BusinessException("谷歌验证码错误");
+        }
+        User user = SecurityUtils.getAuthenticatedUser();
+        user.setGAuthKey(key);
+        userRepo.save(user);
+    }
+
+    public void verifyTAF(String code) {
+        GoogleAuthenticator gAuth = new GoogleAuthenticator();
+        String key = SecurityUtils.getAuthenticatedUser().getGAuthKey();
+        if (!gAuth.authorize(key, Integer.parseInt(code))) {
+            throw new BusinessException("谷歌验证码错误");
+        }
+    }
 }

+ 5 - 0
src/main/java/com/izouma/jmrh/web/OrgInfoController.java

@@ -116,5 +116,10 @@ public class OrgInfoController extends BaseController {
         orgInfo.setAuditTime(LocalDateTime.now());
         orgInfoRepo.save(orgInfo);
     }
+
+    @PostMapping("/saveAll")
+    public void saveAll() {
+        userRepo.saveAll(userRepo.findAll());
+    }
 }
 

+ 33 - 3
src/main/java/com/izouma/jmrh/web/UserController.java

@@ -14,9 +14,12 @@ import com.izouma.jmrh.service.sms.SmsService;
 import com.izouma.jmrh.utils.ObjUtils;
 import com.izouma.jmrh.utils.SecurityUtils;
 import com.izouma.jmrh.utils.excel.ExcelUtils;
+import com.warrenstrange.googleauth.GoogleAuthenticator;
+import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
 import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.data.domain.Page;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.authentication.AuthenticationManager;
@@ -28,9 +31,7 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
 
 @AllArgsConstructor
 @RestController
@@ -197,4 +198,33 @@ public class UserController extends BaseController {
     public String changePassword(@RequestParam String password, @RequestParam String key, @RequestParam String code) {
         return userService.setPasswordCaptcha(SecurityUtils.getAuthenticatedUser().getId(), key, code, password);
     }
+
+    @GetMapping("/tfaStatus")
+    private Map<String, Object> tfaStatus() {
+        Map<String, Object> map = new HashMap<>();
+        map.put("status", !StringUtils.isBlank(SecurityUtils.getAuthenticatedUser().getGAuthKey()));
+        return map;
+    }
+
+    @PostMapping("/getTFAKey")
+    public String getTFAKey() {
+        GoogleAuthenticator gAuth = new GoogleAuthenticator();
+        final GoogleAuthenticatorKey key = gAuth.createCredentials();
+        return key.getKey();
+    }
+
+    @PostMapping("/bindTFA")
+    public void bindTFA(@RequestParam String password, @RequestParam String key, @RequestParam String code) {
+        userService.bindTFA(password, key, code);
+    }
+
+    @PostMapping("/verifyTFA")
+    public void verifyTFA(@RequestParam String code) {
+        userService.verifyTAF(code);
+    }
+
+    @PostMapping("/saveAll")
+    public void saveAll() {
+        userRepo.saveAll(userRepo.findAll());
+    }
 }

+ 5 - 0
src/main/resources/application.yaml

@@ -79,6 +79,7 @@ general:
 encrypt:
   body:
     aes-key: gmMRQzojuaSVGg3weNe6J5BCjDyH5BML
+  enable: false
 ---
 
 spring:
@@ -134,3 +135,7 @@ spring:
 storage:
   provider: local
   local_path: /var/www/upload/
+encrypt:
+  body:
+    aes-key: gmMRQzojuaSVGg3weNe6J5BCjDyH5BML
+  enable: true

+ 2 - 2
src/main/vue/src/views/Login.vue

@@ -46,8 +46,8 @@
                             </el-form-item> -->
                             <el-form-item label="">
                                 <!-- <el-checkbox v-model="rememberMe">7天内免登录 </el-checkbox> -->
-                                <el-button type="text" style="float: right" @click="register = true"
-                                    >注册账号
+                                <el-button type="text" style="float: right" @click="register = true">
+                                    注册账号
                                 </el-button>
                             </el-form-item>
                         </el-form>

+ 11 - 0
src/test/java/com/izouma/jmrh/CommonTest.java

@@ -4,6 +4,8 @@ import cn.licoy.encryptbody.util.Hex2Util;
 import com.izouma.jmrh.domain.BaseEntity;
 import com.izouma.jmrh.domain.User;
 import com.izouma.jmrh.web.BaseController;
+import com.warrenstrange.googleauth.GoogleAuthenticator;
+import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
 import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.commons.text.CaseUtils;
 import org.junit.Test;
@@ -170,4 +172,13 @@ public class CommonTest {
         byte[] byteContent = "hello".getBytes(StandardCharsets.UTF_8);
         System.out.println("encrypted: " + Hex2Util.parseByte2HexStr(cipher.doFinal(byteContent)));
     }
+
+    @Test
+    public void testGoogleAuth() {
+        GoogleAuthenticator gAuth = new GoogleAuthenticator();
+        final GoogleAuthenticatorKey key = gAuth.createCredentials();
+        System.out.println(key.getKey());
+
+        System.out.println(gAuth.authorize("QBE5DWZ43TXFBKF3UVP5ON2LE4F4IVAH", 1906041));
+    }
 }