【Spring Security技術棧開發企業級認證與授權】----使用Spring Security開發基於表單的登錄(三)

前言

本篇博客主要分享:登錄模塊中基於圖片驗證碼的一些騷操作。


實現圖形驗證碼功能:

1.開發生成圖形驗證碼接口

  • 根據隨機數生成圖片
package com.zcw.security.core.validate.code;

import lombok.Data;

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

/**
 * @ClassName : ImageCode
 * @Description :生成圖片驗證碼
 * @Author : Zhaocunwei
 * @Date: 2020-06-22 13:24
 */
@Data
public class ImageCode {
    /**
     * 圖片根據隨機數生成
     */
    private BufferedImage image;
    /**
     * 隨機數,一般存到session中,
     * 用戶登錄時需要進行校驗
     */
    private String code;
    /**
     * 時間:設置過期時間
     */
    private LocalDateTime expireTime;

    public ImageCode(BufferedImage image,String code,LocalDateTime expireTime){
        this.image= image;
        this.code = code;
        this.expireTime = expireTime;
    }
    /**
     * 接收一個在多少秒之內過期
     */
    public ImageCode(BufferedImage image,String code,int expireIn){
        this.image = image;
        this.code = code;
        this.expireTime=LocalDateTime.now().plusSeconds(expireIn);
    }
}



  • 將隨機數存到session中
  • 在將生成的圖片寫到接口的響應中
package com.zcw.security.core.validate.code;

import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
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.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

/**
 * @ClassName : ValidateCodeController
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-22 13:31
 */
@RestController
public class ValidateCodeController {

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

    @GetMapping("/code/image")
    public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ImageCode imageCode = createImageCode(request);
        //- 將隨機數存到session中
        sessionStrategy.setAttribute(new ServletWebRequest(request),SESSION_KEY,imageCode);
        //在將生成的圖片寫到接口的響應中
        ImageIO.write(imageCode.getImage(),"JPEG",response.getOutputStream());
    }

    /**
     * 根據隨機數生成圖片,驗證碼
     * @param request
     * @return
     */
    private ImageCode createImageCode(HttpServletRequest request) {
        int width=67;
        int height=23;
        BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);

        Graphics g = image.getGraphics();
        Random random = new Random();
        g.setColor(getRandColor(200,250));
        g.fillRect(0,0,width,height);
        g.setFont(new Font("Times New Roman",Font.ITALIC,20));
        g.setColor(getRandColor(160,200));
        for(int i=0;i<155;i++){
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            int xl =random.nextInt(12);
            int yl =random.nextInt(12);
            g.drawLine(x,y,x+xl,y+yl);
        }
        String sRand="";
        for(int i=0;i<4;i++){
            String rand = String.valueOf(random.nextInt(10));
            sRand +=rand;
            g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));
            g.drawString(rand,13*i+6,16);
        }
        g.dispose();
        return new ImageCode(image,sRand,60);
    }
    /**
     * 生成隨機背景條紋
     */
    private Color getRandColor(int fc,int bc){
        Random random = new Random();
        if(fc>255){
            fc=255;
        }
        if(bc >255){
            bc =255;
        }
        int r =fc +random.nextInt(bc-fc);
        int g = fc+random.nextInt(bc-fc);
        int b = fc+random.nextInt(bc-fc);
        return new Color(r,g,b);
    }
}


  • 測試:
    在這裏插入圖片描述
    在這裏插入圖片描述

2.認證流程中加入圖形驗證碼校驗

在這裏插入圖片描述

  • 編寫針對驗證碼的異常處理類
package com.zcw.security.core.validate.code;

import org.springframework.security.core.AuthenticationException;
/**
 * @ClassName : ValidateCodeException
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-22 14:24
 */
public class ValidateCodeException extends AuthenticationException {
    public ValidateCodeException(String msg){
        super(msg);
    }
}


  • 修改javaBean

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

import lombok.Data;

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

/**
 * @ClassName : ImageCode
 * @Description :生成圖片驗證碼
 * @Author : Zhaocunwei
 * @Date: 2020-06-22 13:24
 */
public class ImageCode {
    /**
     * 圖片根據隨機數生成
     */
    private BufferedImage image;
    /**
     * 隨機數,一般存到session中,
     * 用戶登錄時需要進行校驗
     */
    private String code;
    /**
     * 時間:設置過期時間
     */
    private LocalDateTime expireTime;

    private boolean expried;

    public ImageCode(BufferedImage image,String code,LocalDateTime expireTime){
        this.image= image;
        this.code = code;
        this.expireTime = expireTime;
    }
    /**
     * 接收一個在多少秒之內過期
     */
    public ImageCode(BufferedImage image,String code,int expireIn){
        this.image = image;
        this.code = code;
        this.expireTime=LocalDateTime.now().plusSeconds(expireIn);
    }

    public BufferedImage getImage() {
        return image;
    }

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

    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.apache.commons.lang.StringUtils;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
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.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.awt.*;
import java.io.IOException;

/**
 * @ClassName : ValidateCodeFilter
 * @Description :實現我們的過濾器,每次只被調用一次
 * @Author : Zhaocunwei
 * @Date: 2020-06-22 14:03
 */
public class ValidateCodeFilter extends OncePerRequestFilter {
    private AuthenticationFailureHandler authenticationFailureHandler;
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse,
                                    FilterChain filterChain) throws ServletException, IOException {
        //過濾器只有在登錄的情況下生效,
        if(StringUtils.equals("/authentication/form",httpServletRequest.getRequestURI())
        && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(),"post")){
            try {
                validate(new ServletWebRequest(httpServletRequest));
            }catch (ValidateCodeException e){
                //自定義失敗處理器
                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,e);
            }
        }
        filterChain.doFilter(httpServletRequest,httpServletResponse);
    }

    /**
     * 從session裏面獲取參數,然後進行校驗
     * @param servletWebRequest
     */
    private void validate(ServletWebRequest request) throws ServletRequestBindingException {
        ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request,
                ValidateCodeController.SESSION_KEY);
        String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),"imageCode");
        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);
    }
}


在這裏插入圖片描述

  • 配置類:
    在這裏插入圖片描述
    在這裏插入圖片描述
package com.zcw.security.core.validate.code;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
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.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.awt.*;
import java.io.IOException;

/**
 * @ClassName : ValidateCodeFilter
 * @Description :實現我們的過濾器,每次只被調用一次
 * @Author : Zhaocunwei
 * @Date: 2020-06-22 14:03
 */
public class ValidateCodeFilter extends OncePerRequestFilter {
    private AuthenticationFailureHandler authenticationFailureHandler;
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse,
                                    FilterChain filterChain) throws ServletException, IOException {
        //過濾器只有在登錄的情況下生效,
        if(StringUtils.equals("/authentication/form",httpServletRequest.getRequestURI())
        && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(),"post")){
            try {
                validate(new ServletWebRequest(httpServletRequest));
            }catch (ValidateCodeException e){
                //自定義失敗處理器
                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,e);
            }
        }
        filterChain.doFilter(httpServletRequest,httpServletResponse);
    }

    /**
     * 從session裏面獲取參數,然後進行校驗
     * @param servletWebRequest
     */
    private void validate(ServletWebRequest request) throws ServletRequestBindingException {
        ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request,
                ValidateCodeController.SESSION_KEY);
        String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),"imageCode");
        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;
    }
}


在這裏插入圖片描述

  • 測試:
    在這裏插入圖片描述
  • 進行修改,不希望前端返回這麼多信息
    在這裏插入圖片描述
  • 修改後爲:
    在這裏插入圖片描述
  • 繼續修改我們的過濾器:
    在這裏插入圖片描述
  • 測試
    在這裏插入圖片描述
  • 直接點登陸:
    在這裏插入圖片描述

3.圖片驗證碼重構

驗證碼基本參數可配置

在這裏插入圖片描述

package com.zcw.security.core.properties;

import lombok.Data;

/**
 * @ClassName : IMageCodeProperties
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-22 17:45
 */
@Data
public class IMageCodeProperties {
    private int width=67;
    private int height=23;
    private int expireIn=60;
}


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


package com.zcw.security.core.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @ClassName : SecurityProperties
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-06-19 13:54
 */
@ConfigurationProperties(prefix = "zcw.security")
@Component
public class MySecurityProperties {
    private BrowserProperties browserProperties = new BrowserProperties();
    private ValidateCodeProperties validateCodeProperties = new ValidateCodeProperties();

    public BrowserProperties getBrowserProperties() {
        return browserProperties;
    }

    public void setBrowserProperties(BrowserProperties browserProperties) {
        this.browserProperties = browserProperties;
    }

    public ValidateCodeProperties getValidateCodeProperties() {
        return validateCodeProperties;
    }

    public void setValidateCodeProperties(ValidateCodeProperties validateCodeProperties) {
        this.validateCodeProperties = validateCodeProperties;
    }
}


我們項目中進行配置:
在這裏插入圖片描述
在這裏插入圖片描述

  • 修改驗證碼長度
    在這裏插入圖片描述
    在這裏插入圖片描述
    上面的長度英文單詞寫錯了,特改爲如下圖所示:
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述

驗證碼攔截的接口可配置

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述


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 ValidateCodeFilter 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()
                                .getImage().getUrl(),",");
        for(String configUrl : configUrls){
            urls.add(configUrl);
        }
        //添加我們之前配置的登錄請求的接口
        urls.add("/authentication/form");
    }

    @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 {
        ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request,
                ValidateCodeController.SESSION_KEY);
        String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),"imageCode");
        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;

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

public interface ValidateCodeGenerator {
    /**
     * <h1>驗證碼生成器</h1>
     */
    ImageCode generate(ServletWebRequest request);
}


實現類:
在這裏插入圖片描述
把上面的驗證碼生成邏輯移動到我們實現類中,


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

import com.zcw.security.core.properties.MySecurityProperties;
import org.springframework.beans.factory.annotation.Autowired;
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
 */
public class ImageCodeGenerator implements ValidateCodeGenerator {
    @Autowired
    private MySecurityProperties mySecurityProperties;
    @Override
    public ImageCode generate(ServletWebRequest request) {
        /**
         * 根據隨機數生成圖片,驗證碼
         * @param request
         * @return
         */
        private ImageCode createImageCode(ServletWebRequest request) {
            int width= ServletRequestUtils.getIntParameter(request.getRequest(),
                    "width",mySecurityProperties.getValidateCodeProperties()
                            .getImage().getWidth());
            int height=ServletRequestUtils.getIntParameter(request.getRequest(),
                    "height",mySecurityProperties.getValidateCodeProperties()
                            .getImage().getHeight());
            BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);

            Graphics g = image.getGraphics();
            Random random = new Random();
            g.setColor(getRandColor(200,250));
            g.fillRect(0,0,width,height);
            g.setFont(new Font("Times New Roman",Font.ITALIC,20));
            g.setColor(getRandColor(160,200));
            for(int i=0;i<155;i++){
                int x = random.nextInt(width);
                int y = random.nextInt(height);
                int xl =random.nextInt(12);
                int yl =random.nextInt(12);
                g.drawLine(x,y,x+xl,y+yl);
            }
            String sRand="";
            for(int i=0;i<mySecurityProperties.getValidateCodeProperties().getImage().getLength();i++){
                String rand = String.valueOf(random.nextInt(10));
                sRand +=rand;
                g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));
                g.drawString(rand,13*i+6,16);
            }
            g.dispose();
            return new ImageCode(image,
                    sRand,
                    mySecurityProperties.getValidateCodeProperties()
                            .getImage().getExpireIn());
        }

    }
    /**
     * 生成隨機背景條紋
     */
    private Color getRandColor(int fc,int bc){
        Random random = new Random();
        if(fc>255){
            fc=255;
        }
        if(bc >255){
            bc =255;
        }
        int r =fc +random.nextInt(bc-fc);
        int g = fc+random.nextInt(bc-fc);
        int b = fc+random.nextInt(bc-fc);
        return new Color(r,g,b);
    }
}


  • controller層方法:
package com.zcw.security.core.validate.code;

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

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

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

    @GetMapping("/code/image")
    public void createCode(ServletWebRequest request, HttpServletResponse response) throws IOException {
        ImageCode imageCode = validateCodeGenerator.generate(request);
        //- 將隨機數存到session中
        sessionStrategy.setAttribute(request,SESSION_KEY,imageCode);
        //在將生成的圖片寫到接口的響應中
        ImageIO.write(imageCode.getImage(),"JPEG",response.getOutputStream());
    }


}


  • 接口實現,變成可配置的
    在這裏插入圖片描述
package com.zcw.security.core.validate.code;

import com.zcw.security.core.properties.MySecurityProperties;
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;
    }
}


在這裏插入圖片描述

3.實現“記住我”功能

  • 記住我功能基本原理
    在這裏插入圖片描述
    在這裏插入圖片描述
  • 記住我功能具體實現
    在這裏插入圖片描述
  • TokenRepository
    用來連接數據庫
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
package com.zcw.security.browser;

import com.zcw.security.core.properties.MySecurityProperties;
import com.zcw.security.core.validate.code.ValidateCodeFilter;
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;
    @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);
        //調用初始化方法
        validateCodeFilter.afterPropertiesSet();
        //表單登錄
        http.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();//跨站攻擊被禁用
    }
}



在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

  • 查詢數據庫
    在這裏插入圖片描述
  • 重新啓動服務器:
    在這裏插入圖片描述
  • 我們不登錄,直接訪問user接口
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章