spring security 短信验证码登录
生活随笔
收集整理的這篇文章主要介紹了
spring security 短信验证码登录
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
短信登錄過濾器??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 短信验证码登录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 设备维护管理系统软件CMMS有哪些
- 下一篇: 一、路由策略原理