配置類
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; } }