须要实现对手机的发送验证码,这里只是简单的处理,打印出来java
定义接口 //发送短信的接口 public interface SmsCodeSender {web
void send(String mobile, String code); } //生成短信接口 public interface ValidateCodeGenerator { ValidateCode generate(ServletWebRequest request); }
实现接口spring
//发送短信的接口的实现 public class DefaultSmsCodeSender implements SmsCodeSender { /* (non-Javadoc) * [@see](https://my.oschina.net/weimingwei) com.imooc.security.core.validate.code.sms.SmsCodeSender#send(java.lang.String, java.lang.String) //生成短信接口的实现 [@Override](https://my.oschina.net/u/1162528) public void send(String mobile, String code) { System.out.println("向手机"+mobile+"发送短信验证码"+code); } }
生成手机验证码 @Component("smsValidateCodeGenerator") public class SmsCodeGenerator implements ValidateCodeGenerator {安全
@Autowired private SecurityProperties securityProperties; /* * (non-Javadoc) * * [@see](https://my.oschina.net/weimingwei) * com.imooc.security.core.validate.code.ValidateCodeGenerator#generate(org. * springframework.web.context.request.ServletWebRequest) */ public ValidateCode generate(ServletWebRequest request) { String code = RandomStringUtils.randomNumeric(securityProperties.getCode().getSms().getLegth()); return new ValidateCode(code, securityProperties.getCode().getSms().getExpireIn()); } public SecurityProperties getSecurityProperties() { return securityProperties; } public void setSecurityProperties(SecurityProperties securityProperties) { this.securityProperties = securityProperties; } }
发送手机验证码session
@GetMapping("/code/sms") public void createSmsCode(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletRequestBindingException { ImageCode imageCode = generate(request); ValidateCode smsCode = imageCodeGenerator.generate(new ServletWebRequest(request)); sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY, smsCode); String mobile = ServletRequestUtils.getRequiredStringParameter(request,"mobile"); smsCodeSender.send(mobile,smsCode.getCode()); }
上面的部分实现了手机验证码的生成,接下来要作的就是手机验证码的验证。app
手机验证码的验证由于不像用户名密码那样能够直接登陆,为了实现手机的验证,须要模仿用户名和密码的登陆方式,对用户名登陆的那一套进行改造,须要建立一个短信验证过滤器SmsAuthenticationFilter,短信验证码的token,SmsAuthenticaitonToken,而后须要一个验证手机号的Provider,SmsAuthenticationProvider,而后将手机号传递给UserDetailService进行相关的处理.dom
SmsAuthenticationToken的代码以下:ide
/** * 封装登陆信息 */ public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = 420L; //放认证信息(登陆以前放手机号,登陆成功以后存放用户的信息) 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); } 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"); } else { super.setAuthenticated(false); } } public void eraseCredentials() { super.eraseCredentials(); } }
SmsCodeAuthenticationFilter.javaweb安全
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter { // ~ Static fields/initializers // ===================================================================================== //拦截的参数的名称 private String mobileParameter = SecurityConstants.DEFAULT_PARAMETER_NAME_MOBILE; //表示是否只容许post请求 private boolean postOnly = true; // ~ Constructors // =================================================================================================== /** * 指定过滤器拦截的请求 */ public SmsCodeAuthenticationFilter() { super(new AntPathRequestMatcher(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, "POST")); } // ~ Methods // ======================================================================================================== public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { //判断是否是post请求 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(); SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile); //认证前将请求的信息放入SmsCodeAuthenticationToken // Allow subclasses to set the "details" property setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } /** * 获取手机号 */ 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, SmsCodeAuthenticationToken authRequest) { authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); } /** * Sets the parameter name which will be used to obtain the username from * the login request. * * @param usernameParameter * the parameter name. Defaults to "username". */ public void setMobileParameter(String usernameParameter) { Assert.hasText(usernameParameter, "Username parameter must not be empty or null"); this.mobileParameter = usernameParameter; } /** * 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 final String getMobileParameter() { return mobileParameter; } }
SmsCodeAuthenticationProvider.javapost
public class SmsCodeAuthenticationProvider implements AuthenticationProvider { private UserDetailsService userDetailsService; /* * (non-Javadoc) * * @see org.springframework.security.authentication.AuthenticationProvider# * authenticate(org.springframework.security.core.Authentication) */ @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication; UserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal()); if (user == null) { throw new InternalAuthenticationServiceException("没法获取用户信息"); } //将认证的用户信息从新构建成一个对象 SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user, user.getAuthorities()); /* SmsCodeAuthenticationFilter的 将以前没验证的SmsCodeAuthenticationToken内的请求信息从新放入到新建立的token里面 */ authenticationResult.setDetails(authenticationToken.getDetails()); return authenticationResult; } /* *判断传递进来的东西是否是SmsCodeAuthenticationToken * (non-Javadoc) * * @see org.springframework.security.authentication.AuthenticationProvider# * supports(java.lang.Class) */ @Override public boolean supports(Class<?> authentication) { return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication); } public UserDetailsService getUserDetailsService() { return userDetailsService; } public void setUserDetailsService(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } }
SmsCodeAuthenticationSecurityConfig.java(短息验证的配置)
/** * 该模块是要在app,也要在browser里面用 */ @Component public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain,HttpSecurity>{ @Autowired private AuthenticationSuccessHandler imoocAuthenticationSuccessHandler; @Autowired private AuthenticationFailureHandler imoocAuthenticationFailureHandler; @Autowired private UserDetailsService userDetailsService; @Override public void configure(HttpSecurity builder) throws Exception { SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter(); //设置manager smsCodeAuthenticationFilter.setAuthenticationManager(builder.getSharedObject(AuthenticationManager.class)); smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(imoocAuthenticationSuccessHandler); smsCodeAuthenticationFilter.setAuthenticationFailureHandler(imoocAuthenticationFailureHandler); //设置Provider SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider(); smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService); //将咱们自定义的Provider加入到全部provider的集合里面 builder.authenticationProvider(smsCodeAuthenticationProvider) .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); } } /** * 专门用来作web安全应用的适配器WebSecurityConfigurerAdapter */ @Configuration public class BrowswerSecurityConfig extends AbstractChannelSecurityConfig { @Autowired private SecurityProperties securityProperties; @Autowired private DataSource dataSource; @Autowired private UserDetailsService userDetailsService; @Autowired private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig; @Autowired private ValidateCodeSecurityConfig validateCodeSecurityConfig; /** * 配置密码编码器 * * @return */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Primary @Bean(name = "dataSource") @ConfigurationProperties(prefix = "spring.datasource") public DataSource dataSource(){ return DataSourceBuilder.create().build(); } /** * @param * @throws Exception */ @Bean public PersistentTokenRepository persistentTokenRepository() { JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl(); //配置数据源 tokenRepository.setDataSource(dataSource); //配置在启动的时候建立表 // tokenRepository.setCreateTableOnStartup(true); return tokenRepository; } @Override protected void configure(HttpSecurity http) throws Exception { applyPasswordAuthenticationConfig(http); http.apply(validateCodeSecurityConfig) .and() .apply(smsCodeAuthenticationSecurityConfig) .and() // .apply(imoocSocialSecurityConfig) // .and() .rememberMe() .tokenRepository(persistentTokenRepository()) .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds()) .userDetailsService(userDetailsService) .and() .authorizeRequests() .antMatchers( SecurityConstants.DEFAULT_UNAUTHENTICATION_URL, SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, securityProperties.getBrowser().getLoginPage(), SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*") .permitAll() .anyRequest() .authenticated() .and() .csrf().disable(); } }
以后就能够实现短信的验证了