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 }

 

posted @ 2019-03-31 11:08  SpringCore  阅读(2412)  评论(0编辑  收藏  举报