JavaWeb-SpringSecurity圖片驗證ImageCode

 

 

  系列博文html

  項目已上傳至guthub  傳送門java

  JavaWeb-SpringSecurity初認識  傳送門mysql

  JavaWeb-SpringSecurity在數據庫中查詢登錄用戶  傳送門git

  JavaWeb-SpringSecurity自定義登錄頁面  傳送門github

  JavaWeb-SpringSecurity實現需求-判斷請求是否以html結尾  傳送門web

  JavaWeb-SpringSecurity自定義登錄配置  傳送門spring

  JavaWeb-SpringSecurity圖片驗證ImageCode  傳送門sql

  JavaWeb-SpringSecurity記住我功能  傳送門數據庫

  JavaWeb-SpringSecurity使用短信驗證碼登錄  傳送門數組

 

 

  建立一個validate.code存放編寫驗證碼校驗代碼,建立ImageCode.class圖片驗證碼工具類

  

 

package com.Gary.GaryRESTful.validate.code;

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

//圖片驗證碼
public class ImageCode {
    
    //給前臺展現的圖片
    private BufferedImage image;
    
    //驗證碼
    private String code;
    
    //過時時間
    private LocalDateTime expireTime;
    
    public ImageCode(BufferedImage image,String code,int expreTime)
    {
        this.image = image;
        this.code = code;
        this.expireTime = LocalDateTime.now().plusSeconds(expreTime);
    }
    
    public ImageCode(BufferedImage image,String code,LocalDateTime expireTime)
    {
        this.image = image;
        this.code = code;
        this.expireTime = expireTime;
    }
    
    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;
    }
    
    
    
}
ImageCode.class

 

  在controller層中建立ValidateCodeController.class

  圖片驗證三步驟

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

 

package com.Gary.GaryRESTful.controller;

import java.io.IOException;

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

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 com.Gary.GaryRESTful.validate.code.ImageCode;

@RestController
public class ValidateCodeController {

    //操做Session
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    
    private String sessionKey = "session_key_image_code";
    
    @GetMapping("/code/image")
    public void createCode(HttpServletRequest request,HttpServletResponse response) throws IOException
    {
        //生成隨機數的圖片
        ImageCode imageCode = createImageCode();
        
        //將隨機數放入到session中
        sessionStrategy.setAttribute(new ServletWebRequest(request), sessionKey, imageCode);
        
        //將咱們生成的圖片寫到接口的響應的輸出流中
        ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
        
    }
    
    //生成圖片驗證碼(驗證碼,圖片,失效的時間)
    private ImageCode createImageCode()
    {
        return null;
    }
    
}
ValidateCodeController.class

 

  編寫驗證碼的邏輯

    一、定義驗證碼圖片長與寬

    二、獲取畫筆工具

    三、設置畫筆工具的顏色

    四、畫長方形

    五、改變畫筆工具的顏色

    六、畫干擾線

    七、改變畫筆工具的顏色

    八、畫數據

    九、關閉畫筆工具

 

  登錄頁面login.html

<form action="/loginPage" method="post">
    
        用戶名:
        <input type="text" name="username">
        <br>
        密碼:
        <input type="password" name="password">
        <br>
        圖片驗證碼:
        <input type="text" name="imageCode">
        <img src="/code/image">
        <input type="submit">
    
</form>

 

  ValidateCodeController.java中實現繪畫驗證碼方法createImageCode()

//生成圖片驗證碼(驗證碼,圖片,失效的時間)
    private ImageCode createImageCode()
    {
        //定義圖片的長和寬
        int width = 67;
        int height = 23;
        
        //生成一張圖片
        BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        
        //得到畫筆工具
        Graphics g = image.getGraphics();
        
        //畫一個矩形
        g.setColor(new Color(255,255,255));
        g.fillRect(0, 0, width, height);
        
        //畫干擾線
        g.setColor(new Color(0,0,0));
        //設置字體
        g.setFont(new Font("Time New Roman",Font.ITALIC,20));
        Random random = new Random();
        
        for(int i=0;i<20;i++)
        {
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            int x1 = random.nextInt(12);
            int y1 = random.nextInt(12);
            //(x,y)到(x+x1,y+y1)
            g.drawLine(x, y, x+x1, y+y1);
        }
        
        //畫數據
        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, 16);
        }
        
        g.dispose();
        
        //生成咱們本身的驗證碼數據(圖片,驗證碼,過時時間)
        return new ImageCode(image,sRand,60);
    }

 

package com.Gary.GaryRESTful.controller;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

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

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 com.Gary.GaryRESTful.validate.code.ImageCode;

@RestController
public class ValidateCodeController {

    //操做Session
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    
    private String sessionKey = "session_key_image_code";
    
    @GetMapping("/code/image")
    public void createCode(HttpServletRequest request,HttpServletResponse response) throws IOException
    {
        //生成隨機數的圖片
        ImageCode imageCode = createImageCode();
        
        //將隨機數放入到session中
        sessionStrategy.setAttribute(new ServletWebRequest(request), sessionKey, imageCode);
        
        //將咱們生成的圖片寫到接口的響應的輸出流中
        ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
        
    }
    
    //生成圖片驗證碼(驗證碼,圖片,失效的時間)
    private ImageCode createImageCode()
    {
        //定義圖片的長和寬
        int width = 67;
        int height = 23;
        
        //生成一張圖片
        BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        
        //得到畫筆工具
        Graphics g = image.getGraphics();
        
        //畫一個矩形
        g.setColor(new Color(255,255,255));
        g.fillRect(0, 0, width, height);
        
        //畫干擾線
        g.setColor(new Color(0,0,0));
        //設置字體
        g.setFont(new Font("Time New Roman",Font.ITALIC,20));
        Random random = new Random();
        
        for(int i=0;i<20;i++)
        {
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            int x1 = random.nextInt(12);
            int y1 = random.nextInt(12);
            //(x,y)到(x+x1,y+y1)
            g.drawLine(x, y, x+x1, y+y1);
        }
        
        //畫數據
        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, 16);
        }
        
        g.dispose();
        
        //生成咱們本身的驗證碼數據(圖片,驗證碼,過時時間)
        return new ImageCode(image,sRand,60);
    }
    
}
ValidateCodeController.java

 

  處理驗證碼攔截器,添加處理圖片驗證碼的Filter

  若是驗證碼登錄成功,則放行,不然進行攔截

  配置驗證碼攔截器ValidateCodeException.java,繼承AuthenticationException.java

//AuthenticationException是springsecurity中全部異常的基類
public class ValidateCodeException extends AuthenticationException{

    public ValidateCodeException(String msg) {
        super(msg);
        // TODO Auto-generated constructor stub
    }

}

 

  檢驗驗證碼是否正確方法

//校驗驗證碼是否正確
    private void validate(ServletWebRequest request) throws ServletRequestBindingException {
        
        // 得到session域中正確的驗證碼
        ImageCode codeInSession =  (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeController.sessionKey);
        // 得到request域中的用戶輸入的驗證碼imageCode
        String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode");

        // 判斷用戶輸入的驗證碼是否爲空
        if(StringUtils.isEmpty(codeInRequest))
        {
            throw new ValidateCodeException("驗證碼不能爲空");
        }
        
        // 判斷session域中的驗證碼是否爲null
        if(codeInSession == null)
        {
            throw new ValidateCodeException("驗證碼不存在");
        }
        
        // 判斷驗證碼是否過時
        if(codeInSession.isExpried())
        {
            throw new ValidateCodeException("驗證碼已過時");
        }
        
        // 校驗兩個驗證碼是否匹配
        if(!StringUtils.pathEquals(codeInSession.getCode(), codeInRequest))
        {
            //System.out.println("正確的:"+codeInSession.getCode());
            //System.out.println("用戶輸入的:"+codeInRequest);
            throw new ValidateCodeException("驗證碼不匹配");
        }
        
        // 將驗證碼從session域中移除
        sessionStrategy.removeAttribute(request, ValidateCodeController.sessionKey);
        
    }

 

  若是用戶驗證碼正確則放行用戶登錄步驟,當用戶登錄輸入正確輸入帳號密碼時,則給與用戶下一步操做,不然返回"壞的憑證"

  驗證碼若是沒有輸入正確,不會放行用戶登錄步驟

  SecurityConfig.java配置

//Web應用安全適配器
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    //告訴SpringSecurity密碼用什麼加密的
    @Bean
    public PasswordEncoder passwordEncoder()
    {
        return new BCryptPasswordEncoder();
    }
    
    @Autowired
    private LoginSuccessHandler loginSuccessHandler;

    @Autowired
    private LoginFailureHandler loginFailureHandler;
    
    protected void configure(HttpSecurity http) throws Exception{
        
        //聲明咱們本身寫的過濾器
        ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
        //給過濾器賦值
        validateCodeFilter.setAuthenticationFailureHandler(loginFailureHandler);
        
        //表單驗證(身份認證)
        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
            
            .formLogin()
            //自定義登錄頁面
            .loginPage("/require")
            //若是URL爲loginPage,則用SpringSecurity中自帶的過濾器去處理該請求
            .loginProcessingUrl("/loginPage")
            //配置登錄成功調用loginSuccessHandler
            .successHandler(loginSuccessHandler)
            //配置登錄失敗調用loginFailureHandler
            .failureHandler(loginFailureHandler)
            .and()
            //請求受權
            .authorizeRequests()
            //在訪問咱們的URL時,咱們是不須要省份認證,能夠當即訪問
            .antMatchers("/login.html","/require","/code/image").permitAll()
            //全部請求都被攔截,跳轉到(/login請求中)
            .anyRequest()
            //都須要咱們身份認證
            .authenticated()
            //SpringSecurity保護機制
            .and().csrf().disable();
    }

 

 

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>Gary登錄頁面</h1>
    <form action="/loginPage" method="post">
    
        用戶名:
        <input type="text" name="username">
        <br>
        密碼:
        <input type="password" name="password">
        <br>
        圖片驗證碼:
        <input type="text" name="imageCode">
        <img src="/code/image">
        <input type="submit">
    
    </form>

</body>
</html>
login.html

 

package com.Gary.GaryRESTful.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.Gary.GaryRESTful.filter.ValidateCodeFilter;
import com.Gary.GaryRESTful.handler.LoginFailureHandler;
import com.Gary.GaryRESTful.handler.LoginSuccessHandler;


//Web應用安全適配器
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    //告訴SpringSecurity密碼用什麼加密的
    @Bean
    public PasswordEncoder passwordEncoder()
    {
        return new BCryptPasswordEncoder();
    }
    
    @Autowired
    private LoginSuccessHandler loginSuccessHandler;

    @Autowired
    private LoginFailureHandler loginFailureHandler;
    
    protected void configure(HttpSecurity http) throws Exception{
        
        //聲明咱們本身寫的過濾器
        ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
        //給過濾器賦值
        validateCodeFilter.setAuthenticationFailureHandler(loginFailureHandler);
        
        //表單驗證(身份認證)
        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
            
            .formLogin()
            //自定義登錄頁面
            .loginPage("/require")
            //若是URL爲loginPage,則用SpringSecurity中自帶的過濾器去處理該請求
            .loginProcessingUrl("/loginPage")
            //配置登錄成功調用loginSuccessHandler
            .successHandler(loginSuccessHandler)
            //配置登錄失敗調用loginFailureHandler
            .failureHandler(loginFailureHandler)
            .and()
            //請求受權
            .authorizeRequests()
            //在訪問咱們的URL時,咱們是不須要省份認證,能夠當即訪問
            .antMatchers("/login.html","/require","/code/image").permitAll()
            //全部請求都被攔截,跳轉到(/login請求中)
            .anyRequest()
            //都須要咱們身份認證
            .authenticated()
            //SpringSecurity保護機制
            .and().csrf().disable();
    }
    
}
SecurityConfig.java

 

package com.Gary.GaryRESTful.validate.code;

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

//圖片驗證碼
public class ImageCode {
    
    //給前臺展現的圖片
    private BufferedImage image;
    
    //驗證碼
    private String code;
    
    //過時時間
    private LocalDateTime expireTime;
    
    public ImageCode(BufferedImage image,String code,int expreTime)
    {
        this.image = image;
        this.code = code;
        this.expireTime = LocalDateTime.now().plusSeconds(expreTime);
    }
    
    public ImageCode(BufferedImage image,String code,LocalDateTime expireTime)
    {
        this.image = image;
        this.code = code;
        this.expireTime = expireTime;
    }
    
    //判斷驗證碼是否過去
    public boolean isExpried()
    {
        //判斷當前時間是否在過時時間以後
        return LocalDateTime.now().isAfter(expireTime);
    }
    
    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;
    }
    
    
    
}
ImageCode.java

 

package com.Gary.GaryRESTful.controller;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

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

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 com.Gary.GaryRESTful.validate.code.ImageCode;

@RestController
public class ValidateCodeController {

    //操做Session
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    
    public SessionStrategy getSessionStrategy() {
        return sessionStrategy;
    }

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

    public static String getSessionKey() {
        return sessionKey;
    }

    public static void setSessionKey(String sessionKey) {
        ValidateCodeController.sessionKey = sessionKey;
    }

    public static String sessionKey = "session_key_image_code";
    
    @GetMapping("/code/image")
    public void createCode(HttpServletRequest request,HttpServletResponse response) throws IOException
    {
        //生成隨機數的圖片
        ImageCode imageCode = createImageCode();
        
        //將隨機數放入到session中
        sessionStrategy.setAttribute(new ServletWebRequest(request), sessionKey, imageCode);
        
        //將咱們生成的圖片寫到接口的響應的輸出流中
        ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
        
    }
    
    //生成圖片驗證碼(驗證碼,圖片,失效的時間)
    private ImageCode createImageCode()
    {
        //定義圖片的長和寬
        int width = 67;
        int height = 23;
        
        //生成一張圖片
        BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        
        //得到畫筆工具
        Graphics g = image.getGraphics();
        
        //畫一個矩形
        g.setColor(new Color(255,255,255));
        g.fillRect(0, 0, width, height);
        
        //畫干擾線
        g.setColor(new Color(0,0,0));
        //設置字體
        g.setFont(new Font("Time New Roman",Font.ITALIC,20));
        Random random = new Random();
        
        for(int i=0;i<20;i++)
        {
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            int x1 = random.nextInt(12);
            int y1 = random.nextInt(12);
            //(x,y)到(x+x1,y+y1)
            g.drawLine(x, y, x+x1, y+y1);
        }
        
        //畫數據
        String sRand = "";
        for(int i = 0;i<4;i++)
        {
            String rand =String.valueOf(random.nextInt(10));
            //System.out.println(rand);
            sRand += rand;
            //每個字都改變一下顏色
            g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));
            //畫每個數據
            g.drawString(rand, 13*i, 16);
        }
        
        g.dispose();
        
        //生成咱們本身的驗證碼數據(圖片,驗證碼,過時時間)
        return new ImageCode(image,sRand,60000);
    }
    
}
ValidateCodeController.java

 

package com.Gary.GaryRESTful.exception;

import org.springframework.security.core.AuthenticationException;


//AuthenticationException是springsecurity中全部異常的基類
public class ValidateCodeException extends AuthenticationException{

    public ValidateCodeException(String msg) {
        super(msg);
        // TODO Auto-generated constructor stub
    }

}
ValidateCodeException.java

 

package com.Gary.GaryRESTful.filter;

import java.io.IOException;

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

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.StringUtils;
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 com.Gary.GaryRESTful.controller.ValidateCodeController;
import com.Gary.GaryRESTful.exception.ValidateCodeException;
import com.Gary.GaryRESTful.validate.code.ImageCode;


//繼承OncePerRequestFilter,保證這個filter只會執行一次
public class ValidateCodeFilter extends OncePerRequestFilter{

    private AuthenticationFailureHandler authenticationFailureHandler;
    
    //操做session域的工具
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    
    //Filter執行
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        
        //loginPage
        if(StringUtils.pathEquals("/loginPage", request.getRequestURI()) && StringUtils.startsWithIgnoreCase(request.getMethod(), "post"))
        {
            //filter纔會執行
            try
            {
                validate(new ServletWebRequest(request));
            }
            catch(ValidateCodeException e) {
                //判處驗證碼的異常
                authenticationFailureHandler.onAuthenticationFailure(request,response,e);
                //一旦出現異常,咱們就不就不能繼續執行(應該放行),應該return
                return;
            }
            
        }
        
        //放行
        filterChain.doFilter(request, response);
        
    }

    //校驗驗證碼是否正確
    private void validate(ServletWebRequest request) throws ServletRequestBindingException {
        
        // 得到session域中正確的驗證碼
        ImageCode codeInSession =  (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeController.sessionKey);
        // 得到request域中的用戶輸入的驗證碼imageCode
        String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode");

        // 判斷用戶輸入的驗證碼是否爲空
        if(StringUtils.isEmpty(codeInRequest))
        {
            throw new ValidateCodeException("驗證碼不能爲空");
        }
        
        // 判斷session域中的驗證碼是否爲null
        if(codeInSession == null)
        {
            throw new ValidateCodeException("驗證碼不存在");
        }
        
        // 判斷驗證碼是否過時
        if(codeInSession.isExpried())
        {
            throw new ValidateCodeException("驗證碼已過時");
        }
        
        // 校驗兩個驗證碼是否匹配
        if(!StringUtils.pathEquals(codeInSession.getCode(), codeInRequest))
        {
            //System.out.println("正確的:"+codeInSession.getCode());
            //System.out.println("用戶輸入的:"+codeInRequest);
            throw new ValidateCodeException("驗證碼不匹配");
        }
        
        // 將驗證碼從session域中移除
        sessionStrategy.removeAttribute(request, ValidateCodeController.sessionKey);
        
    }

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

}
ValidateCodeFilter.java

 

 

優化:增長代碼的重用性

  在GaryRESTful.properties包下建立ValidateCodeProperties.class,用於管理配置圖片驗證碼的功能,再建立一個ImageCodeProperties.class,用於管理圖片驗證碼的生成

  優化圖片驗證碼的生成

  

  application.properties

#配置登錄方式
gary.security.loginType = JSON

server.port=8081

#驗證碼長度
gary.security.code.image.length = 6
#驗證碼圖片的長
gary.security.code.image.width = 100

 

 

 

#datasource
spring.datasource.url=jdbc:mysql:///springsecurity?serverTimezone=UTC&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.dricer-class-name=com.mysql.jdbc.Driver

#jpa
#打印出數據庫語句
spring.jpa.show-sql=true
#更新數據庫表
spring.jpa.hibernate.ddl-auto=update

#配置登錄方式
gary.security.loginType = JSON

server.port=8081

#驗證碼長度
gary.security.code.image.length = 6
#驗證碼圖片的長
gary.security.code.image.width = 100
application.properties

 

package com.Gary.GaryRESTful.controller;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

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.ServletRequestUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest;

import com.Gary.GaryRESTful.properties.GarySecurityProperties;
import com.Gary.GaryRESTful.validate.code.ImageCode;

@RestController
public class ValidateCodeController {
    
    //操做Session
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    
    public static String sessionKey = "session_key_image_code";
    
    @Autowired
    private GarySecurityProperties garySecurityProperties;
    
    public SessionStrategy getSessionStrategy() {
        return sessionStrategy;
    }

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

    public static String getSessionKey() {
        return sessionKey;
    }

    public static void setSessionKey(String sessionKey) {
        ValidateCodeController.sessionKey = sessionKey;
    }


    
    @GetMapping("/code/image")
    public void createCode(HttpServletRequest request,HttpServletResponse response) throws IOException
    {
        //生成隨機數的圖片
        ImageCode imageCode = createImageCode(request);
        
        //將隨機數放入到session中
        sessionStrategy.setAttribute(new ServletWebRequest(request), sessionKey, imageCode);
        
        //將咱們生成的圖片寫到接口的響應的輸出流中
        ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
        
    }
    
    //生成圖片驗證碼(驗證碼,圖片,失效的時間)
    private ImageCode createImageCode(HttpServletRequest request)
    {
        //定義圖片的長和寬
        int width = ServletRequestUtils.getIntParameter(request, "width", garySecurityProperties.getCode().getImage().getWidth());
        int height =  ServletRequestUtils.getIntParameter(request, "height", garySecurityProperties.getCode().getImage().getHeight());;
        
        //生成一張圖片
        BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        
        //得到畫筆工具
        Graphics g = image.getGraphics();
        
        //畫一個矩形
        g.setColor(new Color(255,255,255));
        g.fillRect(0, 0, width, height);
        
        //畫干擾線
        g.setColor(new Color(0,0,0));
        //設置字體
        g.setFont(new Font("Time New Roman",Font.ITALIC,20));
        Random random = new Random();
        
        for(int i=0;i<20;i++)
        {
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            int x1 = random.nextInt(12);
            int y1 = random.nextInt(12);
            //(x,y)到(x+x1,y+y1)
            g.drawLine(x, y, x+x1, y+y1);
        }
        
        //畫數據
        String sRand = "";
        for(int i = 0;i<garySecurityProperties.getCode().getImage().getLength();i++)
        {
            String rand =String.valueOf(random.nextInt(10));
            //System.out.println(rand);
            sRand += rand;
            //每個字都改變一下顏色
            g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));
            //畫每個數據
            g.drawString(rand, 13*i, 16);
        }
        
        g.dispose();
        
        //生成咱們本身的驗證碼數據(圖片,驗證碼,過時時間)
        return new ImageCode(image,sRand,garySecurityProperties.getCode().getImage().getExpireIn());
    }
    
}
ValidateCodeController.java

 

package com.Gary.GaryRESTful.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "gary.security")
public class GarySecurityProperties {
    
    //LoginType登錄的方式,默認爲JSON(restful設計風格)
    private LoginType loginType = LoginType.JSON;

    private ValidateCodeProperties code = new ValidateCodeProperties();
    
    public ValidateCodeProperties getCode() {
        return code;
    }

    public void setCode(ValidateCodeProperties code) {
        this.code = code;
    }

    public LoginType getLoginType() {
        return loginType;
    }

    public void setLoginType(LoginType loginType) {
        this.loginType = loginType;
    }
    
    
    
}
GarySecurityProperties.java

 

package com.Gary.GaryRESTful.properties;

public class ImageCodeProperties {

    private int width = 67;
    private int height = 23;
    
    private int length = 4;
    private int expireIn = 60;
    
    public int getWidth() {
        return width;
    }
    public void setWidth(int width) {
        this.width = width;
    }
    public int getHeight() {
        return height;
    }
    public void setHeight(int height) {
        this.height = height;
    }
    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;
    }
    
    
    
}
ImageCodeProperties.java

 

package com.Gary.GaryRESTful.properties;

public class ValidateCodeProperties {

    //圖片驗證碼
    private ImageCodeProperties image;

    public ImageCodeProperties getImage() {
        return image;
    }

    public void setImage(ImageCodeProperties image) {
        this.image = image;
    }
    
    
    
}
ValidateCodeProperties.java

 

  優化:配置Filter哪些請求須要攔截器執行

  ImageCodeProperties.java中添加一個String類型的url

  application.properties中添加一個gary.security.code.image.url,用來配置哪些須要咱們驗證碼的Filter

  在ValidateCodeFilter.java將用戶請求的url進行切割保存到Set集合當中,遍歷Set集合看是否有請求與咱們request中的url一致

//在garySecurityProperties.code.image.url    /user,/user/*
    //當Bean組裝好以後回調用這個函數
    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();
        
        //切割用戶配置的url
        String[] configUrls = StringUtils.split(garySecurityProperties.getCode().getImage().getUrl(), ",");
        
        //將數組放入urls中
        for(String configURL : configUrls)
        {
            urls.add(configURL);
        }
        //loginPage必定會用到這個Filter,因此咱們必須加上
        urls.add("/loginPage");
            
    }

 

  doFilterInternal()經過循環判斷是否有匹配的路徑

//Filter執行
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        
        //loginPage
        //if(StringUtils.pathEquals("/loginPage", request.getRequestURI()) && StringUtils.startsWithIgnoreCase(request.getMethod(), "post"))
        
        //判斷是否有匹配的路徑
        boolean action = false;
        
        //循環判斷
        for(String url :urls)
        {
            if(antPathMatcher.match(url, request.getRequestURI()))
            {
                action = true;
            }
        }
        
        if(action)
        {
            //filter纔會執行
            try
            {
                validate(new ServletWebRequest(request));
            }
            catch(ValidateCodeException e) {
                //判處驗證碼的異常
                authenticationFailureHandler.onAuthenticationFailure(request,response,e);
                //一旦出現異常,咱們就不就不能繼續執行(應該放行),應該return
                return;
            }
            
        }
        
        //放行
        filterChain.doFilter(request, response);
        
    }

 

 

#datasource
spring.datasource.url=jdbc:mysql:///springsecurity?serverTimezone=UTC&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.dricer-class-name=com.mysql.jdbc.Driver

#jpa
#打印出數據庫語句
spring.jpa.show-sql=true
#更新數據庫表
spring.jpa.hibernate.ddl-auto=update

#配置登錄方式
gary.security.loginType = JSON

server.port=8081

#驗證碼長度
gary.security.code.image.length = 6
#驗證碼圖片的長
gary.security.code.image.width = 100

#配置哪些須要咱們驗證碼的Filter
gary.security.code.image.url = /user,/user/*
application.properties

 

package com.Gary.GaryRESTful.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.Gary.GaryRESTful.filter.ValidateCodeFilter;
import com.Gary.GaryRESTful.handler.LoginFailureHandler;
import com.Gary.GaryRESTful.handler.LoginSuccessHandler;
import com.Gary.GaryRESTful.properties.GarySecurityProperties;


//Web應用安全適配器
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    //告訴SpringSecurity密碼用什麼加密的
    @Bean
    public PasswordEncoder passwordEncoder()
    {
        return new BCryptPasswordEncoder();
    }
    
    @Autowired
    private LoginSuccessHandler loginSuccessHandler;

    @Autowired
    private LoginFailureHandler loginFailureHandler;
     
    @Autowired
    private GarySecurityProperties garySecurityProperties;
    
    protected void configure(HttpSecurity http) throws Exception{
        
        //聲明咱們本身寫的過濾器
        ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
        //給過濾器賦值
        validateCodeFilter.setAuthenticationFailureHandler(loginFailureHandler);
        validateCodeFilter.setGarySecurityProperties(garySecurityProperties);
        validateCodeFilter.afterPropertiesSet();
        
        //表單驗證(身份認證)
        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
            
            .formLogin()
            //自定義登錄頁面
            .loginPage("/require")
            //若是URL爲loginPage,則用SpringSecurity中自帶的過濾器去處理該請求
            .loginProcessingUrl("/loginPage")
            //配置登錄成功調用loginSuccessHandler
            .successHandler(loginSuccessHandler)
            //配置登錄失敗調用loginFailureHandler
            .failureHandler(loginFailureHandler)
            .and()
            //請求受權
            .authorizeRequests()
            //在訪問咱們的URL時,咱們是不須要省份認證,能夠當即訪問
            .antMatchers("/login.html","/require","/code/image").permitAll()
            //全部請求都被攔截,跳轉到(/login請求中)
            .anyRequest()
            //都須要咱們身份認證
            .authenticated()
            //SpringSecurity保護機制
            .and().csrf().disable();
    }
    
}
SecurityConfig.java

 

package com.Gary.GaryRESTful.filter;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

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

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.util.StringUtils;
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 com.Gary.GaryRESTful.controller.ValidateCodeController;
import com.Gary.GaryRESTful.exception.ValidateCodeException;
import com.Gary.GaryRESTful.properties.GarySecurityProperties;
import com.Gary.GaryRESTful.validate.code.ImageCode;


//繼承OncePerRequestFilter,保證這個filter只會執行一次
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean{

    private AuthenticationFailureHandler authenticationFailureHandler;
    
    //操做session域的工具
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    
    private GarySecurityProperties garySecurityProperties;
    
    private Set<String> urls = new HashSet<String>();
    
    //爲了處理/user/*的形式 
    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    
    //在garySecurityProperties.code.image.url    /user,/user/*
    //當Bean組裝好以後回調用這個函數
    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();
        
        //切割用戶配置的url
        String[] configUrls = StringUtils.split(garySecurityProperties.getCode().getImage().getUrl(), ",");
        
        //將數組放入urls中
        for(String configURL : configUrls)
        {
            urls.add(configURL);
        }
        //loginPage必定會用到這個Filter,因此咱們必須加上
        urls.add("/loginPage");
            
    }
    
    
    public GarySecurityProperties getGarySecurityProperties() {
        return garySecurityProperties;
    }

    public void setGarySecurityProperties(GarySecurityProperties garySecurityProperties) {
        this.garySecurityProperties = garySecurityProperties;
    }

    //Filter執行
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        
        //loginPage
        //if(StringUtils.pathEquals("/loginPage", request.getRequestURI()) && StringUtils.startsWithIgnoreCase(request.getMethod(), "post"))
        
        //判斷是否有匹配的路徑
        boolean action = false;
        
        //循環判斷
        for(String url :urls)
        {
            if(antPathMatcher.match(url, request.getRequestURI()))
            {
                action = true;
            }
        }
        
        if(action)
        {
            //filter纔會執行
            try
            {
                validate(new ServletWebRequest(request));
            }
            catch(ValidateCodeException e) {
                //判處驗證碼的異常
                authenticationFailureHandler.onAuthenticationFailure(request,response,e);
                //一旦出現異常,咱們就不就不能繼續執行(應該放行),應該return
                return;
            }
            
        }
        
        //放行
        filterChain.doFilter(request, response);
        
    }

    //校驗驗證碼是否正確
    private void validate(ServletWebRequest request) throws ServletRequestBindingException {
        
        // 得到session域中正確的驗證碼
        ImageCode codeInSession =  (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeController.sessionKey);
        // 得到request域中的用戶輸入的驗證碼imageCode
        String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode");

        // 判斷用戶輸入的驗證碼是否爲空
        if(StringUtils.isEmpty(codeInRequest))
        {
            throw new ValidateCodeException("驗證碼不能爲空");
        }
        
        // 判斷session域中的驗證碼是否爲null
        if(codeInSession == null)
        {
            throw new ValidateCodeException("驗證碼不存在");
        }
        
        // 判斷驗證碼是否過時
        if(codeInSession.isExpried())
        {
            throw new ValidateCodeException("驗證碼已過時");
        }
        
        // 校驗兩個驗證碼是否匹配
        if(!StringUtils.pathEquals(codeInSession.getCode(), codeInRequest))
        {
            //System.out.println("正確的:"+codeInSession.getCode());
            //System.out.println("用戶輸入的:"+codeInRequest);
            throw new ValidateCodeException("驗證碼不匹配");
        }
        
        // 將驗證碼從session域中移除
        sessionStrategy.removeAttribute(request, ValidateCodeController.sessionKey);
        
    }

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

}
ValidateCodeFilter.java
相關文章
相關標籤/搜索