实现短信验证码登录

  开发短信验证码接口

  校验短信验证码并登录

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

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打印:

 

欢迎关注个人公众号一起交流学习: