zoukankan      html  css  js  c++  java
  • 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)、添加验证码

  • 相关阅读:
    使用IntelliJ IDEA配置Tomcat(入门)
    关于hive异常:Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStor
    完整过程解决 ERROR 1045 (28000): Access denied for user 'mysql'@'localhost' (using password: NO)
    Hadoop伪分布式配置
    Spark_scala_Maven项目创建
    Spark-WordCount
    tesseract-ocr的安装及使用
    解决selenium.common.exceptions.WebDriverException: Message: 'chromedriver' executable needs to be in P
    python第三方库资源
    kafka伪分布式配置
  • 原文地址:https://www.cnblogs.com/cq-yangzhou/p/12159759.html
Copyright © 2011-2022 走看看