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