Spring Security构建Rest服务-0801-短信验证码发送

时间:2023-03-10 02:37:48
Spring Security构建Rest服务-0801-短信验证码发送

实现短信验证码登录

  开发短信验证码接口

  校验短信验证码并登录

短信验证码和图片验证码开发思路类似:

1,我们访问一个controller

2,在controller里调用短信验证码生成接口生成验证码

3,验证码存进session

4,从请求里获取手机号,调用短信发送服务商的接口,给手机号发送短信

主要代码:

1,短信验证码Controller:

package com.imooc.security.core.validate.code;

import java.io.IOException;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest; import com.imooc.security.core.properties.SecurityProperties;
import com.imooc.security.core.validate.code.sms.SmsCodeSender; /**
* 验证码Control
* ClassName: ValidateCodeController
* @Description: TODO
* @author lihaoyang
* @date 2018年3月1日
*/
@RestController
public class ValidateCodeController { public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE"; // @Autowired
// private SecurityProperties securityProperties; @Autowired
private ValidateCodeGenerator imageCodeGenerator;//图片验证码 @Autowired
private ValidateCodeGenerator smsCodeGenerator;//短信验证码 //获取session
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); @Autowired
private SmsCodeSender smsCodeSender; //短信验证码发送接口 /**
* 短信验证码
* @Description: TODO
* @param @param request
* @param @param response
* @param @throws IOException
* @return void
* @throws ServletRequestBindingException
* @throws
* @author lihaoyang
* @date 2018年3月7日
*/
@GetMapping("/verifycode/sms")
public void createSmsCode(HttpServletRequest request,HttpServletResponse response) throws Exception{ //调验证码生成接口方式
ValidateCode smsCode = smsCodeGenerator.generator(new ServletWebRequest(request));
sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY, smsCode);
//获取手机号
String mobile = ServletRequestUtils.getRequiredStringParameter(request, "mobile");
//发送短信验证码
smsCodeSender.send(mobile, smsCode.getCode());
} }

短信验证码生成实现类,实现验证码接口,验证码的长度和过期时间做成可配置的,灵活些:

package com.imooc.security.core.validate.code;

import org.apache.commons.lang.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest; import com.imooc.security.core.properties.SecurityProperties; /**
* 短信验证码生成类
* ClassName: ImageCodeGenerator
* @Description: TODO
* @author lihaoyang
* @date 2018年3月2日
*/
@Component("smsCodeGenerator")
public class SmsCodeGenerator implements ValidateCodeGenerator { @Autowired
private SecurityProperties securityProperties; @Override
public ValidateCode generator(ServletWebRequest request) {
//生成验证码,长度从配置读取
String code = RandomStringUtils.randomNumeric(securityProperties.getCode().getSms().getLength());
return new ValidateCode(code, securityProperties.getCode().getSms().getExpireIn());
} public SecurityProperties getSecurityProperties() {
return securityProperties;
} public void setSecurityProperties(SecurityProperties securityProperties) {
this.securityProperties = securityProperties;
} }

验证码类:只有code和过期时间即可

package com.imooc.security.core.validate.code;

import java.awt.image.BufferedImage;
import java.time.LocalDateTime;
import java.time.LocalTime; /**
* 短信验证码
* ClassName: ImageCode
* @Description: 验证码
* @author lihaoyang
* @date 2018年3月1日
*/
public class ValidateCode { private String code; private LocalDateTime expireTime;//过期时间点 /**
*
* <p>Description: </p>
* @param image
* @param code
* @param expireTn 多少秒过期
*/
public ValidateCode(String code, int expireTn) {
this.code = code;
//过期时间=当前时间+过期秒数
this.expireTime = LocalDateTime.now().plusSeconds(expireTn);
} public ValidateCode(String code, LocalDateTime expireTime) {
this.code = code;
this.expireTime = expireTime;
} /**
* 验证码是否过期
* @Description: 验证码是否过期
* @param @return true 过期,false 没过期
* @return boolean true 过期,false 没过期
* @throws
* @author lihaoyang
* @date 2018年3月2日
*/
public boolean isExpired(){
return LocalDateTime.now().isAfter(expireTime);
} public String getCode() {
return code;
} public void setCode(String code) {
this.code = code;
} public LocalDateTime getExpireTime() {
return expireTime;
} public void setExpireTime(LocalDateTime expireTime) {
this.expireTime = expireTime;
} }

验证码发送抽象成接口:

package com.imooc.security.core.validate.code.sms;

/**
* 短信验证码发送接口
* ClassName: SmsCodeSender
* @Description: TODO
* @author lihaoyang
* @date 2018年3月7日
*/
public interface SmsCodeSender { /**
* 发送验证码短信
* @Description: 短信发送
* @param @param mobile 接收验证码的手机号
* @param @param code 验证码
* @return void
* @throws
* @author lihaoyang
* @date 2018年3月7日
*/
void send(String mobile,String code);
}

提供一个默认实现:做成引用该模块可覆盖默认实现的,更灵活

package com.imooc.security.core.validate.code.sms;

/**
* 默认的短信验证码发送类
* ClassName: DefaultSmsCodeSender
* @Description: TODO
* @author lihaoyang
* @date 2018年3月7日
*/
public class DefaultSmsCodeSender implements SmsCodeSender{ @Override
public void send(String mobile, String code) {
System.err.println("向手机 :"+mobile+" 发送短信验证码 :"+code);
} }

配置验证码生成器为spring的bean,,如果spring容器有该SmsCodeSender 接口的实现就用,没有了再配置,即可覆盖

package com.imooc.security.core.validate.code;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import com.imooc.security.core.properties.SecurityProperties;
import com.imooc.security.core.validate.code.sms.DefaultSmsCodeSender;
import com.imooc.security.core.validate.code.sms.SmsCodeSender; /**
* 配置验证码生成接口ValidateCodeGenerator的实际实现类的Bean
* ClassName: ValidateCodeBeanConfig
* @Description:
* 配置验证码生成接口ValidateCodeGenerator的实际实现类的Bean
* 如图片验证码的实现、短信验证码的实现
* @author lihaoyang
* @date 2018年3月5日
*/
@Configuration
public class ValidateCodeBeanConfig { @Autowired
private SecurityProperties securityProperties; /**
* @Description:
* 配置图片验证码生成bean
* @ConditionalOnMissingBean注解意思是当spring容器不存在imageCodeGenerator时才给配置一个该bean
* 作用是使程序更具可扩展性,该配置类是配置在core模块,这就意味着,如果引用该模块的项目
* 如果有一个自己的实现,实现了ValidateCodeGenerator接口,定义了自己的实现,名字也叫imageCodeGenerator时,
* 就用应用级别的实现,没有的话就用这个默认实现。
* @param @return
* @return ValidateCodeGenerator
* @throws
* @author lihaoyang
* @date 2018年3月5日
*/
@Bean
@ConditionalOnMissingBean(name="imageCodeGenerator")
public ValidateCodeGenerator imageCodeGenerator(){
ImageCodeGenerator codeGenerator = new ImageCodeGenerator();
codeGenerator.setSecurityProperties(securityProperties);
return codeGenerator;
} /**
* 配置短信验证码生成bean
* @Description:
* @param @return
* @return SmsCodeSender
* @throws
* @author lihaoyang
* @date 2018年3月7日
*/
@Bean
@ConditionalOnMissingBean(SmsCodeSender.class)
public SmsCodeSender smsCodeSender(){
return new DefaultSmsCodeSender();
}
}

验证码长度、过期时间配置类 SmsCodeProperties:

package com.imooc.security.core.properties;

/**
* 短信验证码配置类
* ClassName: ImageCodeProperties
* @Description: 图片验证码配置类
* @author lihaoyang
* @date 2018年3月2日
*/
public class SmsCodeProperties { //验证码字符个数
private int length = 4;
//过期时间
private int expireIn = 60; private String url; //拦截的url public int getLength() {
return length;
} public void setLength(int length) {
this.length = length;
} public int getExpireIn() {
return expireIn;
} public void setExpireIn(int expireIn) {
this.expireIn = expireIn;
} public String getUrl() {
return url;
} public void setUrl(String url) {
this.url = url;
} }

登录表单:

短信 登录 <br>
<form action="/authentication/mobile" method="post">
<table>
<tr>
<td>手机号:</td>
<td><input type="text" name="mobile" value="13812349876"/></td>
<td></td>
</tr>
<tr>
<td>短信验证码:</td>
<td>
<input width="100" type="text" name="smsCode"/>
<a href="/verifycode/sms?mobile=13812349876">发送验证码</a>
</td>
<td></td>
</tr>
<tr>
<td>记住我</td>
<td><input type="checkbox" name="remember-me" value="true"/></td>
</tr>
<tr>
<td colspan="2" align="right"><button type="submit">登录</button></td>
</tr>
</table>
</form>

Spring Security构建Rest服务-0801-短信验证码发送

control打印:

Spring Security构建Rest服务-0801-短信验证码发送