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

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

  1. 模仿UsernamePasswordAuthenticationToken创建短信验证码的token类SmsAuthenticationToken
/**
 * 手机验证码认证token
 *
 * @author shipc
 * @date 2021/12/13 21:22
 */
public class SmsAuthenticationToken extends AbstractAuthenticationToken {
    private static final long serialVersionUID = 531L;
    /**
     * 手机号 | 用户信息
     */
    private final Object principal;
    /**
     * 验证码
     */
    private Object credentials;


    public SmsAuthenticationToken(Object principal, Object credentials) {
        super(null);
        this.principal = principal;
        this.credentials = credentials;
        this.setAuthenticated(false);
    }

    public SmsAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        this.setAuthenticated(true);
    }

    @Override
    public Object getCredentials() {
        return credentials;
    }

    @Override
    public Object getPrincipal() {
        return principal;
    }


    @Override
    public void eraseCredentials() {
        super.eraseCredentials();
        this.credentials = null;
    }
}

  1. 模仿UsernamePasswordAuthenticationFilter创建处理短信验证码的过滤器
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
    private String usernameParameter = "username";
    private String passwordParameter = "password";
    private boolean postOnly = true;

    public UsernamePasswordAuthenticationFilter() {
        super(new AntPathRequestMatcher("/login", "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 username = this.obtainUsername(request);
            String password = this.obtainPassword(request);
           
            username = username.trim();
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            this.setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }

    @Nullable
    protected String obtainPassword(HttpServletRequest request) {
        return request.getParameter(this.passwordParameter);
    }

    @Nullable
    protected String obtainUsername(HttpServletRequest request) {
        return request.getParameter(this.usernameParameter);
    }

    protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

    public void setUsernameParameter(String usernameParameter) {
        Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
        this.usernameParameter = usernameParameter;
    }

    public void setPasswordParameter(String passwordParameter) {
        Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
        this.passwordParameter = passwordParameter;
    }

    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }

    public final String getUsernameParameter() {
        return this.usernameParameter;
    }

    public final String getPasswordParameter() {
        return this.passwordParameter;
    }
}

  1. 模仿DaoAuthenticationProvider创建处理SmsAuthenticationToken的Provider
/**
 * 短信验证登录provider
 *
 * @author shipc
 * @date 2021/12/13 21:47
 */
public class SmsAuthenticationProvider implements AuthenticationProvider {

    private SmsUserDetailsService userDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        final SmsAuthenticationToken smsAuthenticationToken = (SmsAuthenticationToken) authentication;

        final String mobile = smsAuthenticationToken.getPrincipal() == null ? "" :
                smsAuthenticationToken.getPrincipal().toString();
        final String code = smsAuthenticationToken.getCredentials().toString();

        if (code == null) {
            throw new SmsAuthenticationException(SmsAuthenticationHandler.AuthenticationStatus.NO_VERIFY_CODE);
        }

        checkSmsCode(mobile, code);

        // 根据手机号获取用户信息
        final UserDetails userDetails = userDetailsService.loadUserByUsername(mobile);
        return createSuccessAuthenticationToken(smsAuthenticationToken, userDetails);
    }

    private SmsAuthenticationToken createSuccessAuthenticationToken(SmsAuthenticationToken smsAuthenticationToken, UserDetails userDetails) {
        // 验证成功后,构造一个已经认证的token并返回
        final SmsAuthenticationToken authenticationToken = new SmsAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        authenticationToken.setDetails(smsAuthenticationToken.getDetails());
        return authenticationToken;
    }

    public void setUserDetailsService(SmsUserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    /**
     * 校验手机号和验证码
     * @param mobile 手机号
     * @param code 验证码
     */
    private void checkSmsCode(String mobile, String code) {
        final boolean flag = userDetailsService.checkSmsCode(mobile, code);
        if (!flag) {
            throw new SmsAuthenticationException(SmsAuthenticationHandler.AuthenticationStatus.BAD_VERIFY_CODE);
        }
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return SmsAuthenticationToken.class.isAssignableFrom(aClass);
    }
}
  1. 模仿UserDetailsService
/**
 * 手机号验证码用户service
 *
 * @author shipc
 * @date 2021/12/15 00:22
 */
public interface SmsUserDetailsService extends UserDetailsService {
    /**
     * 校验验证码
     * @param mobile 手机号
     * @param code 验证码
     * @return true/false
     */
    boolean checkSmsCode(String mobile, String code);
}
  1. 创建配置类SmsCodeAuthenticationSecurityConfig
/**
 * 手机验证码认证配置
 *
 * @author shipc
 * @date 2021/12/13 22:43
 */
@Configuration
public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

    @Autowired
    private SmsAuthenticationHandler smsAuthenticationHandler;
    @Autowired
    private MobileUserDetailsServiceImpl mobileUserDetailsService;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        final SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
        // 设置 authenticationManager, 不设置 认证流程会断掉
        smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
        // 扩展认证信息
        smsCodeAuthenticationFilter.setAuthenticationDetailsSource(new ExtendWebAuthenticationDetailsSource());

        // 设置认证成功处理器
        smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(smsAuthenticationHandler);
        // 设置认证失败处理器
        smsCodeAuthenticationFilter.setAuthenticationFailureHandler(smsAuthenticationHandler);

        final SmsAuthenticationProvider smsAuthenticationProvider = new SmsAuthenticationProvider();
        smsAuthenticationProvider.setUserDetailsService(mobileUserDetailsService);


        // 将短信验证过滤器添加到 security 中
        http.authenticationProvider(smsAuthenticationProvider)
                .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }
}
  1. 创建配置类WebSecurityConfig继承WebSecurityConfigurerAdapter,实现void configure(HttpSecurity http)方法。
@Override
    protected void configure(HttpSecurity http) throws Exception {
        // ---- 其他省略
      
        // 应用短信验证码登录
        http.apply(smsCodeAuthenticationSecurityConfig);

        http.csrf().disable();
    }
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超人@不会飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值