Shiro
1 认证
1.1 使用ini配置文件来实现认证
shiro实现简单的认证,用户名的账号和密码放在了根目录下的shiro.ini文件中
[users]
zhangsan=123123
lisi=123123
在设置securityManager的Realm的时候,选择new一个IniRealm,传入shiro.ini的路径
// 创建安全管理器对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
// 给安全管理器设置realm
securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
// SecurityUtils 给全局安全管理器工具类设置安全管理器
SecurityUtils.setSecurityManager(securityManager);
// 关键对象subject主体
Subject subject = SecurityUtils.getSubject();
// 创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123123");
// 用户认证
try {
subject.login(token);
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("账号不存在");
} catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码错误");
}
1.2 使用自己定义的Realm来实现认证
我们自定义的Realm需要继承AuthorizingRealm,重写doGetAuthorizationInfo()授权方法和doGetAuthenticationInfo()认证方法
通过追溯源码可以得到如下依赖图:
- AuthenticatingRealm 认证的Realm抽象类
- AuthorizingRealm 授权的Realm抽象类
- 用户名比较是在SimpleAccountRealm中的doGetAuthenticationInfo 完成用户名校验
- 密码是在AuthenticatingRealm中的assertCredentialsMatch完成校验
- 通过图可以看出,继承AuthorizingRealm就可以重写认证AuthenticatingRealm和授权AuthorizingRealm两个抽象类的方法
自定义Realm类
public class CustomerRealm extends AuthorizingRealm {
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// token中获取用户名
String principal = (String) token.getPrincipal();
System.out.println("principal = " + principal);
// 根据用户名获取数据,例如 queryUserByUsername(principal);
// 这里模拟数据库中获取数据
if ("zhangsan".equals(principal)) {
/**
* 参数1: 返回数据库中正确的用户名
* 参数2: 密码
* 参数3: 提供当前的realm的名字,直接调用父类的getName()方法
*/
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo("zhangsan", "123123", this.getName());
return simpleAuthenticationInfo;
}
return null;
}
}
下述测试自定义Realm类的时候,只是将new IniRealm()改成new自己定义的Realm。后期和数据中的账号密码作比较是在自己定义的Realm中进行的。
// 创建securityManager
DefaultSecurityManager securityManager = new DefaultSecurityManager();
// 设置自定义Realm
securityManager.setRealm(new CustomerRealm());
// 将安全工具类设置为安全工具类
SecurityUtils.setSecurityManager(securityManager);
// 通过安全工具类获取Object
Subject subject = SecurityUtils.getSubject();
// 创建token
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","123123");
// 登录
try {
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
}
1.3 自定义Realm的md5加密加盐比较
md5加密 是不可逆的,同一数据在进行md5加密后得到的加密字符串相同。
自定义MD5Realm类
public class CustomerMD5Realm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 获取身份信息
String principal = (String) token.getPrincipal();
// 根据用户名查询数据库
if ("zhangsan".equals(principal)) {
/**
* 参数一: 数据库中的密码
* 参数二:md5+salt之后的密码
* 参数三:盐值
* 参数四:当前realm的名称
*/
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
"zhangsan",
"f997c74487a098c9477da3a0c10944a5",
ByteSource.Util.bytes("s0*5ps"),
getName());
return info;
}
return null;
}
}
md5加密测试类
相比之前不同的是,我们在new Realm之后,对Realm进行设置加密算法和迭代次数。迭代次数不同得到的MD5加密字符串也不同。
扩展:
这里的盐值我们后期可以通过字符串生成工具来随机生成字符串,并将其存入数据库中,在进行登录的时候,用相同的加密原则来进行判断。例如,我们注册的时候是将盐值加载密码后面再进行加密,我们登录的时候就要将数据库中的盐值加在密码后面。
public class TestCustomerMD5RealmAuthenticator {
public static void main(String[] args) {
// 创建安全管理器
DefaultSecurityManager securityManager = new DefaultSecurityManager();
// 注入realm
CustomerMD5Realm realm = new CustomerMD5Realm();
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 注册算法
hashedCredentialsMatcher.setHashAlgorithmName("md5");
// 散列次数
// hashedCredentialsMatcher.setHashIterations(1024);
realm.setCredentialsMatcher(hashedCredentialsMatcher);
// 为securityManager设置Realm
securityManager.setRealm(realm);
// 设置securityManager
SecurityUtils.setSecurityManager(securityManager);
// 获取subject对象
Subject subject = SecurityUtils.getSubject();
// 创建token
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
try {
subject.login(token);
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户名错误");
} catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码错误");
}
}
}