shiro源码分析
文章目录
个人学习路线
Apache Shiro是一个功能强大且灵活的开源安全框架,可以清晰地处理身份验证,授权,企业会话管理和加密。
这是它的官网:
官网
在官网上面有个快速开始,将源码下载下来,
看看shiro的大体架构:http://shiro.apache.org/architecture.html
我就贴一张最重要的图好了,官网介绍的更加清楚.
跑一遍快速开始:
这时候对shiro有个简单的了解了…
简单介绍
ini配置文件:
# 用户名,密码,角色
[users]
root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz
$ 角色 ,对应的权限
[roles]
admin = *
schwartz = lightsaber:*,aa:*
goodguy = winnebago:drive:eagle5,aa:*
简单看看事例代码:
public class Quickstart {
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
//创建安全中心
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
//通过提供的类去简化操作
SecurityUtils.setSecurityManager(securityManager);
//获取个用户,抽象的用户.
Subject currentUser = SecurityUtils.getSubject();
//获取session
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("Retrieved the correct value! [" + value + "]");
}
//判断是登录
if (!currentUser.isAuthenticated()) {
//用户名密码
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true);
try {
//登录,没有抛出异常就是认证成功,抛出了就是登录失败了.
currentUser.login(token);
//各种异常,顾名思义.账户不存在啊,密码错误啊,用户锁住了等等..
} catch (UnknownAccountException uae) {
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
catch (AuthenticationException ae) {
//unexpected condition? error?
}
}
//登录成功,获取用户
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
//是否有指定的角色
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//是否有指定的权限
if (currentUser.isPermitted("lightsaber:weild")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
//退出
currentUser.logout();
System.exit(0);
}
}
怎么进行认证(登录)的
代码是通过subject调用login方法登录,点到具体实现.
DelegatingSubject这个类实现了Subject,实现了login方法:
Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);
SecurityManager详解
我们看默认的实现DefaultSecurityManager类的uml图,
DefaultSecurityManager继承了SessionsSecurityManager(可以进行session管理)->AuthorizingSecurityManager(可以进行鉴权)->AuthenticatingSecurityManager(可以进行认证)->RealmSecurityManager(可以进行real管理)->CachingSecurityManager(缓存管理了);这不是很明显的装饰者吗?
CachingSecurityManager
这是CachingSecurityManager有CacheManager接口,我们可以实现自己的缓存管理,可以放在redis里,或者mysql,或者整合ehcache等等.然后可以set,get方便扩展.
RealmSecurityManager
RealmSecurityManager内部维护了一个Realm集合,也提供了各种set,get,方便我们扩展.
AuthenticatingSecurityManager
内部维护了一个认证器,当然也可以set,get,默认提供了个简单的认证器ModularRealmAuthenticator
AuthorizingSecurityManager
内部维护了一个鉴权器,也有默认的,set,get等.
SessionsSecurityManager
内部维护了一个session管理器
认证总结
通过一系列的继承,那这个默认的DefaultSecurityManager就可以使用认证啊,鉴权啊,缓存啊,session啊,real管理啊等各种功能啦!!!
登录的话,是认证的功能,
首先通过authenticate方法获取个认证信息authenticate这个方法是认证管理器实现的managerAuthenticatingSecurityManager,使用的认证器去进行认证的,this.authenticator是哪个呢?刚刚说了初始化的时候或设置默认的认证器ModularRealmAuthenticator:
然后发现实现里面没有authenticate方法,那就说明,在父类里面,子类里没有的方法就去父类里面找,准没错,父类里的抽象方法,那就去子类里面找实现了
然后父类AbstractAuthenticator里面又是调用的doAuthenticate,这个的实现是在ModularRealmAuthenticator认证器里面,然后就是判断是单realm还是多realm分别进行认证.:
先看多realm认证的话会判断,是不是要全部通过,才算认证成功,或者是只要一个认证通过,
再看单个realm认证doSingleRealmAuthentication方法:就是通过realm的getAuthenticationInfo(AuthenticationToken token)方法 AuthenticatingRealm这个实现的:
我们看看doGetAuthenticationInfo的简单实现,一般是我们自己去实现的.:
然后就是进行对比了,默认简单的是通过密码进行equals进行对比的…感兴趣可以看看.
当然我们也可以自己实现对比器,CredentialsMatcher这个接口,然后set到我们的管理器里面即可;
对比完成,就是返回认证信息了…
大体流程,我就把网上图贴上来了:
怎么进行鉴权(判断是否有权限)的
鉴权主要就是两个方法,判断是否有对应的角色,判断是否有对应的权限,对应的方法是hasRole跟isPermitted
这两个方法前面讲过,肯定是通过AuthorizingSecurityManager鉴权管理器进行
就看isPermitted方法好了.
代理subject里面实现了isPermitted方法:hasPrincipals方法判断是否登录.是否进行了认证,如果没有进行认证,那判断是否含有权限就没有意义了;
然后就是AuthorizingSecurityManager里的实现,通过使用鉴权器进行鉴权,同理,肯定有初始化的鉴权器的,同样是ModularRealmAuthorizer;认证的是ModularRealmAuthenticator名字都是modularRealm开头,哈哈!
ModularRealmAuthorizer
这里的reaml我们就看sampleRealm好了,先看uml图:
AuthorizingRealm->AuthenticatingRealm->CachingReanm想到了啥,是不是跟跟DefaultSecurityManageer有点相似啊,我就不说了,给个眼神自己体会:
最后就是取出所有权限,然后进行对比,如果有就代表有权限.
授权
然后在判断是否含有指定权限之前,肯定要给用户赋予用户该有的权限吧,
在AuthorizingRealm的各种鉴权方法里面都会先获取用户的鉴权信息getAuthorizationInfo返回AuthorizationInfo
总结,以前在使用shiro的回想
以前做项目的时候总是听别人说,继承AuthorizingRealm然后实现认证,授权两个方法就可以了,shiro就这两个方法,当时也不知道什么原因,只是会用,现在只有通过源码,流程学习,才明白啊!!!
以前也是写个类实现UserDao,其实就是各种操作操作数据库的接口,就是在鉴权里面获取用户啊,权限.等各种信息用的.