【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 {
    /**
     * <h1>驗證碼生成器</h1>
     */
    ImageCode generate(ServletWebRequest request);
    /**
     * <h1>短信驗證碼</h1>
     */
    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 {
//    /**
//     * <h1>驗證碼生成器</h1>
//     */
//    ImageCode generate(ServletWebRequest request);
//    /**
//     * <h1>短信驗證碼</h1>
//     */
//    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);
}


  • 創建實現類:
    在這裏插入圖片描述
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();
}


在這裏插入圖片描述

校驗短信驗證碼並登錄

在這裏插入圖片描述

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
     * <tt>unsuccessfulAuthentication()</tt> method will be called as if handling a failed
     * authentication.
     * <p>
     * Defaults to <tt>true</tt> 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;
    }
}




短信登錄配置

在這裏插入圖片描述

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

    }
}


  • 驗證碼過濾器配置:
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述

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

在這裏插入圖片描述

  • 直接點登錄
    在這裏插入圖片描述
    直接發送驗證碼:
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章