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 进行验证。这样子先进行一个短信的发送,然后模拟一下登录就可用实现了.

  • 相关阅读:
    java DMO及增删改查代码的自动生成
    如果有一天苹果免费了,支付宝怎么办
    jquery实现页面交互的几个小例子
    android中的所谓观察者模式
    随笔——runnable勘误以及其他
    android 源码角度全方位理解filter
    android 你的onfocuschangelistener和android:state_hovered为何不起作用
    android 如何阻断seekbar的触摸事件
    java 传值和传引用
    Android内存优化(使用SparseArray和ArrayMap代替HashMap)
  • 原文地址:https://www.cnblogs.com/wuzhenzhao/p/13232529.html
Copyright © 2011-2022 走看看