SpringSecurity实现短信登录功能
⒈封装短信验证码类
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实现短信登录功能的更多相关文章
- Spring Security 解析(四) ——短信登录开发
Spring Security 解析(四) -- 短信登录开发 在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因此决定先把Spring Security ...
- Android Studio精彩案例(五)《JSMS短信验证码功能实现》
转载本专栏文章,请注明出处,尊重原创 .文章博客地址:道龙的博客 很多应用刚打开的时候,让我们输入手机号,通过短信验证码来登录该应用.那么,这个场景是怎么实现的呢?其实是很多开放平台提供了短信验证功能 ...
- SpringSceurity(4)---短信验证码功能实现
SpringSceurity(4)---短信验证码功能实现 有关SpringSceurity系列之前有写文章 1.SpringSecurity(1)---认证+授权代码实现 2.SpringSecur ...
- Springboot下实现阿里云短信验证功能(含代码)
Springboot下实现阿里云短信验证功能 一 开通阿里云短信服务 阿里云官网注册登录 找到短信服务并开通 打开短信服务的管理台 在国内消息那栏中添加签名管理和模板管理(按照格式要求去写) 在右上角 ...
- day80:luffy:短信sdk接入&点击获取验证码&注册功能的实现&Celery实现短信发送功能
目录 1.短信sdk接入 2.前端点击获取验证码效果 3.注册后端接口实现 4.注册-前端 5.Celery 6.Celery完成短信发送功能 1.短信sdk接入 1.准备工作 1.下载云通讯相关的文 ...
- 短信登录与注册接口、前端所有方式登录注册页面、redis数据库介绍与安装
今日内容概要 短信登陆接口 短信注册接口 登陆注册前端 redis介绍和安装 内容详细 1.短信登陆接口 在视图类 user/views.py中修改并添加: from .serializer impo ...
- 微信开发之移动手机WEB页面(HTML5)Javascript实现一键拨号及短信发送功能
在做一个微信的微网站中的一个便民服务电话功能的应用,用到移动web页面中列出的电话号码,点击需要实现调用通讯录,网页一键拨号的拨打电话功能. 如果需要在移动浏览器中实现拨打电话,发送email,美国服 ...
- Swift - 短信发送功能的实现
使用MessageUI.framework框架可以实现短信发送功能,步骤如下: (1)首先判断设备是否有发送短信功能 (2)如果设备允许发送短信,创建一个MFMessageComposeViewCon ...
- 如何实现php手机短信验证功能
http://www.qdexun.cn/jsp/news/shownews.do?method=GetqtnewsdetailAction&id=1677 下载php源代码 现在网站在建设网 ...
随机推荐
- MegaCli命令使用详解
一.MegaCli命令介绍 MegaCli是一款管理维护硬件RAID软件,可以用来查看raid信息等MegaCli 的Media Error Count: 0 Other Error Count: 0 ...
- Spring Boot学习记录03_一些属性配置文件
转自:http://blog.didispace.com/springbootproperties/ 多环境配置(这个地方跟maven的profile配置有点类似) 我们在开发Spring Boot应 ...
- Visual Studio连接到TFS
我在学校自己使用git,公司使用VSS,然后这个项目又使用TFS.Visual Studio连接到TFS是这样滴 1.点连接到团队项目 2.添加TFS服务器的url,写到你的http:XXX/tfs就 ...
- UDP协议
本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/7532512 更多请看专栏, ...
- 互斥量、条件变量与pthread_cond_wait()函数的使用,详解(二)
1.Linux“线程” 进程与线程之间是有区别的,不过linux内核只提供了轻量进程的支持,未实现线程模型.Linux是一种“多进程单线程”的操作系统.Linux本身只有进程的概念,而其所谓的“线程” ...
- Dubbo服务端结合maven打jar包
<build> <finalName>odao-weixin-user</finalName> <resources> ...
- 如何计算UDP/TCP检验和checksum
如何计算UDP/TCP检验和checksum 一.下面的图是一个UDP的检验和所需要用到的所有信息,包括三个部分:1.UDP伪首部2.UDP首部3.UDP的数据部分(切记不要遗漏该部分,否则就~吐血了 ...
- SpringBoot系列: 集成MyBatis
本文主要修改自下面博客:http://www.ityouknow.com/springboot/2016/11/06/spring-boo-mybatis.htmlhttp://tengj.top/2 ...
- CodeMirror 在线代码编辑器
像百度编辑器插件部分.菜鸟教程示例等高德地图都在使用,这里也记录一下: CodeMirror是一个用于编辑器文本框textarea代码高亮javascript插件...... vue 中使用 参见:h ...
- 开源框架.netCore DncZeus学习(二)配置连接
配置连接字符串,update-database,初始数据后,访问报错,提示offset错误.因为本机上使用的sql2008. .net Core 2.X中的EF访问sqlserver2008默认使用的 ...