shiro框架之擴展增加短信隨機碼認證

        閒暇功夫想起來2年前的一場面試,當時對shiro算是剛剛入門,大佬問道怎麼在shiro基礎上增加短信隨機碼認證?當時確實不知道在怎麼做,今天給大家說下如何擴展shiro.

        默認大家對shiro的應用是熟悉的,我們直接看登錄認證方法,前臺收到用戶名,密碼後封裝到UsernamePasswordToken中,然後調用Subject.login方法,將UsernamePasswordToken傳入,經行驗證

        原有的UsernamePasswordToken只有name  password remember host等字段,我們創建自定義的UsernamePasswordToken繼承shiro的UsernamePasswordToken  增加一個密碼類型 int passwdType 1爲密碼認證.0爲隨機碼認證

public class UsernamePasswordToken extends org.apache.shiro.authc.UsernamePasswordToken {

    //密碼類型  1爲密碼  0位隨機碼
    private int passwdTyppe;

    public int getPasswdTyppe() {
        return passwdTyppe;
    }

    public void setPasswdTyppe(int passwdTyppe) {
        this.passwdTyppe = passwdTyppe;
    }


    public UsernamePasswordToken(String username, String password,int passwdTyppe) {
        super(username,password);
        this.passwdTyppe=passwdTyppe;
    }


    public Object getPasswdCode() {
        return this.getPasswdTyppe();
    }
}

        此時再登錄方法中直接將name password  passwdType 封裝成我們自己的UsernamePasswordToken,在自定義realm的

doGetAuthenticationInfo發中稍作改造如下,採用aes加密
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        LOG.info("開始進行密碼口令驗證");
        //效驗用戶
        String userName= (String)authenticationToken.getPrincipal();
        UserToken user=shiroService.getUserTokenByUserName(userName);
        if(authenticationToken instanceof UsernamePasswordToken){
            int passwdTyppe = ((UsernamePasswordToken) authenticationToken).getPasswdTyppe();
            user.setPwdAuthType(passwdTyppe);
        }
        if(user==null){
            LOG.error("該{}用戶不存在.禁止登陸",userName);
            throw new AuthenticationException("用戶名或者密碼錯誤");
        }
        if(user.getExpirationDate().getTime()<new Date().getTime()){
            throw new ExpiredCredentialsException("用戶密碼已經過期,請聯繫中國電信集團統一賬號認證平臺");
        }
        String plianPasswd =(String) authenticationToken.getCredentials();
        if(plianPasswd==null||plianPasswd.length()<1){
            LOG.error("該{}用戶輸入密碼爲空.禁止登陸",userName);
            throw new AuthenticationException("用戶名或者密碼錯誤");
        }
        //驗證用戶密碼
        return new SimpleAuthenticationInfo(user, user.getPasswd(),getName());
    }

           由於我們還要將密碼管理器進行改造.我們繼承shiro的密碼管理器SimpleCredentialsMatcher重寫doCredentialsMatch

public class AesCredentialsMatcher extends SimpleCredentialsMatcher {

    private static final Logger log= LoggerFactory.getLogger(AesCredentialsMatcher.class);

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        UsernamePasswordToken passwordToken = (UsernamePasswordToken) token;
        String plainPasswd = String.valueOf(passwordToken.getPassword());
        int pwdAuthType = passwordToken.getPasswdTyppe();
        //如果是AES則進行加密驗證
        if(pwdAuthType==1){
            return  AESUtil.getInstance().validatePassword(plainPasswd, String.valueOf(getCredentials(info)));
        }else {
            log.warn("========進行隨機碼認證,對應隨機碼爲:{},傳過來得隨機碼:{}",
                    CacheUtil.getInstanceCache(CacheGroupIdCode.GROUPID_APP_RANDOM)
                    .get((String)passwordToken.getPrincipal()),plainPasswd);
            //如果是隨機碼則直接從緩存中獲取進行對比
            return CacheUtil.getInstanceCache(CacheGroupIdCode.GROUPID_APP_RANDOM)
                    .get((String)passwordToken.getPrincipal()).equals(plainPasswd);
        }
    }
}

            此時運行發現直接報  ClassCastException轉型異常了,我們雖然繼承了shiro的UsernamepasswordToken,並沒有去實例化它,這纔是引起問題的根源,問題發現了要如何解決它呢?我們在配置的使用並並未制定表單過濾器,此時默認使用的是FormAuthenticationFilter,而Token默認使用的也是UsernamePasswordToken,於是我們打開FormAuthenticationFiter源碼,找到它的所有方法發現其父類AuthenticatingFilter中有有createToken方法如下:

打開次方法的源碼如圖:

protected AuthenticationToken createToken(String username, String password, boolean rememberMe, String host) {
        return new UsernamePasswordToken(username, password, rememberMe, host);
    }

終於找到了,就在這裏實例化的UsernamePasswordToken.我們只有重寫此方法了,創建自己的LoginAuthenticationFilter去繼承FormAuthenticationFilter

public class LoginAuthenticationFilter extends FormAuthenticationFilter {
    @Override
    protected AuthenticationToken createToken(String username, String password,ServletRequest request, ServletResponse response) {
        return new UsernamePasswordToken(username,password,Integer.valueOf(request.getParameter("passwdTyppe")));
    }
}

      將我麼自定義的過濾器交給shiro過濾器的FactoryBean管理.在啓動,我們前臺傳入密碼類型爲短信隨機碼,此時可以正常認證通過了.至此我們shiro擴展短信隨機碼認證成功了.

      shiro的默認過濾器有11個之多.我們今天利用的是其中的一個,後續給大家講解其他過濾器的作用,有錯誤之處還望指正

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章