Shrio框架實現(xiàn)自定義密碼校驗規(guī)則詳解
shrio自己內(nèi)置一些密碼校驗規(guī)則,也可以實現(xiàn)簡單的自定義,比如算法類型,hash次數(shù)等,但是有時候我們有一些比較特殊的密碼校驗規(guī)則,需要自定義來實現(xiàn)
1.shiro的密碼校驗是如何做的?
我們在登錄方法內(nèi)做完參數(shù)校驗,驗證碼匹配等基本工作之后,都會將用戶名和密碼通過subject.login(auth) 傳入框架來做用戶匹配,但是,是和什么地方的數(shù)據(jù)進(jìn)行匹配呢?
UsernamePasswordToken auth = new UsernamePasswordToken(username, password,false); Subject subject = SecurityUtils.getSubject(); subject.login(auth);
答案就是在自定義的Realm中定義的AuthenticationInfo 進(jìn)行匹配
可以通過shiro框架源碼AuthenticatingRealm類的 assertCredentialsMatch方法看到,傳入了兩個參數(shù),token就是我們登陸方法中傳入的UsernamePasswordToken對象,而info就是我們在釘釘一Realm中doGetAuthenticationInfo 方法中定義的info對象。
shiro框架校驗:
//shiro源碼--用戶信息認(rèn)證 對比
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
CredentialsMatcher cm = this.getCredentialsMatcher();
if (cm != null) {
//信息認(rèn)證
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 前者是定義用戶認(rèn)證信息的,而后者是定義用戶權(quán)限信息,有點(diǎn)容易混淆。
2.實現(xiàn)自定義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) {
//自定義用戶權(quán)限
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
return info;
}
/**
* 設(shè)定密碼校驗規(guī)則
*/
public void afterPropertiesSet() throws Exception {
CredentialsMatcher matcher = new CredentialsMatcherAdapter(credentialsDigest);
setCredentialsMatcher(matcher);
}
}
上面代碼中SimpleAuthenticationInfo 就是返回的用戶認(rèn)證信息,也是框架中做對比的info
好了,認(rèn)證流程基本弄明白,我們就需要實現(xiàn)自定義校驗,怎么做呢?
3.實現(xiàn)自定義CredentialsMatcher 認(rèn)證類
/**
* 設(shè)定密碼校驗規(guī)則
*/
public void afterPropertiesSet() throws Exception {
CredentialsMatcher matcher = new CredentialsMatcherAdapter(credentialsDigest);
//設(shè)置自定義的用戶信息認(rèn)證類
setCredentialsMatcher(matcher);
}關(guān)鍵就是這里,CredentialsMatcherAdapter 是個實現(xiàn)了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 方法就是實現(xiàn)用戶信息校驗 對比的方法,上面shiro框架的校驗流程(cm.doCredentialsMatch(token, info))就會走到我們這里的方法中
上面credentialsDigest.matches(credentials, plainCredentials); 是我自己實現(xiàn)的密碼比較方法
有加鹽模式和無鹽模式
貼出來給大家參考下
接口
public interface CredentialsDigest {
String digest(String plainCredentials, byte[] salt);
boolean matches(String credentials, String plainCredentials, byte[] salt);
boolean matches(String credentials, String plainCredentials);
}實現(xiàn)類
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中。。。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
在Java8與Java7中HashMap源碼實現(xiàn)的對比
這篇文章主要介紹了在Java8與Java7中HashMap源碼實現(xiàn)的對比,內(nèi)容包括HashMap 的原理簡單介紹、結(jié)合源碼在Java7中是如何解決hash沖突的以及優(yōu)缺點(diǎn),結(jié)合源碼以及在Java8中如何解決hash沖突,balance tree相關(guān)源碼介紹,需要的朋友可以參考借鑒。2017-01-01
IDEA實現(xiàn)純java項目并打包jar的步驟(不使用Maven,Spring)
在Java開發(fā)中我們通常會將我們的項目打包成可執(zhí)行的Jar包,以便于在其他環(huán)境中部署和運(yùn)行,這篇文章主要介紹了IDEA實現(xiàn)純java項目并打包jar(不使用Maven,Spring)的相關(guān)資料,需要的朋友可以參考下2025-08-08
Java自帶的Http?Server實現(xiàn)設(shè)置返回值的類型(content-type)
這篇文章主要介紹了Java自帶的Http?Server實現(xiàn)設(shè)置返回值的類型(content-type),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11
SpringBoot 利用RestTemplate http測試
這篇文章主要介紹了SpringBoot 利用RestTemplate http測試,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
java使用zookeeper實現(xiàn)的分布式鎖示例
這篇文章主要介紹了java使用zookeeper實現(xiàn)的分布式鎖示例,需要的朋友可以參考下2014-05-05
使用okhttp替換Feign默認(rèn)Client的操作
這篇文章主要介紹了使用okhttp替換Feign默認(rèn)Client的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02
Mybatis關(guān)聯(lián)映射的實現(xiàn)
本文介紹了MyBatis關(guān)聯(lián)映射的實現(xiàn)方式,直接查詢和分步查詢,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-12-12

