Apache shiro 是一个java安全性校验框架,因为其简单和易用,所以开始接触Shiro,据说Spring也有一个安全校验框架,这里因为我只有学习了Shiro所以我就不介绍Spring这款了,以后有机会在去学习吧。
Apache shiro比较易用,如果就一个demo 的话,一个jar包,一个以ini为结尾的配置文件,和一个工厂类就可以了。shiro 可以用在JavaSE 和JavaEE 的环境中。Shiro包含的组件有进行身份校验的Authentication和进行角色校验的Authoritarian,还用realm,Shiro 还实现了Session和Cache等等,所以对于一些共享的数据支持还是比较常用。
下面贴一张经典的图片:
Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;
SecurityManager:相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。
Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得Shiro默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
Realm:可以有1个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC实现,也可以是LDAP实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的Realm;
SessionManager:如果写过Servlet就应该知道Session的概念,Session呢需要有人去管理它的生命周期,这个组件就是SessionManager;而Shiro并不仅仅可以用在Web环境,也可以用在如普通的JavaSE环境、EJB等环境;所有呢,Shiro就抽象了一个自己的Session来管理主体与应用之间交互的数据;这样的话,比如我们在Web环境用,刚开始是一台Web服务器;接着又上了台EJB服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到Memcached服务器);
SessionDAO:DAO大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库,那么可以实现自己的SessionDAO,通过如JDBC写到数据库;比如想把Session放到Memcached中,可以实现自己的Memcached SessionDAO;另外SessionDAO中可以使用Cache进行缓存,以提高性能;
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的。
我想介绍的是shiro 主要的几个功能:
在介绍功能之前,我先介绍一下Shiro的启动。Shiro启动的是先配置Shiro自身的工厂方法,并且加载配置文件shiro.ini,通过工厂方法获取SecurityManager,并绑定全局作用域SecurityUtil。
//1.获取SecurityManager工厂,初始化ini文件。Factoryfactory=new IniSecurityManagerFactory("classpath:shiro-first.ini");//2.获取文件信息,并且设置全局化org.apache.shiro.mgt.SecurityManager securityManager=factory.getInstance();SecurityUtils.setSecurityManager(securityManager);
介绍了那么多,终于开始进入正题。
第一:
用户名和密码校验:
完成上面黑框的代码说明已经实现了全局的配置,下面就是功能的实现,使用SercurityUtil.getSubject()获取Subject对象,创建UsernamePasswordToken(“用户名”,“密码”)并返回相关的令牌,使用Subject的login方法即可实现。
//3.获取Subjectorg.apache.shiro.subject.Subject subject=SecurityUtils.getSubject();UsernamePasswordToken token=new UsernamePasswordToken("zhang","123");try { //4.登录校验 subject.login(token);}catch (Exception e){ System.out.println("密码错误");}Assert.assertEquals(true,subject.isAuthenticated());//5.退出subject.logout();
对用户校验的流程主要部分:
subject.login 会委托给SercurityManager,SercurityManager是正在的逻辑调用,根据校验的内容,SercurityManager对象会调用Authenticator,在Authenticator中会根据你的配置不同选择配置文件和调用jdbcRealm或者自定义的realm,把token传入到里面。realm进行进一步判断。
第二进行角色的校验:
Subject获取到的是一个公共的SecurityUtil的对象,在Shiro的Subject中可以获取有关角色验证进行授权。
授权的第一种方式:
Subject subject=SecurityUtil.getSubject();
if(subject.isRole==‘admin’)来进行判读。
第二种方式:
可以使用@RequiresRoles(“admin”)这种方法来进行校验
关于授权的流程:
如果调用hasRole*,则直接获取Authorizationinfo。getRole与传入角色进行比较,
首先调用如isPermitted(“user:view”),通过PermisstionResolver将权限字符串庄华为Permissiton,通过AuthorizationInfo.getObjectPermissions()得到Permission的实列集合,通过AuthorizationInfo.getStringPermission()得到字符串集合,并通过PermissionResolver装换为Permission视力,通过RolePermissionResolver解析对象权限,最后调用implies组个与传入权限比较。
这里面就提到一个类 AuthorizatingRealm类,自定义的realm继承或者实现这个类以后可以重现或者实现其中关于权限校验和密码校验的方法。
/** * Created by pzy on 2017/7/18. */public class UserRealm extends AuthorizingRealm { UserService userService=new UserServiceimpl(); @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { String username= (String) principalCollection.getPrimaryPrincipal(); SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo(); authorizationInfo.setRoles(userService.findRoles(username)); authorizationInfo.setStringPermissions(userService.findPermissions(username)); return authorizationInfo; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { String username= (String) authenticationToken.getPrincipal(); User user=userService.selectByUserName(username); if(user==null){ throw new UnknownAccountException(); } if(Boolean.FALSE.equals(user.getLocked())){ throw new LockedAccountException(); //帐号锁定 } SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), ByteSource.Util.bytes(user.getUsername()+user.getSalt()),getName()); return authenticationInfo; }
}
就是这个类,通过这个类,就可以自定义实现密码校验和权限给予等。这里写完以后需要在配置文件中给自定义的realm一个配置。
接下来介绍一下Session的配置。在shiro提供了一个独立的Session模块,这个模块有三个Session类,其中一个类是给JavaSE做session的一个类,据说这个类可以在JavaSE中实现Session,但是有一个缺点是不能自动更新最后访问时间。shiro提供了一个默认的Session,
org.apache.shiro.web.session.mgt.DefaultWebSessionManager
,可以调用Tomcat中的Session来实现功能,还有一种是Shiro自己的Session,关于配置问题这里就不详细写,因为网上一大堆。
JavaSE和JavaEE对Shiro中Session对象的获取,通过SercurityUtils.getSugect().getSession()获取自己的Session对象。