【Spring Security技术栈开发企业级认证与授权】----使用Spring Security开发基于表单的登录:短信接口开发(四)

前言

本篇博客主要是讲解,在表单登录时我们短信接口的验证及接口可插拔式方法


实现短信验证码登录

开发短信验证码接口

  • 创建javabean:

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

import java.awt.image.BufferedImage;
import java.time.LocalDateTime;

/**
 * @ClassName : ImageCode
 * @Description :生成图片验证码
 * @Author : Zhaocunwei
 * @Date: 2020-06-22 13:24
 */
public class SmsCode {

    /**
     * 随机数,一般存到session中,
     * 用户登录时需要进行校验
     */
    private String code;
    /**
     * 时间:设置过期时间
     */
    private LocalDateTime expireTime;

    private boolean expried;

    public SmsCode( String code, LocalDateTime expireTime){
        this.code = code;
        this.expireTime = expireTime;
    }
    /**
     * 接收一个在多少秒之内过期
     */
    public SmsCode( String code, int expireIn){

        this.code = code;
        this.expireTime=LocalDateTime.now().plusSeconds(expireIn);
    }
    

    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;
    }

    public boolean isExpried() {
        return LocalDateTime.now().isAfter(expireTime);
    }

    public void setExpried(boolean expried) {
        this.expried = expried;
    }
}


  • 接口
package com.zcw.security.core.validate.code;

import org.springframework.web.context.request.ServletWebRequest;

public interface ValidateCodeGenerator {
    /**
     * 

验证码生成器

*/
ImageCode generate(ServletWebRequest request); /** *

短信验证码

*/
SmsCode generateSmsCode(ServletWebRequest request); }

也可以优化上面的代码,把我们的短信验证码改为验证码,然后图片验证码集成验证码类:


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


import java.awt.image.BufferedImage;
import java.time.LocalDateTime;

/**
 * @ClassName : ImageCode
 * @Description :生成图片验证码
 * @Author : Zhaocunwei
 * @Date: 2020-06-22 13:24
 */
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,expireIn);
        this.image = image;
    }

    public BufferedImage getImage() {
        return image;
    }

    public void setImage(BufferedImage image) {
        this.image = image;
    }
}

  • 把之前SmsCode改为ValidateCode

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

import java.awt.image.BufferedImage;
import java.time.LocalDateTime;

/**
 * @ClassName : ImageCode
 * @Description :生成图片验证码
 * @Author : Zhaocunwei
 * @Date: 2020-06-22 13:24
 */
public class ValidateCode {

    /**
     * 随机数,一般存到session中,
     * 用户登录时需要进行校验
     */
    private String code;
    /**
     * 时间:设置过期时间
     */
    private LocalDateTime expireTime;

    private boolean expried;

    public ValidateCode( String code, LocalDateTime expireTime){
        this.code = code;
        this.expireTime = expireTime;
    }
    /**
     * 接收一个在多少秒之内过期
     */
    public ValidateCode( String code, int expireIn){

        this.code = code;
        this.expireTime=LocalDateTime.now().plusSeconds(expireIn);
    }


    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;
    }

    public boolean isExpried() {
        return LocalDateTime.now().isAfter(expireTime);
    }

    public void setExpried(boolean expried) {
        this.expried = expried;
    }
}



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

import org.springframework.web.context.request.ServletWebRequest;

public interface ValidateCodeGenerator {
//    /**
//     * 

验证码生成器

// */ // ImageCode generate(ServletWebRequest request); // /** // *

短信验证码

// */ // SmsCode generateSmsCode(ServletWebRequest request); ValidateCode generate(ServletWebRequest request); }
  • 封装短信验证码发送接口

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

public interface SmsCodeSender {
    void send(String mobile, String code);
}


  • 创建实现类:
    【Spring Security技术栈开发企业级认证与授权】----使用Spring Security开发基于表单的登录:短信接口开发(四)_第1张图片
package com.zcw.security.core.validate.code;

import com.zcw.security.core.properties.MySecurityProperties;
import com.zcw.security.core.validate.code.sms.DefaultSmsCodeSender;
import com.zcw.security.core.validate.code.sms.SmsCodeSender;
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;

/**
 * @ClassName : ValidateCodeBeanConfig
 * @Description : 配置类
 * @Author : Zhaocunwei
 * @Date: 2020-06-23 10:04
 */
@Configuration
public class ValidateCodeBeanConfig {
    @Autowired
    private MySecurityProperties mySecurityProperties;

    @Bean
    @ConditionalOnMissingBean(name ="imageCodeGenerator" )
    public ValidateCodeGenerator  imageCodeGenerator(){
        ImageCodeGenerator codeGenerator = new ImageCodeGenerator();
        codeGenerator.setMySecurityProperties(mySecurityProperties);
        return codeGenerator;
    }
    @Bean
    @ConditionalOnMissingBean(SmsCodeSender.class)
    public SmsCodeSender  smsCodeSender(){
        ImageCodeGenerator codeGenerator = new ImageCodeGenerator();
        codeGenerator.setMySecurityProperties(mySecurityProperties);
        return new DefaultSmsCodeSender();
    }
}


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

/**
 * @ClassName : DefaultSmsCodeSender
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-23 11:52
 */
public class DefaultSmsCodeSender implements SmsCodeSender {
    @Override
    public void send(String mobile, String code) {
        System.out.println("向手机"+mobile+"发送短信验证码"+code);
    }
}



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

import com.zcw.security.core.validate.code.sms.SmsCodeSender;
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 javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @ClassName : ValidateCodeController
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-22 13:31
 */
@RestController
public class ValidateCodeController {
    @Autowired
    private ValidateCodeGenerator  validateCodeGenerator;
    @Autowired
    private ValidateCodeGenerator smsCodeGenerator;
    @Autowired
    private SmsCodeSender  smsCodeSender;

    private SessionStrategy  sessionStrategy= new HttpSessionSessionStrategy();
    private static final String SESSION_KEY="SESSION_KEY_IMAGE_CODE";

    /**
     * 图片验证码
     * @param request
     * @param response
     * @throws IOException
     */
    @GetMapping("/code/image")
    public void createCode(ServletWebRequest request, HttpServletResponse response) throws IOException {
        ImageCode imageCode = (ImageCode) validateCodeGenerator.generate(request);
        //- 将随机数存到session中
        sessionStrategy.setAttribute(request,SESSION_KEY,imageCode);
        //在将生成的图片写到接口的响应中
        ImageIO.write(imageCode.getImage(),"JPEG",response.getOutputStream());
    }

    /**
     * 短信验证码
     * @param request
     * @param response
     * @throws IOException
     */
    @GetMapping("/code/sms")
    public void createSmsCode(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletRequestBindingException {
        ValidateCode smsCode =  smsCodeGenerator.generate(new ServletWebRequest(request));
        //- 将随机数存到session中
        sessionStrategy.setAttribute(new ServletWebRequest(request),SESSION_KEY,smsCode);
       //模拟调用短信服务商
        String mobile = ServletRequestUtils.getRequiredStringParameter(request,"mobile");
        smsCodeSender.send(mobile,smsCode.getCode());
    }
}


  • 编写短信验证码生成器
package com.zcw.security.core.validate.code;

import com.zcw.security.core.properties.MySecurityProperties;
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 java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;

/**
 * @ClassName : ImageCodeGenerator
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-23 10:00
 */
@Component("smsCodeGenerator")
public class SmsCodeGenerator implements ValidateCodeGenerator {
    @Autowired
    private MySecurityProperties mySecurityProperties;
    @Override
    public ValidateCode generate(ServletWebRequest request) {
        String code = RandomStringUtils.randomNumeric(
                mySecurityProperties.getValidateCodeProperties()
                .getSms().getLength()
        );
        return new ValidateCode(code,mySecurityProperties.getValidateCodeProperties()
        .getSms().getExpireIn());
    }

}


  • 编写短信验证码的配置:
package com.zcw.security.core.properties;

import lombok.Data;

/**
 * @ClassName : IMageCodeProperties
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-22 17:45
 */
@Data
public class SmsCodeProperties {

    private int expireIn=60;
    private int length=4;

    private String url;
}


package com.zcw.security.core.properties;

        import lombok.Data;

/**
 * @ClassName : ValidateCodeProperties
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-22 17:48
 */
@Data
public class ValidateCodeProperties {
    private IMageCodeProperties image = new IMageCodeProperties();
    private SmsCodeProperties sms = new SmsCodeProperties();
}


【Spring Security技术栈开发企业级认证与授权】----使用Spring Security开发基于表单的登录:短信接口开发(四)_第2张图片

校验短信验证码并登录

【Spring Security技术栈开发企业级认证与授权】----使用Spring Security开发基于表单的登录:短信接口开发(四)_第3张图片

package com.zcw.security.core.authentication.mobile;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;

import java.util.Collection;

/**
 * @ClassName : SmsCodeAuthenticationToken
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-23 13:12
 */
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {

    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;


    private Object principal;


    public SmsCodeAuthenticationToken(String  mobile) {
        super(null);
        this.principal = mobile;
        setAuthenticated(false);
    }


    public  SmsCodeAuthenticationToken(Object principal,
                                                    Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        super.setAuthenticated(true); // must use super, as we override
    }

    @Override
    public Object getCredentials() {
        return null;
    }

    @Override
    public Object getPrincipal() {
        return this.principal;
    }

    @Override
    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        if (isAuthenticated) {
            throw new IllegalArgumentException(
                    "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        }

        super.setAuthenticated(false);
    }

    @Override
    public void eraseCredentials() {
        super.eraseCredentials();
    }
}

  • SmsAuthenticationFilter
package com.zcw.security.core.authentication.mobile;

import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @ClassName : SmsCodeAuthenticationFilter
 * @Description :过滤器
 * @Author : Zhaocunwei
 * @Date: 2020-06-23 13:20
 */
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    // ~ Static fields/initializers
    // =====================================================================================

    public static final String ZCW_FORM_MOBILE_KEY = "mobile";

    private String mobileParameter = ZCW_FORM_MOBILE_KEY;
    private boolean postOnly = true;

    // ~ Constructors
    // ===================================================================================================

    public SmsCodeAuthenticationFilter() {
        super(new AntPathRequestMatcher("/authentication/mobile", "POST"));
    }

    // ~ Methods
    // ========================================================================================================

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }

        String mobile = obtainMobile(request);

        if (mobile == null) {
            mobile = "";
        }

        mobile = mobile.trim();

        SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(
                mobile);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);

        return this.getAuthenticationManager().authenticate(authRequest);
    }

    /**
     * 获取手机号
     */
    protected String obtainMobile(HttpServletRequest request) {
        return request.getParameter(mobileParameter);
    }

    /**
     * Provided so that subclasses may configure what is put into the authentication
     * request's details property.
     *
     * @param request that an authentication request is being created for
     * @param authRequest the authentication request object that should have its details
     * set
     */
    protected void setDetails(HttpServletRequest request,
                              SmsCodeAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

    /**
     * Sets the parameter name which will be used to obtain the username from the login
     * request.
     *
     * @param mobileParameter the parameter name. Defaults to "username".
     */
    public void setMobileParameter(String mobileParameter) {
        Assert.hasText(mobileParameter, "Mobile parameter must not be empty or null");
        this.mobileParameter = mobileParameter;
    }



    /**
     * Defines whether only HTTP POST requests will be allowed by this filter. If set to
     * true, and an authentication request is received which is not a POST request, an
     * exception will be raised immediately and authentication will not be attempted. The
     * unsuccessfulAuthentication() method will be called as if handling a failed
     * authentication.
     * 

* Defaults to true but may be overridden by subclasses. */ public void setPostOnly(boolean postOnly) { this.postOnly = postOnly; } public final String getMobileParameter() { return mobileParameter; } }

  • SmsAuthenticationProvider
package com.zcw.security.core.authentication.mobile;

import lombok.Data;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;

/**
 * @ClassName : SmsCodeAuthenticationProvider
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-23 13:31
 */
@Data
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
    /**
     * 获取用户信息
     */
    private UserDetailsService  userDetailsService;
    @Override
    public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        SmsCodeAuthenticationToken  authenticationToken = (SmsCodeAuthenticationToken) authentication;
       UserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal());
       if(user ==null){
           throw  new InternalAuthenticationServiceException("无法获取用户信息");
       }
       //设置已经认证的信息
        SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(
                user,user.getAuthorities()
        );
       authenticationResult.setDetails(authenticationToken.getDetails());
       return authenticationResult;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
    }
}


  • Authentication
package com.zcw.security.core.validate.code;
import com.zcw.security.core.properties.MySecurityProperties;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 * @ClassName : ValidateCodeFilter
 * @Description :实现我们的过滤器,每次只被调用一次
 * @Author : Zhaocunwei
 * @Date: 2020-06-22 14:03
 */
public class SmsCodeFilter extends OncePerRequestFilter implements InitializingBean {
    private AuthenticationFailureHandler authenticationFailureHandler;
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

    private Set<String> urls = new HashSet<>();
    private MySecurityProperties mySecurityProperties;
    private AntPathMatcher pathMatcher = new AntPathMatcher();

    @Override
    public void afterPropertiesSet() throws ServletException{
        super.afterPropertiesSet();
        String[] configUrls =StringUtils
                                .splitByWholeSeparatorPreserveAllTokens(mySecurityProperties.getValidateCodeProperties()
                                .getSms().getUrl(),",");
        for(String configUrl : configUrls){
            urls.add(configUrl);
        }
        //添加我们之前配置的登录请求的接口
        urls.add("/authentication/mobile");
    }

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse,
                                    FilterChain filterChain) throws ServletException, IOException {
        boolean action =false;
        for(String url :urls){
            //我们配置文件里面的url地址与前端传递过来的URL进行匹配
            if(pathMatcher.match(url, httpServletRequest.getRequestURI())){
                action =true;
            }
        }

        //过滤器只有在登录的情况下生效,
//        if(StringUtils.equals("/authentication/form",httpServletRequest.getRequestURI())
//         && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(),"post")){
        if(action){
            try {
                validate(new ServletWebRequest(httpServletRequest));
            }catch (ValidateCodeException e){
                //自定义失败处理器
                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,e);
                return;
            }
        }
        filterChain.doFilter(httpServletRequest,httpServletResponse);
    }

    /**
     * 从session里面获取参数,然后进行校验
     * @param servletWebRequest
     */
    private void validate(ServletWebRequest request) throws ServletRequestBindingException {
        ValidateCode codeInSession =  sessionStrategy.getAttribute(request,
                ValidateCodeController.SESSION_KEY);
        String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),"smsCode");
        if(StringUtils.isBlank(codeInRequest)){
            throw new ValidateCodeException("验证码的值不能为空");
        }
        if(codeInSession == null){
            throw new ValidateCodeException("验证码不存在");
        }
        if(codeInSession.isExpried()){
            sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY);
            throw new ValidateCodeException("验证码已过期");
        }
        if(!StringUtils.equals(codeInSession.getCode(),codeInRequest)){
            throw new ValidateCodeException("验证码不匹配");
        }
        sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY);
    }

    public AuthenticationFailureHandler getAuthenticationFailureHandler() {
        return authenticationFailureHandler;
    }

    public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
        this.authenticationFailureHandler = authenticationFailureHandler;
    }

    public SessionStrategy getSessionStrategy() {
        return sessionStrategy;
    }

    public void setSessionStrategy(SessionStrategy sessionStrategy) {
        this.sessionStrategy = sessionStrategy;
    }

    public Set<String> getUrls() {
        return urls;
    }

    public void setUrls(Set<String> urls) {
        this.urls = urls;
    }

    public MySecurityProperties getMySecurityProperties() {
        return mySecurityProperties;
    }

    public void setMySecurityProperties(MySecurityProperties mySecurityProperties) {
        this.mySecurityProperties = mySecurityProperties;
    }
}




短信登录配置

【Spring Security技术栈开发企业级认证与授权】----使用Spring Security开发基于表单的登录:短信接口开发(四)_第4张图片

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

import com.zcw.security.core.authentication.mobile.SmsCodeAuthenticationFilter;
import com.zcw.security.core.authentication.mobile.SmsCodeAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @ClassName : SmsCodeAuthenticationSecurityConfig
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-23 13:47
 */
public class SmsCodeAuthenticationSecurityConfig extends
        SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
    @Autowired
    private AuthenticationFailureHandler zcwAuthenticationFailureHandler;
    @Autowired
    private AuthenticationSuccessHandler zcwAuthenticationSuccessHandler;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public void configure(HttpSecurity http) throws Exception {
       //配置过滤器
        SmsCodeAuthenticationFilter  smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
        smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
        smsCodeAuthenticationFilter.setAuthenticationFailureHandler(zcwAuthenticationFailureHandler);
        smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(zcwAuthenticationSuccessHandler);


        SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
        smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService);

        http.authenticationProvider(smsCodeAuthenticationProvider)
            .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

    }
}


  • 验证码过滤器配置:
    【Spring Security技术栈开发企业级认证与授权】----使用Spring Security开发基于表单的登录:短信接口开发(四)_第5张图片
    【Spring Security技术栈开发企业级认证与授权】----使用Spring Security开发基于表单的登录:短信接口开发(四)_第6张图片

    【Spring Security技术栈开发企业级认证与授权】----使用Spring Security开发基于表单的登录:短信接口开发(四)_第7张图片

package com.zcw.security.browser;

import com.zcw.security.core.properties.MySecurityProperties;
import com.zcw.security.core.validate.code.SmsCodeFilter;
import com.zcw.security.core.validate.code.ValidateCodeFilter;
import com.zcw.security.core.validate.code.sms.SmsCodeAuthenticationSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import javax.xml.ws.soap.Addressing;

/**
 * @ClassName : BrowserSecurityConfig
 * @Description :适配器类
 * @Author : Zhaocunwei
 * @Date: 2020-06-18 17:43
 */
@Component
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService  userDetailsService;
    @Autowired
    private AuthenticationFailureHandler zcwAuthenticationFailureHandler;
    @Autowired
    private MySecurityProperties mySecurityProperties;
    @Autowired
    private AuthenticationSuccessHandler zcwAuthenticationSuccessHandler;
    @Autowired
    private DataSource dataSource;
    @Autowired
    private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    @Bean
    public PersistentTokenRepository  persistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        //启动时创建表,也可以直接进入源代码执行脚本,建议执行脚本,这个地方不要配置
        jdbcTokenRepository.setCreateTableOnStartup(true);
        return jdbcTokenRepository;
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
        validateCodeFilter.setAuthenticationFailureHandler(zcwAuthenticationFailureHandler);
        validateCodeFilter.setMySecurityProperties(mySecurityProperties);
        //短信验证码过滤器配置
        SmsCodeFilter smsCodeFilter = new SmsCodeFilter();
        smsCodeFilter.setAuthenticationFailureHandler(zcwAuthenticationFailureHandler);
        smsCodeFilter.setMySecurityProperties(mySecurityProperties);
        smsCodeFilter.afterPropertiesSet();

        //调用初始化方法
        validateCodeFilter.afterPropertiesSet();
        //表单登录
        http.addFilterBefore(smsCodeFilter,UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin()
                .loginPage("/authentication/require")
                .loginProcessingUrl("/authentication/form")
                .successHandler(zcwAuthenticationSuccessHandler)
                .failureHandler(zcwAuthenticationFailureHandler)
                //记住我的配置
                .and()
                .rememberMe()
                    .tokenRepository(persistentTokenRepository())
                //配置token 过期的秒数
                    .tokenValiditySeconds(mySecurityProperties
                            .getBrowserProperties()
                            .getRemeberMeSeconds())
                    .userDetailsService(userDetailsService)
                .and()
                //授权
                .authorizeRequests()
                //授权匹配器
                .antMatchers("/authentication/require",
                        mySecurityProperties.getBrowserProperties().getLoginPage(),
                        "/code/image").permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .csrf().disable()//跨站攻击被禁用
                        .apply(smsCodeAuthenticationSecurityConfig);
    }
}

【Spring Security技术栈开发企业级认证与授权】----使用Spring Security开发基于表单的登录:短信接口开发(四)_第8张图片

  • 直接点登录
    【Spring Security技术栈开发企业级认证与授权】----使用Spring Security开发基于表单的登录:短信接口开发(四)_第9张图片
    直接发送验证码:
    在这里插入图片描述
    【Spring Security技术栈开发企业级认证与授权】----使用Spring Security开发基于表单的登录:短信接口开发(四)_第10张图片
    【Spring Security技术栈开发企业级认证与授权】----使用Spring Security开发基于表单的登录:短信接口开发(四)_第11张图片

你可能感兴趣的:(Spring,#,Spring)