zoukankan      html  css  js  c++  java
  • SpringBoot之自定义验证码

    代码地址如下:
    http://www.demodashi.com/demo/14280.html

    项目介绍

    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
    Spring Security的核心功能就是对用户进行身份认证和授权。而他的认证机制就是通过Spring Security一系列的过滤器链,当一个请求来的时候,首先要通过过滤器链的校验,校验通过之后才会访问用户各种信息
    

    但是在进行一些登录,注册等操作的时候,往往还需要使用验证码来进行校验。本案例采用的面向接口编程,将自定义的验证码过滤器添加到Spring Security的过滤器链上来实现灵活的,可扩展,可重用,可配置的验证码验证。

    项目结构

    项目实现过程

    1. 首先,定义图片验证码实体类。用来封装验证码相关信息
    public class ImageCode{
    	
        private BufferedImage image;
    	//失效时间
        private LocalDateTime expireTime;
    	//验证码
        private String code;
    
        public ImageCode(BufferedImage image, String code, int expireIn){
            this.code = code;
            this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
            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;
        }
    
        public BufferedImage getImage() {
            return image;
        }
    
        public void setImage(BufferedImage image) {
            this.image = image;
        }
    
        public boolean isExpried() {
            return LocalDateTime.now().isAfter(expireTime);
        }
    }
    
    2.验证码属性配置。
    • 系统配置
     @ConfigurationProperties(prefix = "jcohy.security")
    public class SecurityProperties {
    
        /**
         * 验证码配置
         */
        private ValidateCodeProperties validateCode = new ValidateCodeProperties();
    
        public ValidateCodeProperties getValidateCode() {
            return validateCode;
        }
    
        public void setValidateCode(ValidateCodeProperties validateCode) {
            this.validateCode = validateCode;
        }
    }
    
    • 验证码配置。方便大家进行扩展。如要添加其他验证配置,添加其他配置属性类即可
    public class ValidateCodeProperties {
        /**
         * 图片验证码选项
         */
        private ImageCodeProperties imageCode = new ImageCodeProperties();
    
        public ImageCodeProperties getImageCode() {
            return imageCode;
        }
    
        public void setImageCode(ImageCodeProperties imageCode) {
            this.imageCode = imageCode;
        }
    }
    
    • 图片验证码配置
    public class ImageCodeProperties{
    
        private int length = 4;
    
        private int expireIn = 60;
        /**
         * 验证码的宽
         */
        private int width = 67;
    
        /**
         * 验证码的高
         */
        private int height = 23;
    
        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;
        }
    
        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;
        }
    }
    
    • 使配置生效
     @Configuration
    @EnableConfigurationProperties(SecurityProperties.class)
    public class PropertiesConfig {
    
    }
    
    3.关于验证码配置的说明
    • 验证码的配置分为三个级别,
    • 默认配置:当什么都不配置时,采用默认配置
    • 应用级配置:配置在application.properties文件中。例如:jcohy.security.validateCode.imageCode.width=100
    • 请求级配置:在请求中携带验证码相关参数。例如:"/code/image?width=100"。说明指定验证码图片宽度为100px。

    其优先级如下:请求级配置>应用级配置>默认配置。当前一个存在时,会覆盖后面的配置。

    4.好了,前面已经对验证码的基本属性和配置已经编写完成,接下来根据配置文件属性生成验证码图片。此处采用的是面向接口编程的方法,方便大家定制不同的验证码生成策略,多种方式验证。使用更高级的验证码策略等。我这里只提供了一种简单的实现。
    public interface ValidateCodeGenerator {
        /**
         * 图形验证码实现方法接口
         * @param request
         * @return
         */
        ImageCode generate(ServletWebRequest request);
    }
    
    public class ImageCodeGenerator implements ValidateCodeGenerator{
    
        /**
         * 系统配置
         */
        @Autowired
        private SecurityProperties securityProperties;
    
        @Override
        public ImageCode generate(ServletWebRequest request) {
         ....
    	 ....
    	 //绘制并生成图片验证码过程
    	 ....
    	 ....
        }
    
        public SecurityProperties getSecurityProperties() {
            return securityProperties;
        }
    
        public void setSecurityProperties(SecurityProperties securityProperties) {
            this.securityProperties = securityProperties;
        }
    }
    
    5.图片验证码生成的过程已经完成,接下来就是对验证码进行验证了。我们使用过滤器来进行验证
    @Component("validateCodeFilter")
    public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {
    
        /**
         * 验证码校验失败处理器
         */
        private AuthenticationFailureHandler authenticationFailureHandler;
    
        private SecurityProperties securityProperties;
    
        private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
       //需要进行验证的的请求地址。
        private Set<String> urls = new HashSet<>();
    
        /**
         * 验证请求url与配置的url是否匹配的工具类
         */
        private AntPathMatcher pathMatcher = new AntPathMatcher();
        
        @Override
        public void afterPropertiesSet() throws ServletException {
            super.afterPropertiesSet();
    		//“/authentication/form”为登录请求。
            urls.add("/authentication/form");
        }
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
                throws ServletException, IOException {
            boolean action = false;
            for(String url:urls){
                if(pathMatcher.match(url,request.getRequestURI())){
                    action = true;
                }
            }
    
            if(action){
                try{
                    validate(new ServletWebRequest(request));
                }catch (ValidateCodeException e){
                    authenticationFailureHandler.onAuthenticationFailure(request,response,e);
                    return;
                }
            }
            chain.doFilter(request, response);
    
        }
    
        private void validate(ServletWebRequest request) throws ServletRequestBindingException {
            ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request, UserController.SESSION_KEY);
            String codeInRequest;
            try {
                codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),
                        "imageCode");
            } catch (ServletRequestBindingException e) {
                throw new ValidateCodeException("获取验证码的值失败");
            }
    
            if (StringUtils.isBlank(codeInRequest)) {
                throw new ValidateCodeException("imageCode:验证码的值不能为空");
            }
    
            if (codeInSession == null) {
                throw new ValidateCodeException("imageCode:验证码不存在");
            }
    
            if (codeInSession.isExpried()) {
                sessionStrategy.removeAttribute(request,UserController.SESSION_KEY);
                throw new ValidateCodeException("imageCode: 验证码已过期");
            }
    
            if (!StringUtils.equals(codeInSession.getCode(), codeInRequest)) {
                throw new ValidateCodeException("imageCode:验证码不匹配");
            }
            sessionStrategy.removeAttribute(request,UserController.SESSION_KEY);
        }
    
        public AuthenticationFailureHandler getAuthenticationFailureHandler() {
            return authenticationFailureHandler;
        }
    
        public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
            this.authenticationFailureHandler = authenticationFailureHandler;
        }
    
        public SecurityProperties getSecurityProperties() {
            return securityProperties;
        }
    
        public void setSecurityProperties(SecurityProperties securityProperties) {
            this.securityProperties = securityProperties;
        }
    }
    
    • 将生成验证码的Bean加入到容器中
    @Configuration
    public class ValidateCodeBeanConfig {
    
        @Autowired
        private SecurityProperties securityProperties;
    	//条件匹配。此处可扩展其他的验证条件和相应生成器
        @Bean
        @ConditionalOnMissingBean(name = "imageCodeGenerator")
        public ValidateCodeGenerator imageCodeGenerator(){
            ImageCodeGenerator imageCodeGenerator = new ImageCodeGenerator();
            imageCodeGenerator.setSecurityProperties(securityProperties);
            return imageCodeGenerator;
        }
    }
    
    6。将过滤器添加到过滤器链上
    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        /**
         * 验证码校验失败处理器
         */
        @Autowired
        private AuthenticationFailureHandler authenticationFailureHandler;
    
        @Autowired
        private AuthenticationSuccessHandler JcohyAuthenticationSuccessHandler;
        /**
         * 系统配置信息
         */
        @Autowired
        private SecurityProperties securityProperties;
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
            validateCodeFilter.setAuthenticationFailureHandler(authenticationFailureHandler);
            validateCodeFilter.setSecurityProperties(securityProperties);
            validateCodeFilter.afterPropertiesSet();
            http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin()
                .loginPage("/signIn.html")
                .loginProcessingUrl("/authentication/form")
                .successHandler(JcohyAuthenticationSuccessHandler)
                .and()
                .authorizeRequests()
                .antMatchers("/signIn.html"
                ,"/code/image").permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .csrf().disable();
        }
    }
    

    项目运行

    • 本项目使用IntelliJ IDEA工具。使用maven构建。下载后导入到工具中可直接运行
    • 运行成功后访问http://localhost:8080,出现如下界面
    • 本项目主要侧重于验证码的生成和验证,用户部分比较简单,用户名随便写,密码是123456,想要修改密码可以在MyUserDetailService类中修改。
      SpringBoot之自定义验证码

    代码地址如下:
    http://www.demodashi.com/demo/14280.html

    注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

  • 相关阅读:
    编写C#程序的IDE
    MacBook Pro装Win7后喇叭没有声音
    MacBook Pro装Win7后喇叭没有声音
    MacBook Pro装Win7后喇叭没有声音
    MacBook Pro装Win7后喇叭没有声音
    Linux从入门到精通系列之NFS
    Linux从入门到精通系列之NFS
    Linux从入门到精通系列之NFS
    Docker之docker设置系统的环境变量
    mq put 消息到远程队列
  • 原文地址:https://www.cnblogs.com/demodashi/p/10474061.html
Copyright © 2011-2022 走看看