欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringSceurity實(shí)現(xiàn)短信驗(yàn)證碼功能的示例代碼

 更新時(shí)間:2020年06月09日 09:41:53   作者:雨點(diǎn)的名字  
這篇文章主要介紹了SpringSceurity實(shí)現(xiàn)短信驗(yàn)證碼功能的示例代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

一、思考

1、設(shè)計(jì)思路

在獲取短信驗(yàn)證碼功能和圖形驗(yàn)證碼還是有很多相似的地方,所以這里在設(shè)計(jì)獲取短信驗(yàn)證的時(shí)候,將之前開(kāi)發(fā)好的的圖形驗(yàn)證碼進(jìn)一步整合、抽象與重構(gòu)。

在獲取驗(yàn)證碼的時(shí)候,它們最大的不同在于: 圖形驗(yàn)證碼是通過(guò)接口返回獲取給前端。而短信驗(yàn)證碼而言是通過(guò)第三方API向我們手機(jī)推送。

但是它們?cè)诘顷懙臅r(shí)候就有很大的不同了,對(duì)于圖形驗(yàn)證碼而言驗(yàn)證通過(guò)之前就走UsernamePasswordAuthenticationFilter 過(guò)濾器了開(kāi)始校驗(yàn)用戶名密碼了。

但對(duì)于短信登陸而言,確實(shí)也需要先現(xiàn)在短信驗(yàn)證碼是否通過(guò),但是一旦通過(guò)他是不走 UsernamePasswordAuthenticationFilter,而是通過(guò)其它方式查詢用戶信息來(lái)校驗(yàn)

認(rèn)證已經(jīng)通過(guò)了。

這篇博客只寫(xiě)獲取獲取短信驗(yàn)證碼的功能,不寫(xiě)通過(guò)短信驗(yàn)證碼登陸的邏輯。

2、重構(gòu)設(shè)計(jì)

這里才是最重要的,如何去設(shè)計(jì)和整合短信驗(yàn)證碼和圖形驗(yàn)證碼的代碼,是我們最應(yīng)該思考的。如何將相似部分抽離出來(lái),然后去實(shí)現(xiàn)不相同的部分。

整理后發(fā)現(xiàn)不同點(diǎn)主要在于

 1、獲取驗(yàn)證碼。因?yàn)閷?duì)于圖形驗(yàn)證碼需要有個(gè)畫(huà)布,而短信驗(yàn)證碼并不需要,所以它們可以實(shí)現(xiàn)同一個(gè)接口,來(lái)完成不同的邏輯。
 2、發(fā)送驗(yàn)證碼。對(duì)于圖形驗(yàn)證碼來(lái)講只要把驗(yàn)證碼返給前端就可以,而短信驗(yàn)證碼而言是通過(guò)第三方API將驗(yàn)證碼發(fā)到我們的手機(jī)上。
    所以這里也可以通過(guò)實(shí)現(xiàn)統(tǒng)一接口來(lái)具體實(shí)現(xiàn)不同的方法。

相同部分我可以通過(guò)抽象類來(lái)完成實(shí)現(xiàn),不同部分可以通過(guò)具體的實(shí)現(xiàn)類來(lái)實(shí)現(xiàn)。

AbstractValidateCodeProcessorService 抽象類是用來(lái)實(shí)現(xiàn)兩種驗(yàn)證碼可以抽離的部分。ImageCodeProcessorServiceImpl 和

SmsCodeProcessorServiceImpl方法是來(lái)實(shí)現(xiàn)兩種驗(yàn)證碼不同的發(fā)送方式。

在簡(jiǎn)單看下時(shí)序圖可能會(huì)更加明白點(diǎn)。

一個(gè)接口只有一個(gè)方法(processor)就是處理驗(yàn)證碼,它其實(shí)需要做三件事。

 1、獲取驗(yàn)證碼。2、將驗(yàn)證碼存入session。3、將驗(yàn)證碼信息通過(guò)短信或者圖形驗(yàn)證碼發(fā)送出去。

首先講生成獲取驗(yàn)證碼,這里有一個(gè)公共接口和兩個(gè)實(shí)現(xiàn)類

對(duì)于保存驗(yàn)證碼信息而言,可以在直接在 AbstractValidateCodeProcessorService抽象類來(lái)完成,都不需要去實(shí)現(xiàn)。

對(duì)發(fā)送驗(yàn)證碼信息而言,只需要實(shí)現(xiàn)AbstractValidateCodeProcessorService抽象類的send發(fā)送驗(yàn)證碼接口即可。

整個(gè)大致接口設(shè)計(jì)就是這樣,具體的可以通過(guò)代碼來(lái)展示。

二、代碼實(shí)現(xiàn)

1、驗(yàn)證碼屬性

短信驗(yàn)證碼和圖形驗(yàn)證后包含屬性有 codeexpireTime,短信驗(yàn)證碼只有這兩個(gè)屬性,而圖形驗(yàn)證碼還多一個(gè)BufferedImage實(shí)例對(duì)象屬性,所以將共同屬性進(jìn)行抽取

,抽取為ValidateCode類,代碼如下:

ValidateCode實(shí)體

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ValidateCode {

 private String code;

 private LocalDateTime expireTime;

 public boolean isExpired() {
 return LocalDateTime.now().isAfter(expireTime);
 }

}

對(duì)于圖形驗(yàn)證碼而言,除了需要code和過(guò)期時(shí)間還需要圖片的畫(huà)布,所以繼承ValidateCode之后再寫(xiě)自己屬性

ImageCode實(shí)體

@Data
public class ImageCode extends ValidateCode {

 private BufferedImage image;

 public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {
 super(code, expireTime);
 this.image = image;
 }

 public ImageCode(BufferedImage image, String code, int expireIn) {
 super(code, LocalDateTime.now().plusSeconds(expireIn));
 this.image = image;
 }
}

對(duì)于短信驗(yàn)證碼而言,暫時(shí)不需要添加自己的屬性字段了。

SmsCode實(shí)體

public class SmsCode extends ValidateCode {

 public SmsCode(String code, LocalDateTime expireTime) {
 super(code, expireTime);
 }

 public SmsCode(String code, int expireIn) {
 super(code, LocalDateTime.now().plusSeconds(expireIn));
 }
}

2、ValidateCodeProcessor接口

ValidateCodeProcessor接口主要是完成 驗(yàn)證碼的生成、保存與發(fā)送的完整流程,接口的主要設(shè)計(jì)如下所示:

ValidateCodeProcessorService接口

public interface ValidateCodeProcessorService {

 /**
 * 因?yàn)楝F(xiàn)在有兩種驗(yàn)證碼,所以存放到seesion的key不能一樣,所以前綴+具體type
 */
 String SESSION_KEY_PREFIX = "SESSION_KEY_FOR_CODE_";
 /**
 * 通過(guò)也是 type+CODE_PROCESSOR獲取對(duì)于的bean
 */
 String CODE_PROCESSOR = "CodeProcessorService";

 /**
 * 這個(gè)接口要做三件事
 * 1、獲取驗(yàn)證碼。
 * 2、將驗(yàn)證碼存入session
 * 3、將驗(yàn)證碼信息通過(guò)短信或者圖形驗(yàn)證碼發(fā)送出去。
 * (將spring-boot-security-study-03接口里的那三步進(jìn)行里封裝)
 *
 */
 void processor(ServletWebRequest request) throws Exception;

由于圖片驗(yàn)證碼和短信驗(yàn)證碼的 生成和保存、發(fā)送等流程是固定的。所以這里寫(xiě)一個(gè)抽象類來(lái)實(shí)現(xiàn)ValidateCodeProcessor接口,來(lái)實(shí)現(xiàn)相似部分。

AbstractValidateCodeProcessorService抽象類

@Component
public abstract class AbstractValidateCodeProcessorService<C> implements ValidateCodeProcessorService {

 private static final String SEPARATOR = "/code/";

 /**
 * 操作session的工具集
 */
 private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

 /**
 * 這是Spring的一個(gè)特性,就是在項(xiàng)目啟動(dòng)的時(shí)候會(huì)自動(dòng)收集系統(tǒng)中 {@link ValidateCodeGeneratorService} 接口的實(shí)現(xiàn)類對(duì)象
 *
 * key為bean name
 */
 @Autowired
 private Map<String, ValidateCodeGeneratorService> validateCodeGeneratorMap;

 @Override
 public void processor(ServletWebRequest request) throws Exception {
 //第一件事
 C validateCode = generate(request);
 //第二件事
 save(request, validateCode);
 //第三件事
 send(request, validateCode);
 }

 /**
 * 生成驗(yàn)證碼
 *
 */
 private C generate(ServletWebRequest request) {
 String type = getProcessorType(request);
 //這里 image+CodeGenerator = imgCodeGenerator 對(duì)應(yīng)的就是ImageCodeGeneratorServiceService
 ValidateCodeGeneratorService validateCodeGenerator = validateCodeGeneratorMap.get(type.concat(ValidateCodeGeneratorService.CODE_GENERATOR));
 return (C) validateCodeGenerator.generate(request);
 }

 /**
 * 保存驗(yàn)證碼到session中
 */
 private void save(ServletWebRequest request, C validateCode) {
 //這里也是封裝了一下
 sessionStrategy.setAttribute(request, SESSION_KEY_PREFIX.concat(getProcessorType(request).toUpperCase()), validateCode);
 }

 /**
 * 發(fā)送驗(yàn)證碼 (只有發(fā)送驗(yàn)證碼是需要自己去實(shí)現(xiàn)的。)
 */
 protected abstract void send(ServletWebRequest request, C validateCode) throws Exception;

 /**
 * 獲取請(qǐng)求URL中具體請(qǐng)求的驗(yàn)證碼類型
 *
 */
 private String getProcessorType(ServletWebRequest request) {
 // 獲取URI分割后的第二個(gè)片段 (/code/image 通過(guò)/code/ 切割后就只剩下 image
 return StringUtils.substringAfter(request.getRequest().getRequestURI(), SEPARATOR);
 }
}

簡(jiǎn)單說(shuō)明

1、這里用到了Spring一個(gè)特性就是Map<String, ValidateCodeGeneratorService> validateCodeGeneratorMap 可以把ValidateCodeGeneratorService所以的實(shí)現(xiàn)類都放
到這個(gè)map中,key為bean的名稱。

2、抽象類中實(shí)現(xiàn)了 ValidateCodeProcessor接口的processor方法,它主要是完成了驗(yàn)證碼的創(chuàng)建、保存和發(fā)送的功能。

3、generate 方法根據(jù)傳入的不同泛型而生成了特定的驗(yàn)證碼。

4、save 方法是將生成的驗(yàn)證碼實(shí)例對(duì)象存入到session中,兩種驗(yàn)證碼的存儲(chǔ)方式一致,只是有個(gè)key不一致,所以代碼也是通用的。

5、send 方法一個(gè)抽象方法,分別由ImageCodeProcessorService和SmsCodeProcessorService來(lái)具體實(shí)現(xiàn),也是根據(jù)泛型來(lái)判斷具體調(diào)用哪一個(gè)具體的實(shí)現(xiàn)類的send方法。

3、驗(yàn)證碼的生成接口

上面說(shuō)過(guò)驗(yàn)證的生成應(yīng)該也是通過(guò)實(shí)現(xiàn)類

ValidateCodeGeneratorService

public interface ValidateCodeGeneratorService {

 /**
 * 這個(gè)常量也是用來(lái) type+CodeGeneratorService獲取對(duì)于bean對(duì)象
 */
 String CODE_GENERATOR = "CodeGeneratorService";

 /**
 * 生成驗(yàn)證碼
 * 具體是圖片驗(yàn)證碼 還是短信驗(yàn)證碼就需要對(duì)應(yīng)的實(shí)現(xiàn)類
 */
 ValidateCode generate(ServletWebRequest request);
}

圖形驗(yàn)證碼具體實(shí)現(xiàn)類

mageCodeGeneratorServiceImpl

@Data
@Component("imageCodeGeneratorService")
public class ImageCodeGeneratorServiceImpl implements ValidateCodeGeneratorService {

 private static final String IMAGE_WIDTH_NAME = "width";
 private static final String IMAGE_HEIGHT_NAME = "height";
 private static final Integer MAX_COLOR_VALUE = 255;

 @Autowired
 private ValidateCodeProperties validateCodeProperties;

 @Override
 public ImageCode generate(ServletWebRequest request) {
 int width = ServletRequestUtils.getIntParameter(request.getRequest(), IMAGE_WIDTH_NAME, validateCodeProperties.getImage().getWidth());
 int height = ServletRequestUtils.getIntParameter(request.getRequest(), IMAGE_HEIGHT_NAME,validateCodeProperties.getImage().getHeight());
 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
 Graphics g = image.getGraphics();

 Random random = new Random();

 // 生成畫(huà)布
 g.setColor(getRandColor(200, 250));
 g.fillRect(0, 0, width, height);
 g.setFont(new Font("Times New Roman", Font.ITALIC, 20));
 g.setColor(getRandColor(160, 200));
 for (int i = 0; i < 155; i++) {
 int x = random.nextInt(width);
 int y = random.nextInt(height);
 int xl = random.nextInt(12);
 int yl = random.nextInt(12);
 g.drawLine(x, y, x + xl, y + yl);
 }

 // 生成數(shù)字驗(yàn)證碼
 StringBuilder sRand = new StringBuilder();
 for (int i = 0; i < validateCodeProperties.getImage().getLength(); i++) {
 String rand = String.valueOf(random.nextInt(10));
 sRand.append(rand);
 g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
 g.drawString(rand, 13 * i + 6, 16);
 }

 g.dispose();

 return new ImageCode(image, sRand.toString(), validateCodeProperties.getImage().getExpireIn());
 }

 /**
 * 生成隨機(jī)背景條紋
 *
 * @param fc 前景色
 * @param bc 背景色
 * @return RGB顏色
 */
 private Color getRandColor(int fc, int bc) {
 Random random = new Random();
 if (fc > MAX_COLOR_VALUE) {
 fc = MAX_COLOR_VALUE;
 }
 if (bc > MAX_COLOR_VALUE) {
 bc = MAX_COLOR_VALUE;
 }
 int r = fc + random.nextInt(bc - fc);
 int g = fc + random.nextInt(bc - fc);
 int b = fc + random.nextInt(bc - fc);
 return new Color(r, g, b);
 }
}

短信驗(yàn)證碼具體實(shí)現(xiàn)類

SmsCodeGeneratorServiceImpl

@Data
@Component("smsCodeGeneratorService")
public class SmsCodeGeneratorServiceImpl implements ValidateCodeGeneratorService {

 @Autowired
 private ValidateCodeProperties validateCodeProperties;


 @Override
 public SmsCode generate(ServletWebRequest request) {
 //生成隨機(jī)數(shù)
 String code = RandomStringUtils.randomNumeric(validateCodeProperties.getSms().getLength());
 return new SmsCode(code, validateCodeProperties.getSms().getExpireIn());
 }
}

4、驗(yàn)證碼的發(fā)送邏輯類

獲取的實(shí)現(xiàn)類寫(xiě)好了,我們?cè)趯?xiě)發(fā)送具體的發(fā)送實(shí)現(xiàn)類,發(fā)送類的實(shí)現(xiàn)類是實(shí)現(xiàn)AbstractValidateCodeProcessorService抽象類的。

圖片發(fā)送實(shí)現(xiàn)類

ImageCodeProcessorServiceImpl

@Component("imageCodeProcessorService")
public class ImageCodeProcessorServiceImpl extends AbstractValidateCodeProcessorService<ImageCode> {

 private static final String FORMAT_NAME = "JPEG";

 /**
 * 發(fā)送圖形驗(yàn)證碼,將其寫(xiě)到相應(yīng)中
 *
 * @param request ServletWebRequest實(shí)例對(duì)象
 * @param imageCode 驗(yàn)證碼
 */
 @Override
 protected void send(ServletWebRequest request, ImageCode imageCode) throws Exception {
 ImageIO.write(imageCode.getImage(), FORMAT_NAME, request.getResponse().getOutputStream());
 }
}

短信發(fā)送具體實(shí)現(xiàn)類。這里只是后臺(tái)輸出就好了,實(shí)際中只要接入對(duì)于的SDK就可以了。

SmsCodeProcessorServiceImpl

@Component("smsCodeProcessorService")
public class SmsCodeProcessorServiceImpl extends AbstractValidateCodeProcessorService<SmsCode> {
 private static final String SMS_CODE_PARAM_NAME = "mobile";

 @Override
 protected void send(ServletWebRequest request, SmsCode smsCode) throws Exception {
 //這里有一個(gè)參數(shù)也是前端需要傳來(lái)的 就是用戶的手機(jī)號(hào)
 String mobile = ServletRequestUtils.getRequiredStringParameter(request.getRequest(), SMS_CODE_PARAM_NAME);
 // 這里僅僅寫(xiě)個(gè)打印,具體邏輯一般都是調(diào)用第三方接口發(fā)送短信
 System.out.println("向手機(jī)號(hào)為:" + mobile + "的用戶發(fā)送驗(yàn)證碼:" + smsCode.getCode());
 }

整個(gè)大致就是這樣,我們?cè)賮?lái)測(cè)試一下。

三、測(cè)試

1、ValidateCodeController接口

獲取驗(yàn)證碼接口

@RestController
@Slf4j
public class ValidateCodeController {


 @Autowired
 private Map<String, ValidateCodeProcessorService> validateCodeProcessorMap;


 @RequestMapping("/code/{type}")
 public void createCode(HttpServletRequest request, HttpServletResponse response, @PathVariable String type) throws Exception {
 if(!StringUtils.equalsAny(type, "image", "sms")){
 log.info("type類型錯(cuò)誤 type={}",type);
 return;
 };

 //根據(jù)type獲取具體的實(shí)現(xiàn)類
 ValidateCodeProcessorService validateCodeProcessorService = validateCodeProcessorMap.get(type.concat(ValidateCodeProcessorService.CODE_PROCESSOR));
 validateCodeProcessorService.processor(new ServletWebRequest(request, response));

 }

}

2、獲得圖形驗(yàn)證碼

獲取成功

3、獲取短信驗(yàn)證碼

獲取短信驗(yàn)證碼需要多傳一個(gè)參數(shù)就是mobile 手機(jī)號(hào)碼

因?yàn)檫@里發(fā)送短信沒(méi)有接第三方SDK,而是直接在控制臺(tái)輸出


參考

1、Spring Security技術(shù)棧開(kāi)發(fā)企業(yè)級(jí)認(rèn)證與授權(quán)(JoJo)

這一套架構(gòu)設(shè)計(jì)真的非常的好,代碼可讀性和可用性都非常高,以后如果要接第三個(gè)驗(yàn)證碼只要實(shí)現(xiàn)發(fā)送和獲取的接口來(lái)自定義實(shí)a現(xiàn)就好了。很受啟發(fā)!

總結(jié)

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

相關(guān)文章

  • springmvc path請(qǐng)求映射到bean 方法的流程

    springmvc path請(qǐng)求映射到bean 方法的流程

    這篇文章主要介紹了springmvc path請(qǐng)求映射到bean 方法的流程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-07-07
  • Springboot?Vue實(shí)現(xiàn)單點(diǎn)登陸功能示例詳解

    Springboot?Vue實(shí)現(xiàn)單點(diǎn)登陸功能示例詳解

    這篇文章主要為大家介紹了Springboot?Vue實(shí)現(xiàn)單點(diǎn)登陸功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • SpringBoot+Maven 多模塊項(xiàng)目的構(gòu)建、運(yùn)行、打包實(shí)戰(zhàn)

    SpringBoot+Maven 多模塊項(xiàng)目的構(gòu)建、運(yùn)行、打包實(shí)戰(zhàn)

    這篇文章主要介紹了SpringBoot+Maven 多模塊項(xiàng)目的構(gòu)建、運(yùn)行、打包實(shí)戰(zhàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • IDEA?Eval?Reset?使用方法匯總

    IDEA?Eval?Reset?使用方法匯總

    本文給大家介紹了IDEA?Eval?Reset?使用方法,安裝插件包括離線安裝方式和在線安裝方式,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2023-10-10
  • Java反射獲取實(shí)例的速度對(duì)比分析

    Java反射獲取實(shí)例的速度對(duì)比分析

    這篇文章主要介紹了Java反射獲取實(shí)例的速度對(duì)比分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09
  • Java 實(shí)戰(zhàn)范例之進(jìn)銷存管理系統(tǒng)的實(shí)現(xiàn)

    Java 實(shí)戰(zhàn)范例之進(jìn)銷存管理系統(tǒng)的實(shí)現(xiàn)

    讀萬(wàn)卷書(shū)不如行萬(wàn)里路,只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+vue+Springboot+ssm+mysql+maven+redis實(shí)現(xiàn)一個(gè)前后端分離的進(jìn)銷存管理系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平
    2021-11-11
  • Java與WebUploader相結(jié)合實(shí)現(xiàn)文件上傳功能(實(shí)例代碼)

    Java與WebUploader相結(jié)合實(shí)現(xiàn)文件上傳功能(實(shí)例代碼)

    這篇文章主要介紹了Java結(jié)合WebUploader實(shí)現(xiàn)文件上傳功能,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-03-03
  • java基于UDP實(shí)現(xiàn)圖片群發(fā)功能

    java基于UDP實(shí)現(xiàn)圖片群發(fā)功能

    這篇文章主要為大家詳細(xì)介紹了java基于UDP實(shí)現(xiàn)圖片群發(fā)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • 淺談Java轉(zhuǎn)義符\\|

    淺談Java轉(zhuǎn)義符\\|

    java中\(zhòng)需要用\\來(lái)表示吧這個(gè)你應(yīng)該知道,而split中傳入的參數(shù)是什么呢 他并不是普通的字符串 你可以查一下api文檔 它要求傳入的是正則表達(dá)式 而正則表達(dá)式也需要這個(gè)\所以在這里有這個(gè)\\
    2015-06-06
  • MybatisPlus 主鍵策略的幾種實(shí)現(xiàn)方法

    MybatisPlus 主鍵策略的幾種實(shí)現(xiàn)方法

    MybatisPlus-Plus支持多種主鍵生成策略,可以通過(guò)@TableId注解的type屬性配置,主要策略包括AUTO、INPUT、ASSING_ID、ASSING_UUID和NONE,每種策略適用于不同的場(chǎng)景,下面就來(lái)介紹一下
    2024-10-10

最新評(píng)論