⒈封装短信验证码类

 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. C# 实现身份验证之WCF篇(2)

    前面总结了三种方法,今天又将分享三种方法,完成WCF篇. 第四种:SOAP Header验证 首先定义一个WCF服务契约及服务实现类(后面的各种验证均采用该WCF服务),我这里直接采用默认的代码,如下 ...

  2. Ajax结合Json进行交互数据(四)

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  3. python自动化开发-[第十九天]-分页,cookie,session

    今日概要: 1.cookie和session 2.csrf 跨站请求伪造 3.自定义分页 一.上节回顾: http请求的本质: - Http请求本质 浏览器(socket客户端): 2. socket ...

  4. HDU4560 二分最大流

    http://acm.hdu.edu.cn/showproblem.php?pid=4560 网络流好像经常搭配上二分和拆点. n个歌手,m种歌曲流派(n<=m<=75) 我们想要安排尽可 ...

  5. Java 语言特性

    介绍以下几种语言特性: Java5的特性 1.静态引用 2.可变参数 3.自动装箱和拆箱  包装类的缓存设计 4.枚举 一.静态引用(语法糖,不推荐使用,了解一下即可) 先看看普通的引用,就是impo ...

  6. FastDFS与springboot整合例子

    余庆先生提供了一个Java客户端,但是作为一个C程序员,写的java代码可想而知.而且已经很久不维护了. 这里推荐一个开源的FastDFS客户端,支持最新的SpringBoot2.0. 配置使用极为简 ...

  7. SQL语句实例集合

    SQL语句实例 表操作     例 1  对于表的教学管理数据库中的表 STUDENTS ,可以定义如下: CREATE  TABLE  STUDENTS (SNO      NUMERIC (6, ...

  8. 关于selenium的那些坑

    selenium 辅助工具 splinter 总有人看不明白,以防万一,先在开头大写加粗说明一下: frameset不用切,frame需层层切! 很多人在用selenium定位页面元素的时候会遇到定位 ...

  9. 解析ArcGis拓扑——根据拓扑错误记录提取shp文件、导出Excel表格

    在ArcGis拓扑检查的流程——以面重叠检查为例中讲述了如何在ArcGis进行拓扑检查与修改. 在实际操作中,有时我们还需要将ArcGis拓扑检查的结果制作成报告或者提取错误信息反馈作业方. 本文仍然 ...

  10. EFCodeFirst示例

    1.定义一个基础模板类 namespace WebApplication1.Models { /// <summary> /// 可持久到数据库的领域模型的基类. /// </sum ...