1、手机号登录是不需要密码的,通过短信验证码实现免密登录功能。  

   a.向手机发送手机验证码,使用第三方短信平台 SDK 发送,如: 阿里云短信服务

   b.登录表单输入短信验证码

   c.使用自定义短信验证码校验过滤器SmsVerifyCodeValidateFilter

   d.当验证码校验通过后,进入自定义手机认证过滤器 MobileAuthenticationFilter 校验手机号是否存在

   e.自定义 MobileAuthenticationToken 提供给  MobileAuthenticationFilter

   f.自定义 MobileAuthenticationProvider 提供给 ProviderManager 处理

   g.创建针对手机号查询用户信息的  MobileUserDetailsService ,交给  MobileAuthenticationProvider

   h.自定义 MobileAuthenticationConfig 配置类将上面组件连接起来,添加到容器中

   i.将 MobileAuthenticationConfig 添加到 SpringSecurityConfig 安全配置的过滤器链上。

 

2、跳转到短信登录认证页面,在 SpringSecurityConfig 放行手机登录与短信发送相关请求URL

/**
     * 前往手机验证码登录页
     *
     * @return
     */
    @RequestMapping("/mobile/page")
    public String toMobilePage() {
        return "login-mobile"; // templates/login-mobile.html
    }
/**
     * 资源权限配置(过滤器链):
     * 1、被拦截的资源
     * 2、资源所对应的角色权限
     * 3、定义认证方式:httpBasic 、httpForm
     * 4、定制登录页面、登录请求地址、错误处理方式
     * 5、自定义 spring security 过滤器
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //http.httpBasic()//采用httpBasic 认证方式
        /*http.formLogin()
                .loginPage("/login/page")// 交给 /login/page 响应认证(登录)页面
                .loginProcessingUrl("/login/form")  // 登录表单提交处理Url, 默认是 /login
                .usernameParameter("name") // 默认用户名的属性名是 username
                .passwordParameter("pwd") // 默认密码的属性名是 password
                .and()
                .authorizeRequests()//认证请求
                .antMatchers("/login/page").permitAll()//自定义登录页不需要认证
                .anyRequest().authenticated();// 所有进入应用的HTTP请求都要进行认证*/

        http
                .addFilterBefore(imageVerifyCodeValidateFilter, UsernamePasswordAuthenticationFilter.class)//将校验过滤器 imageCodeValidateFilter 添加到 UsernamePasswordAuthenticationFilter 前面
                .addFilterBefore(smsVerifyCodeValidateFilter,UsernamePasswordAuthenticationFilter.class)//将校验过滤器 smsVerifyCodeValidateFilter 添加到 UsernamePasswordAuthenticationFilter 前面
                .formLogin()
                .loginPage(securityProperties.getLoginPage())// 交给 /login/page 响应认证(登录)页面
                .loginProcessingUrl(securityProperties.getLoginProcessingUrl())  // 登录表单提交处理Url, 默认是 /login
                .usernameParameter(securityProperties.getUsernameParameter()) // 默认用户名的属性名是 username
                .passwordParameter(securityProperties.getPasswordParameter()) // 默认密码的属性名是 password
                .successHandler(customAuthenticationSuccessHandler)//自定义认证成功处理器
                .failureHandler(customAuthenticationFailureHandler)//自定义认证失败处理器
                .and()
                .authorizeRequests()//认证请求
                .antMatchers(securityProperties.getLoginPage(),securityProperties.getMobilePage(),securityProperties.getImageCodeUrl(),securityProperties.getMobileCodeUrl()).permitAll()//自定义登录页不需要认证,生成图片验证码,发送短信获取验证码也不需要验证
                .anyRequest().authenticated()// 所有进入应用的HTTP请求都要进行认证
                .and()
                .rememberMe()//记住我功能
                .tokenRepository(jdbcTokenRepository())//保存登录信息
                .tokenValiditySeconds(securityProperties.getTokenValiditySeconds());//记住我有效时长一周

        // 将手机相关的配置绑定过滤器链上
        http.apply(mobileAuthenticationConfig);
    }

 

 

3、创建阿里云短信发送服务接口

 

springcloud 登录验证码 spring security手机验证码登录_自定义

上述自己去阿里云短信服务模块去申请

@Component
@ConfigurationProperties(prefix = "moblie.sms")
@Data
public class SmsProperties {
    private String  accessKeyId;
    private String  accessKeySecret;
    private String  signName;
    private String  templateCode;
    private String  product;
    private String  domain;
}
@Controller
public class SmsSendController {

    @Autowired
    SmsSendService smsSendService;

    @RequestMapping(value = "/code/mobile",method = RequestMethod.GET)
    @ResponseBody
    public Result smsCodeSend(@RequestParam("mobile") String phoneNumbers, HttpServletRequest request){
        return smsSendService.sendSms(phoneNumbers,request);
    }
}
/**
 * 短信发送接口
 */
public interface SmsSendService {
    /**
     * 短信发送
     * @param PhoneNumbers 手机号
     * @return
     */
    Result sendSms(String PhoneNumbers, HttpServletRequest request);
}
/**
 * 发送短信验证码
 */
@Service
public class SmsSendServiceImpl implements SmsSendService {
    Logger logger= LoggerFactory.getLogger(SmsSendServiceImpl.class);

    public static final String SESSION_SMS_VERIFY_CODE = "SESSION_SMS_VERIFY_CODE";

    @Autowired
    SmsProperties smsProperties;

    @Override
    public Result sendSms(String PhoneNumbers, HttpServletRequest request) {
        //1.生成一个手机短信验证码
        String code = RandomUtil.randomNumbers(4);
        //2.将验证码发到session中
        HttpSession session = request.getSession();
        session.setAttribute(SESSION_SMS_VERIFY_CODE,code);
        //3.发送短信
        SendSmsResponse response = toSendSms(PhoneNumbers, code);
        logger.info("向手机号" + PhoneNumbers + "发送的验证码为::" + code);
        if (response.getCode().equals("ok")){
            return Result.ok("获取验证码成功");
        }else {
            return Result.build(500,"获取验证码失败");
        }
    }

    public  SendSmsResponse toSendSms(String PhoneNumbers, String code)  {

        //可自助调整超时时间
        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
        System.setProperty("sun.net.client.defaultReadTimeout", "10000");

        //初始化acsClient,暂不支持region化
        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", smsProperties.getAccessKeyId(), smsProperties.getAccessKeySecret());
        DefaultProfile.addEndpoint( "cn-hangzhou", smsProperties.getProduct(), smsProperties.getDomain());
        IAcsClient acsClient = new DefaultAcsClient(profile);

        //组装请求对象-具体描述见控制台-文档部分内容
        SendSmsRequest request = new SendSmsRequest();
        //必填:待发送手机号
        request.setPhoneNumbers(PhoneNumbers);
        //必填:短信签名-可在短信控制台中找到
        request.setSignName(smsProperties.getSignName());
        //必填:短信模板-可在短信控制台中找到
        request.setTemplateCode(smsProperties.getTemplateCode());
        //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
        request.setTemplateParam("{\"code\":\"" + code + "\"}");

        //选填-上行短信扩展码(无特殊需求用户请忽略此字段)
        //request.setSmsUpExtendCode("90997");

        //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
        request.setOutId("yourOutId");

        //hint 此处可能会抛出异常,注意catch
        SendSmsResponse sendSmsResponse = null;
        try {
            sendSmsResponse = acsClient.getAcsResponse(request);
        } catch (Exception e) {
            e.printStackTrace();
           throw new RuntimeException("发送短信失败");
        }
        return sendSmsResponse;
    }



}

4、实现短信验证码校验过滤器 SmsVerifyCodeValidateFilter,校验输入的验证码与发送的短信验证是否一致。

/**
 *
 * 短信验证码校验过滤器
 * OncePerRequestFilter: 所有请求之前被调用一次
 */
@Component("smsVerifyCodeValidateFilter")
public class SmsVerifyCodeValidateFilter extends OncePerRequestFilter {
    @Autowired
    SecurityProperties securityProperties;
     @Autowired
     CustomAuthenticationFailureHandler customAuthenticationFailureHandler;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //1.判断手机登录认证 且请求方式为post
        if (securityProperties.getMobileProcessingUrl().equalsIgnoreCase(request.getRequestURI())&&request.getMethod().equalsIgnoreCase("post")){
             try {
                 //校验验证码合法性
                validate(request);
            } catch (AuthenticationException e) {
                //将验证失败的抛出的异常交给自定义认证失败处理器处理异常
                customAuthenticationFailureHandler.onAuthenticationFailure(request,response,e);
                return;
            }

        }
        filterChain.doFilter(request,response);

    }

    private void validate(HttpServletRequest request) {
        //先获取session中的验证码
        String sessionImageCode = (String) request.getSession().getAttribute(SmsSendServiceImpl.SESSION_SMS_VERIFY_CODE);
        //获取用户输入的验证码
        String inputCode = request.getParameter("code");
        if (StringUtils.isBlank(inputCode)){
            throw new SmsVerifyCodeException("验证码不能为空");
        }
        if (!inputCode.equalsIgnoreCase(sessionImageCode)){
             throw new SmsVerifyCodeException("验证码输入错误");
        }
    }
}

5、实现手机认证登录过滤器MobileAuthenticationFilter,模仿UsernamePasswordAuthenticationFilter进行改造

**
 * 手机登录过滤器
 * 实现同UsernamePasswordAuthenticationFilter
 * 将username相关的都改成mobile,而且手机登录只有手机号,没有密码,所以去掉密码
 * 相应的参数最好写成可配置的
 *
 */
public class MobileAuthenticationFilter extends
        AbstractAuthenticationProcessingFilter {
    // ~ Static fields/initializers
    // =====================================================================================

    @Resource
    SecurityProperties securityProperties;

    /**
     * 前端表单中的手机号码参数
     */
    private String mobileParameter = "mobile";
    private boolean postOnly = true;

    // ~ Constructors
    // ===================================================================================================

    public MobileAuthenticationFilter() {
        super(new AntPathRequestMatcher("/mobile/form", "POST"));
    }

    // ~ Methods
    // ========================================================================================================

    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }

        String mobile = obtainMobile(request);
        if (mobile == null) {
            mobile = "";
        }


        mobile = mobile.trim();

        MobileAuthenticationToken authRequest = new MobileAuthenticationToken(mobile);


        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);

        return this.getAuthenticationManager().authenticate(authRequest);
    }


    /**
     * Enables subclasses to override the composition of the username, such as by
     * including additional values and a separator.
     *
     * @param request so that request attributes can be retrieved
     * @return the username that will be presented in the <code>Authentication</code>
     * request token to the <code>AuthenticationManager</code>
     */
    @Nullable
    protected String obtainMobile(HttpServletRequest request) {
        return request.getParameter(mobileParameter);
    }

    /**
     * Provided so that subclasses may configure what is put into the authentication
     * request's details property.
     *
     * @param request     that an authentication request is being created for
     * @param authRequest the authentication request object that should have its details
     *                    set
     */
    protected void setDetails(HttpServletRequest request,
                              MobileAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }


    /**
     * Defines whether only HTTP POST requests will be allowed by this filter. If set to
     * true, and an authentication request is received which is not a POST request, an
     * exception will be raised immediately and authentication will not be attempted. The
     * <tt>unsuccessfulAuthentication()</tt> method will be called as if handling a failed
     * authentication.
     * <p>
     * Defaults to <tt>true</tt> but may be overridden by subclasses.
     */
    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }

    public String getMobileParameter() {
        return mobileParameter;
    }

    public void setMobileParameter(String mobileParameter) {
        this.mobileParameter = mobileParameter;
    }
}

6、封装手机认证Token  MobileAuthenticationToken提供给上面自定义的 MobileAuthenticationFilter 使用。模仿UsernamePasswordAuthenticationToken进行改造

/**
 * 封装手机认证Token
 *  实现同UsernamePasswordAuthenticationToken
 */
public  class MobileAuthenticationToken extends AbstractAuthenticationToken {

    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

    //认证之前存放机号,认证之后放用户信息
    private final Object principal;

    /**
     * 开始认证时,创建一个MobileAuthenticationToken实例 接收的是手机号码, 并且 标识未认证
     *
     * @param principal 手机号
     */
    public MobileAuthenticationToken(Object principal) {
        super(null);
        this.principal = principal;  // 手机号
        setAuthenticated(false);
    }


    /**
     * 当认证通过后,会重新创建一个新的MobileAuthenticationToken,来标识它已经认证通过,
     *
     * @param principal   用户信息
     * @param authorities 用户权限
     */
    public MobileAuthenticationToken(Object principal,
                                     Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;// 用户信息
        super.setAuthenticated(true);  // 标识已经认证通过
    }



    @Override
    public Object getCredentials() {
        return null;
    }

    public Object getPrincipal() {
        return this.principal;
    }

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

        super.setAuthenticated(false);
    }

    @Override
    public void eraseCredentials() {
        super.eraseCredentials();
    }
}

7、实现手机认证提供者 MobileAuthenticationProvider,提供给底层 ProviderManager 使用。

/**
 * 实现手机认证提供者 MobileAuthenticationProvider提供给底层 ProviderManager 使用
 */
public class MobileAuthenticationProvider implements AuthenticationProvider {

    UserDetailsService userDetailsService;

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    /**
     * 认证处理:
     * 1. 通过 手机号 去数据库查询用户信息(UserDeatilsService)
     * 2. 再重新构建认证信息
     *
     * @param authentication
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        MobileAuthenticationToken mobileAuthenticationToken = (MobileAuthenticationToken) authentication;

        // 获取用户输入的手机号
        String mobile = (String) mobileAuthenticationToken.getPrincipal();
        // 查询数据库
        UserDetails userDetails = userDetailsService.loadUserByUsername(mobile);
        //未查询到用户信息
        if (userDetails == null) {
            throw new AuthenticationServiceException("该手机未注册");

        }

        // 查询到了用户信息, 则认证通过,就重新构建 MobileAuthenticationToken 实例
        MobileAuthenticationToken authenticationToken = new MobileAuthenticationToken(userDetails, userDetails.getAuthorities());

        authenticationToken.setDetails(mobileAuthenticationToken.getDetails());
        return authenticationToken;


    }

    /**
     * 通过此方法,来判断 采用哪一个 AuthenticationProvider
     *
     * @param authentication
     * @return
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return MobileAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

8、手机号获取用户信息 MobileUserDetailsService

/**
 * 通过手机号获取用户信息和权限信息
 */
@Component("mobileUserDetailsService")
public class MobileUserDetailsService implements UserDetailsService {
    Logger logger = LoggerFactory.getLogger(MobileUserDetailsService.class);

    @Override
    public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException {

        logger.info("请求的手机号是:" + mobile);
        // 1. 通过手机号查询用户信息(查询数据库)
        
        // 2. 如果有此用户,则查询用户权限
        // 3. 封装用户信息
        return new User(mobile, "", true, true, true, true, AuthorityUtils.commaSeparatedStringToAuthorityList("ADMIN"));

    }
}

9、自定义管理认证配置 MobileAuthenticationConfig,将上面定义的组件绑定起来,添加到容器中

/**
 * 自定义管理认证配置
 * 将定义的手机短信认证相关的组件组合起来,一起添加到容器中
 */
@Component("mobileAuthenticationConfig")
public class MobileAuthenticationConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
    @Autowired
    CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
    @Autowired
    CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
    @Autowired
    MobileUserDetailsService mobileUserDetailsService;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // 创建校验手机号过滤器实例
        MobileAuthenticationFilter mobileAuthenticationFilter = new MobileAuthenticationFilter();
        // 接收 AuthenticationManager 认证管理器
        mobileAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
        // 采用哪个成功、失败处理器
        mobileAuthenticationFilter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);
        mobileAuthenticationFilter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);

        //手机登录记住我功能
        mobileAuthenticationFilter.setRememberMeServices(http.getSharedObject(RememberMeServices.class));


        // 为 Provider 指定明确 的mobileUserDetailsService 来查询用户信息
        MobileAuthenticationProvider provider = new MobileAuthenticationProvider();
        provider.setUserDetailsService(mobileUserDetailsService);
        // 将 provider 绑定到 HttpSecurity 上面,
        // 并且将 手机认证加到 用户名密码认证之后
        http.authenticationProvider(provider).addFilterAfter(mobileAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

10、绑定到安全配置 SpringSecurityConfig

  向 SpringSecurityConfig 中注入SmsVerifyCodeValidateFilter和MobileAuthenticationConfig实例

  将  SmsVerifyCodeValidateFilter实例添加到UsernamePasswordAuthenticationFilter前面

http
                .addFilterBefore(imageVerifyCodeValidateFilter, UsernamePasswordAuthenticationFilter.class)//将校验过滤器 imageCodeValidateFilter 添加到 UsernamePasswordAuthenticationFilter 前面
                .addFilterBefore(smsVerifyCodeValidateFilter,UsernamePasswordAuthenticationFilter.class)//将校验过滤器 smsVerifyCodeValidateFilter 添加到 UsernamePasswordAuthenticationFilter 前面

  在 SpringSecurityConfifig#confifigure(HttpSecurity http) 方法体最后调用 apply 添加mobileAuthenticationConfig(将手机相关的配置绑定过滤器链上)

// 将手机相关的配置绑定过滤器链上
 http.apply(mobileAuthenticationConfig);

11、SpringSecurityConfig安全配置类完整代码

/**
 * 安全配置类作为安全控制中心, 用于实现身份认证与授权配置功能
 */
@Configuration
@EnableWebSecurity //启动 SpringSecurity 过滤器链功能
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    SecurityProperties securityProperties;

    Logger logger = LoggerFactory.getLogger(SpringSecurityConfig.class);


    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        // 加密存储   明文+随机盐值
        return new BCryptPasswordEncoder();
    }


    @Autowired
    CustomUserDetailsService customUserDetailsService;


    /**
     * 认证管理器:
     * 1、认证信息提供方式(用户名、密码、当前用户的资源权限)
     * 2、可采用内存存储方式,也可能采用数据库方式等
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //基于内存存储认证信息 存储的密码必须是加密后的 不然会报错:There is no PasswordEncoder mapped for the id "null"
        //auth.inMemoryAuthentication().withUser("zcc").password("123").authorities("ADMIN");
        /*String password = bCryptPasswordEncoder().encode("123");
        logger.info("加密后的密码:" + password);
        auth.inMemoryAuthentication().withUser("zcc").password(password).authorities("ADMIN");*/


        // 指定使用自定义查询用户信息来完成身份认证
        auth.userDetailsService(customUserDetailsService);

    }



    @Autowired
    CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
    @Autowired
    CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
    @Autowired
    ImageVerifyCodeValidateFilter imageVerifyCodeValidateFilter;

    @Autowired
    SmsVerifyCodeValidateFilter smsVerifyCodeValidateFilter;
    @Autowired
    MobileAuthenticationConfig mobileAuthenticationConfig;
    /**
     * 记住我 功能
     */
    @Autowired
    DataSource dataSource;
    @Bean
    public JdbcTokenRepositoryImpl jdbcTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        // 是否启动时自动创建表,第一次启动创建就行,后面启动把这个注释掉,不然报错已存在表
        //jdbcTokenRepository.setCreateTableOnStartup(true);
        return jdbcTokenRepository;
    }

    /**
     * 资源权限配置(过滤器链):
     * 1、被拦截的资源
     * 2、资源所对应的角色权限
     * 3、定义认证方式:httpBasic 、httpForm
     * 4、定制登录页面、登录请求地址、错误处理方式
     * 5、自定义 spring security 过滤器
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //http.httpBasic()//采用httpBasic 认证方式
        /*http.formLogin()
                .loginPage("/login/page")// 交给 /login/page 响应认证(登录)页面
                .loginProcessingUrl("/login/form")  // 登录表单提交处理Url, 默认是 /login
                .usernameParameter("name") // 默认用户名的属性名是 username
                .passwordParameter("pwd") // 默认密码的属性名是 password
                .and()
                .authorizeRequests()//认证请求
                .antMatchers("/login/page").permitAll()//自定义登录页不需要认证
                .anyRequest().authenticated();// 所有进入应用的HTTP请求都要进行认证*/

        http
                .addFilterBefore(imageVerifyCodeValidateFilter, UsernamePasswordAuthenticationFilter.class)//将校验过滤器 imageCodeValidateFilter 添加到 UsernamePasswordAuthenticationFilter 前面
                .addFilterBefore(smsVerifyCodeValidateFilter,UsernamePasswordAuthenticationFilter.class)//将校验过滤器 smsVerifyCodeValidateFilter 添加到 UsernamePasswordAuthenticationFilter 前面
                .formLogin()
                .loginPage(securityProperties.getLoginPage())// 交给 /login/page 响应认证(登录)页面
                .loginProcessingUrl(securityProperties.getLoginProcessingUrl())  // 登录表单提交处理Url, 默认是 /login
                .usernameParameter(securityProperties.getUsernameParameter()) // 默认用户名的属性名是 username
                .passwordParameter(securityProperties.getPasswordParameter()) // 默认密码的属性名是 password
                .successHandler(customAuthenticationSuccessHandler)//自定义认证成功处理器
                .failureHandler(customAuthenticationFailureHandler)//自定义认证失败处理器
                .and()
                .authorizeRequests()//认证请求
                .antMatchers(securityProperties.getLoginPage(),securityProperties.getMobilePage(),securityProperties.getImageCodeUrl(),securityProperties.getMobileCodeUrl()).permitAll()//自定义登录页不需要认证,生成图片验证码,发送短信获取验证码也不需要验证
                .anyRequest().authenticated()// 所有进入应用的HTTP请求都要进行认证
                .and()
                .rememberMe()//记住我功能
                .tokenRepository(jdbcTokenRepository())//保存登录信息
                .tokenValiditySeconds(securityProperties.getTokenValiditySeconds());//记住我有效时长一周

        // 将手机相关的配置绑定过滤器链上
        http.apply(mobileAuthenticationConfig);
    }

    /**
     * 放行静态资源(js css 等)
     *
     * @param web
     */
    @Override
    public void configure(WebSecurity web) {
        //web.ignoring().antMatchers("/dist/**", "/modules/**", "/plugins/**");
        web.ignoring().antMatchers(securityProperties.getStaticPaths());
    }
}

12、测试

  

springcloud 登录验证码 spring security手机验证码登录_springcloud 登录验证码_02

 

springcloud 登录验证码 spring security手机验证码登录_springcloud 登录验证码_03

记住我功能测试:(记住我 的 input 标签的 name="remember-me")

springcloud 登录验证码 spring security手机验证码登录_ide_04

 

 

springcloud 登录验证码 spring security手机验证码登录_自定义_05