1. 程式人生 > >CAS5.1.8新增驗證碼

CAS5.1.8新增驗證碼

老生常談的題目了吧。但是,要在網上找出一篇能跑又看得明白的相關教程,還真不容易。

我費了九牛二虎之力,終於添加了驗證碼功能,記錄如下。

一、執行結果
在這裡插入圖片描述

二、程式碼結構
程式碼是純新增的,不想修改現有的程式碼。CAS實在是太龐大了,idea載入都要幾分鐘。作為一個java小白,基本看不懂裡面的程式碼,搞不清裡面的結構,怎麼改?能不動就不動,可以動也不動,打死都不動。

新加的程式碼,就在“lt”這個目錄裡,如圖所示。
在這裡插入圖片描述

三、程式碼講解
程式碼呢,基本也是抄過來的(原始碼好像是在這裡),我也不大懂,所以只能按照自己的理解說一下,說錯勿怪。

程式碼檔案職責以及呼叫關係。
在這裡插入圖片描述


梳理一下,從上到下:
1、在spring.factories裡註冊自定義配置類

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.landtool.sso.support.auth.config.CustomAuthenticationEventExecutionPlanConfiguration

2、自定義配置類裡負責將生成驗證碼、驗證流程、驗證碼比較類注入

    @Bean
    public CaptchaController captchaController(){
        return
new CaptchaController(); } @Bean public CaptchaAction captchaAction(){ return new CaptchaAction(); } @Bean @Order(1) public CasWebflowConfigurer defaultWebflowConfigurer() { final CustDefaultWebflowConfigurer c = new CustDefaultWebflowConfigurer(flowBuilderServices,
loginFlowDefinitionRegistry); return c; }

3、驗證碼比較和流程,需要用到自定義憑據
憑據裡儲存了驗證碼,然後驗證的時候,讀取這個憑據。憑據一直都有,預設的,但沒有驗證碼。這裡過載了憑據類,將驗證碼加了進去。然後又過載了流程,流程裡引用了憑據。

自定義憑據CustUsernamePasswordCredential

public class CustUsernamePasswordCredential extends UsernamePasswordCredential {

    private static final long serialVersionUID = 1767227441947916650L;

    private String captcha;

    public CustUsernamePasswordCredential() {
    }

    public CustUsernamePasswordCredential(String userName, String password, String captcha) {
        super(userName, password);
        this.captcha = captcha;
    }

    public String getCaptcha() {
        return captcha;
    }

    public void setCaptcha(String captcha) {
        this.captcha = captcha;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof CustUsernamePasswordCredential)) return false;
        if (!super.equals(o)) return false;
        CustUsernamePasswordCredential that = (CustUsernamePasswordCredential) o;
        return Objects.equals(getCaptcha(), that.getCaptcha());
    }

    @Override
    public int hashCode() {
        return Objects.hash(super.hashCode(), getCaptcha());
    }
}

自定義流程

/**
 * 參考 {@link org.apereo.cas.web.flow.DefaultWebflowConfigurer}
 */
public class CustDefaultWebflowConfigurer extends DefaultWebflowConfigurer {

    public CustDefaultWebflowConfigurer(FlowBuilderServices flowBuilderServices,
                                        FlowDefinitionRegistry flowDefinitionRegistry) {
        super(flowBuilderServices, flowDefinitionRegistry);
    }

    /**
     * 重寫 {@link org.apereo.cas.web.flow.DefaultWebflowConfigurer} 的方法
     *
     * Create remember me authn webflow config.
     *
     * @param flow the flow
     */
    protected void createRememberMeAuthnWebflowConfig(final Flow flow) {
        /**
         *用我們拓展CustUsernamePasswordCredential來替換原來的
         * {@link org.apereo.cas.authentication.UsernamePasswordCredential}
         */
        super.createFlowVariable(flow, CasWebflowConstants.VAR_ID_CREDENTIAL, CustUsernamePasswordCredential.class);
    }
}

驗證“驗證碼“CaptchaAction.java

    @Override
    protected Event doExecute(RequestContext context) throws Exception {
        HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getNativeResponse();
        HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getNativeRequest();
        HttpSession session = request.getSession();
        String captchaKey = (String) session.getAttribute(CAPTCHA_KEY);
        if(null == captchaKey){
            LOGGER.error("驗證碼驗證異常");
            return this.getError(context,CUSTOM_CAPTCHA_FAIL_EXCEPTION);
        }

		//憑據--------------------
        CustUsernamePasswordCredential credential = (CustUsernamePasswordCredential) WebUtils.getCredential(context);
        if(null == credential || (null == credential.getCaptcha() || "".equals(credential.getCaptcha().trim()))){
            LOGGER.error("請輸入驗證碼");
            return this.getError(context,CUSTOM_CAPTCHA_FAIL_ERROR);
        }

        String requestCaptcha = credential.getCaptcha();
        if(!captchaKey.toLowerCase().equals(requestCaptcha.toLowerCase())){
            LOGGER.error("驗證碼錯誤");
            return this.getError(context,CUSTOM_CAPTCHA_FAIL_ERROR);
        }

        return super.success();
    }

4、前端訪問的是控制器"生成驗證碼"
\webapp\resources\templates\fragments/loginform.html

        <section class="row">
            <label for="captcha" th:utext="#{screen.welcome.label.captcha}"/>

            <div>
                <input class="required"
                       type="text"
                       id="captcha"
                       name="captcha"
                       size="25"
                       tabindex="2"
                       autocomplete="off"/>
                <img th:src="@{/captcha/get}"/>
            </div>
        </section>

可以發現,其中的核心是配置類CustomAuthenticationEventExecutionPlanConfiguration(為什麼要這麼長,裝逼吧),它負責組織、注入各種例項。裡面的這些神祕字眼“@Bean”呀,“@Autowired”呀,稱之為註解。感覺跟C#裡的Attitude(特性)差不多,宣告一下,Spring容器就知道該幹什麼了。小字眼,做大事。

注入,這裡面屢屢提到注入,很黃很暴力。其實,注入(DI)與依賴倒置、控制反轉(IoC)都是面向介面程式設計的一種表述(見拙作)。何謂注入?編碼的時候面向介面程式設計,比如A程式碼裡使用了物件B,但它不是直接構造一個物件B,而是使用了介面IB,類B實現了介面IB,在執行過程中,將B的例項賦給介面IB,此為注入。在這裡,這個配置類就是配置一下,用哪些例項來進行注入。

附錄:
完整的LT程式碼