一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - Spring Security實現短信驗證碼功能的示例代碼

Spring Security實現短信驗證碼功能的示例代碼

2020-06-28 12:21雨點的名字 JAVA教程

這篇文章主要介紹了Spring Security實現短信驗證碼功能的示例代碼,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

一、思考

1、設計思路

在獲取短信驗證碼功能和圖形驗證碼還是有很多相似的地方,所以這里在設計獲取短信驗證的時候,將之前開發好的的圖形驗證碼進一步整合、抽象與重構。

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

但是它們在登陸的時候就有很大的不同了,對于圖形驗證碼而言驗證通過之前就走UsernamePasswordAuthenticationFilter 過濾器了開始校驗用戶名密碼了。

但對于短信登陸而言,確實也需要先現在短信驗證碼是否通過,但是一旦通過他是不走 UsernamePasswordAuthenticationFilter,而是通過其它方式查詢用戶信息來校驗

認證已經通過了。

這篇博客只寫獲取獲取短信驗證碼的功能,不寫通過短信驗證碼登陸的邏輯。

2、重構設計

這里才是最重要的,如何去設計和整合短信驗證碼和圖形驗證碼的代碼,是我們最應該思考的。如何將相似部分抽離出來,然后去實現不相同的部分。

整理后發現不同點主要在于

 1、獲取驗證碼。因為對于圖形驗證碼需要有個畫布,而短信驗證碼并不需要,所以它們可以實現同一個接口,來完成不同的邏輯。
 2、發送驗證碼。對于圖形驗證碼來講只要把驗證碼返給前端就可以,而短信驗證碼而言是通過第三方API將驗證碼發到我們的手機上。
    所以這里也可以通過實現統一接口來具體實現不同的方法。

相同部分我可以通過抽象類來完成實現,不同部分可以通過具體的實現類來實現。

Spring Security實現短信驗證碼功能的示例代碼

AbstractValidateCodeProcessorService 抽象類是用來實現兩種驗證碼可以抽離的部分。ImageCodeProcessorServiceImpl 和

SmsCodeProcessorServiceImpl方法是來實現兩種驗證碼不同的發送方式。

在簡單看下時序圖可能會更加明白點。

Spring Security實現短信驗證碼功能的示例代碼

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

 1、獲取驗證碼。2、將驗證碼存入session。3、將驗證碼信息通過短信或者圖形驗證碼發送出去。

首先講生成獲取驗證碼,這里有一個公共接口和兩個實現類

Spring Security實現短信驗證碼功能的示例代碼

對于保存驗證碼信息而言,可以在直接在 AbstractValidateCodeProcessorService抽象類來完成,都不需要去實現。

對發送驗證碼信息而言,只需要實現AbstractValidateCodeProcessorService抽象類的send發送驗證碼接口即可。

整個大致接口設計就是這樣,具體的可以通過代碼來展示。

二、代碼實現

1、驗證碼屬性

短信驗證碼和圖形驗證后包含屬性有 codeexpireTime,短信驗證碼只有這兩個屬性,而圖形驗證碼還多一個BufferedImage實例對象屬性,所以將共同屬性進行抽取

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

ValidateCode實體

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ValidateCode {
 
 private String code;
 
 private LocalDateTime expireTime;
 
 public boolean isExpired() {
 return LocalDateTime.now().isAfter(expireTime);
 }
 
}

對于圖形驗證碼而言,除了需要code和過期時間還需要圖片的畫布,所以繼承ValidateCode之后再寫自己屬性

ImageCode實體

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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;
 }
}

對于短信驗證碼而言,暫時不需要添加自己的屬性字段了。

SmsCode實體

?
1
2
3
4
5
6
7
8
9
10
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接口主要是完成 驗證碼的生成、保存與發送的完整流程,接口的主要設計如下所示:

ValidateCodeProcessorService接口

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface ValidateCodeProcessorService {
 
 /**
 * 因為現在有兩種驗證碼,所以存放到seesion的key不能一樣,所以前綴+具體type
 */
 String SESSION_KEY_PREFIX = "SESSION_KEY_FOR_CODE_";
 /**
 * 通過也是 type+CODE_PROCESSOR獲取對于的bean
 */
 String CODE_PROCESSOR = "CodeProcessorService";
 
 /**
 * 這個接口要做三件事
 * 1、獲取驗證碼。
 * 2、將驗證碼存入session
 * 3、將驗證碼信息通過短信或者圖形驗證碼發送出去。
 * (將spring-boot-security-study-03接口里的那三步進行里封裝)
 *
 */
 void processor(ServletWebRequest request) throws Exception;

由于圖片驗證碼和短信驗證碼的 生成和保存、發送等流程是固定的。所以這里寫一個抽象類來實現ValidateCodeProcessor接口,來實現相似部分。

AbstractValidateCodeProcessorService抽象類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@Component
public abstract class AbstractValidateCodeProcessorService<C> implements ValidateCodeProcessorService {
 
 private static final String SEPARATOR = "/code/";
 
 /**
 * 操作session的工具集
 */
 private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
 
 /**
 * 這是Spring的一個特性,就是在項目啟動的時候會自動收集系統中 {@link ValidateCodeGeneratorService} 接口的實現類對象
 *
 * 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);
 }
 
 /**
 * 生成驗證碼
 *
 */
 private C generate(ServletWebRequest request) {
 String type = getProcessorType(request);
 //這里 image+CodeGenerator = imgCodeGenerator 對應的就是ImageCodeGeneratorServiceService
 ValidateCodeGeneratorService validateCodeGenerator = validateCodeGeneratorMap.get(type.concat(ValidateCodeGeneratorService.CODE_GENERATOR));
 return (C) validateCodeGenerator.generate(request);
 }
 
 /**
 * 保存驗證碼到session中
 */
 private void save(ServletWebRequest request, C validateCode) {
 //這里也是封裝了一下
 sessionStrategy.setAttribute(request, SESSION_KEY_PREFIX.concat(getProcessorType(request).toUpperCase()), validateCode);
 }
 
 /**
 * 發送驗證碼 (只有發送驗證碼是需要自己去實現的。)
 */
 protected abstract void send(ServletWebRequest request, C validateCode) throws Exception;
 
 /**
 * 獲取請求URL中具體請求的驗證碼類型
 *
 */
 private String getProcessorType(ServletWebRequest request) {
 // 獲取URI分割后的第二個片段 (/code/image 通過/code/ 切割后就只剩下 image
 return StringUtils.substringAfter(request.getRequest().getRequestURI(), SEPARATOR);
 }
}

簡單說明

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

2、抽象類中實現了 ValidateCodeProcessor接口的processor方法,它主要是完成了驗證碼的創建、保存和發送的功能。

3、generate 方法根據傳入的不同泛型而生成了特定的驗證碼。

4、save 方法是將生成的驗證碼實例對象存入到session中,兩種驗證碼的存儲方式一致,只是有個key不一致,所以代碼也是通用的。

5、send 方法一個抽象方法,分別由ImageCodeProcessorService和SmsCodeProcessorService來具體實現,也是根據泛型來判斷具體調用哪一個具體的實現類的send方法。

3、驗證碼的生成接口

上面說過驗證的生成應該也是通過實現類

ValidateCodeGeneratorService

?
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface ValidateCodeGeneratorService {
 
 /**
 * 這個常量也是用來 type+CodeGeneratorService獲取對于bean對象
 */
 String CODE_GENERATOR = "CodeGeneratorService";
 
 /**
 * 生成驗證碼
 * 具體是圖片驗證碼 還是短信驗證碼就需要對應的實現類
 */
 ValidateCode generate(ServletWebRequest request);
}

圖形驗證碼具體實現類

mageCodeGeneratorServiceImpl

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@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();
 
 // 生成畫布
 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);
 }
 
 // 生成數字驗證碼
 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());
 }
 
 /**
 * 生成隨機背景條紋
 *
 * @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);
 }
}

短信驗證碼具體實現類

SmsCodeGeneratorServiceImpl

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Data
@Component("smsCodeGeneratorService")
public class SmsCodeGeneratorServiceImpl implements ValidateCodeGeneratorService {
 
 @Autowired
 private ValidateCodeProperties validateCodeProperties;
 
 
 @Override
 public SmsCode generate(ServletWebRequest request) {
 //生成隨機數
 String code = RandomStringUtils.randomNumeric(validateCodeProperties.getSms().getLength());
 return new SmsCode(code, validateCodeProperties.getSms().getExpireIn());
 }
}

4、驗證碼的發送邏輯類

獲取的實現類寫好了,我們在寫發送具體的發送實現類,發送類的實現類是實現AbstractValidateCodeProcessorService抽象類的。

圖片發送實現類

ImageCodeProcessorServiceImpl

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component("imageCodeProcessorService")
public class ImageCodeProcessorServiceImpl extends AbstractValidateCodeProcessorService<ImageCode> {
 
 private static final String FORMAT_NAME = "JPEG";
 
 /**
 * 發送圖形驗證碼,將其寫到相應中
 *
 * @param request ServletWebRequest實例對象
 * @param imageCode 驗證碼
 */
 @Override
 protected void send(ServletWebRequest request, ImageCode imageCode) throws Exception {
 ImageIO.write(imageCode.getImage(), FORMAT_NAME, request.getResponse().getOutputStream());
 }
}

短信發送具體實現類。這里只是后臺輸出就好了,實際中只要接入對于的SDK就可以了。

SmsCodeProcessorServiceImpl

?
1
2
3
4
5
6
7
8
9
10
11
@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 {
 //這里有一個參數也是前端需要傳來的 就是用戶的手機號
 String mobile = ServletRequestUtils.getRequiredStringParameter(request.getRequest(), SMS_CODE_PARAM_NAME);
 // 這里僅僅寫個打印,具體邏輯一般都是調用第三方接口發送短信
 System.out.println("向手機號為:" + mobile + "的用戶發送驗證碼:" + smsCode.getCode());
 }

整個大致就是這樣,我們再來測試一下。

三、測試

1、ValidateCodeController接口

獲取驗證碼接口

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@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類型錯誤 type={}",type);
 return;
 };
 
 //根據type獲取具體的實現類
 ValidateCodeProcessorService validateCodeProcessorService = validateCodeProcessorMap.get(type.concat(ValidateCodeProcessorService.CODE_PROCESSOR));
 validateCodeProcessorService.processor(new ServletWebRequest(request, response));
 
 }
 
}

2、獲得圖形驗證碼

Spring Security實現短信驗證碼功能的示例代碼

獲取成功

3、獲取短信驗證碼

獲取短信驗證碼需要多傳一個參數就是mobile 手機號碼

Spring Security實現短信驗證碼功能的示例代碼

因為這里發送短信沒有接第三方SDK,而是直接在控制臺輸出

Spring Security實現短信驗證碼功能的示例代碼

參考

1、Spring Security技術棧開發企業級認證與授權(JoJo)

這一套架構設計真的非常的好,代碼可讀性和可用性都非常高,以后如果要接第三個驗證碼只要實現發送和獲取的接口來自定義實a現就好了。很受啟發!

總結

到此這篇關于Spring Security實現短信驗證碼功能的示例代碼的文章就介紹到這了,更多相關Spring Security短信驗證碼內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://www.cnblogs.com/qdhxhz/p/12879466.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲香蕉视频 | 国产精品久久国产精品99 | 亚洲国产美女精品久久 | 日本xxx在线观看免费播放 | 国内精品一区二区在线观看 | 国产成人精品男人的天堂538 | 久青草国产在视频在线观看 | 五月天在线视频观看 | 日本孕妇大胆孕交 | 国产综合色在线视频区色吧图片 | 三年片韩国在线 | 精品久久久久中文字幕日本 | 久久热r在线视频精品 | 久久五月综合婷婷中文云霸高清 | 爆操| 日本一片免费观看高清完整 | 国产精品一区久久精品 | gayxxx视频 | 久久综合久久伊人 | 91香蕉国产在线观看免费永久 | 韩国情事伦理片观看地址 | 国产精品区牛牛影院 | 精品一区二区三区色花堂 | 国产麻豆精品免费视频 | 污网站免费观看在线高清 | 国产在线欧美日韩精品一区二区 | 四虎综合九九色九九综合色 | 互换娇妻爽文100系列小说 | 国产成人综合网亚洲欧美在线 | 亚洲人成伊人成综合网久久 | 国产成人精品视频午夜 | 亚洲精品国产成人中文 | 给我一个黄色网址 | 疯狂刺激的3p国产在线 | 亚洲色图图 | 欧美精品成人a多人在线观看 | 国产大乳美女挤奶视频 | 久热人人综合人人九九精品视频 | 亚洲国产综合精品 | 欧美高清在线精品一区二区不卡 | 天海翼最新 |