Shiro学习——密码的加密解密
Posted sadoshi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Shiro学习——密码的加密解密相关的知识,希望对你有一定的参考价值。
前言
前面几篇文章的密码都是以明文形式存储。在真实项目中当然不可能明文存储密码,密码一定是以加密的形式存储的。前面我们可以看到当我们给出帐号和密码后,shiro就会去查找ini配置文件或者数据库对应字段来匹配账号密码。那如果我们把存储的密码加密,shiro又如何根据我们提交的明文密码与存储的加密密码匹配呢?
Shiro加密与匹配的原理
这里不准备展示源码。相信很多读者看文章时面对大量源码也看得一头雾水,并不能总结出什么来,所以这里大致讲讲思路,有兴趣的读者可以自行跟踪源码。这里以读取ini文件为例:
1、当我们使用subject.login(token)这个命令时,会调用IniRealm的父类SimpleAccountRealm的doGetAuthenticationInfo获取存储的认证信息。PS:如果我们自定义realm的话,如何获取认证通常也是重写这个方法。
2、通过AuthenticatingRealm的assertCredentialsMatch方法对比我们提交的帐号密码token与上一步获取的认证信息是否匹配。如果匹配就返回认证信息,否则就抛出异常。
3、第2步匹配的方法调用CredentialsMatcher接口的doCredentialsMatch方法。例如默认的匹配类为SimpleCredentialsMatcher,它的doCredentialsMatch非常简单,就是比较提交的密码与第1步获取的认证密码是否一致。这过程没做任何加密解密处理。所以对于加密或者解密操作,最主要的实现在doCredentialsMatch方法上进行。
shiro本身提供了一些加密解密的类和方法,例如PasswordMatcher类,当然也可以自定义,只需配置到securityManager中即可。
配置加密解密类
匹配类
shiro默认的匹配类是SimpleCredentialsMatcher。读者可以参考下其源码,实现非常简单。shiro提供的匹配接口是CredentialsMatcher,只要实现doCredentialsMatch方法即可:
public interface CredentialsMatcher {
boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info);
}
加密与验证服务
shiro提供了加密与验证服务的接口PasswordService接口:
public interface PasswordService {
String encryptPassword(Object plaintextPassword) throws IllegalArgumentException;
boolean passwordsMatch(Object submittedPlaintext, String encrypted);
}
第一个方式用于加密,第二个方法用于进行对密码进行匹配。shiro自身提供的加密解密类都实现了这个接口。通常这个接口是配合CredentialsMatcher使用的。一般CredentialsMatcher的doCredentialsMatch方法对提交的帐号密码与存储的帐号密码信息进行一些处理,后面再调用PasswordService的passwordsMatch方法进行匹配。
为什么有CredentialsMatcher还要设计PasswordService呢?
我的理解,只用CredentialsMatcher也可以。默认匹配类SimpleCredentialsMatcher就没有使用PasswordService。CredentialsMatcher接口doCredentialsMatch传入的参数是AuthenticationToken(用户登录提交的帐号密码信息)和AuthenticationInfo(系统存储的帐号密码信息),我们可以在doCredentialsMatch中提取并组合后放到PasswordService的passwordsMatch中处理。把PasswordService提出来可以更方便替换不同的加密算法。
代码示例
现在做一个简单的例子,展示一下PasswordService和CredentialsMatcher怎么使用。shiro默认的CredentialsMatcher是PasswordMatcher,默认的PasswordService是DefaultPasswordService。即使我们不设置也行,但例子中还是希望手动设置,让读者更好理解。
首先我们先得到一个加密的密码:
public class PassWordCrypto {
public static String encriptPassword(String password) {
PasswordService service = new DefaultPasswordService();
return service.encryptPassword(password);
}
public static void main(String[] args) {
System.out.println(encriptPassword("123"));
}
}
执行上面那段代码,得到密码“123”加密结果为$shiro1$SHA-256$500000$3mRjauseIWB330SU++9msA==$eHTXKKktQduiDi6Kut8HzwBJHeiwx7pXDemieEhgDkE=
我们本次以ini位置文件为例子,创建password.ini,并把刚才得到的加密密码填入:
[main]
passwordMatcher=org.apache.shiro.authc.credential.PasswordMatcher
passwordService=org.apache.shiro.authc.credential.DefaultPasswordService
passwordMatcher.passwordService=$passwordService
iniRealm.credentialsMatcher=$passwordMatcher
securityManager.realms=$iniRealm
[users]
zhang=$shiro1$SHA-256$500000$3mRjauseIWB330SU++9msA==$eHTXKKktQduiDi6Kut8HzwBJHeiwx7pXDemieEhgDkE=,role1
li=123
[roles]
role1=user:create,update
role2=user:create,delete
上面[main]中把DefaultPasswordService设置到PasswordMatcher中,再把PasswordMatcher设置到iniRealm中。这里的iniRealm是默认创建出来的,如果我们自己新建一个IniRealm设置进去的话反而会报错。
接着写测试代码:
package com.sadoshi.shiro.crypto;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.env.BasicIniEnvironment;
import org.apache.shiro.env.Environment;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
public class PasswordServiceTest {
private Subject subject;
@Before
public void init() {
Environment env = new BasicIniEnvironment("classpath:password.ini");
org.apache.shiro.mgt.SecurityManager securityManager = env.getSecurityManager();
SecurityUtils.setSecurityManager(securityManager);
subject = SecurityUtils.getSubject();
}
@Test
public void hashCrypto() {
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
try {
subject.login(token);
System.out.println("ok");
} catch (AuthenticationException e) {
e.printStackTrace();
}
subject.logout();
}
}
如果密码对不上,就会抛出异常。我们这里执行结果正确。
小结
本文只是初步展示一下CredentialsMatcher和PasswordService是怎么使用的。后面的文章还会展示其他加密方式。
以上是关于Shiro学习——密码的加密解密的主要内容,如果未能解决你的问题,请参考以下文章