SpringSecurity 增加Filter过滤器
配置类
package com.imooc.security.browser; import com.imooc.security.core.properties.SecurityProperties; import com.imooc.security.core.validate.code.ValidateCodeFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private AuthenticationSuccessHandler imoocAuthenticationSuccessHandler; @Autowired private AuthenticationFailureHandler imoocAuthenctiationFailureHandler; @Bean public PasswordEncoder passwordEncoder() { return new SCryptPasswordEncoder(); } @Autowired private SecurityProperties securityProperties; @Override public void configure(HttpSecurity http) throws Exception { // 验证码过滤器 ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter(); validateCodeFilter.setAuthenticationFailureHandler(imoocAuthenctiationFailureHandler); validateCodeFilter.setSecurityProperties(securityProperties); validateCodeFilter.afterPropertiesSet(); // 添加一个图片验证filter, 在UsernamePasswordAuthenticationFilter之前执行 http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // .httpBasic() // 默认方式 .formLogin() // 设置认证的登录方式 表单方式 .loginPage("/authentication/require") // 自定义登录页面 .loginProcessingUrl("/authentication/form") // 自定义表单url, 默认是login .successHandler(imoocAuthenticationSuccessHandler) // 不适用默认的认证成功处理器 .failureHandler(imoocAuthenctiationFailureHandler) // 登录失败处理器 // .failureForwardUrl("/authentication/require") // .failureUrl("/authentication/require") .and() .authorizeRequests() // 需要授权 // 当匹配到这个页面时,不需要授权 .antMatchers("/authentication/require", securityProperties.getBrowser().getLoginPage(), "/code/image").permitAll() .anyRequest() // 所有请求 .authenticated() .and() // 关闭csrf .csrf() .disable(); } }
Filter
package com.imooc.security.core.validate.code; import com.imooc.security.core.properties.SecurityProperties; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.social.connect.web.HttpSessionSessionStrategy; import org.springframework.social.connect.web.SessionStrategy; import org.springframework.util.AntPathMatcher; import org.springframework.web.bind.ServletRequestBindingException; import org.springframework.web.bind.ServletRequestUtils; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashSet; import java.util.Set; /** * OncePerRequestFilter 只会调用一次Filter */ public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean { private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); private Set<String> urls = new HashSet<>(); private SecurityProperties securityProperties; public void setSecurityProperties(SecurityProperties securityProperties) { this.securityProperties = securityProperties; } private AuthenticationFailureHandler authenticationFailureHandler; private AntPathMatcher pathMatcher = new AntPathMatcher(); // 加载完配置文件之后,获取所有需要使用验证码的url @Override public void afterPropertiesSet() throws ServletException { super.afterPropertiesSet(); String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getCode().getImage().getUrl(), ","); for (String url : configUrls) { urls.add(url); } urls.add("/authentication/form"); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 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; } } filterChain.doFilter(request, response); } public void validate(ServletWebRequest request) throws ServletRequestBindingException { ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeController.SESSION_KEY); String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode"); if (StringUtils.isBlank(codeInRequest)) { throw new ValidateCodeException( "验证码的值不能为空"); } if (codeInRequest == null) { throw new ValidateCodeException( "验证码不存在"); } if (codeInSession.isExpried()) { sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY); throw new ValidateCodeException( "验证码已过期"); } if (!StringUtils.equals(codeInSession.getCode(), codeInRequest)) { throw new ValidateCodeException( "验证码不匹配"); } sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY); } public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) { this.authenticationFailureHandler = authenticationFailureHandler; } }