oauth2 验证码拦截filter

package com.huajie.cloud.auth.config;

/**
 * Description:
 *
 * @author IceZhang
 * Copyright  2018-2019  创捷运维智能科技有限公司
 * All rights reserved.
 * @version: 1.0
 * Reversion:
 * 1.0 - 新建
 */
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.NullRememberMeServices;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 参考 {@link org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter}
 * @version 0.1
 * @auth admin.
 * @time 2018/2/25 13:04
 * @since 0.1
 */
public class CodeFilter extends GenericFilterBean {

    /** Logger available to subclasses */
    protected final Log logger = LogFactory.getLog(getClass());

    //验证码拦截路径
    private static final String CODE_ANT_URL = "/oauth/mobile";
    private static final String SPRING_SECURITY_FORM_CAPTCHA_KEY = "code";

    private String captchaParameter = SPRING_SECURITY_FORM_CAPTCHA_KEY;

    private boolean postOnly = true;

    //请求路径匹配
    private RequestMatcher requiresAuthenticationRequestMatcher;

    private RememberMeServices rememberMeServices = new NullRememberMeServices();
    private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler(CODE_ANT_URL); //设置验证失败重定向路径

    public CodeFilter() {
        this.requiresAuthenticationRequestMatcher = new AntPathRequestMatcher(CODE_ANT_URL, "POST");
    }

    public CodeFilter(RequestMatcher requiresAuthenticationRequestMatcher) {
        Assert.notNull(requiresAuthenticationRequestMatcher,"requiresAuthenticationRequestMatcher cannot be null");
        this.requiresAuthenticationRequestMatcher = requiresAuthenticationRequestMatcher;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        // 不是需要过滤的路径,执行下一个过滤器
        if (!requiresAuthentication(request, response)) {
            filterChain.doFilter(request, response);
            return;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Request is to process authentication");
        }

        Authentication authResult;
        try {
            authResult = this.attemptAuthentication(request, response);
            if (authResult == null) {
                logger.error("Authentication is null!");
                // return immediately as subclass has indicated that it hasn't completed
                // authentication
                return;
            }

        } catch (InternalAuthenticationServiceException failed) {
            logger.error("An internal error occurred while trying to authenticate the user.",failed);
            return;
        } catch (AuthenticationException failed) {
            logger.error("Authentication failed.", failed);
            //Authentication failed
            unsuccessfulAuthentication(request, response, failed);
            return;
        }

        //认证成功,执行下个过滤器
        filterChain.doFilter(request, response);
    }

    private Authentication attemptAuthentication(HttpServletRequest request,
                                                 HttpServletResponse response) throws AuthenticationException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }
        //获取验证码
        String captcha = request.getParameter(captchaParameter);

        if (captcha == null) {
            captcha = "";
        }

        captcha = captcha.trim();

        CodeAuthenticationToken authRequest = new CodeAuthenticationToken(captcha);

        //这里可以直接省略掉,用provider直接验证
        CodeAuthenticationManager manager = new CodeAuthenticationManager(new CodeAuthenticationProvider());
        return manager.authenticate(authRequest);
    }

    /**
     * 比较需要过滤的请求路径
     *
     * @param request
     * @param response
     * @return
     */
    protected boolean requiresAuthentication(HttpServletRequest request,
                                             HttpServletResponse response) {
        return requiresAuthenticationRequestMatcher.matches(request);
    }

    /**
     * 处理验证码认证失败
     * 参考 {@link org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter}
     * Default behaviour for unsuccessful authentication.
     * <ol>
     * <li>Clears the {@link SecurityContextHolder}</li>
     * <li>Stores the exception in the session (if it exists or
     * <tt>allowSesssionCreation</tt> is set to <tt>true</tt>)</li>
     * <li>Informs the configured <tt>RememberMeServices</tt> of the failed login</li>
     * <li>Delegates additional behaviour to the {@link AuthenticationFailureHandler}.</li>
     * </ol>
     */
    protected void unsuccessfulAuthentication(HttpServletRequest request,
                                              HttpServletResponse response, AuthenticationException failed)
            throws IOException, ServletException {
        SecurityContextHolder.clearContext();

        if (logger.isDebugEnabled()) {
            logger.debug("Authentication request failed: " + failed.toString(), failed);
            logger.debug("Updated SecurityContextHolder to contain null Authentication");
            logger.debug("Delegating to authentication failure handler " + failureHandler);
        }

        rememberMeServices.loginFail(request, response);

        failureHandler.onAuthenticationFailure(request, response, failed);
    }
}

 

package com.huajie.cloud.auth.config;

/**
 * Description:
 *
 * @author IceZhang
 * Copyright  2018-2019  创捷运维智能科技有限公司
 * All rights reserved.
 * @version: 1.0
 * Reversion:
 * 1.0 - 新建
 */
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

import java.util.Collection;

/**
 * 参考 {@link org.springframework.security.authentication.UsernamePasswordAuthenticationToken}
 * @version 0.1
 * @auth admin.
 * @time 2018/2/25 11:24
 * @since 0.1
 */
public class CodeAuthenticationToken extends AbstractAuthenticationToken{

    /**
     * 验证码
     */
    private Object captcha;

    /**
     * 验证码验证标识
     * true:通过
     * false:错误
     */
    private boolean flag;

    public CodeAuthenticationToken() {
        super(null);
    }

    public CodeAuthenticationToken(Object captcha) {
        super(null);
        this.captcha = captcha;
        this.flag = false;
        setAuthenticated(false);
    }

    public CodeAuthenticationToken(Collection<? extends GrantedAuthority> authorities, Object captcha) {
        super(authorities);
        this.captcha = captcha;
        this.flag = false;
        super.setAuthenticated(true); // must use super, as we override
    }

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

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

    public Object getCaptcha() {
        return captcha;
    }

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


    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

 

package com.huajie.cloud.auth.config;

/**
 * Description:
 *
 * @author IceZhang
 * Copyright  2018-2019  创捷运维智能科技有限公司
 * All rights reserved.
 * @version: 1.0
 * Reversion:
 * 1.0 - 新建
 */
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.util.Assert;

/**
 * 验证码验证类
 * 参考 {@link org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider}
 * @version 0.1
 * @auth admin.
 * @time 2018/2/25 11:32
 * @since 0.1
 */
public class CodeAuthenticationProvider implements AuthenticationProvider {

    /** Logger available to subclasses */
    protected final Log logger = LogFactory.getLog(getClass());

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        logger.debug("custom captcha authentication");

        Assert.isInstanceOf(CodeAuthenticationToken.class, authentication,"错误的类");

        CodeAuthenticationToken custCaptchaToken = (CodeAuthenticationToken) authentication;
        String captcha = custCaptchaToken.getCaptcha().toString();

        if(captcha.equals("")){
            logger.debug("验证码为空");
            throw new CodeAuthenticationException("验证码错误!");
        }

        //写死一个验证码,具体可以自己修改
        if(!captcha.equals("1000")){
            logger.debug("验证码错误");
            throw new CodeAuthenticationException("验证码错误!");
        }

        //返回验证成功对象
        custCaptchaToken.setFlag(true);
        return custCaptchaToken;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return (CodeAuthenticationToken.class.isAssignableFrom(authentication));
    }
}

 

package com.huajie.cloud.auth.config;

/**
 * Description:
 *
 * @author IceZhang
 * Copyright  2018-2019  创捷运维智能科技有限公司
 * All rights reserved.
 * @version: 1.0
 * Reversion:
 * 1.0 - 新建
 */
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.util.Assert;

/**
 * 由于是拓展spring security的验证,不能在ProviderManager中调用到,这里单独处理一个验证管理器。
 * @version 0.1
 * @auth admin.
 * @time 2018/2/25 11:27
 * @since 0.1
 */
public class CodeAuthenticationManager implements AuthenticationManager {

    /**
     * 自己实现的验证码认证器
     */
    private AuthenticationProvider provider;

    public CodeAuthenticationManager(AuthenticationProvider provider) {
        Assert.notNull(provider, "provider cannot be null");
        this.provider = provider;
    }

    public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        return this.provider.authenticate(authentication);
    }
}

 

package com.huajie.cloud.auth.config;

/**
 * Description:
 *
 * @author IceZhang
 * Copyright  2018-2019  创捷运维智能科技有限公司
 * All rights reserved.
 * @version: 1.0
 * Reversion:
 * 1.0 - 新建
 */
import org.springframework.security.core.AuthenticationException;

/**
 * 自定义验证码验证失败异常
 * @version 0.1
 * @auth admin.
 * @time 2018/2/25 11:37
 * @since 0.1
 */
public class CodeAuthenticationException extends AuthenticationException {

    public CodeAuthenticationException(String msg, Throwable t) {
        super(msg, t);
    }

    public CodeAuthenticationException(String msg) {
        super(msg);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值