PAMapp/pages/login/index.vue | ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程 | |
pamapi/src/doc/登入API/顧問登入驗證碼流程.txt | ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程 | |
pamapi/src/main/java/com/pollex/pam/config/SecurityConfiguration.java | ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程 | |
pamapi/src/main/java/com/pollex/pam/service/util/VerifyCodeUtil.java | ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程 | |
pamapi/src/main/java/com/pollex/pam/web/rest/ConsultantLoginValidateResource.java | ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程 |
PAMapp/pages/login/index.vue
@@ -119,8 +119,9 @@ <el-row type="flex" justify="center" class="pam-login-page__action-bar mt-30"> <el-button type="primary" v-if="connectDevice === 'MOBILE' && onPhoneVerifyStep === 'INPUT_OTP'" :disabled="!otpCode || !phoneNumber || !phoneValid" v-if="connectDevice === 'MOBILE' && onPhoneVerifyStep === 'INPUT_OTP'" :disabled="isSubmitBtnDisabled" @click="phoneLogin"> éåº </el-button> @@ -129,7 +130,7 @@ <el-dialog title="æ¡è¿æ°ä½¿ç¨è " :custom-class="'pam-register-dialog'" :visible.sync="registerDialogVisable" :visible.sync="registerDialogVisible" :fullscreen="true" :close-on-click-modal="false" :show-close="false" @@ -153,7 +154,8 @@ <el-row class="pt-10"> <div class="mdTxt pam-register-dialog__contract" @scroll="detectContructReadStatus"> ref="contract" @scroll="detectContractReadStatus"> <h3>èéåäººè³æåç¥äºé </h3> <p class="mt-10"> éµå®åäººè³æä¿è·æ³è¦å®ï¼å¨æ¨æä¾åäººè³æäºæ¬èåï¼ä¾æ³å @@ -216,15 +218,15 @@ </el-row> <el-row class="pt-30"> <div class="pam-agree-radio"> <label for="agreeControct" class="pam-radio" <label for="agreeContract" class="pam-radio" :class="{disabled: !isReadContract}"> <input type="radio" id="agreeControct" @click="agreeControct = !agreeControct" id="agreeContract" @click="agreeContract = !agreeContract" :disabled="!isReadContract" value="agreeControct"> <i :class="agreeControct ?'icon-checkbox-1': 'icon-checkbox'"></i>æåæä¸¦ç¹¼çº value="agreeContract"> <i :class="agreeContract ?'icon-checkbox-1': 'icon-checkbox'"></i>æåæä¸¦ç¹¼çº </label> </div> </el-row> @@ -232,7 +234,7 @@ <span slot="footer" class="dialog-footer"> <el-button type="primary" :disabled="!name || !agreeControct || !isReadContract" :disabled="!name || !agreeContract || !isReadContract" @click="applyAccount" >å»ºç«æ°å¸³è </el-button> @@ -288,7 +290,7 @@ <script lang="ts"> import { namespace } from 'nuxt-property-decorator'; import { Vue, Component } from 'vue-property-decorator'; import { Vue, Component, Ref } from 'vue-property-decorator'; import { LoginRequest, loginVerify, OtpInfo, register, RegisterInfo, sendOtp } from '~/assets/ts/api/consultant'; import { Role } from '~/assets/ts/models/enum/Role'; @@ -298,6 +300,7 @@ export default class Login extends Vue { @roleStorage.Mutation storageIdToken!: (token:string) => void; @roleStorage.Mutation storageRole!: (role:string) => void; @Ref('contract') readonly contract!: any; connectDevice: 'MOBILE' | 'EMAIL' = 'MOBILE'; @@ -309,6 +312,10 @@ otpInterval: any; phoneOtpInfo!: OtpInfo; get isSubmitBtnDisabled(): boolean { return !this.otpCode || !this.phoneNumber || !this.phoneValid; } email = ''; onEmailVerifyResendStatus: 'APPLY_OTP' | 'CAN_RESEND' = 'APPLY_OTP'; emailResendCounter = 30; @@ -316,18 +323,18 @@ emailOtpInfo!: OtpInfo; name = ''; agreeControct = false; agreeContract = false; isReadContract = false; phoneSuccessConfirmVisable = false; emailOtpConfirmVisable = false; registerDialogVisable = false; registerDialogVisible = false; registerSuccessConfirmVisable = false; applyAccount_onAction = false; detectContructReadStatus(event: any): void { detectContractReadStatus(event?: any): void { const scrollTop = Math.round(event.target.scrollTop); const height = event.target.scrollHeight - event.target.clientHeight; if (Math.floor(scrollTop/10) === (Math.floor(height/10))) { @@ -472,15 +479,22 @@ indexKey: this.phoneOtpInfo.indexKey, otpCode: this.otpCode } loginVerify(login).then(res => { this.storageIdToken(res.data.id_token); this.storageRole(Role.USER); this.phoneSuccessConfirmVisable = true; }).catch(error => { if (error.response.status === 401) { this.registerDialogVisable = true; this.registerDialogVisible = true; setTimeout(() => { const isScrollBarNeedless = this.contract.scrollHeight <= this.contract.clientHeight; if (isScrollBarNeedless) { this.isReadContract = true; } }, 1000); } }) }); } destroyed() { @@ -530,6 +544,7 @@ border-radius: 6px; border: 1px solid #707070; padding: 20px; max-height: 335px; } .pam-radio { pamapi/src/doc/µn¤JAPI/ÅU°Ýµn¤JÅçÃÒ½X¬yµ{.txt
¤ñ¹ï·sÀÉ®× @@ -0,0 +1,29 @@ * å ç¼éåå¾é©è碼åç http get : http://localhost:8080/api/login/validate/get_img_code response content type: image/jpeg * å°é©è碼éè³å¾ç«¯é²è¡é©è http get : http://localhost:8080/api/login/validate/verify_img_code/{imgCode} ex: http://localhost:8080/api/login/validate/verify_img_code/4Rrcp response : true (é©èæå), false(é©è失æ) pamapi/src/main/java/com/pollex/pam/config/SecurityConfiguration.java
@@ -83,6 +83,7 @@ .antMatchers("/api/activate").permitAll() .antMatchers("/api/testLogin/**").permitAll() .antMatchers("/api/otp/**").permitAll() .antMatchers("/api/login/validate/**").permitAll() .antMatchers("/api/eService/authenticate").permitAll() .antMatchers("/api/account/reset-password/init").permitAll() .antMatchers("/api/account/reset-password/finish").permitAll() pamapi/src/main/java/com/pollex/pam/service/util/VerifyCodeUtil.java
¤ñ¹ï·sÀÉ®× @@ -0,0 +1,68 @@ package com.pollex.pam.service.util; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.util.Random; public final class VerifyCodeUtil { public static String createcode() { String code = ""; code = ""; String randomRange = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";// Randomly generated character // range (0-9, a-z, A-Z) // The number of digits for generating the verification code (here 4 digits) for (int i = 0; i < 4; i++) { int index = (int) (Math.random() * 62);// Will produce a [0,62) number, excluding decimals char randomCode = randomRange.charAt(index); code = code + randomCode; } return code; } // 3. Generate pictures public static BufferedImage createimage(String code) { // The first 2 parameters are: width, height. The back is the image type // Create a BufferedImage object without transparent color, TYPE_INT_ARGB is // with transparent color BufferedImage bi = new BufferedImage(130, 50, BufferedImage.TYPE_INT_RGB); // 1. Get a canvas Graphics g = bi.getGraphics(); // 2. Add background color g.setColor(Color.WHITE); g.fillRect(0, 0, 130, 50); // 3. Add interference lines for (int i = 0; i < 10; i++) { Random r = new Random(); int red = r.nextInt(256); int green = r.nextInt(256); int blue = r.nextInt(256); Color c = new Color(red, green, blue); g.setColor(c); int x1 = r.nextInt(131); int y1 = r.nextInt(51); int x2 = r.nextInt(131); int y2 = r.nextInt(51); g.drawLine(x1, y1, x2, y2);// Draw a line // g.drawOval(x1, y1, x2, y2);//Draw a curve } // 3. Add text g.setColor(Color.BLACK); g.setFont(new Font(" ", Font.BOLD, 40)); // 4. Fill the text into the artboard g.drawString(code, 15, 40); // 5. Close the canvas g.dispose(); return bi; } } pamapi/src/main/java/com/pollex/pam/web/rest/ConsultantLoginValidateResource.java
¤ñ¹ï·sÀÉ®× @@ -0,0 +1,62 @@ package com.pollex.pam.web.rest; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import javax.imageio.ImageIO; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.pollex.pam.service.util.VerifyCodeUtil; @RestController @RequestMapping("/api/login/validate") public class ConsultantLoginValidateResource { @GetMapping("/get_img_code") public void getVerifyCodeImg(HttpServletResponse response, HttpServletRequest request) { try { String code = VerifyCodeUtil.createcode(); BufferedImage image = VerifyCodeUtil.createimage(code); // Return to the client in a stream response.setContentType("image/jpeg"); // response.setContentType("image/bmp"); ByteArrayOutputStream bt = new ByteArrayOutputStream(); // Convert pictures to byte stream ImageIO.write(image, "jpeg", bt); ServletOutputStream outputStream = response.getOutputStream(); outputStream.write(bt.toByteArray()); HttpSession session = request.getSession(); session.setAttribute("img_code", code); } catch (IOException e) { e.printStackTrace(); } } @GetMapping("/verify_img_code/{imgCode}") public boolean verifyImgCode(HttpServletResponse response, HttpServletRequest request, @PathVariable String imgCode) { HttpSession session = request.getSession(); String sessionImpCode = (String) session.getAttribute("img_code"); if (!StringUtils.hasText(sessionImpCode) || !StringUtils.hasText(imgCode)) { return false; } if (imgCode.equals(sessionImpCode)) { return true; } return false; } }