当前位置: 代码网 > it编程>编程语言>Java > Shrio框架实现自定义密码校验规则详解

Shrio框架实现自定义密码校验规则详解

2026年03月14日 Java 我要评论
shrio自己内置一些密码校验规则,也可以实现简单的自定义,比如算法类型,hash次数等,但是有时候我们有一些比较特殊的密码校验规则,需要自定义来实现1.shiro的密码校验是如何做的?我们在登录方法

shrio自己内置一些密码校验规则,也可以实现简单的自定义,比如算法类型,hash次数等,但是有时候我们有一些比较特殊的密码校验规则,需要自定义来实现

1.shiro的密码校验是如何做的?

我们在登录方法内做完参数校验,验证码匹配等基本工作之后,都会将用户名和密码通过subject.login(auth) 传入框架来做用户匹配,但是,是和什么地方的数据进行匹配呢?

usernamepasswordtoken auth = new usernamepasswordtoken(username, password,false);
subject subject = securityutils.getsubject();
subject.login(auth);

答案就是在自定义的realm中定义的authenticationinfo 进行匹配

可以通过shiro框架源码authenticatingrealm类的 assertcredentialsmatch方法看到,传入了两个参数,token就是我们登陆方法中传入的usernamepasswordtoken对象,而info就是我们在钉钉一realm中dogetauthenticationinfo 方法中定义的info对象。

shiro框架校验:

//shiro源码--用户信息认证 对比
 protected void assertcredentialsmatch(authenticationtoken token, authenticationinfo info) throws authenticationexception {
        credentialsmatcher cm = this.getcredentialsmatcher();
        if (cm != null) {
            //信息认证
            if (!cm.docredentialsmatch(token, info)) {
                string msg = "submitted credentials for token [" + token + "] did not match the expected credentials.";
                throw new incorrectcredentialsexception(msg);
            }
        } else {
            throw new authenticationexception("a credentialsmatcher must be configured in order to verify credentials during authentication.  if you do not wish for credentials to be examined, you can configure an " + allowallcredentialsmatcher.class.getname() + " instance.");
        }
    }

这里需要弄清楚 dogetauthenticationinfo和dogetauthorizationinfo  前者是定义用户认证信息的,而后者是定义用户权限信息,有点容易混淆。

2.实现自定义realm

public class shirofilerealm extends authorizingrealm implements initializingbean {
	
	@autowired
	public void setcredentialsdigest(credentialsdigest credentialsdigest) {
		this.credentialsdigest = credentialsdigest;
	}

	@override
	protected authenticationinfo dogetauthenticationinfo(
			authenticationtoken authctoken) throws authenticationexception {
		usernamepasswordtoken token = (usernamepasswordtoken) authctoken;
		user user = userservice.getuser(token.getusername());
		if (user != null) {
            //此处返回的对象就是上面的info
			return new simpleauthenticationinfo(new shirouser(user.getusername()), user.getpassword(),getname());
		}
		return null;
	}

	@override
	protected authorizationinfo dogetauthorizationinfo(
			principalcollection principals) {
		//自定义用户权限
		simpleauthorizationinfo info = new simpleauthorizationinfo();
		
		return info;
	}
	/**
	 * 设定密码校验规则
	 */
	public void afterpropertiesset() throws exception {
		credentialsmatcher matcher = new credentialsmatcheradapter(credentialsdigest);
		setcredentialsmatcher(matcher);
	}

}

上面代码中simpleauthenticationinfo 就是返回的用户认证信息,也是框架中做对比的info

好了,认证流程基本弄明白,我们就需要实现自定义校验,怎么做呢?

3.实现自定义credentialsmatcher 认证类

/**
	 * 设定密码校验规则
	 */
	public void afterpropertiesset() throws exception {
		credentialsmatcher matcher = new credentialsmatcheradapter(credentialsdigest);
		//设置自定义的用户信息认证类
        setcredentialsmatcher(matcher);
	}

关键就是这里,credentialsmatcheradapter 是个实现了credentialsmatcher接口的自定义类

public class credentialsmatcheradapter implements credentialsmatcher {
    private credentialsdigest credentialsdigest;

    public credentialsmatcheradapter(credentialsdigest credentialsdigest) {
        assert.notnull(credentialsdigest, "the argument must not be null");
        this.credentialsdigest = credentialsdigest;
    }

    public boolean docredentialsmatch(authenticationtoken token, authenticationinfo info) {
        string plaincredentials, credentials;
        byte[] saltbyte = null;
        plaincredentials = tostringcredentials(token.getcredentials());
        if (info instanceof saltedauthenticationinfo) {
            bytesource salt = ((saltedauthenticationinfo) info).getcredentialssalt();
            if (salt != null) saltbyte = salt.getbytes();
        }
        credentials = tostringcredentials(info.getcredentials());
        if(saltbyte != null){
            return credentialsdigest.matches(credentials, plaincredentials, saltbyte);
        }
        return credentialsdigest.matches(credentials, plaincredentials);
    }

    private static string tostringcredentials(object credentials) {
        if (credentials == null) {
            return null;
        } else if (credentials instanceof string) {
            return (string) credentials;
        } else if (credentials instanceof char[]) {
            return new string((char[]) credentials);
        } else {
            throw new illegalargumentexception("credentials only support string or char[].");
        }
    }
}

docredentialsmatch 方法就是实现用户信息校验 对比的方法,上面shiro框架的校验流程(cm.docredentialsmatch(token, info))就会走到我们这里的方法中

上面credentialsdigest.matches(credentials, plaincredentials); 是我自己实现的密码比较方法

有加盐模式和无盐模式

贴出来给大家参考下

接口

public interface credentialsdigest {
    string digest(string plaincredentials, byte[] salt);
    boolean matches(string credentials, string plaincredentials, byte[] salt);
    boolean matches(string credentials, string plaincredentials);
}

实现类

public  abstract class hashcredentialsdigest implements credentialsdigest {
    static final int hash_iterations = 1024;
    public string digest(string plaincredentials, byte[] salt) {
        if (stringutils.isblank(plaincredentials)) return null;
        byte[] hashpassword = digest(plaincredentials.getbytes(standardcharsets.utf_8), salt);
        return hex.encodehexstring(hashpassword);
    }
    public boolean matches(string credentials, string plaincredentials, byte[] salt) {
        if (stringutils.isblank(credentials) && stringutils.isblank(plaincredentials)) return true;
        return stringutils.equals(credentials, digest(plaincredentials, salt));
    }
    public boolean matches(string credentials, string plaincredentials) {
        if (stringutils.isblank(credentials) && stringutils.isblank(plaincredentials)) return true;
        return stringutils.equals(credentials, plaincredentials);
    }
    protected abstract byte[] digest(byte[] input, byte[] salt);
}

主要的自定义代码就在credentialsmatcheradapter和credentialsdigest中。。。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com