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个之多.我们今天利用的是其中的一个,后续给大家讲解其他过滤器的作用,有错误之处还望指正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值