自定义Filter及Token等工具类准备:
/**
* 仿写UsernamePasswordAuthenticationFilter
*
* @author zhaohaibin
*/
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final String DEMO_FORM_MOBILE_KEY = SecurityConstants.DEFAULT_PARAMETER_NAME_MOBILE;
private String mobileParameter = DEMO_FORM_MOBILE_KEY;
/**
* 只处理post请求
*/
private boolean postOnly = true;
public SmsCodeAuthenticationFilter() {
super(new AntPathRequestMatcher("/authentication/mobile", "POST"));
}
/**
* 认证流程
*
* @param request
* @param response
* @return
* @throws AuthenticationException
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
} else {
String mobile = this.obtainMobile(request);
if (mobile == null) {
mobile = "";
}
mobile = mobile.trim();
SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);
this.setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
/**
* 获取手机号
*
* @param request
* @return
*/
@Nullable
protected String obtainMobile(HttpServletRequest request) {
return request.getParameter(this.mobileParameter);
}
protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
}
public void setMobileParameter(String usernameParameter) {
Assert.hasText(usernameParameter, "Mobile parameter must not be empty or null");
this.mobileParameter = usernameParameter;
}
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
public final String getMobileParameter() {
return this.mobileParameter;
}
}
/**
* 仿写UsernamePasswordAuthenticationToken
*
* @author zhaohaibin
*/
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final Object principal;
public SmsCodeAuthenticationToken(String mobile) {
super((Collection) null);
this.principal = mobile;
this.setAuthenticated(false);
}
public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getPrincipal() {
return this.principal;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
} else {
super.setAuthenticated(false);
}
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
}
}
/**
* @author zhaohaibin
*/
@Data
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
private UserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
UserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal());
if (null == user) {
throw new InternalAuthenticationServiceException("无法获取用户信息");
}
SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user, user.getAuthorities());
authenticationResult.setDetails(authenticationToken.getDetails());
return authenticationResult;
}
@Override
public boolean supports(Class<?> aClass) {
return SmsCodeAuthenticationToken.class.isAssignableFrom(aClass);
}
}
/**
* 短信验证码认证配置
*
* @author zhaohaibin
*/
@Component
public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
@Autowired
private AuthenticationSuccessHandler demoAuthenticationSuccessHandler;
@Autowired
private AuthenticationFailureHandler demoAuthenticationFailureHandler;
@Autowired
private ISystemUserService userDetailsService;
@Override
public void configure(HttpSecurity httpSecurity) throws Exception {
SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
smsCodeAuthenticationFilter.setAuthenticationManager(httpSecurity.getSharedObject(AuthenticationManager.class));
smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(demoAuthenticationSuccessHandler);
smsCodeAuthenticationFilter.setAuthenticationFailureHandler(demoAuthenticationFailureHandler);
SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService);
httpSecurity.authenticationProvider(smsCodeAuthenticationProvider)
.addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
}
1.新建短信验证码属性配置类:
/**
* 短信验证码属性配置
*
* @author zhaohaibin
*/
@Data
public class SmsCodeProperties {
private int length = 6;
/**
* 有效期
*/
private int expireIn = 60;
/**
* 需要图片验证码的url
*/
private String url;
}
2.更新图片验证码属性配置类ImageCodeProperties:
/**
* 图片验证码属性配置
*
* @author zhaohaibin
*/
@Data
public class ImageCodeProperties extends SmsCodeProperties{
public ImageCodeProperties(){
setLength(4);
}
private int width = 67;
private int height = 23;
}
3.新建短信验证码生成逻辑:
/**
* 验证码生成逻辑实现类-短信验证码
*
* @author zhaohaibin
*/
@Data
@Component("smsValidateCodeGenerator")
public class SmsValidateCodeGenerator implements ValidateCodeGenerator {
@Autowired
private SecurityProperties securityProperties;
@Override
public ValidateCode generate(ServletWebRequest request) {
String code = RandomStringUtils.randomNumeric(securityProperties.getCode().getSms().getLength());
return new ValidateCode(code, securityProperties.getCode().getSms().getExpireIn());
}
}
4.新建短信验证码发送接口及默认实现,更新ValidateCodeBeanConfig:
/**
* 短信验证码发送接口
*
* @author zhaohaibin
*/
public interface SmsCodeSender {
/**
* 发送抽象方法
* @param mobile
* @param code
*/
void send(String mobile, String code);
}
/**
* 短信验证码发送默认实现
*
* @author zhaohaibin
*/
@Slf4j
public class DefaultSmsCodeSender implements SmsCodeSender {
@Override
public void send(String mobile, String code) {
log.info("向手机【" + mobile + "】发送验证:" + code);
}
}
/**
* 生成器实现方式可配置
* 效果同@Component,但是可以配置@ConditionalOnMissingBean,这样会优先寻找匹配name的生成实现方法
*
* @author zhaohaibin
*/
@Configuration
public class ValidateCodeBeanConfig {
@Autowired
private SecurityProperties securityProperties;
/**
* 图片验证码默认生成器
*
* @return
*/
@Bean
@ConditionalOnMissingBean(name = "imageValidateCodeGenerator")
public ValidateCodeGenerator imageValidateCodeGenerator() {
ImageValidateCodeGenerator codeGenerator = new ImageValidateCodeGenerator();
codeGenerator.setSecurityProperties(securityProperties);
return codeGenerator;
}
/**
* 短信验证码默认发送实现
*
* @return
*/
@Bean
@ConditionalOnMissingBean(SmsCodeSender.class)
public SmsCodeSender smsCodeSender() {
return new DefaultSmsCodeSender();
}
}
5.新建短信验证码处理类SmsValidateCodeProcessor及调用控制器ValidateCodeController更新:
/**
* 短信验证码处理器
*
* @author zhaohaibin
*/
@Component("smsValidateCodeProcessor")
public class SmsValidateCodeProcessor extends AbstractValidateCodeProcessor<ValidateCode> {
/**
* 短信验证码发送器
*/
@Autowired
private SmsCodeSender smsCodeSender;
@Override
protected void send(ServletWebRequest request, ValidateCode smsCode) throws Exception {
// 短信发送操作
String mobile = ServletRequestUtils.getRequiredStringParameter(request.getRequest(), SecurityConstants.DEFAULT_PARAMETER_NAME_MOBILE);
smsCodeSender.send(mobile, smsCode.getCode());
}
}
/**
* 验证码控制器
*
* @author zhaohaibin
*/
@RestController
public class ValidateCodeController {
@Autowired
private Map<String, ValidateCodeProcessor> validateCodeProcessors;
/**
* 验证码处理逻辑,根据类型不通,调用不同的{@link ValidateCodeProcessor}接口实现
*
* @param request
* @param response
* @throws IOException
*/
@GetMapping(SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX + "/{type}")
public void createCode(HttpServletRequest request, HttpServletResponse response, @PathVariable String type) throws Exception {
// 获取处理器对应实现的处理方法
validateCodeProcessors.get(type + "ValidateCodeProcessor").create(new ServletWebRequest(request, response));
}
}
6.新建验证码处理器抽象类AbstractValidateCodeProcessor:
/**
* 抽象验证码处理器
*
* @author zhailiang
*/
public abstract class AbstractValidateCodeProcessor<C extends ValidateCode> implements ValidateCodeProcessor {
/**
* 收集系统中所有的 {@link ValidateCodeGenerator} 接口的实现,以name为key放入Map
*/
@Autowired
private Map<String, ValidateCodeGenerator> validateCodeGenerators;
@Autowired
private ValidateCodeRepository validateCodeRepository;
/**
* 抽象创建逻辑
*
* @param request
* @throws Exception
*/
@Override
public void create(ServletWebRequest request) throws Exception {
C validateCode = generate(request);
save(request, validateCode);
send(request, validateCode);
}
/**
* 生成校验码
*
* @param request
* @return
*/
@SuppressWarnings("unchecked")
private C generate(ServletWebRequest request) {
String type = getValidateCodeType(request).toString().toLowerCase();
String generatorName = type + ValidateCodeGenerator.class.getSimpleName();
ValidateCodeGenerator validateCodeGenerator = validateCodeGenerators.get(generatorName);
if (validateCodeGenerator == null) {
throw new ValidateCodeException("验证码生成器" + generatorName + "不存在");
}
return (C) validateCodeGenerator.generate(request);
}
/**
* 保存校验码
*
* @param request
* @param validateCode
*/
private void save(ServletWebRequest request, C validateCode) {
ValidateCode code = new ValidateCode(validateCode.getCode(), validateCode.getExpireTime());
validateCodeRepository.save(request, code, getValidateCodeType(request));
}
/**
* 发送校验码,由子类实现
*
* @param request
* @param validateCode
* @throws Exception
*/
protected abstract void send(ServletWebRequest request, C validateCode) throws Exception;
/**
* 根据请求的url获取校验码的类型
*
* @param request
* @return
*/
private ValidateCodeType getValidateCodeType(ServletWebRequest request) {
String type = StringUtils.substringBefore(getClass().getSimpleName(), "ValidateCodeProcessor");
return ValidateCodeType.valueOf(type.toUpperCase());
}
@Override
public void validate(ServletWebRequest request) {
ValidateCodeType codeType = getValidateCodeType(request);
C codeInSession = (C) validateCodeRepository.get(request, codeType);
String codeInRequest;
try {
codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),
codeType.getParamNameOnValidate());
} catch (ServletRequestBindingException e) {
throw new ValidateCodeException("获取验证码的值失败");
}
if (StringUtils.isBlank(codeInRequest)) {
throw new ValidateCodeException(codeType + "验证码的值不能为空");
}
if (codeInSession == null) {
throw new ValidateCodeException(codeType + "验证码不存在");
}
if (codeInSession.isExpried()) {
validateCodeRepository.remove(request, codeType);
throw new ValidateCodeException(codeType + "验证码已过期");
}
if (!StringUtils.equals(codeInSession.getCode(), codeInRequest)) {
throw new ValidateCodeException(codeType + "验证码不匹配");
}
validateCodeRepository.remove(request, codeType);
}
}
7.更新验证码生成器接口ValidateCodeGenerator:
/**
* 验证码生成逻辑抽象类
*
* @author zhaohaibin
*/
public interface ValidateCodeGenerator {
/**
* 验证码生成抽象方法
*
* @param request
* @return
*/
ValidateCode generate(ServletWebRequest request);
}
8.新建验证码存取接口及Session方式实现:
/**
* 验证码存取接口
*
* @author zhaohaibin
*/
public interface ValidateCodeRepository {
/**
* 保存验证码
*
* @param request
* @param code
* @param validateCodeType
*/
void save(ServletWebRequest request, ValidateCode code, ValidateCodeType validateCodeType);
/**
* 获取验证码
*
* @param request
* @param validateCodeType
* @return
*/
ValidateCode get(ServletWebRequest request, ValidateCodeType validateCodeType);
/**
* 移除验证码
*
* @param request
* @param codeType
*/
void remove(ServletWebRequest request, ValidateCodeType codeType);
}
/**
* 基于session的验证码存取器
*
* @author zhaohaibin
*/
@Component
public class SessionValidateCodeRepository implements ValidateCodeRepository {
/**
* 验证码放入session时的前缀
*/
String SESSION_KEY_PREFIX = "SESSION_KEY_FOR_CODE_";
/**
* 操作session的工具类
*/
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
@Override
public void save(ServletWebRequest request, ValidateCode code, ValidateCodeType validateCodeType) {
sessionStrategy.setAttribute(request, getSessionKey(request, validateCodeType), code);
}
/**
* 构建验证码放入session时的key
*
* @param request
* @return
*/
private String getSessionKey(ServletWebRequest request, ValidateCodeType validateCodeType) {
return SESSION_KEY_PREFIX + validateCodeType.toString().toUpperCase();
}
@Override
public ValidateCode get(ServletWebRequest request, ValidateCodeType validateCodeType) {
return (ValidateCode) sessionStrategy.getAttribute(request, getSessionKey(request, validateCodeType));
}
@Override
public void remove(ServletWebRequest request, ValidateCodeType codeType) {
sessionStrategy.removeAttribute(request, getSessionKey(request, codeType));
}
}
9.新建验证码类型枚举:
/**
* 验证码类型
*
* @author zhaohaibin
*/
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;
}
};
/**
* 校验时从请求中获取的参数的名字
*
* @return
*/
public abstract String getParamNameOnValidate();
}
10.更新验证码拦截器ValidateCodeFilter:
/**
* 自定义验证码拦截器
* InitializingBean覆写afterPropertiesSet方法
*
* @author zhaohaibin
*/
@Data
@Slf4j
@Component("validateCodeFilter")
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {
/**
* 引入失败处理逻辑
*/
@Autowired
private AuthenticationFailureHandler authenticationFailureHandler;
/**
* 系统配置信息
*/
@Autowired
private SecurityProperties securityProperties;
@Autowired
private ValidateCodeProcessorHolder validateCodeProcessorHolder;
/**
* 存放所有需要校验验证码的url
*/
private Map<String, ValidateCodeType> urlMap = new HashMap<>();
/**
* 缓存获取验证码
*/
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
/**
* 方便匹配/*等路径
*/
private AntPathMatcher pathMatcher = new AntPathMatcher();
@Override
public void afterPropertiesSet() throws ServletException {
super.afterPropertiesSet();
// 图片验证码
urlMap.put(SecurityConstants.DEFAULT_PROJECT_NAME_URL + SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM, ValidateCodeType.IMAGE);
addUrlToMap(securityProperties.getCode().getImage().getUrl(), ValidateCodeType.IMAGE);
// 短信验证码
urlMap.put(SecurityConstants.DEFAULT_PROJECT_NAME_URL + SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_MOBILE, ValidateCodeType.SMS);
addUrlToMap(securityProperties.getCode().getImage().getUrl(), ValidateCodeType.SMS);
}
/**
* 将系统中配置的需要校验验证码的URL根据校验的类型放入map
*
* @param urlString
* @param type
*/
protected void addUrlToMap(String urlString, ValidateCodeType type) {
if (StringUtils.isNotBlank(urlString)) {
// ","号分隔
String[] urls = StringUtils.splitByWholeSeparatorPreserveAllTokens(urlString, ",");
if (null != urls && urls.length > 0) {
for (String url : urls) {
urlMap.put(url, type);
}
}
}
}
/**
* 判断路径是否需要校验并校验
*
* @param request
* @param response
* @param filterChain
* @throws ServletException
* @throws IOException
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
ValidateCodeType type = getValidateCodeType(request);
if (type != null) {
log.info("校验请求【" + request.getRequestURI() + "】中的验证码,验证码类型:" + type);
try {
validateCodeProcessorHolder.findValidateCodeProcessor(type)
.validate(new ServletWebRequest(request, response));
log.info("验证码校验通过");
} catch (ValidateCodeException exception) {
authenticationFailureHandler.onAuthenticationFailure(request, response, exception);
return;
}
}
filterChain.doFilter(request, response);
}
/**
* 获取校验码的类型,如果当前请求不需要校验,则返回null
*
* @param request
* @return
*/
private ValidateCodeType getValidateCodeType(HttpServletRequest request) {
ValidateCodeType result = null;
if (!StringUtils.equalsIgnoreCase(request.getMethod(), "get")) {
Set<String> urls = urlMap.keySet();
if (null != urls && urls.size() > 0) {
for (String url : urls) {
if (pathMatcher.match(url, request.getRequestURI())) {
result = urlMap.get(url);
}
}
}
}
return result;
}
}
ValidateCodeProcessorHolder:
/**
* 校验码处理器管理器
*
* @author zhaohaibin
*/
@Component
public class ValidateCodeProcessorHolder {
@Autowired
private Map<String, ValidateCodeProcessor> validateCodeProcessors;
/**
* @param type
* @return
*/
public ValidateCodeProcessor findValidateCodeProcessor(ValidateCodeType type) {
return findValidateCodeProcessor(type.toString().toLowerCase());
}
/**
* @param type
* @return
*/
public ValidateCodeProcessor findValidateCodeProcessor(String type) {
String name = type.toLowerCase() + ValidateCodeProcessor.class.getSimpleName();
ValidateCodeProcessor processor = validateCodeProcessors.get(name);
if (processor == null) {
throw new ValidateCodeException("验证码处理器" + name + "不存在");
}
return processor;
}
}
11.更新Security拦截配置WebSecurityConfig:
/**
* SpringSecurity配置
*
* @author zhaohaibin
*/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SecurityProperties securityProperties;
// /**
// * 自定义成功处理逻辑
// */
// @Autowired
// private DemoAuthenticationSuccessHandler demoAuthenticationSuccessHandler;
// /**
// * 自定义失败处理逻辑
// */
// @Autowired
// private DemoAuthenticationFailureHandler demoAuthenticationFailureHandler;
/**
* 记住我功能引入数据源配置
*/
@Autowired
private DataSource dataSource;
/**
* 对UserDetailsService的自定义实现
*/
@Autowired
private ISystemUserService userDetailsService;
@Autowired
private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
@Autowired
private ValidateCodeSecurityConfig validateCodeSecurityConfig;
@Autowired
private FormAuthenticationConfig formAuthenticationConfig;
/**
* 加密解密官方实现
*
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 记住我功能token存储配置
*
* @return
*/
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
// 启动创建表,创建后记得注释掉
// tokenRepository.setCreateTableOnStartup(true);
return tokenRepository;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// // 图片验证码相关
// ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
// // 传入自定义失败处理
// validateCodeFilter.setAuthenticationFailureHandler(demoAuthenticationFailureHandler);
// // 传入自定义路径配置
// validateCodeFilter.setSecurityProperties(securityProperties);
// // 执行分隔操作
// validateCodeFilter.afterPropertiesSet();
//
// // 短信验证码相关
// SmsCodeFilter smsCodeFilter = new SmsCodeFilter();
// // 传入自定义失败处理
// smsCodeFilter.setAuthenticationFailureHandler(demoAuthenticationFailureHandler);
// // 传入自定义路径配置
// smsCodeFilter.setSecurityProperties(securityProperties);
// // 执行分隔操作
// smsCodeFilter.afterPropertiesSet();
// 表单登录改为配置引入
formAuthenticationConfig.configure(http);
// 默认/表单登录方式
// http.httpBasic()
http
// // 在默认UsernamePasswordAuthenticationFilter前增加自定义验证码校验拦截,并传入自定义失败对象
// .addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class)
// // 在默认UsernamePasswordAuthenticationFilter前增加自定义验证码校验拦截,并传入自定义失败对象
// .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
// .formLogin()
// // 自定义登录页面
// .loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)
// .loginProcessingUrl(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM)
// .successHandler(demoAuthenticationSuccessHandler)
// .failureHandler(demoAuthenticationFailureHandler)
// 自定义拦截配置应用
// 验证码拦截配置
.apply(validateCodeSecurityConfig)
.and()
// 短信验证码认证拦截配置
.apply(smsCodeAuthenticationSecurityConfig)
.and()
// 记住我相关配置
.rememberMe()
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
.userDetailsService(userDetailsService)
.and()
// 对任何请求授权
.authorizeRequests()
// 匹配页面授权所有权限
.antMatchers(
// API
"/swagger-ui.html",
// 默认登录页
SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
// 自定义登录页(demoLogin)
securityProperties.getBrowser().getLoginPage(),
// 验证码
SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX + "/*").permitAll()
// 任何请求
.anyRequest()
// 都需要被认证
.authenticated()
.and()
// 请求伪造防护功能关闭
.csrf().disable();
}
}
FormAuthenticationConfig:
/**
* 表单登录配置
*
* @author zhaohaibin
*/
@Component
public class FormAuthenticationConfig {
@Autowired
protected AuthenticationSuccessHandler demoAuthenticationSuccessHandler;
@Autowired
protected AuthenticationFailureHandler demoAuthenticationFailureHandler;
public void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)
.loginProcessingUrl(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM)
.successHandler(demoAuthenticationSuccessHandler)
.failureHandler(demoAuthenticationFailureHandler);
}
}
SecurityConstants:
/**
* 拦截器常量配置
*
* @author zhaohaibin
*/
public interface SecurityConstants {
/**
* 默认的处理验证码的url前缀
*/
String DEFAULT_VALIDATE_CODE_URL_PREFIX = "/code";
/**
* 当请求需要身份认证时,默认跳转的url
*
* @see BrowserSecurityController
*/
String DEFAULT_UNAUTHENTICATION_URL = "/authentication/require";
String DEFAULT_PROJECT_NAME = "";
// String DEFAULT_PROJECT_NAME = "demo";
String DEFAULT_URL_SEPARATOR = "/";
String DEFAULT_PROJECT_NAME_URL = StringUtils.isNotBlank(DEFAULT_PROJECT_NAME) ? DEFAULT_URL_SEPARATOR + DEFAULT_PROJECT_NAME : "";
/**
* 默认的用户名密码登录请求处理url
*/
String DEFAULT_SIGN_IN_PROCESSING_URL_FORM = "/authentication/form";
/**
* 默认的手机验证码登录请求处理url
*/
String DEFAULT_SIGN_IN_PROCESSING_URL_MOBILE = "/authentication/mobile";
/**
* 默认登录页面
*
* @see BrowserSecurityController
*/
String DEFAULT_SIGN_IN_PAGE_URL = DEFAULT_PROJECT_NAME_URL + "/login.html";
/**
* 验证图片验证码时,http请求中默认的携带图片验证码信息的参数的名称
*/
String DEFAULT_PARAMETER_NAME_CODE_IMAGE = "imageCode";
/**
* 验证短信验证码时,http请求中默认的携带短信验证码信息的参数的名称
*/
String DEFAULT_PARAMETER_NAME_CODE_SMS = "smsCode";
/**
* 发送短信验证码 或 验证短信验证码时,传递手机号的参数的名称
*/
String DEFAULT_PARAMETER_NAME_MOBILE = "mobile";
}
12.登录页增加短信验证码表单提交代码,启动测试:
<h2>短信登录</h2>
<form action="authentication/mobile" method="post">
<div><label>手机号</label><input type="text" name="mobile" placeholder="请输入手机号" value="13012345678"/></div>
<div><label>验证码</label><input type="text" name="smsCode" placeholder="请输入验证码"/>
<a href="code/sms?mobile=13012345678">发送验证码</a>
</div>
<div><input type="submit" value="登录"/></div>
</form>
问题排查:
测试过程若出现"请引导用户到登录页"提示,且控制台无报错,提示"引发跳转的请求是...",请debug跟踪检查,可能是代码异常失败跳转导致