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