首页 > 其他 > 详细

验证码重构

时间:2020-01-30 11:09:01      阅读:65      评论:0      收藏:0      [点我收藏+]

技术分享图片

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;
    }
}

验证码重构

原文:https://www.cnblogs.com/fly-book/p/12242224.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!