zoukankan      html  css  js  c++  java
  • spring security 自定义短信验证登录

      在之前的博客中我们实现了基于验证码的登陆方式。但是我们仅仅实现了通过图片验证码的登录,现在我们基于security 实现一下基于 短信验证码的登录。

      基于之前对于 security 的了解,我们知道了要实现一个验证的基本流程,其中最重要的是  AbstractAuthenticationToken(令牌类)、AuthenticationProvider(认证类)、AbstractAuthenticationProcessingFilter (过滤器类)这三个类。

      我们可以基于用户密码的登陆方式来实现,对于用户名密码的登录涉及的三个类为:UsernamePasswordAuthenticationToken、UsernamePasswordAuthenticationFilter、而 AuthenticationProvider 在之前我们也是自定义的 ,修改一下就可以用。

    1. 编写 SmsCodeAuthenticationToken

    /**
     * Create with IntelliJ IDEA
     * User: Wuzhenzhao
     * Date: 2019/3/13
     * Time: 18:13
     * Description: 参考 UsernamePasswordAuthenticationToken 写
     * 封装登录信息
     */
    public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
    
        private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
    
        // ~ Instance fields
        // ================================================================================================
    
        private final Object principal;
    
        //这个在 UsernamePasswordAuthenticationToken 是登录密码,不需要 干掉。
        private Object credentials;
    
        // ~ Constructors
        // ===================================================================================================
    
        /**
         * This constructor can be safely used by any code that wishes to create a
         * <code>UsernamePasswordAuthenticationToken</code>, as the {@link #isAuthenticated()}
         * will return <code>false</code>.
         *
         */
        public SmsCodeAuthenticationToken(String mobile) {
            super(null);
            this.principal = mobile;
            setAuthenticated(false);
        }
    
        /**
         * This constructor should only be used by <code>AuthenticationManager</code> or
         * <code>AuthenticationProvider</code> implementations that are satisfied with
         * producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>)
         * authentication token.
         *
         * @param principal
         * @param authorities
         */
        public SmsCodeAuthenticationToken(Object principal,
                Collection<? extends GrantedAuthority> authorities) {
            super(authorities);
            this.principal = principal;
            super.setAuthenticated(true); // must use super, as we override
        }
    
        // ~ Methods
        // ========================================================================================================
    
        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");
            }
    
            super.setAuthenticated(false);
        }
    
        @Override
        public void eraseCredentials() {
            super.eraseCredentials();
        }
    }

     2. SmsCodeAuthenticationProvider ,用于短信登陆的验证

    public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
    
        private UserDetailsService userDetailsService;
    
        /*
         * (non-Javadoc)
         *
         * @see org.springframework.security.authentication.AuthenticationProvider#
         * authenticate(org.springframework.security.core.Authentication)
         */
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    
            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;
        }
    
        /*
         * (non-Javadoc)
         *
         * @see org.springframework.security.authentication.AuthenticationProvider#
         * supports(java.lang.Class)
         */
        @Override
        public boolean supports(Class<?> authentication) {
            return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
        }
    
        public UserDetailsService getUserDetailsService() {
            return userDetailsService;
        }
    
        public void setUserDetailsService(UserDetailsService userDetailsService) {
            this.userDetailsService = userDetailsService;
        }
    
    }

    3.  SmsCodeAuthenticationFilter  拦截短信登陆请求

    /**
     * Create with IntelliJ IDEA
     * User: Wuzhenzhao
     * Date: 2019/3/13
     * Time: 18:13
     * Description: 参考 UsernamePasswordAuthenticationFilter
     */
    public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
        // ~ Static fields/initializers
        // =====================================================================================
        /**
         * 发送短信验证码 或 验证短信验证码时,传递手机号的参数的名称
         */
        private String mobileParameter = "mobile";
        private boolean postOnly = true;
    
        // ~ Constructors
        // ===================================================================================================
    
        public SmsCodeAuthenticationFilter() {
            /**
             * 默认的手机验证码登录请求处理url
             * http://localhost:8889/code/sms?mobile=13888888888
             */
            super(new AntPathRequestMatcher("/authentication/mobile", "POST"));
        }
    
        // ~ Methods
        // ========================================================================================================
    
        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
                throws AuthenticationException {
            if (postOnly && !request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
            }
    
            String mobile = obtainMobile(request);
    
            if (mobile == null) {
                mobile = "";
            }
    
            mobile = mobile.trim();
    
            SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);
    
            // Allow subclasses to set the "details" property
            setDetails(request, authRequest);
    
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    
    
        /**
         * 获取手机号
         */
        protected String obtainMobile(HttpServletRequest request) {
            return request.getParameter(mobileParameter);
        }
    
        /**
         * Provided so that subclasses may configure what is put into the
         * authentication request's details property.
         *
         * @param request
         *            that an authentication request is being created for
         * @param authRequest
         *            the authentication request object that should have its details
         *            set
         */
        protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
            authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
        }
    
        /**
         * Sets the parameter name which will be used to obtain the username from
         * the login request.
         *
         * @param usernameParameter
         *            the parameter name. Defaults to "username".
         */
        public void setMobileParameter(String usernameParameter) {
            Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
            this.mobileParameter = usernameParameter;
        }
    
    
        /**
         * Defines whether only HTTP POST requests will be allowed by this filter.
         * If set to true, and an authentication request is received which is not a
         * POST request, an exception will be raised immediately and authentication
         * will not be attempted. The <tt>unsuccessfulAuthentication()</tt> method
         * will be called as if handling a failed authentication.
         * <p>
         * Defaults to <tt>true</tt> but may be overridden by subclasses.
         */
        public void setPostOnly(boolean postOnly) {
            this.postOnly = postOnly;
        }
    
        public final String getMobileParameter() {
            return mobileParameter;
        }
    
    }

    4.在 security 配置类中注入:

    @Configuration
    @EnableWebSecurity// 开启Security
    @EnableGlobalMethodSecurity(prePostEnabled = true)//开启Spring方法级安全
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
    
        @Autowired
        private MyAuthenticationProvider myAuthenticationProvider;
    
        @Autowired
        private MyAuthenctiationFailureHandler myAuthenctiationFailureHandler;
    
        @Autowired
        private MyUserDetailService myUserDetailService;
    
    
        // 自定义认证配置
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.authenticationProvider(myAuthenticationProvider);
            SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
            smsCodeAuthenticationProvider.setUserDetailsService(myUserDetailService);
            auth.authenticationProvider(smsCodeAuthenticationProvider);
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
         // .......省略部分代码
            SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
            smsCodeAuthenticationFilter.setAuthenticationManager(this.authenticationManager());
            smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);
            smsCodeAuthenticationFilter.setAuthenticationFailureHandler(myAuthenctiationFailureHandler);
    
            http.addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        }
    }

      对于提交登录得时候验证码的校验逻辑在 博客 :https://www.cnblogs.com/wuzhenzhao/p/13169023.html 中提到的 AbstractValidateCodeProcessor 进行验证。这样子先进行一个短信的发送,然后模拟一下登录就可用实现了.

  • 相关阅读:
    在 Windows 上测试 Redis Cluster的集群填坑笔记
    vmware安装黑苹果教程
    微信支付v3发布到iis时的证书问题
    Linux下安装SQL Server 2016(连接篇SQL Server on linux)
    Linux下安装SQL Server 2016(连接篇SQL Server on linux)
    Linux下安装SQL Server 2016(安装篇SQL Server on linux)
    Linux下安装SQL Server 2016(准备篇SQL Server on linux)
    客服端与服务端APP支付宝支付接口联调的那些坑
    ASP.NET MVC]WebAPI应用支持HTTPS的经验总结
    .net平台下C#socket通信(中)
  • 原文地址:https://www.cnblogs.com/wuzhenzhao/p/13232529.html
Copyright © 2011-2022 走看看