【知识回顾】
在上篇博客中,通过对Shiro架构及其组件的总结学习,对Shiro有了一个整体的认识。
本篇博客将进一步学习其认证组件,Authentication: 身份认证/登录,验证用户是不是拥有相应的身份;
【认证流程图】
【认证流程描述】
1. 获取当前的Subject,调用SecurityUtils.getSubject();
2. 测试当前的用户是否已经被认证,即是否已经登录。调用Subject的isAuthenticated();
3. 若没有被认证,则把用户名和密码封装为UsernamePasswordToken对象;
4. 执行登录:调用Subject的login(AuthenticationToken)方法;
【环境-需要加入的jar包】
【认证实现1:直接读取用户信息认证】
1. 流程代码如下:
/**
* 用户的登录与退出
**/
public void testLoginAndLogout() {
// 创建securityManager工厂,通过ini配置文件创建securityManager工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory(
"classpath:shiro-first.ini");
// 创建SecurityManager
SecurityManager securityManager = factory.getInstance();
// 将securityManager设置当前的运行环境中
SecurityUtils.setSecurityManager(securityManager);
// 从SecurityUtils里边创建一个subject
Subject subject = SecurityUtils.getSubject();
// 测试当前的用户是否已经被认证,即是否已经登录
// 调用 Subject 的 isAuthenticated()
if (!currentUser.isAuthenticated()) {
// 在认证提交前准备token(令牌)
// 这里的账号和密码 将来是由用户输入进去
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan",
"111111");
try {
// 执行登录
currentUser.login(token);
}
// 若没有指定的账户,则shiro会抛出UnknownAccountException异常
catch (UnknownAccountException uae) {
log.info("----> There is no user with username of " + token.getPrincipal());
return;
}
//若账户存在,但密码不匹配,则会抛出IncorrectCredentialsException异常
catch (IncorrectCredentialsException ice) {
log.info("----> Password for account " + token.getPrincipal() + " was incorrect!");
return;
}
// 用户被锁定的异常 LockedAccountException
catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
// ... catch more exceptions here (maybe custom ones specific to your application?
// 所有认证时异常的父类
catch (AuthenticationException ae) {
//unexpected condition? error?
}
}
// 是否认证通过
boolean isAuthenticated = subject.isAuthenticated();
System.out.println("是否认证通过:" + isAuthenticated);
// 退出操作
subject.logout();
// 是否认证通过
isAuthenticated = subject.isAuthenticated();
System.out.println("是否认证通过:" + isAuthenticated);
}
2. 流程总结如下:
此认证流程是直接从shiro-first.ini文件中读取的用户名和密码,shiro-first.ini文件内容如下:
#对用户信息进行配置
[users]
#用户账号和密码
zhangsan=111111
lisi=22222
【认证实现2:自定义realm认证】
代码如上例一样,只需要改下读取的ini文件。
1. 流程代码如下:
/**
* 用户的登录与退出
**/
public void testLoginAndLogout() {
// 创建securityManager工厂,通过ini配置文件创建securityManager工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory(
"classpath:shiro-first.ini");
2. 流程总结如下:
此方法是通过自定义Realm实现的,实际开发中我们也需要通过realm去查询数据库,获取用户信息。
CustomRealm代码如下:
public class CustomRealm extends AuthorizingRealm {
// 设置realm的名称
@Override
public void setName(String name) {
super.setName("customRealm");
}
// 用于认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
// token是用户输入的
// 第一步从token中取出身份信息
String userCode = (String) token.getPrincipal();
// 第二步:根据用户输入的userCode从数据库查询
// ....
// 如果查询不到返回null
//数据库中用户账号是zhangsansan
/*if(!userCode.equals("zhangsansan")){//
return null;
}*/
// 模拟从数据库查询到密码
String password = "111112";
// 如果查询到返回认证信息AuthenticationInfo
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
userCode, password, this.getName());
return simpleAuthenticationInfo;
}
}
shiro-realm.ini文件代码如下:
[main]
#自定义realm
customRealm=com.hzt.shiro.realm.CustomRealm
#将realm设置到SecurityManager,相当于Spring中注入
securityManager.realms=$customRealm
【认证实现3:自定义realm,MD5加密认证】
实际开发中,我们一般都不会直接使用明文,而是会将用户密码进行加密处理,所以我们在自定义realm中,需要进行加密后的值对比。
CustomRealm添加MD5加密算法,修改代码如下:
public class CustomRealmMd5 extends AuthorizingRealm {
// 设置realm的名称
@Override
public void setName(String name) {
super.setName("customRealmMd5");
}
// 用于认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
// token是用户输入的
// 第一步从token中取出身份信息
String userCode = (String) token.getPrincipal();
// 第二步:根据用户输入的userCode从数据库查询
// ....
// 如果查询不到返回null
// 数据库中用户账号是zhangsansan
/*
* if(!userCode.equals("zhangsansan")){// return null; }
*/
// 模拟从数据库查询到密码,散列值
String password = "f3694f162729b7d0254c6e40260bf15c";
// 从数据库获取salt
String salt = "qwerty";
//上边散列值和盐对应的明文:111111
// 如果查询到返回认证信息AuthenticationInfo
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
userCode, password, ByteSource.Util.bytes(salt), this.getName());
return simpleAuthenticationInfo;
}
}
shiro-realm-MD5.ini文件代码如下:
[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次数
credentialsMatcher.hashIterations=1
#将凭证匹配器设置到realm
customRealm=com.hzt.shiro.realm.CustomRealmMd5
customRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$customRealm