spring security 使用篇 短信认证

2018-10-15  本文已影响115人  怪诞140819

1.认证基本流程

认证基本流程

2.实现手机验证码登录

2.1 开发步骤

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

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);


    }
}
上一篇下一篇

猜你喜欢

热点阅读