springboot整合spring-Security实现验证码登录

参考地址:https://www.jianshu.com/p/9d08c767b33e

springboot整合spring-security实现简单的登录注销 的基础上进行开发。

1、添加生成验证码的控制器。

(1)、生成验证码

 1     /**
 2      * 引入 Security 配置属性类
 3      */
 4     @Autowired
 5     private SecurityProperties securityProperties;
 6 
 7 
 8     @Override
 9     public ImageCode createCode(HttpServletRequest request ) {
10         //如果请求中有 width 参数,则用请求中的,否则用 配置属性中的
11         int width = ServletRequestUtils.getIntParameter(request,"width",securityProperties.getWidth());
12         //高度(宽度)
13         int height = ServletRequestUtils.getIntParameter(request,"height",securityProperties.getHeight());
14         //图片验证码字符个数
15         int length = securityProperties.getLength();
16         //过期时间
17         int expireIn = securityProperties.getExpireIn();
18 
19         BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
20 
21         Graphics g = image.getGraphics();
22 
23         Random random = new Random();
24 
25         g.setColor(getRandColor(200, 250));
26         g.fillRect(0, 0, width, height);
27         g.setFont(new Font("Times New Roman", Font.ITALIC, 20));
28         g.setColor(getRandColor(160, 200));
29         for (int i = 0; i < 155; i++) {
30             int x = random.nextInt(width);
31             int y = random.nextInt(height);
32             int xl = random.nextInt(12);
33             int yl = random.nextInt(12);
34             g.drawLine(x, y, x + xl, y + yl);
35         }
36 
37         String sRand = "";
38         for (int i = 0; i < length; i++) {
39             String rand = String.valueOf(random.nextInt(10));
40             sRand += rand;
41             g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
42             g.drawString(rand, 13 * i + 6, 16);
43         }
44 
45         g.dispose();
46 
47         return new ImageCode(image, sRand, expireIn);
48     }
49 
50     /**
51      * 生成随机背景条纹
52      */
53     private Color getRandColor(int fc, int bc) {
54         Random random = new Random();
55         if (fc > 255) {
56             fc = 255;
57         }
58         if (bc > 255) {
59             bc = 255;
60         }
61         int r = fc + random.nextInt(bc - fc);
62         int g = fc + random.nextInt(bc - fc);
63         int b = fc + random.nextInt(bc - fc);
64         return new Color(r, g, b);
65     }
View Code

(2)、验证码控制器

 public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";

    @Autowired
    private ValidateCodeGenerator imageCodeGenerator;

    /**
     * Session 对象
     */
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

    @GetMapping("/code/image")
    public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ImageCode imageCode = imageCodeGenerator.createCode(request);
        //将随机数 放到Session中
        sessionStrategy.setAttribute(new ServletWebRequest(request),SESSION_KEY,imageCode);
        request.getSession().setAttribute(SESSION_KEY,imageCode);
        //写给response 响应
        response.setHeader("Cache-Control", "no-store, no-cache");
        response.setContentType("image/jpeg");
        ImageIO.write(imageCode.getImage(),"JPEG",response.getOutputStream());
    }

(3)、其它辅助类

 1 @Data
 2 public class ImageCode {
 3 
 4     /**
 5      * 图片
 6      */
 7     private BufferedImage image;
 8     /**
 9      * 随机数
10      */
11     private String code;
12     /**
13      * 过期时间
14      */
15     private LocalDateTime expireTime;
16 
17     public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {
18         this.image = image;
19         this.code = code;
20         this.expireTime = expireTime;
21     }
22     public ImageCode(BufferedImage image, String code, int  expireIn) {
23         this.image = image;
24         this.code = code;
25         //当前时间  加上  设置过期的时间
26         this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
27     }
28 
29     public boolean isExpried(){
30         //如果 过期时间 在 当前日期 之前,则验证码过期
31         return LocalDateTime.now().isAfter(expireTime);
32     }
33 }
View Code
 1 @ConfigurationProperties(prefix = "sso.security.code.image")
 2 @Component
 3 @Data
 4 public class SecurityProperties {
 5 
 6     /**
 7      * 验证码宽度
 8      */
 9     private int width = 67;
10     /**
11      * 高度
12      */
13     private int height = 23;
14     /**
15      * 长度(几个数字)
16      */
17     private int length = 4;
18     /**
19      * 过期时间
20      */
21     private int expireIn = 60;
22 
23     /**
24      * 需要图形验证码的 url
25      */
26     private String url;
27 }
View Code

(4)、验证

2、添加过滤器,进行验证码验证

 1 @Component
 2 @Slf4j
 3 public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {
 4 
 5     /**
 6      * 登录失败处理器
 7      */
 8     @Autowired
 9     private AuthenticationFailureHandler failureHandler;
10     /**
11      * Session 对象
12      */
13     private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
14 
15     /**
16      * 创建一个Set 集合 存放 需要验证码的 urls
17      */
18     private Set<String> urls = new HashSet<>();
19     /**
20      * spring的一个工具类:用来判断 两字符串 是否匹配
21      */
22     private AntPathMatcher pathMatcher = new AntPathMatcher();
23 
24     @Autowired
25     private SecurityProperties securityProperties;
26     /**
27      * 这个方法是 InitializingBean 接口下的一个方法, 在初始化配置完成后 运行此方法
28      */
29     @Override
30     public void afterPropertiesSet() throws ServletException {
31         super.afterPropertiesSet();
32         //将 application 配置中的 url 属性进行 切割
33         String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getUrl(), ",");
34         //添加到 Set 集合里
35         urls.addAll(Arrays.asList(configUrls));
36         //因为登录请求一定要有验证码 ,所以直接 add 到set 集合中
37         urls.add("/authentication/form");
38     }
39 
40     @Override
41     protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
42 
43         boolean action = false;
44         for (String url:urls){
45             //如果请求的url 和 配置中的url 相匹配
46             if (pathMatcher.match(url,httpServletRequest.getRequestURI())){
47                 action = true;
48             }
49         }
50 
51         //拦截请求
52         if (action){
53             logger.info("拦截成功"+httpServletRequest.getRequestURI());
54             //如果是登录请求
55             try {
56                 validate(new ServletWebRequest(httpServletRequest));
57             }catch (ValidateCodeException exception){
58                 //返回错误信息给 失败处理器
59                 failureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,exception);
60                 return;
61             }
62 
63         }
64         filterChain.doFilter(httpServletRequest,httpServletResponse);
65 
66     }
67     private void validate(ServletWebRequest request) throws ServletRequestBindingException {
68         //从session中取出 验证码
69         ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request,ValidateCodeController.SESSION_KEY);
70         //从request 请求中 取出 验证码
71         String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),"imageCode");
72 
73         if (StringUtils.isBlank(codeInRequest)){
74             logger.info("验证码不能为空");
75             throw new ValidateCodeException("验证码不能为空");
76         }
77         if (codeInSession == null){
78             logger.info("验证码不存在");
79             throw new ValidateCodeException("验证码不存在");
80         }
81         if (codeInSession.isExpried()){
82             logger.info("验证码已过期");
83             sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY);
84             throw new ValidateCodeException("验证码已过期");
85         }
86         if (!StringUtils.equals(codeInSession.getCode(),codeInRequest)){
87             logger.info("验证码不匹配"+"codeInSession:"+codeInSession.getCode() +", codeInRequest:"+codeInRequest);
88             throw new ValidateCodeException("验证码不匹配");
89         }
90         //把对应 的 session信息  删掉
91         sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY);
92     }
View Code

3、在核心配置BrowserSecurityConfig中添加过滤器配置

 1  @Autowired
 2     private ValidateCodeFilter validateCodeFilter;
 3 
 4     @Override
 5     protected void configure(HttpSecurity http) throws Exception {
 6         //在UsernamePasswordAuthenticationFilter 过滤器前 加一个过滤器 来搞验证码
 7         http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
 8                 //表单登录 方式
 9                 .formLogin()
10                 .loginPage("/authentication/require")
11                 //登录需要经过的url请求
12                 .loginProcessingUrl("/authentication/form")
13                 .passwordParameter("pwd")
14                 .usernameParameter("user")
15                 .successHandler(mySuccessHandler)
16                 .failureHandler(myFailHandler)
17                 .and()
18                 //请求授权
19                 .authorizeRequests()
20                 //不需要权限认证的url
21                 .antMatchers("/authentication/*","/code/image").permitAll()
22                 //任何请求
23                 .anyRequest()
24                 //需要身份认证
25                 .authenticated()
26                 .and()
27                 //关闭跨站请求防护
28                 .csrf().disable();
29         //默认注销地址:/logout
30         http.logout().
31                 //注销之后 跳转的页面
32                 logoutSuccessUrl("/authentication/require");
33     }
View Code

4、异常辅助类

public class ValidateCodeException extends AuthenticationException {
    public ValidateCodeException(String msg, Throwable t) {
        super(msg, t);
    }

    public ValidateCodeException(String msg) {
        super(msg);
    }
}

5、测试

(1)、不输入验证码

 (2)、添加验证码

posted @ 2020-01-07 09:39  炫舞风中  阅读(964)  评论(0编辑  收藏  举报