SpringSecurity實現圖形驗證碼功能


⒈封裝驗證碼類

 1 package cn.coreqi.security.validate;
 2 
 3 import java.awt.image.BufferedImage;
 4 import java.time.LocalDateTime;
 5 
 6 public class ImageCode {
 7     private BufferedImage image;
 8     private String code;
 9     private LocalDateTime expireTime;   //過期時間
10 
11     public ImageCode(BufferedImage image, String code, Integer expireIn) {
12         this.image = image;
13         this.code = code;
14         this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
15     }
16 
17     public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {
18         this.image = image;
19         this.code = code;
20         this.expireTime = expireTime;
21     }
22 
23     public boolean isExpried(){
24         return LocalDateTime.now().isAfter(expireTime);
25     }
26 
27     public BufferedImage getImage() {
28         return image;
29     }
30 
31     public void setImage(BufferedImage image) {
32         this.image = image;
33     }
34 
35     public String getCode() {
36         return code;
37     }
38 
39     public void setCode(String code) {
40         this.code = code;
41     }
42 
43     public LocalDateTime getExpireTime() {
44         return expireTime;
45     }
46 
47     public void setExpireTime(LocalDateTime expireTime) {
48         this.expireTime = expireTime;
49     }
50 }

⒉封裝驗證碼控制器

 1 package cn.coreqi.security.controller;
 2 
 3 import cn.coreqi.security.validate.ImageCode;
 4 import com.sun.image.codec.jpeg.JPEGCodec;
 5 import com.sun.image.codec.jpeg.JPEGImageEncoder;
 6 import org.springframework.social.connect.web.HttpSessionSessionStrategy;
 7 import org.springframework.social.connect.web.SessionStrategy;
 8 import org.springframework.web.bind.annotation.GetMapping;
 9 import org.springframework.web.bind.annotation.RestController;
10 import org.springframework.web.context.request.ServletWebRequest;
11 
12 import javax.imageio.ImageIO;
13 import javax.servlet.http.HttpServletRequest;
14 import javax.servlet.http.HttpServletResponse;
15 import java.awt.*;
16 import java.awt.image.BufferedImage;
17 import java.io.IOException;
18 import java.util.Random;
19 
20 @RestController
21 public class ValidateController {
22 
23     public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";
24     private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
25 
26     @GetMapping("code/image")
27     public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
28         ImageCode imageCode = createImageCode(request);
29         sessionStrategy.setAttribute(new ServletWebRequest(request),SESSION_KEY,imageCode);
30         
31         response.setHeader("Pragma","No-cache");
32         response.setHeader("Cache-Control","no-cache");
33         //response.setDateHeader("Expires", 0);
34         
35         JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(response.getOutputStream());
36         encoder.encode(imageCode.getImage());
37         
38         //ImageIO.write(imageCode.getImage(),"JPEG",response.getOutputStream());    //當tomcat下temp文件夾不存在則"Can't create output stream"
39     }
40 
41     private ImageCode createImageCode(HttpServletRequest request) {
42         int width = 67;
43         int height = 23;
44         BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
45 
46         Graphics g = image.getGraphics();
47 
48         Random random = new Random();
49 
50         g.setColor(getRandColor(200,250));
51         g.fillRect(0,0,width,height);
52         g.setFont(new Font("Times New Roman",Font.ITALIC,20));
53         g.setColor(getRandColor(160,200));
54         for (int i = 0;i < 155; i++){
55             int x = random.nextInt(width);
56             int y = random.nextInt(height);
57             int xl = random.nextInt(12);
58             int yl = random.nextInt(12);
59             g.drawLine(x,y,x+xl,y+yl);
60         }
61         String sRand = "";
62         for(int i = 0;i < 4; i++){
63             String rand = String.valueOf(random.nextInt(10));
64             sRand += rand;
65             g.setColor(new Color(20 + random.nextInt(110),20 + random.nextInt(110),20 + random.nextInt(110)));
66             g.drawString(rand,13 * i + 6,16);
67         }
68         g.dispose();
69         return new ImageCode(image,sRand,60);
70     }
71 
72     /**
73      * 生成隨機背景條紋
74      * @param fc
75      * @param bc
76      * @return
77      */
78     private Color getRandColor(int fc, int bc) {
79         Random random = new Random();
80         if(fc > 255){
81             fc = 255;
82         }
83         if(bc > 255){
84             bc = 255;
85         }
86         int r = fc + random.nextInt(bc - fc);
87         int g = fc + random.nextInt(bc - fc);
88         int b = fc + random.nextInt(bc - fc);
89         return new Color(r,g,b);
90     }
91 }

 

⒊放行驗證碼的Rest地址

⒋表單添加驗證碼

1             <tr>
2                 <td>圖形驗證碼:</td>
3                 <td>
4                     <input type="text" name="imageCode">
5                     <img src="/code/image">
6                 </td>
7             </tr>

⒌聲明一個驗證碼異常,用於拋出特定的驗證碼異常

1 package cn.coreqi.security.validate;
2 
3 import org.springframework.security.core.AuthenticationException;
4 
5 public class ValidateCodeException extends AuthenticationException {
6     public ValidateCodeException(String msg) {
7         super(msg);
8     }
9 }

⒍創建一個過濾器,用於驗證請求中的驗證碼是否正確

 1 package cn.coreqi.security.Filter;
 2 
 3 import cn.coreqi.security.validate.ImageCode;
 4 import cn.coreqi.security.validate.ValidateCodeException;
 5 import org.springframework.security.web.authentication.AuthenticationFailureHandler;
 6 import org.springframework.social.connect.web.HttpSessionSessionStrategy;
 7 import org.springframework.social.connect.web.SessionStrategy;
 8 import org.springframework.util.StringUtils;
 9 import org.springframework.web.bind.ServletRequestBindingException;
10 import org.springframework.web.bind.ServletRequestUtils;
11 import org.springframework.web.context.request.ServletWebRequest;
12 import org.springframework.web.filter.OncePerRequestFilter;
13 import cn.coreqi.security.controller.*;
14 
15 import javax.servlet.FilterChain;
16 import javax.servlet.ServletException;
17 import javax.servlet.http.HttpServletRequest;
18 import javax.servlet.http.HttpServletResponse;
19 import java.io.IOException;
20 
21 public class ValidateCodeFilter extends OncePerRequestFilter {
22 
23     private AuthenticationFailureHandler authenticationFailureHandler;
24 
25     private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
26 
27     public AuthenticationFailureHandler getAuthenticationFailureHandler() {
28         return authenticationFailureHandler;
29     }
30 
31     public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
32         this.authenticationFailureHandler = authenticationFailureHandler;
33     }
34 
35     public SessionStrategy getSessionStrategy() {
36         return sessionStrategy;
37     }
38 
39     public void setSessionStrategy(SessionStrategy sessionStrategy) {
40         this.sessionStrategy = sessionStrategy;
41     }
42 
43     @Override
44     protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
45         if (httpServletRequest.equals("/authentication/form") && httpServletRequest.getMethod().equals("post")) {
46             try {
47                 validate(new ServletWebRequest(httpServletRequest));
48 
49             }catch (ValidateCodeException e){
50                 authenticationFailureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,e);
51                 return;
52             }
53         }
54         filterChain.doFilter(httpServletRequest,httpServletResponse);   //如果不是登錄請求,直接調用后面的過濾器鏈
55     }
56 
57     private void validate(ServletWebRequest request) throws ServletRequestBindingException {
58         ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request,ValidateController.SESSION_KEY);
59         String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),"imageCode");
60         if(!StringUtils.hasText(codeInRequest)){
61             throw new ValidateCodeException("驗證碼的值不能為空!");
62         }
63         if(codeInSession == null){
64             throw new ValidateCodeException("驗證碼不存在!");
65         }
66         if(codeInSession.isExpried()){
67             sessionStrategy.removeAttribute(request,ValidateController.SESSION_KEY);
68             throw new ValidateCodeException("驗證碼已過期!");
69         }
70         if(!codeInSession.getCode().equals(codeInRequest)){
71             throw new ValidateCodeException("驗證碼不正確!");
72         }
73         sessionStrategy.removeAttribute(request,ValidateController.SESSION_KEY);
74     }
75 }

⒎在SpringSecurity過濾器鏈中注冊我們的過濾器

 1 package cn.coreqi.security.config;
 2 
 3 import cn.coreqi.security.Filter.ValidateCodeFilter;
 4 import org.springframework.beans.factory.annotation.Autowired;
 5 import org.springframework.context.annotation.Bean;
 6 import org.springframework.context.annotation.Configuration;
 7 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 8 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 9 import org.springframework.security.crypto.password.NoOpPasswordEncoder;
10 import org.springframework.security.crypto.password.PasswordEncoder;
11 import org.springframework.security.web.authentication.AuthenticationFailureHandler;
12 import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
13 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
14 
15 @Configuration
16 public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
17 
18     @Autowired
19     private AuthenticationSuccessHandler coreqiAuthenticationSuccessHandler;
20 
21     @Autowired
22     private AuthenticationFailureHandler coreqiAuthenticationFailureHandler;
23 
24     @Bean
25     public PasswordEncoder passwordEncoder(){
26         return NoOpPasswordEncoder.getInstance();
27     }
28 
29     @Override
30     protected void configure(HttpSecurity http) throws Exception {
31         ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
32         validateCodeFilter.setAuthenticationFailureHandler(coreqiAuthenticationFailureHandler);
33 
34         //http.httpBasic()    //httpBasic登錄 BasicAuthenticationFilter
35         http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)    //加載用戶名密碼過濾器的前面
36                 .formLogin()    //表單登錄 UsernamePasswordAuthenticationFilter
37                 .loginPage("/coreqi-signIn.html")  //指定登錄頁面
38                 //.loginPage("/authentication/require")
39                 .loginProcessingUrl("/authentication/form") //指定表單提交的地址用於替換UsernamePasswordAuthenticationFilter默認的提交地址
40                 .successHandler(coreqiAuthenticationSuccessHandler) //登錄成功以后要用我們自定義的登錄成功處理器,不用Spring默認的。
41                 .failureHandler(coreqiAuthenticationFailureHandler) //自己體會把
42                 .and()
43                 .authorizeRequests()    //對授權請求進行配置
44                 .antMatchers("/coreqi-signIn.html","/code/image").permitAll() //指定登錄頁面不需要身份認證
45                 .anyRequest().authenticated()  //任何請求都需要身份認證
46                 .and().csrf().disable();    //禁用CSRF
47             //FilterSecurityInterceptor 整個SpringSecurity過濾器鏈的最后一環
48     }
49 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM