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程式碼