⒈封装短信验证码类

 package cn.coreqi.security.validate;

 import java.time.LocalDateTime;

 public class ValidateCode {
private String code;
private LocalDateTime expireTime; //过期时间 public ValidateCode(String code, Integer expireIn) {
this.code = code;
this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
} public ValidateCode(String code, LocalDateTime expireTime) {
this.code = code;
this.expireTime = expireTime;
} public boolean isExpried(){
return LocalDateTime.now().isAfter(expireTime);
}
public String getCode() {
return code;
} public void setCode(String code) {
this.code = code;
} public LocalDateTime getExpireTime() {
return expireTime;
} public void setExpireTime(LocalDateTime expireTime) {
this.expireTime = expireTime;
} }

⒉封装短信验证码接口及实现类

 package cn.coreqi.security.validate;

 public interface SmsCodeSender {
void send(String mobile,String code);
}
 package cn.coreqi.security.validate;

 public class DefaultSmsCodeSender implements SmsCodeSender {
@Override
public void send(String mobile, String code) {
System.out.println("向手机"+mobile+"发送短信验证码"+code+"");
}
}

⒊封装验证码控制器

 package cn.coreqi.security.controller;

 import cn.coreqi.security.validate.DefaultSmsCodeSender;
import cn.coreqi.security.validate.SmsCodeSender;
import cn.coreqi.security.validate.ValidateCode;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; @RestController
public class ValidateSmsController { public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); @GetMapping("/code/sms")
public void createSmsCode(HttpServletRequest request, HttpServletResponse response) throws ServletRequestBindingException {
ValidateCode smsCode = new ValidateCode(RandomStringUtils.randomNumeric(4),60);
sessionStrategy.setAttribute(new ServletWebRequest(request),SESSION_KEY,smsCode);
String mobile = ServletRequestUtils.getRequiredStringParameter(request,"mobile");
SmsCodeSender smsCodeSender = new DefaultSmsCodeSender();
smsCodeSender.send(mobile,smsCode.getCode());
}
}

⒋放行验证码的Rest地址

⒌修改登录表单

     <h3>短信登陆</h3>
<form action="/authentication/mobile" method="post">
<table>
<tr>
<td>手机号码:</td>
<td><input type="text" name="mobile" value="13800138000"></td>
</tr>
<tr>
<td>短信验证码:</td>
<td>
<input type="text" name="smsCode">
<a href="/code/sms?mobile=13800138000"/>
</td>
</tr>
<tr>
<td colspan="2"><button type="submit">登录</button></td>
</tr>
</table>
</form>

⒍封装安全验证流程相关

  1.验证码验证Filter

 package cn.coreqi.security.Filter;

 import cn.coreqi.security.controller.ValidateSmsController;
import cn.coreqi.security.validate.ValidateCode;
import cn.coreqi.security.validate.ValidateCodeException;
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.StringUtils;
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; /**
* 短信验证码过滤器
*/
public class SmsCodeFilter extends OncePerRequestFilter {
private AuthenticationFailureHandler authenticationFailureHandler; private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); public AuthenticationFailureHandler getAuthenticationFailureHandler() {
return authenticationFailureHandler;
} public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
this.authenticationFailureHandler = authenticationFailureHandler;
} public SessionStrategy getSessionStrategy() {
return sessionStrategy;
} public void setSessionStrategy(SessionStrategy sessionStrategy) {
this.sessionStrategy = sessionStrategy;
} @Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
if (httpServletRequest.equals("/authentication/mobile") && httpServletRequest.getMethod().equals("post")) {
try {
validate(new ServletWebRequest(httpServletRequest)); }catch (ValidateCodeException e){
authenticationFailureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,e);
return;
}
}
filterChain.doFilter(httpServletRequest,httpServletResponse); //如果不是登录请求,直接调用后面的过滤器链
} private void validate(ServletWebRequest request) throws ServletRequestBindingException {
ValidateCode codeInSession = (ValidateCode) sessionStrategy.getAttribute(request, ValidateSmsController.SESSION_KEY);
String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),"smsCode");
if(!StringUtils.hasText(codeInRequest)){
throw new ValidateCodeException("验证码的值不能为空!");
}
if(codeInSession == null){
throw new ValidateCodeException("验证码不存在!");
}
if(codeInSession.isExpried()){
sessionStrategy.removeAttribute(request,ValidateSmsController.SESSION_KEY);
throw new ValidateCodeException("验证码已过期!");
}
if(!codeInSession.getCode().equals(codeInRequest)){
throw new ValidateCodeException("验证码不正确!");
}
sessionStrategy.removeAttribute(request,ValidateSmsController.SESSION_KEY);
}
}

  2.封装短信登陆Token类

 package cn.coreqi.security.Token;

 import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion; import java.util.Collection; 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);
} 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();
}
}

  3.短信登陆请求Filter 

 package cn.coreqi.security.Filter;

 import cn.coreqi.security.Token.SmsCodeAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final String COREQI_FORM_MOBILE_KEY = "mobile";
private String mobileParameter = COREQI_FORM_MOBILE_KEY; //请求中携带手机号的参数名称
private boolean postOnly = true; //指定当前过滤器是否只处理POST请求 public SmsCodeAuthenticationFilter() {
super(new AntPathRequestMatcher("/authentication/mobile", "POST")); //指定当前过滤器处理的请求
} 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
*/
protected String obtainMobile(HttpServletRequest request) {
return request.getParameter(this.mobileParameter);
} /**
* 把请求的详情,例如请求ip、SessionId等设置到验证请求中去
* @param request
* @param authRequest
*/
protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
} public void setMobileParameter(String mobileParameter) {
Assert.hasText(mobileParameter, "Username parameter must not be empty or null");
this.mobileParameter = mobileParameter;
} public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
} public final String getMobileParameter() {
return this.mobileParameter;
} }

  4.短信身份认证类

 package cn.coreqi.security.Provider;

 import cn.coreqi.security.Token.SmsCodeAuthenticationToken;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService; public class SmsCodeAuthenticationProvider implements AuthenticationProvider { private UserDetailsService userDetailsService; public UserDetailsService getUserDetailsService() {
return userDetailsService;
} public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
} /**
* 进行身份认证的逻辑
* @param authentication 就是我们传入的Token
* @return
* @throws AuthenticationException
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException { //利用UserDetailsService获取用户信息,拿到用户信息后重新组装一个已认证的Authentication SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken)authentication;
UserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal()); //根据手机号码拿到用户信息
if(user == null){
throw new InternalAuthenticationServiceException("无法获取用户信息");
}
SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user,user.getAuthorities());
authenticationResult.setDetails(authenticationToken.getDetails());
return authenticationResult;
} /**
* AuthenticationManager挑选一个AuthenticationProvider来处理传入进来的Token就是根据supports方法来判断的
* @param aClass
* @return
*/
@Override
public boolean supports(Class<?> aClass) {
return SmsCodeAuthenticationToken.class.isAssignableFrom(aClass); //判断出入进来的是不是SmsCodeAuthenticationToken类型
}
}

⒎配置

 package cn.coreqi.security.config;

 import cn.coreqi.security.Filter.SmsCodeAuthenticationFilter;
import cn.coreqi.security.Provider.SmsCodeAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component; @Component
public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> { @Autowired
private AuthenticationSuccessHandler coreqiAuthenticationSuccessHandler; @Autowired
private AuthenticationFailureHandler coreqiAuthenticationFailureHandler; @Autowired
private UserDetailsService userDetailsService; @Override
public void configure(HttpSecurity http) throws Exception {
SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(coreqiAuthenticationSuccessHandler);
smsCodeAuthenticationFilter.setAuthenticationFailureHandler(coreqiAuthenticationFailureHandler); SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService); http.authenticationProvider(smsCodeAuthenticationProvider)
.addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
}
 package cn.coreqi.security.config;

 import cn.coreqi.security.Filter.SmsCodeFilter;
import cn.coreqi.security.Filter.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.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired
private AuthenticationSuccessHandler coreqiAuthenticationSuccessHandler; @Autowired
private AuthenticationFailureHandler coreqiAuthenticationFailureHandler; @Autowired
private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig; @Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
} @Override
protected void configure(HttpSecurity http) throws Exception {
ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
validateCodeFilter.setAuthenticationFailureHandler(coreqiAuthenticationFailureHandler); SmsCodeFilter smsCodeFilter = new SmsCodeFilter(); //http.httpBasic() //httpBasic登录 BasicAuthenticationFilter
http.addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class) //加载用户名密码过滤器的前面
.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) //加载用户名密码过滤器的前面
.formLogin() //表单登录 UsernamePasswordAuthenticationFilter
.loginPage("/coreqi-signIn.html") //指定登录页面
//.loginPage("/authentication/require")
.loginProcessingUrl("/authentication/form") //指定表单提交的地址用于替换UsernamePasswordAuthenticationFilter默认的提交地址
.successHandler(coreqiAuthenticationSuccessHandler) //登录成功以后要用我们自定义的登录成功处理器,不用Spring默认的。
.failureHandler(coreqiAuthenticationFailureHandler) //自己体会把
.and()
.authorizeRequests() //对授权请求进行配置
.antMatchers("/coreqi-signIn.html","/code/image").permitAll() //指定登录页面不需要身份认证
.anyRequest().authenticated() //任何请求都需要身份认证
.and().csrf().disable() //禁用CSRF
.apply(smsCodeAuthenticationSecurityConfig);
//FilterSecurityInterceptor 整个SpringSecurity过滤器链的最后一环
}
}

SpringSecurity实现短信登录功能的更多相关文章

  1. Spring Security 解析(四) ——短信登录开发

    Spring Security 解析(四) -- 短信登录开发   在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因此决定先把Spring Security ...

  2. Android Studio精彩案例(五)《JSMS短信验证码功能实现》

    转载本专栏文章,请注明出处,尊重原创 .文章博客地址:道龙的博客 很多应用刚打开的时候,让我们输入手机号,通过短信验证码来登录该应用.那么,这个场景是怎么实现的呢?其实是很多开放平台提供了短信验证功能 ...

  3. SpringSceurity(4)---短信验证码功能实现

    SpringSceurity(4)---短信验证码功能实现 有关SpringSceurity系列之前有写文章 1.SpringSecurity(1)---认证+授权代码实现 2.SpringSecur ...

  4. Springboot下实现阿里云短信验证功能(含代码)

    Springboot下实现阿里云短信验证功能 一 开通阿里云短信服务 阿里云官网注册登录 找到短信服务并开通 打开短信服务的管理台 在国内消息那栏中添加签名管理和模板管理(按照格式要求去写) 在右上角 ...

  5. day80:luffy:短信sdk接入&点击获取验证码&注册功能的实现&Celery实现短信发送功能

    目录 1.短信sdk接入 2.前端点击获取验证码效果 3.注册后端接口实现 4.注册-前端 5.Celery 6.Celery完成短信发送功能 1.短信sdk接入 1.准备工作 1.下载云通讯相关的文 ...

  6. 短信登录与注册接口、前端所有方式登录注册页面、redis数据库介绍与安装

    今日内容概要 短信登陆接口 短信注册接口 登陆注册前端 redis介绍和安装 内容详细 1.短信登陆接口 在视图类 user/views.py中修改并添加: from .serializer impo ...

  7. 微信开发之移动手机WEB页面(HTML5)Javascript实现一键拨号及短信发送功能

    在做一个微信的微网站中的一个便民服务电话功能的应用,用到移动web页面中列出的电话号码,点击需要实现调用通讯录,网页一键拨号的拨打电话功能. 如果需要在移动浏览器中实现拨打电话,发送email,美国服 ...

  8. Swift - 短信发送功能的实现

    使用MessageUI.framework框架可以实现短信发送功能,步骤如下: (1)首先判断设备是否有发送短信功能 (2)如果设备允许发送短信,创建一个MFMessageComposeViewCon ...

  9. 如何实现php手机短信验证功能

    http://www.qdexun.cn/jsp/news/shownews.do?method=GetqtnewsdetailAction&id=1677 下载php源代码 现在网站在建设网 ...

随机推荐

  1. mysql全备份脚本速查

    mysql全备份脚本 # 快捷备份方式[root@nb scripts]# cat db.backup.sh #!/bin/bashmysqldump -ubackup -pbackuppwd -P3 ...

  2. 创建免密码sudo用户

    创建免密码sudo用户 #!/bin/bash c1=`grep -w 'bkuser' /etc/passwd | wc -l` ]; then echo "bkuser已经存在" ...

  3. hive metastore && hiveserver2 . 基本配置

    <?xml version="1.0" encoding="UTF-8" standalone="no"?><?xml-s ...

  4. Jvm threaddump,heapdump的分析及问题定位

    1 一.Thread Dump介绍 1.1 1.1什么是Thread Dump? 1.2 1.2 Thread Dump特点 1.3 1.3 Thread Dump 能诊断的问题 1.4 1.4如何抓 ...

  5. UDP协议

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/7532512 更多请看专栏, ...

  6. python第二次周末大作业

    题目 ''' HR人力资源管理. 1. 菜单: ("查看员⼯信息","添加员⼯信息", "修改员⼯信息", "删除员⼯信息&quo ...

  7. linux 内核模块makefile通用模板

    ifneq ($(KERNELRELEASE),)# 在 mylist 后面添加需要编译的模块数量 mylist=hello.o a.o# 为每一个模块添加所需的文件 hello-objs := ma ...

  8. HDU - 5071 Chat(模拟)

    原题链接 题意:有各种操作,模拟这个程序并输出每次操作的信息 分析:恶心模拟题...用个map记录一下各个等级女孩的谈话数,同时也便于查找权值为u的在不在队列里.因为n很小,其他就暴力模拟了. #in ...

  9. 前端常用UI框架

    移动端UI框架 Mint UI(饿了么团队) 中文官网:http://mint-ui.github.io/#!/en 饿了么前端团队推出的基于Vue.js的移动端组件库 GitHub地址:https: ...

  10. CRM项目之RBAC权限组件-day26

    写在前面 上课第26天,打卡: 世间安得双全法 不负如来不负卿 s17day26 CRM项目 项目概要:XX公司CRM - 权限管理,公共组件,app ***** - 熟悉增删改查,Low *** - ...