短信登录过滤器  SmsAuthenticationFilter 

import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AuthenticationManager;
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 SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter {// 设置拦截/sms/login短信登录接口private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/sms/login", "POST");// 认证参数private String principalParameter = "phone"; //对应手机号private String credentialsParameter = "code"; //对应验证码private boolean postOnly = true;public SmsAuthenticationFilter() {super(DEFAULT_ANT_PATH_REQUEST_MATCHER);}public SmsAuthenticationFilter(AuthenticationManager authenticationManager) {super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);}@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {if (this.postOnly && !"POST".equals(request.getMethod())) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());} else {String phone = this.obtainPrincipal(request);phone = phone != null ? phone : "";phone = phone.trim();String code = this.obtainCredentials(request);code = code != null ? code : "";SmsAuthenticationToken authRequest = new SmsAuthenticationToken(phone, code);this.setDetails(request, authRequest);// 认证return this.getAuthenticationManager().authenticate(authRequest);}}@Nullableprotected String obtainCredentials(HttpServletRequest request) {return request.getParameter(this.credentialsParameter);}@Nullableprotected String obtainPrincipal(HttpServletRequest request) {return request.getParameter(this.principalParameter);}protected void setDetails(HttpServletRequest request, SmsAuthenticationToken authRequest) {authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));}public void setPrincipalParameter(String principalParameter) {Assert.hasText(principalParameter, "principal parameter must not be empty or null");this.principalParameter = principalParameter;}public void setCredentialsParameter(String credentialsParameter) {Assert.hasText(credentialsParameter, "credentials parameter must not be empty or null");this.credentialsParameter = credentialsParameter;}public void setPostOnly(boolean postOnly) {this.postOnly = postOnly;}public final String getPrincipalParameter() {return this.principalParameter;}public final String getCredentialsParameter() {return this.credentialsParameter;}}

短信登录令牌        SmsAuthenticationToken

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;import java.util.Collection;/*** 短信登录令牌*/
public class SmsAuthenticationToken extends AbstractAuthenticationToken {private static final long serialVersionUID = 1L;private final Object phone;private Object code;public SmsAuthenticationToken(Object phone, Object credentials) {super((Collection) null);this.phone = phone;this.code = credentials;this.setAuthenticated(false);}public SmsAuthenticationToken(Object phone, Object code, Collection<? extends GrantedAuthority> authorities) {super(authorities);this.phone = phone;this.code = code;super.setAuthenticated(true);}@Overridepublic Object getCredentials() {return this.code;}@Overridepublic Object getPrincipal() {return this.phone;}@Overridepublic void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {Assert.isTrue(!isAuthenticated, "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");super.setAuthenticated(false);}@Overridepublic void eraseCredentials() {super.eraseCredentials();this.code = null;}
}

 短信登录校验器

import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.constant.Constants;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.AuthenticatedPrincipal;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;import java.security.Principal;/*** 短信登录校验器*/
@Component
public class SmsAuthenticationProvider implements AuthenticationProvider {private SmsUserDetailsService smsUserDetailsService;private RedisTemplate<String, String> redisTemplate;public SmsAuthenticationProvider(@Qualifier("smsUserDetailsService") SmsUserDetailsService smsUserDetailsService, RedisTemplate<String, String> redisTemplate) {this.smsUserDetailsService = smsUserDetailsService;this.redisTemplate = redisTemplate;}@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {Object principal = authentication.getPrincipal();// 获取凭证也就是用户的手机号String mobile = "";if (principal instanceof UserDetails) {mobile = ((UserDetails) principal).getUsername();} else if (principal instanceof AuthenticatedPrincipal) {mobile = ((AuthenticatedPrincipal) principal).getName();} else if (principal instanceof Principal) {mobile = ((Principal) principal).getName();} else {mobile = principal == null ? "" : principal.toString();}String inputCode = (String) authentication.getCredentials(); // 获取输入的验证码// 1. 根据手机号查询用户信息UserDetails userDetails = smsUserDetailsService.loadUserByUsername(mobile);if (userDetails == null) {throw new InternalAuthenticationServiceException("用户不存在");}// 2. 检验Redis手机号的验证码String verifyKey = Constants.PHONE_CODE_KEY + mobile;String redisCode = redisTemplate.opsForValue().get(verifyKey);if (StrUtil.isEmpty(redisCode)) {throw new BadCredentialsException("验证码已经过期或尚未发送,请重新发送验证码");}if (!inputCode.equals(redisCode)) {throw new BadCredentialsException("输入的验证码不正确,请重新输入");}redisTemplate.delete(verifyKey);//用完即删// 3. 重新创建已认证对象,SmsAuthenticationToken authenticationResult = new SmsAuthenticationToken(principal, inputCode, userDetails.getAuthorities());authenticationResult.setDetails(userDetails);return authenticationResult;}@Overridepublic boolean supports(Class<?> aClass) {return SmsAuthenticationToken.class.isAssignableFrom(aClass);}}

 查询短信登录信息并封装为UserDetails

import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.system.service.ISysUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;/*** 查询短信登录信息并封装为UserDetails  这里可以抽取一个抽象类,权限加载和校验租户等逻辑交给父类处理*/
@Service("smsUserDetailsService")
public class SmsUserDetailsService implements UserDetailsService {private static final Logger log = LoggerFactory.getLogger(SmsUserDetailsService.class);@Autowiredprivate ISysUserService userService;@Autowiredprivate SysPermissionService permissionService;@Overridepublic UserDetails loadUserByUsername(String phone) throws UsernameNotFoundException {SysUser user = userService.selectUserByPhone(phone);if (StringUtils.isNull(user)) {log.info("手机号:{} 不存在.", phone);throw new InternalAuthenticationServiceException("手机号:" + phone + " 不存在");} else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {log.info("登录用户:{} 已被删除.", phone);throw new ServiceException("对不起,您的账号:" + phone + " 已被删除");} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {log.info("登录用户:{} 已被停用.", phone);throw new DisabledException("对不起,您的账号:" + phone + " 已停用");}return createLoginUser(user);}public UserDetails createLoginUser(SysUser user) {return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));}
}

适配器 SmsSecurityConfigurerAdapter

import com.ruoyi.framework.sms.handle.FailAuthenticationHandler;
import com.ruoyi.framework.sms.handle.SuccessAuthenticationHandler;
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.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;/*** Author: LiuLin* Date: 2022/5/27 16:25* Description:*/
@Component
public class SmsSecurityConfigurerAdapter extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {@Autowiredprivate SuccessAuthenticationHandler successHandler;@Autowiredprivate FailAuthenticationHandler failureHandler;@Autowiredprivate SmsAuthenticationProvider smsAuthenticationProvider;@Overridepublic void configure(HttpSecurity builder) throws Exception {SmsAuthenticationFilter filter = new SmsAuthenticationFilter();filter.setAuthenticationSuccessHandler(successHandler);filter.setAuthenticationFailureHandler(failureHandler);builder.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);filter.setAuthenticationManager(authenticationManager);builder.authenticationProvider(smsAuthenticationProvider);super.configure(builder);}
}

登录失败

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** Author: LiuLin* Date: 2022/5/30 10:19* Description:*/@Component
public class FailAuthenticationHandler extends SimpleUrlAuthenticationFailureHandler {@Autowiredprivate ISysUserService userService;@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,AuthenticationException exception) throws IOException, ServletException {String mobile = request.getParameter("phone");SysUser sysUser = userService.selectUserByPhone(mobile);if (sysUser == null) {AsyncManager.me().execute(AsyncFactory.recordLogininfor("未知", Constants.LOGIN_FAIL, "手机号:" + mobile + "不存在"));} else {AsyncManager.me().execute(AsyncFactory.recordLogininfor(sysUser.getUserName(), Constants.LOGIN_FAIL, exception.getMessage()));}JSONObject res = new JSONObject();//返回前端数据res.put("code", com.ruoyi.common.constant.HttpStatus.ERROR);res.put("msg", exception.getMessage());response.setContentType("application/json;charset=UTF-8");response.getWriter().write(String.valueOf(res));}}

登录成功

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** Author: LiuLin* Date: 2022/5/30 10:18* Description:*/@Component
public class SuccessAuthenticationHandler extends SavedRequestAwareAuthenticationSuccessHandler {@Autowiredprivate TokenService tokenService;@Autowiredprivate ISysUserService userService;@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,Authentication authentication) throws ServletException, IOException {LoginUser loginUser = (LoginUser) authentication.getDetails();recordLoginInfo(loginUser.getUserId());AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginUser.getUsername(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));JSONObject res = new JSONObject();//返回前端数据res.put("msg", "操作成功");res.put("code", HttpStatus.SUCCESS);res.put("token", tokenService.createToken(loginUser));response.setContentType("application/json;charset=UTF-8");response.getWriter().write(String.valueOf(res));//response.getWriter().write(objectMapper.writeValueAsString(res));}public void recordLoginInfo(Long userId) {SysUser sysUser = new SysUser();sysUser.setUserId(userId);sysUser.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));sysUser.setLoginDate(DateUtils.getNowDate());userService.updateUserProfile(sysUser);}}

spring security配置

@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{@Autowiredprivate SmsSecurityConfigurerAdapter smsSecurityConfigurerAdapter;/*** 解决 无法直接注入 AuthenticationManager** @return* @throws Exception*/@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception{return super.authenticationManagerBean();}@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception{httpSecurity// CSRF禁用,因为不使用session.csrf().disable()// 基于token,所以不需要session                        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()// 过滤请求.authorizeRequests()// 对于登录login 注册register 验证码captchaImage 允许匿名访问.antMatchers("/sms/login", "/sendCode").anonymous()              // 添加手机号短信登录httpSecurity.apply(smsSecurityConfigurerAdapter);}/*** 强散列哈希加密实现*/@Beanpublic BCryptPasswordEncoder bCryptPasswordEncoder(){return new BCryptPasswordEncoder();}}

spring security 短信验证码登录相关推荐

  1. 5.Spring Security 短信验证码登录

    Spring Security 短信验证码登录 在 Spring Security 添加图形验证码一节中,我们已经实现了基于 Spring Boot + Spring Security 的账号密码登录 ...

  2. Spring Security 短信验证码登录(5)

    在Spring Security添加图形验证码中,我们已经实现了基于Spring Boot + Spring Security的账号密码登录,并集成了图形验证码功能.时下另一种非常常见的网站登录方式为 ...

  3. Spring Security简单增加短信验证码登录

    查网上资料增加短信验证码登录都要增加一大推,要重头写Spring Security的实现,我呢,只想在原来的密码登录基础上简单实现一下短信验证码登录. 1.首先得先一个认证类,来认证验证码是否正确,这 ...

  4. Spring Security OAuth2 优雅的集成短信验证码登录以及第三方登录

    基于SpringCloud做微服务架构分布式系统时,OAuth2.0作为认证的业内标准,Spring Security OAuth2也提供了全套的解决方案来支持在Spring Cloud/Spring ...

  5. security模仿密码登录实现短信验证码登录

    security模仿密码登录实现短信验证码登录 模仿UsernamePasswordAuthenticationToken创建短信验证码的token类SmsAuthenticationToken /* ...

  6. SpringSecurity短信验证码登录

    短信验证码登录 时下另一种非常常见的网站登录方式为手机短信验证码登录,但Spring Security默认只提供了账号密码的登录认证逻辑,所以要实现手机短信验证码登录认证功能,我们需要模仿Spring ...

  7. 手把手带你在集成SpringSecurity的SpringBoot应用中添加短信验证码登录认证功能

    本文目录 前言 1 自定义AuthenticationToken类 2 自定义AuthenticationProvider类 3 自定义MobilePhoneAuthenticationFilter ...

  8. SpringBoot + SpringSecurity 短信验证码登录功能实现

    实现原理 在之前的文章中,我们介绍了普通的帐号密码登录的方式:SpringBoot + Spring Security 基本使用及个性化登录配置(http://www.deiniu.com/artic ...

  9. cas5.3.9自定义手机短信验证码登录

    cas自定义多种登录方式 cas添加手机短信验证码登录 cas添加手机短信验证码登录 全部基于SpringBoot,以及SpringWebflow开发,请在有此基础再进行学习! 添加Maven依赖 & ...

最新文章

  1. saas的计费数据库设计_如何构建和扩展SaaS计费解决方案
  2. 为“证明实力”,某医院前网管离职后远程入侵服务器,致诊疗系统瘫痪
  3. 深圳.Net俱乐部2.26活动资源下载之——从SmartClient到ClickOnce
  4. 【控制】《多智能体系统一致性协同演化控制理论与技术》纪良浩老师-第8章-二阶连续时间多智能体系统加权一致性
  5. 日常生活小技巧 -- 百度地图坐标拾取
  6. 如何迁移 Flink 任务到实时计算
  7. Windows下文件名或目录的简写
  8. NLP快速入门:手把手教你用HanLP做中文分词
  9. poj1681 Painter's Problem高斯消元
  10. mysql使用手册_mysql使用手册
  11. Spring总结四:IOC和DI 注解方式
  12. python语言基本语句例句-关于python:使用WordNet查找同义词,定义和例句
  13. 关于Stateflow中chart输入事件的激活
  14. vue 深度拷贝数组_前端深拷贝和浅拷贝
  15. mysql中游标能不能更新数据库_MySQL与MariaDB中游标的使用
  16. 如何从0开发一个Atom组件
  17. 动态图php打不开,PHP如何判断一个gif图片是否为动态图片
  18. MS DTC服务无法启动解决方法
  19. LeetCode 分割整数数组,分割为两部分的和相等
  20. ARM9——五级流水线结构,以及PC指针

热门文章

  1. python网易云爬虫——实现网易云歌词的爬取(输入歌手的id,即可实现歌词的爬取)
  2. 一般数据库服务器物理机配置,ironic部署物理机
  3. PHP在线云加密系统V8.01开源版
  4. 大数据中位数怎么运算_大数据查找中位数
  5. 什么是Divi主题生成器?
  6. 从0到1开发可视化数据大屏
  7. Say Hello to everybody !
  8. 桥田快换产品在超声波焊接工艺的应用
  9. 存储调研:Lustre并行文件系统体系结构
  10. 小米路由器3G建站折腾笔记2 - 刷ROM和开启SSH