一、shiro相关介绍
1、什么是shiro
shiro是Apache基金会下的开源项目,用于身份认证、用户授权、会话管理、密码管理。
2、优点
目前主流的权限的框架除了shiro还有spring security,但是spring的依赖过高,ershiro小巧灵活,使用方便。
3、shiro的功能
shiro有四大功能:身份认证、用户授权、会话管理、密码管理。
(1)身份认证
Authentication:即是登陆,用于校验用户身份的合法性;
(2)用户授权
Authorization:在认证通过的基础上,校验用户是否有所请求资源的权限;
(3)会话管理
SessionManagement:可以用于JavaEE和JavaSE环境;
(4)密码管理
Cryptography:shiro提供了一套加密解密的组件。
(5)其他功能
1)Web Support:对于web程序的支持,保护web应用;
2)Caching:对于缓存的支持,确保数据的操作安全而高效;
3)Concurrency:对于并发的支持;
4)Testing:用于帮助编写单元测试;
5)Remenber Me:在会话中记住用户身份,免于频繁登陆;
6)Run As:在系统的许可下,允许用户以另一个身份登陆并访问对应的资源;
4、shiro的架构图
(1)shiro高层次的抽象架构图
(2)shiro内部架构图
1)Subject
指与当前系统交互的实体,包括但不限于用户,第三方服务,爬虫);
2)SecurityManager(安全管理器)
是shiro的核心,负责协调管理其他组件进行工作,是整个shirl的入口;
3)Authenticator(认证器)
用于校验用户的身份,认证器知道如何与Realm协同工作来访问存储相关用户的身份信息,并从Realm中获取数据来验证用户身份;
4)Authorizer(授权器)
用于校验用户的访问权限,授权器从realm中获取信息,来决定是否对用户的请求进行放行;
5)SessionManager(会话管理器)
负责创建并管理用户的session声明周期,看完用于非web环境;
6)Realm
realm在数据和shiro之间担任中间人的角色。
认证器和首圈起都需要通过调用Realm来获取用户信息并完成工作,可以按需要配置一个或多个Realm。
7)SessionDao
sessionDao是对会话操作的一套接口,用于对session的持久化操作。
8)CacheManager(缓存管理器)
缓存管理器将用户的一些数据缓存起来,在多次访问中提高操作效率。
9)Criptograph(密码管理器)
二、shrio入门案例
1、什么是ini配置文件?
ini配置文件是基于键值对的配置文件,除此之外,还有一个section(节),类似于properties文件,节点可以配置多个。
shiro的ini配置文件基本上建立了一小组静态账户,对于基础了shiro应用程序可供测试数据(模拟数据库)。
2、shiro认证工作流程
(1)整个shiro的核心是SecurityManager,作为整个shiro的入门和管理者,类似于SpringMVC中的DispatcherServlet,负责安全相关的所有事情。
(2)当subject执行login方法的时候,整个工作流程就被SecurityManager所接管。
(3)首先执行认证操作,SecurityManager调用认证器Authenticator来进行认证。如果没有配置或定义,默认使用的是ModularRealmAuthenticator来进行认证。
(4)认证器在接收到认证请求之后,调用Realm对象(IniRealm)查询用户信息传入token。
(5)Realm对象根据token获取身份信息(用户名,principle),到数据库进行查询,将查询结果封装为一个对象:AuthenticatorcationInfo。
(6)认证器获取到返回的AuthenticatorInfor对象:
1)查询到的用户不为null,则比对密码,密码比对成功,则认证成功;密码比对失败则比对失败(异常:IncoriectCredentialsException);
2)查询到的用户为null,表示用户不存在(异常:UnkownAccountException)。
注意:密码比对不是在realm中完成,而是在认证器中完成。
3、shiro入门案例步骤:
(1)导入jar包
commons-beanutils、junit、shiro-core、slf4j-api、slf4j-log4j12、log4j、commons-logging、lombok;
(2)创建配置文件
1)创建ini配置文件
ini配置文件用来定义用户的角色和权限信息。
例如:
用户名为admin,密码为123456,对应的角色和权限资源如下:
#define user info
[users]
admin = 123456, role1, role2
[roles]
role1 = user:create, user:query
role2 = course:create, course:query
role3 = teacher:create, teacher:query
2)导入log4j.properties文件
(3)创建测试类
1)加载ini配置文件
加载ini配置文件获取安全管理器工厂:
SecurityManager securityManager = new
IniSecurityManagerFactory(“classpath:shiro.ini”);
2)获取安全管理器
通过SecurityManagerFactory获取一个安全管理器的实例SecurityManager:
SecurityManager securityManager = SecurityManagerFactory.creatInstance();
3)将安全管理器securityManager设置到当前应用的上下文中
使用SecurityUtils静态工具类获取:
SecurityUtils.setSecurityManager(securityManager);
4)获取当前的subject对象
subject对象即是:当前线程与系统交互的对象,通过SecurityUtils静态工具类获取:
Subject
subject = SecurityUtils.getSubject();
5)构建token
token包含了用户名和密码:
UserNamePasswordToken token = new UserNamePasswordToken(“用户名”,“密码”);
注意:此处的密码即是从前端页面表单获取的用户名和密码。
6)执行认证操作
通过subject对象和token参数执行认证操作:
a.登入:subject.login(token);
b.登出:subject.logout();
7)api
A.判断用户是否有相关角色
①subject.isAuthenticated():判断用户是否登陆,结果是布尔值;
②subject.hasAllRoles(“角色名称的集合”):判断用户是否拥有集合中所有的角色,结果是布尔值数组;
③subject.hasRole(“角色名”):判断用户是否拥有这个角色,结果为布尔值;
④subject.checkRole(“角色”):断言该用户是否拥有指定角色,有则增长执行后续代码,没有则抛异常。
B.判断用户是否有相关资源
①subject.isPermitted(“资源名称”):判断当前用户是否具有指定权限,结果是布尔值;
②subject.isPermitted(“资源名称的集合”):判断当前用户是否拥有集合中的资源的权限,结果是布尔值数组。
注意:在shiro中资源,例如/user/query表示为:user:query。
C.测试会话管理
通过subject对象获取session对象;
Session
session = subject.getsession();
注意:
a.session也是一个作用域,可用于存值取值;
b.shiro中的session同servlet中的session不是同一个session;
shiro中的sessionshiro原生的session,其管理器为DefaultWebSessionManager,在jsp页面获取不到session值。
8)测试
在执行认证操作前后可通过log.info(提示信息)在控制台输出提示信息,查看认证状态。
三、shiro认证
自定义realm完成认证步骤:
1、自定义一个realm
(1)继承AuthorizingRealm(推荐使用)
AuthorizingRealm为AuthenticatingRealm的子类;
(2)继承AuthenticatingRealm
AuthenticatingRealm为AuthorizingRealm的父类;
2、在realm中完成认证的业务逻辑
(1)重写doGetAuthenticationInfo()方法
该方法有一个形参AuthenticationToken token,包含了当前用户的身份信息;
(2)根据认证器传入的token对象获取身份信息
String
username = (String) token.getPrincipal();
注意:
1)token中包含了用户的用户名和密码,可将token向下转型为UsernamePasswordToken,在获取用户名;
2)principal表示用户的身份信息,即是用户输入的用户名;
3)通过认证后,会将从数据库查询到的用户的对象存入principal;
(3)根据身份信息查询数据库
通过username到数据库查询是否有当前用户存在,并返回当前用户的Java bean;
(4)将查询到得当前用户封装并返回
通过SimpleAuthenticationInfo将查询到的当前用户的Java bean进行封装:
SimpleAuthticationInfo
simpleAuthenticationInfo =
new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
再将simpleAuthenticationInfo返回。
注意:第一个参数:在数据库查询到的当前用户对于的Java bean;
第二个参数:当前用户在数据库查询到的密码;
第三个参数:当前自定义realm对象的名称。
3、配置ini配置文件
1)定义[main]
2)定义自定义的realm
jdbcRealm(自定义realm类名首字母小写) = com.chen.realm.JdbcRealm(自定义realm全限定名)
3)将自定义的realm注入到安全管理器(securityManager)中securityManager.realms = $jdbcRealm
[main]
#定义自定义的realm
jdbcRealm = com.chen.realm.JdbcRealm
#将自定义的realm注入到安全管理器
securityManager.realms = $jdbcRealm
4、测试
四、shiro授权
注意:权限相关的数据,都通过realm来获取。但是realm只负责提供数据,不负责数据的校验工作。
1、自定义realm
2、完成认证的业务逻辑
3、完成授权的业务逻辑
(1)重写doGetAuthorizationInfo()方法
该方法的形参为带有当前用户的身份信息:Principal principal;
(2)通过身份信息获取当前用户名
User
user = (User)
principal.GetPrimaryPrincipal();
String
username = user.getUserName();
注意:
通过认证后,realm会将从数据库查询到的用户的对象存入principal,所以可以通过principal获取从数据库查询到的用户的所有的信息;
(3)通过用户名查询数据库中当前用户拥有的权限资源
(4)封装查询到的权限集合并返回
通过SimpleAuthorizationInfo对象进行封装:
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
1)封装资源权限
simpleAuthorizationInfo.addStringPermissions(hashSet);
2)封装角色权限
simpleAuthorizationInfo.addRoles(rolesList);
再将封装好的simpleAuthorizationInfo返回。
注意:封装时,api的参数为在数据库中查询到的权限名称集合(List或Set)。
4、配置ini配置文件
1)定义[main]
2)定义自定义的realm
jdbcRealm(自定义realm类名首字母小写) = com.chen.realm.JdbcRealm(自定义realm全限定名)
3)将自定义的realm注入到安全管理器(securityManager)中securityManager.realms
= $jdbcRealm
[main]
#定义自定义的realm
jdbcRealm = com.chen.realm.JdbcRealm
#将自定义的realm注入到安全管理器
securityManager.realms = $jdbcRealm
5、测试
五、shiro的密码管理
1、加密解密
shiro提供了一套加密解密的组件。
在数据库中存储的密码不应该是明文密码,应该是加密后的密码。
将当前用户从前端传入的密码按照与数据库加密的密码相同的加密方式进行加密,然后与数据库查询到的加密的密码进行匹配,匹配成功则认证成功,否则认证失败。
2、哈希(hash)算法
哈希算法即是:将一个任意长度的输入,映射为一个固定长度的输出。
3、Md5Hash对象密码管理
shiro提供了Md5Hash对象,进行密码管理。
为了安全起见,一般在进行hash运算时,需要加salt(盐)。
在比对密码时,输入的密码和数据库的密码要有相同的hash苏三发,相同的hash次数和相同的盐,才能匹配成功。
4、自定义支持Md5加密的realm
(1)自定义realm
(2)完成认证的业务逻辑
1)重写doGetAuthenticationInfo()方法
该方法有一个形参AuthenticationToken token,包含了当前用户的身份信息;
2)通过身份信息获取当前用户名
String username = (String)token.getPrincipal();
3)通过用户名查询数据库中当前用户信息
通过username查询到当前用户的加密时的salt,及加密后的密码;
4)封装查询到的用户并返回
通过SimpleAuthorizationInfo对象进行封装:
SimpleAuthenticationInfo simpleAuthenticationInfo =
new SimpleAuthenticationInfo (
user,
user.getPasword(),
ByteSource.Util.bytes(user.getSalt()),
this.getName());
再将封装好的simpleAuthorizationInfo返回。
注意:第一个参数:在数据库查询到的当前用户对于的Java bean;
第二个参数:当前用户在数据库查询到的密码;
第三个参数:加密时的盐。
第四个参数:当前自定义realm对象的名称。
5、配置ini配置文件
(1)定义[main]
(2)定义凭证匹配器
credentialsMatcher = org.apache.shiro.authc.credential.HashedCredentialsMatcher
(3)定义加密算法
credentialsMatcher.hashAlgorithmName = md5
(4)定义哈希次数
credentialsMatcher.hashIterations = 2(次数可自己设定,一般为2)
(5)定义自定义的realm
md5Realm(自定义realm首字母小写)= com.chen.realm.Md5Realm(自定义realm类的全限定名)
(6)将凭证匹配器注入到自定义的realm中
md5Realm.credentialsMatcher = $credentialsMatcher
(7)将自定义的realm注入到安全管理器中
securityManager.realms = $md5Realm
[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#定义哈希算法
credentialsMatcher.hashAlgorithmName=md5
#定义哈希次数
credentialsMatcher.hashIterations=2
#定义自定义的realm
md5Realm = com.chen.realm.Md5Realm
#将凭证匹配器注入自定义的realm
md5Realm.credentialsMatcher =$credentialsMatcher
#将自定义的realm注入安全管理器
securityMannager.realms = $md5Realm
6、测试
(1)产生加密的密码
String encryption = new Md5Hash( password , salt , times).toString();
(2)新建测试类
新建测试类,构建token,进行测试。