美文网首页
spring security 使用篇 短信认证

spring security 使用篇 短信认证

作者: 怪诞140819 | 来源:发表于2018-10-15 11:14 被阅读115次

    1.认证基本流程

    认证基本流程

    2.实现手机验证码登录

    2.1 开发步骤

    对照上面的图我们大概知道分为以下几个步骤

    • (1)自定义一个AuthenticationToken继承自AbstractAuthenticationToken可以参考UsernamePasswordAuthenticationFilter
    • (2)自定义一个AuthenticationFilter继承自AbstractAuthenticationProcessingFilter可以参考``UsernamePasswordAuthenticationFilter
    • (3)自定义一个AuthenticationProvider实现AuthenticationProvider,这里定义具体的逻辑
      *(4)配置spring security使这些定义生效

    2.2 自定义SmsAuthenticationToken

    存放认证信息

    public class SmsAuthenticationToken extends AbstractAuthenticationToken {
    
        private static final long serialVersionUID = 420L;
        private  Object principal;
    
        public SmsAuthenticationToken(Object principal) {
            super((Collection)null);
            this.principal = principal;
            this.setAuthenticated(false);
        }
    
        public SmsAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
            super(authorities);
            this.principal = principal;
            super.setAuthenticated(true);
        }
    
    
        @Override
        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");
            } else {
                super.setAuthenticated(false);
            }
        }
    }
    

    2.3 自定义SmsAuthenticationFilter

    public class SmsAuthenticationFilter  extends AbstractAuthenticationProcessingFilter {
        public static final String GUAIDAN_MOBILE_KEY = "mobile";
        private String mobileParameter = "mobile";
        private boolean postOnly = true;
    
        public SmsAuthenticationFilter() {
            super(new AntPathRequestMatcher("/authentication/mobile", "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 mobile = this.obtainMobile(request);
                if (mobile == null) {
                    mobile = "";
                }
    
    
                mobile = mobile.trim();
                SmsAuthenticationToken authRequest = new SmsAuthenticationToken(mobile);
                this.setDetails(request, authRequest);
                return this.getAuthenticationManager().authenticate(authRequest);
            }
        }
    
    
    
        protected String obtainMobile(HttpServletRequest request) {
            return request.getParameter(this.mobileParameter);
        }
    
        protected void setDetails(HttpServletRequest request, SmsAuthenticationToken authRequest) {
            authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
        }
    
    
    
        public void setPostOnly(boolean postOnly) {
            this.postOnly = postOnly;
        }
    
        public String getMobileParameter() {
            return mobileParameter;
        }
    
        public void setMobileParameter(String mobileParameter) {
            this.mobileParameter = mobileParameter;
        }
    }
    
    

    2.4 自定义SmsAuthenticationProvider

    定义用户认证逻辑

    public class SmsAuthenticationProvider implements AuthenticationProvider {
    
        private UserDetailsService userDetailsService;
    
    
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            SmsAuthenticationToken smsAuthenticationToken = (SmsAuthenticationToken)(authentication);
            UserDetails user =  userDetailsService.loadUserByUsername((String) smsAuthenticationToken.getPrincipal());
            if(null == user){
                throw new InternalAuthenticationServiceException("无法获取用户信息");
            }
            SmsAuthenticationToken tokenResult = new SmsAuthenticationToken(smsAuthenticationToken.getPrincipal(),user.getAuthorities());
            tokenResult.setDetails(smsAuthenticationToken.getDetails());
            return tokenResult;
        }
    
        @Override
        public boolean supports(Class<?> authentication) {
            return SmsAuthenticationToken.class.isAssignableFrom(authentication);
        }
    
        public UserDetailsService getUserDetailsService() {
            return userDetailsService;
        }
    
        public void setUserDetailsService(UserDetailsService userDetailsService) {
            this.userDetailsService = userDetailsService;
        }
    }
    

    2.5 自定义SmsCodeValidateFilter验证手机验证码

    public class SmsCodeValidateFilter  extends OncePerRequestFilter implements InitializingBean {
    
        private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    
        private AuthenticationFailureHandler authenticationFailureHandler;
    
        private SecurityProperties securityProperties;
    
        private Set<String> urls = new HashSet<>();
    
        private AntPathMatcher pathMatcher = new AntPathMatcher();
    
        public SmsCodeValidateFilter(AuthenticationFailureHandler authenticationFailureHandler,SecurityProperties securityProperties) throws ServletException {
            this.authenticationFailureHandler = authenticationFailureHandler;
            this.securityProperties = securityProperties;
        }
    
    
        @Override
        public void afterPropertiesSet() throws ServletException{
            super.afterPropertiesSet();
            String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getValidate().getSms().getUrls(),",");
            if(null==configUrls){
                configUrls = new String[0];
            }
            for (String configUrl:configUrls){
                urls.add(configUrl);
            }
    
            urls.add("/authentication/mobile");
    
        }
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    
            boolean action = false;
            for(String url:urls){
                if(pathMatcher.match(url,request.getRequestURI())){
                    action = true;
                }
            }
    
            if(action){
               //对短信验证码进行验证
                try{
                    validate(request);
                }catch (ValidateImageCodeException e){
                    authenticationFailureHandler.onAuthenticationFailure(request,response,e);
                    return;
                }
            }
            filterChain.doFilter(request,response);
        }
    
        private void validate(HttpServletRequest request) {
            String imageCode = request.getParameter("smsCode");
            ServletWebRequest servletWebRequest = new ServletWebRequest(request);
            ValidateCode sessionImageCode = (ValidateCode) sessionStrategy.getAttribute(servletWebRequest, AbstractValidateGeneratorProcess.VALIDATE_CODE_KEY+"sms");
    
            if(StringUtils.isBlank(imageCode)){
                throw new ValidateImageCodeException("验证码为空");
            }
    
            if(null==sessionImageCode){
                throw new ValidateImageCodeException("验证码不存在");
            }
    
            if(LocalDateTime.now().isAfter(sessionImageCode.getExpiredTime())){
                sessionStrategy.removeAttribute(servletWebRequest, AbstractValidateGeneratorProcess.VALIDATE_CODE_KEY+"sms");
                throw new ValidateImageCodeException("验证码已过期");
            }
    
            if(!StringUtils.equalsIgnoreCase(imageCode,sessionImageCode.getCode())){
                throw new ValidateImageCodeException("验证码不匹配");
            }
            sessionStrategy.removeAttribute(servletWebRequest, AbstractValidateGeneratorProcess.VALIDATE_CODE_KEY+"sms");
        }
    }
    

    2.6 添加配置到spring security

    @Component
    public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
    
        @Autowired
        private AuthenticationSuccessHandler customAuthenticationSuccessHandler;
    
        @Autowired
        private AuthenticationFailureHandler customAuthenticationFailureHandler;
    
        @Autowired
        private UserDetailsService userDetailsService;
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            SmsAuthenticationFilter smsAuthenticationFilter = new SmsAuthenticationFilter();
            smsAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
            smsAuthenticationFilter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);
            smsAuthenticationFilter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);
    
            SmsAuthenticationProvider provider = new SmsAuthenticationProvider();
            provider.setUserDetailsService(userDetailsService);
    
            http.authenticationProvider(provider)
                .addFilterAfter(smsAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    
    
        }
    }
    

    相关文章

      网友评论

          本文标题:spring security 使用篇 短信认证

          本文链接:https://www.haomeiwen.com/subject/mxwoaftx.html