实现短信验证码登录
开发短信验证码接口
校验短信验证码并登录
短信验证码和图片验证码开发思路类似:
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>
control打印: