spring security自定义登录增加为短信验证码校验实战

辛苦搞了2周终于搞出来了!遇到各种问题百度各中资料,各种尝试终于不辜负啊。

  1. 主要功能:用户输入用户名、密码、短信验证码,后台校验用户名、密码和手机验证码后登录成功。
  2. 技术栈:使用spring security框架搭建并继承 (我没有使用xml配置模式看别人有用xml文件配置的,应该是异曲同工吧)
    不多废话,正文附源码,疑问可评论。
    1.继承WebSecurityConfigureAdapter的类:WebApplicationSecurityConfig
    网上这个类的说明一抓一大把我也就是照猫画虎的本事,关注点是在本类的初始化JwtAuthenticationProvider在内的provider对象时很容易犯错,也要注意相同的addFilter、addFilterBefore、addFilterAfter等各类过滤器也有先后顺序。
    2.继承UsernamePasswordAuthenticationFilter的类:JwtLoginFilter(主要验证码逻辑位置)
    3.继承UsernamePasswordAuthenticationToken的类:JwtAuthenticationToken
    4.继承DaoAuthenticationProvider的类:JwtAuthenticationProvider
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
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.stereotype.Component;
@Component
public class JwtAuthenticationProvider extends DaoAuthenticationProvider {
    public JwtAuthenticationProvider(JdbcUserDetailsServiceImpl userDetailsService) {
        super();
		setHiderUserNotFoundException(false);
		setUserDetailsService(userDetailsService);
    }
	public JwtAuthenticationProvider() {
        super();
	}
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    //手机号和密码登录可是使用下列逻辑直接认证,我是账号密码再家短信验证码所以此处逻辑还是账号密码逻辑无需变更
	//	JwtCodeAuthenticationToken authenticationToken = (JwtCodeAuthenticationToken) authentication;
    //    Object principal = authentication.getPrincipal();// 获取凭证也就是用户的手机号
    //    String phone = "";
    //    if (principal instanceof String) {
    //        phone = (String) principal;
    //    }
    //    
    //    String inputCode = (String) authentication.getCredentials(); // 获取输入的验证码
    //    
    //    Integer cacheObject = redisCache.getCacheObject("login"+phone);
    //	
    //    // 1. 检验Redis手机号的验证码
    //    if (cacheObject == null) {
    //        throw new BadCredentialsException("验证码已经过期或尚未发送,请重新发送验证码");
    //    }
    //    if (!inputCode.equals(cacheObject+"")) {
    //        throw new BadCredentialsException("输入的验证码不正确,请重新输入");
    //    }
    //    // 2. 根据手机号查询用户信息
    //    UserDetails userDetails = userDetailsServiceImpl.loadUserByUsername(phone);
    //    if (userDetails == null) {
    //        throw new InternalAuthenticationServiceException("phone用户不存在,请注册");
    //    }
    //     // 3. 重新创建已认证对象,
    //    JwtCodeAuthenticationToken authenticationResult = new JwtAuthenticationToken(userDetails, inputCode, userDetails.getAuthorities());
    //    authenticationResult.setDetails(authenticationToken.getDetails());
    //    return authenticationResult;
		return super.authenticate(authentication);
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return JwtAuthenticationToken.class.isAssignableFrom(aClass);
    }
}

分割线

ackage com.seahorse.youliao.security;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

import java.util.Collection;

/**
 * @Description: 自定义令牌对象
 * @author:songqiang
 * @Date:2020-01-10 11:28
 **/
public class JwtAuthenticatioToken extends UsernamePasswordAuthenticationToken {


    private static final long serialVersionUID = 1L;

    private String token;

    public JwtAuthenticatioToken(Object principal, Object credentials){
        super(principal, credentials);
    }

    public JwtAuthenticatioToken(Object principal, Object credentials, String token){
        super(principal, credentials);
        this.token = token;
    }

    public JwtAuthenticatioToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
        super(principal, credentials, authorities);
    }

    public JwtAuthenticatioToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities, String token) {
        super(principal, credentials, authorities);
        this.token = token;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public static long getSerialversionuid() {
        return serialVersionUID;
    }

}
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.context.support.MessageSourceAccessor;

public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {
//	自定义对象校验短信验证码是否正确
	private static final MobileService mobileService =ContextHolder.getbean(MobileService.class);
//	
	@Autowired
	private SessionRegistry sessionRegistry;
    
	public JwtLoginFilter(AuthenticationManager authManager,
                          AuthenticationSuccessHandler successHandler,
                          AuthenticationFailureHandler failureHandler,
                          ApplicationEventPublisher eventPublisher,
						  MessageSourceAccessor messages,
						  SessionRegistry sessionRegistry) {
		setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login","POST"));
        setAuthenticationManager(authManager);
        setAuthenticationSuccessHandler(successHandler);
        setAuthenticationFailureHandler(failureHandler);
        setApplicationEventPublisher(eventPublisher);
		this.sessionRegistry=sessionRegistry;
		//messages用于各项验证码失败类抛出异常处理
		this.messages=messages;
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        // POST 请求 /login 登录时拦截, 由此方法触发执行登录认证流程,可以在此覆写整个登录认证逻辑
        super.doFilter(req, res, chain);
    }


    /**
     * 此过滤器的用户名密码默认从request.getParameter()获取
     * 在此做验证码的验证
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
	
		String username=request.getParameter("username");
		String password=request.getParameter("password");
		String message=request.getParameter("message");
		String phone=request.getParameter("phone");
		//账户和密码
        if (username == null) {
            username = "";
        }
        if (password == null) {
            password = "";
        }
		//先验证用户名和密码
		JwtAuthenticatioToken authRequest=new JwtAuthenticatioToken(authRequest);
		this.setDetails(request,authRequest);
		authentication auth=getAuthenticationManager().authenticate(authRequest);
        if(phone)
		{
			boolean status=mobileService.checkMessage(username,phone,message);
			if (status==false)
			{
				RedirectStrategy redirect=new DefaultRedirectStrategy();
				//messages用于各项验证码失败类抛出异常的翻译器
				request.getSession(true).setAttribute("SPRING_SECURITY_LAST_EXCEPTION",new Exception(messages.getMessage("JwtAuthenticationProvider.credentialsExpired")));
				redirect.setRedirect(request,response,"/login.jsp?error=404");
				return null;
			}
		}
		SecurityContextHolder.getContext().setAuthentication(auth);
		String  hello=request.getSession().getId();
		sessionRegistry.registerNewSession(hello,auth.getPrincipal());
		return auth;
    }
	protected void setDetails(HttpServletRequest request,JwtAuthenticatioToken authRequest)
	{
		authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要实现Spring Security自定义短信验证码登录,需要遵循以下步骤: 1. 配置短信验证码的过滤器 在Spring Security的配置类中,添加一个短信验证码过滤器,用于拦截短信验证码登录请求,并校验验证码是否正确。可以参考以下代码: ``` @Bean public SmsCodeFilter smsCodeFilter() throws Exception { SmsCodeFilter smsCodeFilter = new SmsCodeFilter(); smsCodeFilter.setAuthenticationManager(authenticationManagerBean()); smsCodeFilter.setAuthenticationFailureHandler(authenticationFailureHandler()); return smsCodeFilter; } ``` 2. 实现短信验证码校验逻辑 创建一个实现了`AuthenticationProvider`接口的短信验证码认证提供者,并在其中实现短信验证码校验逻辑。可以参考以下代码: ``` @Component public class SmsCodeAuthenticationProvider implements AuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication; String mobile = (String) authenticationToken.getPrincipal(); String smsCode = (String) authenticationToken.getCredentials(); // 校验短信验证码 if (smsCodeIsValid(mobile, smsCode)) { // 构造认证通过的令牌 SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(mobile); authenticationResult.setDetails(authenticationToken.getDetails()); return authenticationResult; } else { throw new BadCredentialsException("短信验证码不正确"); } } private boolean smsCodeIsValid(String mobile, String smsCode) { // 根据手机号和短信验证码进行校验 // ... return true; } @Override public boolean supports(Class<?> authentication) { return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication); } } ``` 3. 配置AuthenticationManager 在Spring Security的配置类中,配置`AuthenticationManager`,并将自定义短信验证码认证提供者加入到其中。可以参考以下代码: ``` @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(smsCodeAuthenticationProvider()); } ``` 4. 配置登录接口 在Spring Security的配置类中,配置短信验证码登录登录接口。可以参考以下代码: ``` @Override protected void configure(HttpSecurity http) throws Exception { http.addFilterBefore(smsCodeFilter(), UsernamePasswordAuthenticationFilter.class) .authorizeRequests() .antMatchers("/sms-login").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .and() .csrf().disable(); } ``` 5. 发送短信验证码登录接口中,添加发送短信验证码的逻辑。可以参考以下代码: ``` @PostMapping("/sms-code") @ResponseBody public String sendSmsCode(@RequestParam String mobile) { // 发送短信验证码 // ... return "success"; } ``` 以上就是Spring Security自定义短信验证码登录的实现步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值