zoukankan      html  css  js  c++  java
  • 验证码重构

    ValidateCodeProcessor

    				|
    

    AbstractValidateCodeProcessor -> ValidateCodeGenerator生成ValidateCode并发送

    ValidateCodeFilter->ValidateCodeProcessorHolder验证返回ValidateCodeProcessor并调用其验证方法

    /**
     * 校验码处理器,封装不同校验码的处理逻辑
     */
    public interface ValidateCodeProcessor {
        //验证码放入session时的前缀
        String SESSION_KEY_PREFIX = "SESSION_KEY_FOR_CODE_";
        //创建校验码 ServletWebRequest 已经包含request response
        void create(ServletWebRequest request) throws Exception;
        //校验验证码
        void validate(ServletWebRequest servletWebRequest);
    }
    
    
    public abstract class AbstractValidateCodeProcessor<C extends ValidateCode> implements ValidateCodeProcessor {
        private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
        /**
         * 收集系统中所有的 {@link ValidateCodeGenerator} 接口的实现。
         */
        @Autowired
        private Map<String, ValidateCodeGenerator> validateCodeGenerators;
    
        @Override
        public void create(ServletWebRequest request) throws Exception {
            C validateCode = generate(request);
            save(request,validateCode);
            send(request,validateCode);
        }
    
        /**
         * 保存验证码到session
         * @param request
         * @param validateCode
         */
        private void save(ServletWebRequest request, C validateCode){
            sessionStrategy.setAttribute(request,getSessionKey(),validateCode);
        }
    
        private String getSessionKey(){
            return SESSION_KEY_PREFIX + getValidateCodeType().toString().toUpperCase();
        }
    
        /**
         * 发送验证码有子类实现
         * @param request
         * @param validateCode
         * @throws Exception
         */
        protected abstract void send(ServletWebRequest request, C validateCode) throws Exception;
    
        //生成验证码
        @SuppressWarnings("unchecked")
        private C generate(ServletWebRequest request) {
            String type = getValidateCodeType().toString().toLowerCase();
            ValidateCodeGenerator validateCodeGenerator = validateCodeGenerators.get(type + "CodeGenerator");
            return (C) validateCodeGenerator.generate(request);
        }
    
        //根据请求的url获取校验码的类型
    //    private String getProcessorType(ServletWebRequest request){
    //        return StringUtils.substringAfter(request.getRequest().getRequestURI(),"/code/");
    //    }
    
        //根据实例名获取校验码的类型
        private ValidateCodeType getValidateCodeType() {
            String type = StringUtils.substringBefore(getClass().getSimpleName(), "CodeProcessor");
            return ValidateCodeType.valueOf(type.toUpperCase());
        }
        @SuppressWarnings("unchecked")
        @Override
        public void validate(ServletWebRequest request) {
            ValidateCodeType processorType = getValidateCodeType();
            String sessionKey = getSessionKey();
            C codeInSession = (C) sessionStrategy.getAttribute(request,sessionKey);
            String codeInRequest;
            try {
                codeInRequest = ServletRequestUtils.getRequiredStringParameter(request.getRequest(),
                        processorType.getParamNameOnValidate());
            }catch (ServletRequestBindingException e){
                throw new VerificationCodeException("获取验证码的值失败");
            }
            if (StringUtils.isEmpty(codeInRequest)){
                throw new VerificationCodeException("验证码的值不能为空");
            }
            if (codeInSession==null){
                throw new VerificationCodeException("验证码不存在");
            }
            if (codeInSession.isExpried()){
                sessionStrategy.removeAttribute(request,sessionKey);
                throw new VerificationCodeException("验证码已过期");
            }
            if (!codeInRequest.equals(codeInSession.getCode())){
                throw new VerificationCodeException("验证码不匹配");
            }
            sessionStrategy.removeAttribute(request,sessionKey);
        }
    }
    
    

    过滤器重构

    
    @Component("validateCodeFilter")
    public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {
        @Autowired
        private SecurityProperties securityProperties;
        @Autowired
        private AuthenticationFailureHandler flyAuthenticationFailureHandler;
        //存放所有需要校验验证码的url
        private Map<String, ValidateCodeType> urlMap = new HashMap<>();
        //验证url是否匹配的工具类
        private AntPathMatcher pathMatcher = new AntPathMatcher();
    
        @Autowired
        private ValidateCodeProcessorHolder validateCodeProcessorHolder;
    
        @Override
        public void afterPropertiesSet() throws ServletException {
            super.afterPropertiesSet();
            urlMap.put(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM,ValidateCodeType.IMAGE);
            urlMap.put(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, ValidateCodeType.SMS);
        }
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            ValidateCodeType type = this.getValidateCodeType(request);
            if (type!=null){
                try {
                    validateCodeProcessorHolder.findValidateCodeProcessor(type)
                            .validate(new ServletWebRequest(request, response));
                }catch (VerificationCodeException e){
                    flyAuthenticationFailureHandler.onAuthenticationFailure(request,response,e);
                    return;
                }
            }
            filterChain.doFilter(request,response);
        }
    
        private ValidateCodeType getValidateCodeType(HttpServletRequest request) {
            ValidateCodeType result = null;
            if (!"get".equalsIgnoreCase(request.getMethod())){
                Set<String> urls = urlMap.keySet();
                for (String url : urls) {
                    if (pathMatcher.match(url,request.getRequestURI())){
                        result = urlMap.get(url);
                    }
                }
            }
            return result;
        }
    }
    
    public enum ValidateCodeType {
        SMS{
            @Override
            public String getParamNameOnValidate() {
                return SecurityConstants.DEFAULT_PARAMETER_NAME_CODE_SMS;
            }
        },
        IMAGE{
            @Override
            public String getParamNameOnValidate() {
                return SecurityConstants.DEFAULT_PARAMETER_NAME_CODE_IMAGE;
            }
        };
        //校验时从请求中获取的参数的名字
        public abstract String getParamNameOnValidate();
    
    }
    
    
    /**
     * 校验码处理器
     */
    @Component
    public class ValidateCodeProcessorHolder {
        @Autowired
        private Map<String,ValidateCodeProcessor> validateCodeProcessors;
    
        public ValidateCodeProcessor findValidateCodeProcessor(ValidateCodeType type) {
            return findValidateCodeProcessor(type.toString().toLowerCase());
        }
    
        public ValidateCodeProcessor findValidateCodeProcessor(String type) {
            String name = type.toLowerCase() + ValidateCodeProcessor.class.getSimpleName();
            ValidateCodeProcessor processor = validateCodeProcessors.get(name);
            if (processor == null) {
                throw new VerificationCodeException("验证码处理器" + name + "不存在");
            }
            return processor;
        }
    }
    

    定义的常量

    
    public class SecurityConstants {
        /**
         * 默认的处理验证码的url前缀
         */
        public static final String DEFAULT_VALIDATE_CODE_URL_PREFIX = "/code";
        /**
         * 当请求需要身份认证时,默认跳转的url
         */
        public static final String DEFAULT_UNAUTHENTICATION_URL = "/authentication/require";
        /**
         * 默认的用户名密码登录请求处理url
         */
        public static final String DEFAULT_LOGIN_PROCESSING_URL_FORM = "/authentication/form";
        /**
         * 默认的手机验证码登录请求处理url
         */
        public static final String DEFAULT_LOGIN_PROCESSING_URL_MOBILE = "/authentication/mobile";
        /**
         * 默认登录页面
         */
        public static final String DEFAULT_LOGIN_PAGE_URL = "/fly-login.html";
        /**
         * 验证图片验证码时,http请求中默认的携带图片验证码信息的参数的名称
         */
        public static final String DEFAULT_PARAMETER_NAME_CODE_IMAGE = "imageCode";
        /**
         * 验证短信验证码时,http请求中默认的携带短信验证码信息的参数的名称
         */
        public static final String DEFAULT_PARAMETER_NAME_CODE_SMS = "smsCode";
        /**
         * 发送短信验证码 或 验证短信验证码时,传递手机号的参数的名称
         */
        public static final String DEFAULT_PARAMETER_NAME_MOBILE = "mobile";
        /**
         * session失效默认的跳转地址
         */
        public static final String DEFAULT_SESSION_INVALID_URL = "/session/invalid";
    }
    
    
    
    
    public class AbstractChannelSecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        private AuthenticationSuccessHandler flyAuthenticationSuccessHandler;
        @Autowired
        private AuthenticationFailureHandler flyAuthenticationFailureHandler;
        protected void applyPasswordAuthenticationConfig(HttpSecurity http) throws Exception {
            http.formLogin()
                    .loginPage(SecurityConstants.DEFAULT_LOGIN_PAGE_URL)
                    .loginProcessingUrl(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM)
                    .successHandler(flyAuthenticationSuccessHandler)
                    .failureHandler(flyAuthenticationFailureHandler);
        }
    }
    
    
    @Component("validateCodeSecurityConfig ")
    public class ValidateCodeSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
        @Autowired
        private Filter validateCodeFilter;
    
        //AbstractAuthenticationProcessingFilter
        @Override
        public void configure(HttpSecurity builder) throws Exception {
            builder.addFilterBefore(validateCodeFilter, AbstractPreAuthenticatedProcessingFilter.class);
        }
    }
    
    @Configuration
    public class BrowserSecurityConfig extends AbstractChannelSecurityConfig {
        @Autowired
        private SecurityProperties securityProperties;
    
        @Autowired
        private PasswordEncoder passwordEncoder;
        @Bean
        public PasswordEncoder setPasswordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Autowired
        private DataSource dataSource;
    
        @Autowired
        private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
        @Autowired
        private ValidateCodeSecurityConfig validateCodeSecurityConfig;
    
        @Bean
        public PersistentTokenRepository persistentTokenRepository(){
            JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
    //        tokenRepository.setCreateTableOnStartup(true);
            tokenRepository.setDataSource(dataSource);
            return tokenRepository;
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            applyPasswordAuthenticationConfig(http);
            http.apply(validateCodeSecurityConfig)
                    .and()
                .apply(smsCodeAuthenticationSecurityConfig)
                    .and()
                .rememberMe()
                    .tokenRepository(persistentTokenRepository())
                    .tokenValiditySeconds(securityProperties.getBrowser().getRememberMe())
                    .userDetailsService(userDetails())
                    .and()
                .authorizeRequests()
                    .antMatchers(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
                            SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE,
                            SecurityConstants.DEFAULT_LOGIN_PAGE_URL,
                            securityProperties.getBrowser().getLoginPage(),
                            SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*")
                            .permitAll()
                    .anyRequest().authenticated()
                    .and()
                .csrf().disable();
        }
        @Bean
        public UserDetailsService userDetails(){
            InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
            manager.createUser(User.withUsername("user").password(passwordEncoder.encode("123")).roles("USER").build());
            manager.createUser(User.withUsername("13312345678").password(passwordEncoder.encode("123")).roles("USER").build());
            return manager;
        }
    }
    
    
  • 相关阅读:
    [GIT] warning: LF will be replaced by CRLF问题解决方法
    最近想学的工具
    如何在webstrom中配置eslint和less
    Git常用命令
    windows下nginx安装、配置与使用
    关于 addEventListener 和 handleEvent 方法
    PHP中的魔术方法总结 :__construct, __destruct , __call, __callStatic,__get, __set, __isset, __unset , __sleep, __wakeup, __toStr
    Git使用详细教程
    9个永恒的UI设计原则
    常见浏览器兼容性问题与解决方案
  • 原文地址:https://www.cnblogs.com/fly-book/p/12242224.html
Copyright © 2011-2022 走看看