亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java實(shí)現(xiàn)用戶短信驗(yàn)證碼登錄功能實(shí)例代碼

 更新時(shí)間:2024年10月11日 11:16:08   作者:Light._.House  
現(xiàn)在不管是各類(lèi)的網(wǎng)站,還是大小社交app,登錄方式是越來(lái)越多了,但是大部分還是以短信登錄為主,本文主要介紹了java短信驗(yàn)證碼登錄功能設(shè)計(jì)與實(shí)現(xiàn),感興趣的可以了解一下

此處使用阿里提供的API解決方案

同時(shí)需要注意的是,此文章在Java項(xiàng)目操作上需要有一定的編程基礎(chǔ),因?yàn)椴幌肓_里吧嗦的一大堆,對(duì)于分層理解較差和基礎(chǔ)編程能力較低的小白不建議

1.前置阿里云操作

1.登錄阿里云后,搜索“短信服務(wù)”

2.點(diǎn)擊后進(jìn)入如下界面此處點(diǎn)擊“免費(fèi)開(kāi)通”,

此處若項(xiàng)目必須要求有真實(shí)短信發(fā)送,則建議購(gòu)買(mǎi)最便宜的先進(jìn)行測(cè)試即可

3.點(diǎn)擊“快速學(xué)習(xí)和測(cè)試”,依次根據(jù)提示,申請(qǐng)“資質(zhì)”,“簽名”,“模板”

此處三個(gè)都對(duì)應(yīng)個(gè)人和企業(yè),申請(qǐng)需要時(shí)間

4.模擬測(cè)試,在“快速學(xué)習(xí)和測(cè)試”界面的下方,有測(cè)試的模板可以使用,測(cè)試需要綁定測(cè)試手機(jī)號(hào)、申請(qǐng)自定義測(cè)試模板和自定義測(cè)試簽名

5.調(diào)用API發(fā)送短信

在此需要注意的是,VS Code和IEDA需要下載對(duì)應(yīng)的插件才能保證在后續(xù)自己的項(xiàng)目中能正常調(diào)用到短信發(fā)送的API接口

  點(diǎn)擊SDK實(shí)例后能看到完整的調(diào)用代碼,此時(shí)建議使用V2.0,代碼包含java(異步)和java

,采用哪種方式都無(wú)所謂,只需將代碼全部復(fù)制即可

2.java項(xiàng)目操作

0.注意事項(xiàng)

在此之前,你需要準(zhǔn)備的東西如下

1.短信簽名名稱  SignName

2.短信模板Code  TemplateCode

3.SDK實(shí)例代碼

4.對(duì)應(yīng)編譯器的插件必須安裝完畢

5.對(duì)應(yīng)的ALIBABA_CLOUD_ACCESS_KEY_ID和ALIBABA_CLOUD_ACCESS_KEY_SECRET

ALIBABA_CLOUD_ACCESS_KEY_ID和ALIBABA_CLOUD_ACCESS_KEY_SECRET可以在個(gè)人中心看到

 創(chuàng)建對(duì)應(yīng)的AccessKey時(shí),需要保存好對(duì)應(yīng)的ALIBABA_CLOUD_ACCESS_KEY_ID和ALIBABA_CLOUD_ACCESS_KEY_SECRET,這是調(diào)用API時(shí)傳遞到阿里的憑證,十分重要!

以上準(zhǔn)備完畢后,就可以在java項(xiàng)目中嵌入對(duì)應(yīng)的API實(shí)現(xiàn)驗(yàn)證碼的發(fā)送

1.創(chuàng)建兩個(gè)接口,一個(gè)是獲取驗(yàn)證碼,一個(gè)是攜帶驗(yàn)證碼登錄

在此方案下,采用了將驗(yàn)證碼存儲(chǔ)到Redis中,此處存入Redis后,可設(shè)置驗(yàn)證碼的過(guò)期時(shí)間,減少對(duì)底層的訪問(wèn)壓力,也能實(shí)現(xiàn)驗(yàn)證碼限時(shí)的操作,另外此出也可加入對(duì)應(yīng)的手機(jī)號(hào)在固定時(shí)間內(nèi)對(duì)于獲取驗(yàn)證碼接口的訪問(wèn)次數(shù)限制,避免惡意訪問(wèn)造成服務(wù)器壓力過(guò)大。

Controller

package com.ruoyi.controller;

import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ruoyi.common.constant.ReturnConstants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.domain.dto.*;
import com.ruoyi.domain.entity.User;
import com.ruoyi.pojo.vo.CurrentPrincipal;
import com.ruoyi.security.center.RequestLimit;
import com.ruoyi.service.WeChatLoginService;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.service.IUserService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;


@Slf4j
@RestController
@RequestMapping("/v1/user")
public class UserController extends BaseController
{
    @Autowired
    private IUserService userService;

    @Autowired
    private WxMpService wxService;

    @Autowired
    private WeChatLoginService weChatLoginService;

    /**
     * - 手機(jī)號(hào)格式錯(cuò)誤
     *   - 為空
     *   - 不符合手機(jī)號(hào)
     * - 手機(jī)號(hào)未注冊(cè)
     * - 用戶被禁用
     *   - 在此處直接驗(yàn)證,以減少發(fā)送短信的成本
     *   驗(yàn)證碼存儲(chǔ)到redis中,在五分鐘內(nèi)可以通過(guò)驗(yàn)證碼登錄
     * @param userVerifyDTO
     * @return
     * @throws Exception
     */
    @RequestLimit
    @PostMapping("verify")
    public AjaxResult verify(@RequestBody UserVerifyDTO userVerifyDTO) throws Exception {
        log.debug("處理驗(yàn)證碼獲取");
        log.debug("驗(yàn)證信息:{}", userVerifyDTO);
        return AjaxResult.success(userService.verify(userVerifyDTO));
    }

    /**
     * 登錄請(qǐng)求,匹配redis中的驗(yàn)證碼和數(shù)據(jù)庫(kù)中的信息
     * @param userLoginDTO
     * @param request
     * @return
     * @throws SocketException
     * @throws UnknownHostException
     */
    @RequestLimit
    @PostMapping("login")
    public Object login(@RequestBody UserLoginDTO userLoginDTO, HttpServletRequest request) throws SocketException, UnknownHostException {
        log.debug("處理登錄請(qǐng)求-攜帶驗(yàn)證碼");
        log.debug("登錄信息:{}", userLoginDTO);
        return userService.login(userLoginDTO, request);
    }
}

2.編寫(xiě)service的實(shí)現(xiàn),

ServiceImpl

package com.ruoyi.service.impl;

import java.net.SocketException;
import java.net.UnknownHostException;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ruoyi.common.constant.ReturnConstants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.AddressUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.uuid.UUID;
import com.ruoyi.constans.JwtConstans;
import com.ruoyi.domain.bo.UserInsertBo;
import com.ruoyi.domain.dto.*;
import com.ruoyi.domain.entity.Addr;
import com.ruoyi.domain.entity.Logininfor;
import com.ruoyi.domain.entity.Loginlogs;
import com.ruoyi.domain.entity.User;
import com.ruoyi.domain.param.UserLoginInfoVO;
import com.ruoyi.domain.vo.UserLoginResultVO;
import com.ruoyi.mapper.LoginlogsMapper;
import com.ruoyi.pojo.vo.CurrentPrincipal;
import com.ruoyi.pojo.vo.PageData;
import com.ruoyi.pojo.vo.UserCachePO;
import com.ruoyi.service.IUserService;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.utils.*;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import com.ruoyi.common.core.redis.RedisCache;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;
import com.ruoyi.mapper.UserMapper;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import static com.ruoyi.common.utils.PageUtils.startPage;


@Slf4j
@Service
public class UserServiceImpl implements IUserService, JwtConstans
{

    @Value("${token.secret}")
    private String secretKey;

    @Value("${token.expireTime}")
    private Integer expireTime;

    @Value("${wkzr.redis.test}")
    private String secret;

    /**
     * 驗(yàn)證碼過(guò)期時(shí)間
     */
    @Value("${wkzr.redis.verificationExpirationTime}")
    private Integer verificationExpirationTime;


    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private LoginlogsMapper loginlogsMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisCache redisCache;

    //設(shè)置初始密碼屬性
    @Value("${userPassword.initPassword}")
    private String initPassword;

    /**
     * - 手機(jī)號(hào)格式錯(cuò)誤
     *   - 為空
     *   - 不符合手機(jī)號(hào)
     * - 手機(jī)號(hào)未注冊(cè)
     * - 用戶被禁用
     *   - 在此處直接驗(yàn)證,以減少發(fā)送短信的成本
     *   驗(yàn)證碼存儲(chǔ)到redis中,在五分鐘內(nèi)可以通過(guò)驗(yàn)證碼登錄
     * @param userVerifyDTO
     * @return
     * @throws Exception
     */
    @Override
    public String verify(UserVerifyDTO userVerifyDTO) throws Exception {
        String phoneNumber = userVerifyDTO.getPhoneNumber();

        // 格式錯(cuò)誤
        if (phoneNumber.length() != 11) {
            throw new AccessDeniedException(ReturnConstants.PHONENUMBER_FORMAT_ERROR);
        }

        // 不能為空
        if (StringUtils.isEmpty(phoneNumber)) {
            throw new AccessDeniedException(ReturnConstants.PHONENUMBER_NOT_EMPTY);
        }

        User user = userMapper.selectUserByPhone(phoneNumber);
        // 用戶不存在
        if (user == null) {
            throw new AccessDeniedException(ReturnConstants.ACCOUNT_NOT_EXIST);
        }

        // 未啟用
        if (user.getEnabled() != null && user.getEnabled() == 0) {
            throw new AccessDeniedException(ReturnConstants.USER_IS_UNENABLED);
        }

        // 生成隨機(jī)驗(yàn)證碼
//        String verificationCode = SendCodeUtils.generateVerificationCode();
        String verificationCode = "000000";
        System.out.println("驗(yàn)證碼:" + verificationCode);

        // 發(fā)送驗(yàn)證碼
        log.info("發(fā)送驗(yàn)證碼!");
        SendCodeUtils.verify(phoneNumber, verificationCode);

        // 存儲(chǔ)驗(yàn)證碼到Redis,設(shè)置有效期為5分鐘
        String rediskey = secret + phoneNumber;
        redisTemplate.opsForValue().set(rediskey, verificationCode, verificationExpirationTime, TimeUnit.MINUTES); // 單位為分鐘
        return ReturnConstants.CAPTCHA_SEND_SUCCESS;
    }

    /**
     * 從redis中獲取驗(yàn)證碼
     *  匹配
     * - 未通過(guò)
     * - 不存在或過(guò)期
     * - 存在且通過(guò)
     * @param userLoginDTO
     * @param request
     * @return
     * @throws SocketException
     * @throws UnknownHostException
     */
    @Override
    public Object login(UserLoginDTO userLoginDTO, HttpServletRequest request) throws SocketException, UnknownHostException {
        log.info("request:{}", request);
        String phoneNumber = userLoginDTO.getPhoneNumber();
        String remoteAddr = IpUtils.getIpAddr();// ip地址
        String macaddr = GetMacAddr.getLocalMac(remoteAddr);//mac地址

        //獲取瀏覽器
        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
        String browser = userAgent.getBrowser().getName();
        //獲取操作系統(tǒng)
        String os = userAgent.getOperatingSystem().getName();
        //獲取操作地點(diǎn)
        String location = AddressUtils.getRealAddressByIP(remoteAddr);

        log.info("remoteAddr:{}", remoteAddr);
        log.info("userAgent:{}", userAgent);

        // 從Redis中獲取存儲(chǔ)的驗(yàn)證碼
        String redisKey = secret + phoneNumber;
        String storedCode = redisTemplate.opsForValue().get(redisKey);

        if (storedCode == null) {
            return AjaxResult.forbidden(ReturnConstants.CAPTCHA_NOT_EXIT);
        }

        if (!userLoginDTO.getVerificationCode().equals(storedCode)) {
            return AjaxResult.forbidden(ReturnConstants.CAPTCHA_IS_ERROR);
        }

        User user = userMapper.selectUserByPhone(phoneNumber);
        log.info("user信息:{}", user);
        if (user == null) {
            return AjaxResult.forbidden(ReturnConstants.USER_NOT_EXIST);
        }

        if (user.getEnabled() == 0){
            return AjaxResult.forbidden(ReturnConstants.USER_IS_UNENABLED);
        }

        // 獲取用戶信息
        Integer userId = user.getUserId();
        String userAccount = user.getUserAccount();
        String userName = user.getUsername();

        Logininfor logininfor = Logininfor.builder()
                .userAccount(userAccount)
                .userName(userName)
                .ipaddr(remoteAddr)
                .macAddr(macaddr)
                .browser(browser)
                .os(os)
                .loginLocation(location)
                .loginTime(new Date())
                .phoneNumber(user.getPhoneNumber())
                .enable(1)
                .wxBind(user.getWxBind())
                .status(1)
                .build();
        loginlogsMapper.insertTrinvLoginlogs(logininfor);

        // 生成token
        // JWT
        Map<String, Object> claims = new HashMap<>();
        String uuid = UUID.randomUUID().toString();
        claims.put(CLAIM_USER_ID, userId);
        claims.put(CLAIM_UUID, uuid);
        claims.put(CLAIM_PHONE_NUMBER, phoneNumber);
        claims.put(CLAIM_USER_ACCOUNT, userAccount);
        claims.put(CLAIM_USER_NAME, userName);
        claims.put(CLAIM_USER_AGENT, userAgent); // mac
        claims.put(CLAIM_REMOTE_ADDR, remoteAddr); // ip
        claims.put(CLAIM_OS, os); // os操作系統(tǒng)
        claims.put(CLAIM_MAC, macaddr); // mac地址
        claims.put(CLAIM_BROWSER, browser); // 瀏覽器名稱
        String jwt = JwtUtils.createJWT(claims, secretKey);
        log.info("生成用戶的JWT數(shù)據(jù):{}", jwt);

        UserLoginInfoVO userLoginInfoVO = userMapper.getLoginInfoByUsername(userName);
        log.info("userLoginInfoVO:{}", userLoginInfoVO);

        List<GrantedAuthority> authorities = new ArrayList<>();
        // 獲取角色關(guān)鍵字 用于后續(xù)權(quán)限判斷
        List<String> rolekeys = userLoginInfoVO.getRolekeys();
        for (String rolekey : rolekeys) {
            authorities.add(new SimpleGrantedAuthority(rolekey));
        }
        String authoritiesJsonString = JSON.toJSONString(authorities);

        UserCachePO userCachePO = new UserCachePO();
        userCachePO.setEnable(userLoginInfoVO.getEnable());
        userCachePO.setAuthoritiesJsonString(authoritiesJsonString);
        userCachePO.setToken(jwt);

        // 轉(zhuǎn)換hash數(shù)據(jù)類(lèi)型,存入redis
        String jwtRedisKey = "JWT_Token:" + uuid;// 鍵
        HashOperations<String, Object, Object> opsForHash = redisTemplate.opsForHash();
        Map<String, Object> userLoginInfoMap = BeanUtil.beanToMap(userCachePO);
        opsForHash.putAll(jwtRedisKey, userLoginInfoMap);
        redisTemplate.expire(jwtRedisKey, 86400, TimeUnit.MINUTES);// 過(guò)期時(shí)間
        log.info("向緩存中存入用戶狀態(tài)數(shù)據(jù):{}", userCachePO);

        // 返回登錄結(jié)果VO
        UserLoginResultVO userLoginResultVO = new UserLoginResultVO()
                .setUserId(userId)
                .setUsername(userName)
                .setToken(jwt)
                .setAuthorities(rolekeys);
        return AjaxResult.success(userLoginResultVO);
    }
}

3.發(fā)送短信的API

此處代碼有兩個(gè)工具類(lèi)

        // 生成隨機(jī)驗(yàn)證碼
        String verificationCode = SendCodeUtils.generateVerificationCode();
        System.out.println("驗(yàn)證碼:" + verificationCode);

        // 發(fā)送驗(yàn)證碼
        log.info("發(fā)送驗(yàn)證碼!");
        SendCodeUtils.verify(phoneNumber, verificationCode);
package com.ruoyi.utils;

import com.aliyun.tea.TeaException;

import java.util.Random;

public class SendCodeUtils {

    public static String generateVerificationCode() {
        // 設(shè)置驗(yàn)證碼長(zhǎng)度為6
        int length = 6;
        // 驗(yàn)證碼字符集
        String digits = "0123456789";
        Random random = new Random();
        StringBuilder sb = new StringBuilder();

        // 生成六位數(shù)驗(yàn)證碼
        for (int i = 0; i < length; i++) {
            int index = random.nextInt(digits.length());
            sb.append(digits.charAt(index));
        }
        return sb.toString();
    }

    /**
     * <b>description</b> :
     * <p>使用AK&amp;SK初始化賬號(hào)Client</p>
     * @return Client
     *
     * @throws Exception
     */
    public static com.aliyun.dysmsapi20170525.Client createClient() throws Exception {
        // 工程代碼泄露可能會(huì)導(dǎo)致 AccessKey 泄露,并威脅賬號(hào)下所有資源的安全性。以下代碼示例僅供參考。
        // 建議使用更安全的 STS 方式,更多鑒權(quán)訪問(wèn)方式請(qǐng)參見(jiàn):https://help.aliyun.com/document_detail/378657.html。
        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                // 必填,請(qǐng)確保代碼運(yùn)行環(huán)境設(shè)置了環(huán)境變量 ALIBABA_CLOUD_ACCESS_KEY_ID。
                .setAccessKeyId("ALIBABA_CLOUD_ACCESS_KEY_ID")
                // 必填,請(qǐng)確保代碼運(yùn)行環(huán)境設(shè)置了環(huán)境變量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
                .setAccessKeySecret("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
        // Endpoint 請(qǐng)參考 https://api.aliyun.com/product/Dysmsapi
        config.endpoint = "dysmsapi.aliyuncs.com";
        return new com.aliyun.dysmsapi20170525.Client(config);
    }

    public static String verify(String phoneNumber, String verificationCode) throws Exception {
//        java.util.List<String> args = java.util.Arrays.asList(args_);
        com.aliyun.dysmsapi20170525.Client client = SendCodeUtils.createClient();
        com.aliyun.dysmsapi20170525.models.SendSmsRequest sendSmsRequest = new com.aliyun.dysmsapi20170525.models.SendSmsRequest()
                .setPhoneNumbers(phoneNumber)
                .setSignName("簽名名稱")
                .setTemplateCode("模板Code")
                .setTemplateParam("{\"code\":\"" + verificationCode + "\"}");
        try {
            // 復(fù)制代碼運(yùn)行請(qǐng)自行打印 API 的返回值
            client.sendSmsWithOptions(sendSmsRequest, new com.aliyun.teautil.models.RuntimeOptions());
            return verificationCode;
        } catch (TeaException error) {
            // 此處僅做打印展示,請(qǐng)謹(jǐn)慎對(duì)待異常處理,在工程項(xiàng)目中切勿直接忽略異常。
            // 錯(cuò)誤 message
            System.out.println(error.getMessage());
            // 診斷地址
            System.out.println(error.getData().get("Recommend"));
            com.aliyun.teautil.Common.assertAsString(error.message);
            return null;
        } catch (Exception _error) {
            TeaException error = new TeaException(_error.getMessage(), _error);
            // 此處僅做打印展示,請(qǐng)謹(jǐn)慎對(duì)待異常處理,在工程項(xiàng)目中切勿直接忽略異常。
            // 錯(cuò)誤 message
            System.out.println(error.getMessage());
            // 診斷地址
            System.out.println(error.getData().get("Recommend"));
            com.aliyun.teautil.Common.assertAsString(error.message);
            return null;
        }
    }
}

之前需要的幾個(gè)關(guān)鍵信息可以在此發(fā)揮用處

此處若是公司內(nèi)部代碼可將ALIBABA_CLOUD_ACCESS_KEY_ID和ALIBABA_CLOUD_ACCESS_KEY_SECRET的值直接填入對(duì)應(yīng)位置

但若是代碼可能會(huì)泄露,則還是建議在對(duì)應(yīng)的環(huán)境中部署環(huán)境變量,代碼運(yùn)行時(shí)獲取環(huán)境變量自動(dòng)填入,涉及的Windows和Linux環(huán)境下的環(huán)境變量設(shè)置在后續(xù)文章中可找到,此處不多贅述。

// 必填,請(qǐng)確保代碼運(yùn)行環(huán)境設(shè)置了環(huán)境變量 ALIBABA_CLOUD_ACCESS_KEY_ID。
.setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
// 必填,請(qǐng)確保代碼運(yùn)行環(huán)境設(shè)置了環(huán)境變量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
.setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));

4.以上操作和數(shù)據(jù)庫(kù)都完成后,即可實(shí)現(xiàn)短信驗(yàn)證碼的發(fā)送,登錄時(shí)要求用戶攜帶驗(yàn)證碼,并與Redis中存儲(chǔ)的驗(yàn)證碼匹配即可通過(guò)校驗(yàn)。

到此這篇關(guān)于Java實(shí)現(xiàn)用戶短信驗(yàn)證碼登錄功能的文章就介紹到這了,更多相關(guān)Java用戶短信驗(yàn)證碼登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringCloud 服務(wù)負(fù)載均衡和調(diào)用 Ribbon、OpenFeign的方法

    SpringCloud 服務(wù)負(fù)載均衡和調(diào)用 Ribbon、OpenFeign的方法

    這篇文章主要介紹了SpringCloud 服務(wù)負(fù)載均衡和調(diào)用 Ribbon、OpenFeign的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Java深度復(fù)制功能與用法實(shí)例分析

    Java深度復(fù)制功能與用法實(shí)例分析

    這篇文章主要介紹了Java深度復(fù)制功能與用法,簡(jiǎn)單講述了深度復(fù)制的概念、功能并結(jié)合實(shí)例形式分析了java實(shí)現(xiàn)深度復(fù)制的具體操作技巧,需要的朋友可以參考下
    2018-01-01
  • mybatis?plus常用注解的具體使用

    mybatis?plus常用注解的具體使用

    本文主要介紹了mybatis?plus常用注解的具體使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • MyBatis的SQL執(zhí)行結(jié)果和客戶端執(zhí)行結(jié)果不一致問(wèn)題排查

    MyBatis的SQL執(zhí)行結(jié)果和客戶端執(zhí)行結(jié)果不一致問(wèn)題排查

    本文主要介紹了MyBatis的SQL執(zhí)行結(jié)果和客戶端執(zhí)行結(jié)果不一致問(wèn)題排查,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Java通俗易懂系列設(shè)計(jì)模式之適配器模式

    Java通俗易懂系列設(shè)計(jì)模式之適配器模式

    這篇文章主要介紹了Java通俗易懂系列設(shè)計(jì)模式之適配器模式,對(duì)設(shè)計(jì)模式感興趣的同學(xué),一定要看一下
    2021-04-04
  • java中TCP實(shí)現(xiàn)回顯服務(wù)器及客戶端

    java中TCP實(shí)現(xiàn)回顯服務(wù)器及客戶端

    本文主要介紹了java中TCP實(shí)現(xiàn)回顯服務(wù)器及客戶端,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • idea如何關(guān)閉頁(yè)面顯示的瀏覽器圖標(biāo)

    idea如何關(guān)閉頁(yè)面顯示的瀏覽器圖標(biāo)

    這篇文章主要介紹了idea如何關(guān)閉頁(yè)面顯示的瀏覽器圖標(biāo)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Java多線程之JUC(java.util.concurrent)的常見(jiàn)類(lèi)(多線程編程常用類(lèi))

    Java多線程之JUC(java.util.concurrent)的常見(jiàn)類(lèi)(多線程編程常用類(lèi))

    這篇文章主要給大家介紹了關(guān)于Java多線程之JUC(java.util.concurrent)的常見(jiàn)類(lèi)(多線程編程常用類(lèi))的相關(guān)資料,Java中的JUC(java.util.concurrent)包提供了一些并發(fā)編程中常用的類(lèi),這些類(lèi)可以幫助我們更方便地實(shí)現(xiàn)多線程編程,需要的朋友可以參考下
    2024-02-02
  • 使用maven方式創(chuàng)建springboot項(xiàng)目的方式

    使用maven方式創(chuàng)建springboot項(xiàng)目的方式

    使用Spring Initializr創(chuàng)建spring boot項(xiàng)目,因?yàn)橥饩W(wǎng)問(wèn)題導(dǎo)致很難成功,所以只能使用maven方式,這里介紹下使用maven方式創(chuàng)建springboot項(xiàng)目的方法,感興趣的朋友一起看看吧
    2022-09-09
  • Springboot詳解底層啟動(dòng)過(guò)程

    Springboot詳解底層啟動(dòng)過(guò)程

    這篇文章主要介紹了SpringBoot啟動(dòng)過(guò)程的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07

最新評(píng)論