1.自定义Realm
前面的案例是通过在ini文件中配置账号密码来实现的,显然这种方式并没有太多的通用性,结合实际的开发情况来介绍一下自定义Realm的实现
1.1 创建Realm
package com.shiro.realm;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* 自定义的Realm
*/
public class MyRealm extends AuthorizingRealm {
/**
* 认证操作
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
String password = new String(token.getPassword());
System.out.println("登录的账号密码是:" + username + " " + password);
//通过JDBC操作和数据库中的账号密码匹配
if("zhang".equals(username)){
//账号正确,假设查询出的zhnag的密码是123
AuthenticationInfo info=new SimpleAuthenticationInfo(username,"123","myrealm");
return info;
}
return null;
}
/**
* 授权操作
* @param principalCollection
* @return
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
1.2 设置自定义Realm
[main]
# 自定义Realm
customeRealm=com.shiro.realm.MyRealm
# 将自定义的Realm设置到SecurityManager
securityManager.realms=$customeRealm
1.3 测试代码
测试代码和前面案例的一样
package com.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
public class test1 {
public static void main(String[] args) {
//1.获取一个SecurityManager工厂对象
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini");
//2.通过SecurityManager工厂对象创建SecurityManager
SecurityManager securityManager = factory.getInstance();
//3.将SecurityManager对象添加到当前的运行环境中去
SecurityUtils.setSecurityManager(securityManager);
//4.获取Subject对象
Subject subject = SecurityUtils.getSubject();
//5.获取用户提交的要认证的账号密码
String userName="zhang";
String password="123";
//6.将用户提交的账号密码封装为一个Token对象
AuthenticationToken token=new UsernamePasswordToken(userName,password);
//7.完成认证操作
subject.login(token);
//8.获取认证状态
System.out.println(subject.isAuthenticated());
}
}
2.认证原理分析
2.1从测试方法的login方法作为入口
2.2 进入DelegatingSubject的login方法中
public void login(AuthenticationToken token) throws AuthenticationException {
this.clearRunAsIdentitiesInternal();
//进入securityManager的login方法中
Subject subject = this.securityManager.login(this, token);
//认证完成后的方法
String host = null;
PrincipalCollection principals;
if (subject instanceof DelegatingSubject) {
DelegatingSubject delegating = (DelegatingSubject)subject;
principals = delegating.principals;
host = delegating.host;
} else {
principals = subject.getPrincipals();
}
if (principals != null && !principals.isEmpty()) {
this.principals = principals;
this.authenticated = true;
if (token instanceof HostAuthenticationToken) {
host = ((HostAuthenticationToken)token).getHost();
}
if (host != null) {
this.host = host;
}
Session session = subject.getSession(false);
if (session != null) {
this.session = this.decorate(session);
} else {
this.session = null;
}
} else {
String msg = "Principals returned from securityManager.login( token ) returned a null or empty value. This value must be non null and populated with one or more elements.";
throw new IllegalStateException(msg);
}
}
2.3 进入DefaultSecurityManager的login方法中
public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info;
try {
//关键方法,认证方法
info = this.authenticate(token);
} catch (AuthenticationException var7) {
AuthenticationException ae = var7;
try {
this.onFailedLogin(token, ae, subject);
} catch (Exception var6) {
if (log.isInfoEnabled()) {
log.info("onFailedLogin method threw an exception. Logging and propagating original AuthenticationException.", var6);
}
}
throw var7;
}
2.4 进入AbstractAuthenticator的authenticate方法中
public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
if (token == null) {
throw new IllegalArgumentException("Method argument (authentication token) cannot be null.");
} else {
log.trace("Authentication attempt received for token [{}]", token);
AuthenticationInfo info;
try {
//关键代码 完成认证
info = this.doAuthenticate(token);
if (info == null) {
String msg = "No account information found for authentication token [" + token + "] by this Authenticator instance. Please check that it is configured correctly.";
throw new AuthenticationException(msg);
}
} catch (Throwable var8) {
AuthenticationException ae = null;
if (var8 instanceof AuthenticationException) {
ae = (AuthenticationException)var8;
}
if (ae == null) {
String msg = "Authentication failed for token submission [" + token + "]. Possible unexpected error? (Typical or expected login exceptions should extend from AuthenticationException).";
ae = new AuthenticationException(msg, var8);
if (log.isWarnEnabled()) {
log.warn(msg, var8);
}
}
try {
this.notifyFailure(token, ae);
} catch (Throwable var7) {
if (log.isWarnEnabled()) {
String msg = "Unable to send notification for failed authentication attempt - listener error?. Please check your AuthenticationListener implementation(s). Logging sending exception and propagating original AuthenticationException instead...";
log.warn(msg, var7);
}
}
throw ae;
}
log.debug("Authentication successful for token [{}]. Returned account [{}]", token, info);
this.notifySuccess(token, info);
return info;
}
}
2.5 再进入ModularRealmAuthenticator的doAuthenticate方法中
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
this.assertRealmsConfigured();
//获取到自定义的Realm
Collection<Realm> realms = this.getRealms();
//判断是否进入单域还是多域
return realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);
}
2.6 进入doSingleRealmAuthentication方法中
protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
if (!realm.supports(token)) {
String msg = "Realm [" + realm + "] does not support authentication token [" + token + "]. Please ensure that the appropriate Realm implementation is configured correctly or that the realm accepts AuthenticationTokens of this type.";
throw new UnsupportedTokenException(msg);
} else {
//具体的验证操作
AuthenticationInfo info = realm.getAuthenticationInfo(token);
if (info == null) {
String msg = "Realm [" + realm + "] was unable to find account data for the submitted AuthenticationToken [" + token + "].";
//这是我们熟悉的异常,账号错误的异常信息
throw new UnknownAccountException(msg);
} else {
return info;
}
}
}
2.7 进入到AuthenticatingRealm的getAuthenticationInfo方法中
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info = this.getCachedAuthenticationInfo(token);
if (info == null) {
//其实就是会执行我们自定义的MyRealm中的验证方法
info = this.doGetAuthenticationInfo(token);
log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
if (token != null && info != null) {
this.cacheAuthenticationInfoIfPossible(token, info);
}
} else {
log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
}
if (info != null) {
this.assertCredentialsMatch(token, info);
} else {
log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token);
}
return info;
}
3.加密
加密,是以某种特殊的算法改变原有的信息数据,使得未授权的用户及时获得了已加密的信息,但因不知解密的方法,仍然无法了解信息的内容
3.1 概念
数据加密的基本过程就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码,通常称为“密文”,使其只能在输入相应的密钥之后才能显示出本来内容,通过这样的途径来达到保护数据不被非法人窃取、阅读的目的。该过程的逆过程为解密,即将该编码信息转化为其原来数据的过程。
3.2 加密分类
对称加密
双方使用的同一个密钥,这种加密方法称为对称加密,也称为单密钥加密
非对称加密
一对密钥由公钥和私钥组成(可以使用很多对密钥)。私钥解密公钥加密数据,公钥解密私钥加密数据(私钥公钥可以互相加密解密)
3.3 加密算法分类
单向加密
单向加密是不可逆的,也就是只能加密,不能解密。通常用来传输类似用户名和密码,直接将加密后的数据提交到后台,因为后台不需要知道用户名和密码,可以直接将收到的加密后的数据存储到数据库
图示:
双向加密
通常分为对称性加密算法和非对称性加密算法,对于对称性加密算法,信息接收双方都需事先知道密匙和加解密算法且其密匙是相同的,之后便是对数据进行 加解密了。非对称算法与之不同,发送双方A,B事先均生成一堆密匙,然后A将自己的公有密匙发送给B,B将自己的公有密匙发送给A,如果A要给B发送消 息,则先需要用B的公有密匙进行消息加密,然后发送给B端,此时B端再用自己的私有密匙进行消息解密,B向A发送消息时为同样的道理。
3.4 MD5加密
MD5加密应该算是一个应用非常广泛的一种加密方式。是一种单向加密,不可逆的加密算法。
package com.shiro.md5;
import org.apache.shiro.crypto.hash.Md5Hash;
public class MD5Demo1 {
public static void main(String[] args) {
//单个信息加密
Md5Hash md5Hash=new Md5Hash("123456");
System.out.println(md5Hash);
//加密添加盐值 增大破解难度
md5Hash=new Md5Hash("123456","123");
System.out.println(md5Hash);
//加密添加盐值 及添加迭代次数
md5Hash=new Md5Hash("123456","123",1024);
System.out.println(md5Hash);
}
}
结果
3.5 盐值的作用
使用MD5存在一个问题,相同的password生成的hash值是相同的,如果两个用户设置了相同密码,那么数据库中会存储两个相同的值,这是极不安全的,加Salt可以在一定程度上解决这一问题,所谓的加Salt方法,就是加点“佐料”。其基本思想是这样的,当用户首次提供密码时(通常是注册时)由系统自动往这个密码里撒一点“佐料”,然后再散列,而当用户登录时,系统为用户提供的代码上撒上相同的“佐料”,然后散列,再比较散列值,来确定密码是否正确。
加盐的原理:给原文加入随机数生成新的MD5的值
3.6 Shiro中使用MD5
3.6.1 首先我们需要在认证的方法中修改密码相关信息
package com.shiro.realm;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.SimpleByteSource;
/**
* 自定义的Realm
*/
public class MyRealm extends AuthorizingRealm {
/**
* 认证操作
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
String password = new String(token.getPassword());
System.out.println("登录的账号密码是:" + username + " " + password);
//通过JDBC操作和数据库中的账号密码匹配
if("zhang".equals(username)){
//账号正确,假设查询出的zhnag的密码是123
String pwd="b2793335f43645fd8e00c7d18e14e05f";
String salt="123";
AuthenticationInfo info=new SimpleAuthenticationInfo(username,
pwd,
new SimpleByteSource(salt),
"myrealm");
return info;
}
return null;
}
/**
* 授权操作
* @param principalCollection
* @return
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
3.6.2 修改ini文件
[main]
# 定义凭证匹配器
credentialsMathcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
# 散列的算法
credentialsMathcher.hashAlgorithmName=md5
# 散列迭代的次数
credentialsMathcher.hashIterations=1024
# 自定义Realm
customeRealm=com.shiro.realm.MyRealm
# 把匹配器设置到Realm
customeRealm.credentialsMatcher=$credentialsMathcher
# 将自定义的Realm设置到SecurityManager
securityManager.realms=$customeRealm
3.6.3 测试代码(和之前一样)
package com.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
public class test2 {
public static void main(String[] args) {
//1.获取一个SecurityManager工厂对象
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini");
//2.通过SecurityManager工厂对象创建SecurityManager
SecurityManager securityManager = factory.getInstance();
//3.将SecurityManager对象添加到当前的运行环境中去
SecurityUtils.setSecurityManager(securityManager);
//4.获取Subject对象
Subject subject = SecurityUtils.getSubject();
//5.获取用户提交的要认证的账号密码
String userName="zhang";
String password="123";
//6.将用户提交的账号密码封装为一个Token对象
AuthenticationToken token=new UsernamePasswordToken(userName,password);
//7.完成认证操作
try{
subject.login(token);
System.out.println("登录成功");
}catch (UnknownAccountException e){
System.out.println("账号错误");
}catch (IncorrectCredentialsException e){
System.out.println("密码错误");
}
}
}
4.授权
4.1 概念
授权,又称为访问控制,是对资源的访问管理的过程,即对于认证通过的用户,授予它的访问某些资源的权限。
4.2 授权的方式
shiro支持3种方式的授权
代码触发
通过if/else授权代码块完成
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}
注解触发
通过在执行的Java方法上放置相应的注解完成
@RequiresRoles("admin")
public void hello() {
//有权限
}
标签触发
在JSP/GSP 页面通过相应的标签完成
<shiro:hasRole name="admin">
<!— 有权限—>
</shiro:hasRole>
4.3 授权流程图
4.4 简单的授权案例
4.4.1 在ini配置文件中配置相关的用户对应的角色和相关的权限信息
[users]
root=123456,role1,role2
# 账号是root,密码是123456 具有的角色是 role1,role2
[roles]
# 角色role具有的权限
role1=user:create,user:update
role2=user:query,user:create
role3=user:delete,user:query
4.4.2 然后再测试代码的认证成功后做权限的管理操作
package com.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import java.util.Arrays;
public class test2 {
public static void main(String[] args) {
//1.获取一个SecurityManager工厂对象
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini");
//2.通过SecurityManager工厂对象创建SecurityManager
SecurityManager securityManager = factory.getInstance();
//3.将SecurityManager对象添加到当前的运行环境中去
SecurityUtils.setSecurityManager(securityManager);
//4.获取Subject对象
Subject subject = SecurityUtils.getSubject();
//5.获取用户提交的要认证的账号密码
String userName="root";
String password="123456";
//6.将用户提交的账号密码封装为一个Token对象
AuthenticationToken token=new UsernamePasswordToken(userName,password);
//7.完成认证操作
try{
subject.login(token);
System.out.println("登录成功");
System.out.println("认证状态:"+subject.isAuthenticated());
System.out.println("是否具有role1角色:"+subject.hasRole("role1"));
System.out.println("是否具有role3角色:"+subject.hasRole("role3"));
boolean[] booleans = subject.hasRoles(Arrays.asList("role1", "role2","role3"));
System.out.println(Arrays.toString(booleans));
System.out.println(subject.getPrincipal()+"是否有role1和role2两个角色:"+
subject.hasAllRoles(Arrays.asList("role1","role2")));
System.out.println(subject.getPrincipal()+"是否有role1和role3两个角色:"+
subject.hasAllRoles(Arrays.asList("role1","role3")));
//check开头方法校验不通过同样抛出异常信息
subject.checkRole("role1");
//做权限的检验
System.out.println(subject.getPrincipal()+"是否具有user:create权限:"+
subject.isPermitted("user:create"));
System.out.println(subject.getPrincipal()+"是否具有user:delete权限:"+
subject.isPermitted("user:delete"));
//check开头的校验方法不通过会抛出异常
subject.checkPermission("user:delete");
}catch (UnknownAccountException e){
System.out.println("账号错误");
}catch (IncorrectCredentialsException e){
System.out.println("密码错误");
}
}
}
结果:
登录成功
认证状态:true
是否具有role1角色:true
是否具有role3角色:false
[true, true, false]
root是否有role1和role2两个角色:true
root是否有role1和role3两个角色:false
root是否具有user:create权限:true
root是否具有user:delete权限:false
Exception in thread "main" org.apache.shiro.authz.UnauthorizedException: Subject does not have permission [user:delete]
at org.apache.shiro.authz.ModularRealmAuthorizer.checkPermission(ModularRealmAuthorizer.java:323)
at org.apache.shiro.mgt.AuthorizingSecurityManager.checkPermission(AuthorizingSecurityManager.java:137)
at org.apache.shiro.subject.support.DelegatingSubject.checkPermission(DelegatingSubject.java:205)
at com.shiro.test2.main(test2.java:51)
4.5 自定义Realm中实现授权操作
上面是将用户的权限配置在ini文件中的,实际开发中用户关联的权限信息会非常的复杂,我们一般都是设置在关系型数据库中,因此我们也要考虑相关的实现
4.5.1 在自定义Realm中重写doGetAuthorizationInfo方法
认证成功后会执行的授权方法要注意的是doGetAuthorizationInfo方法中的形参的实际数据是认证方法中返回的SimpleAuthenticationInfo中的第一个参数
/**
* 授权操作
*
* 认证成功后会执行的授权方法
* 要注意的是doGetAuthorizationInfo方法中的形参的实际数据是
* 认证方法中返回的SimpleAuthenticationInfo中的第一个参数
* @param principalCollection
* @return
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取当前登录的账号
String username = principalCollection.getPrimaryPrincipal().toString();
System.out.println("当前登录的账号是:" + username);
//根据登录的账号去数据库中查询对应的角色和权限信息
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRole("role1");
info.addRole("role2");
info.addStringPermission("user:create");
info.addStringPermission("user:update");
return info;
}
4.5.2 在配置文件中关联自定义的Realm即可
[main]
# 自定义Realm
customeRealm=com.shiro.realm.MyRealm
# 将自定义的Realm设置到SecurityManager中
securityManager.realms=$customeRealm
4.5.3 测试代码(和之前一样)
package com.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import java.util.Arrays;
public class test2 {
public static void main(String[] args) {
//1.获取一个SecurityManager工厂对象
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini");
//2.通过SecurityManager工厂对象创建SecurityManager
SecurityManager securityManager = factory.getInstance();
//3.将SecurityManager对象添加到当前的运行环境中去
SecurityUtils.setSecurityManager(securityManager);
//4.获取Subject对象
Subject subject = SecurityUtils.getSubject();
//5.获取用户提交的要认证的账号密码
String userName="root";
String password="123";
//6.将用户提交的账号密码封装为一个Token对象
AuthenticationToken token=new UsernamePasswordToken(userName,password);
//7.完成认证操作
try{
subject.login(token);
System.out.println("登录成功");
System.out.println("认证状态:"+subject.isAuthenticated());
System.out.println("是否具有role1角色:"+subject.hasRole("role1"));
System.out.println("是否具有role3角色:"+subject.hasRole("role3"));
boolean[] booleans = subject.hasRoles(Arrays.asList("role1", "role2","role3"));
System.out.println(Arrays.toString(booleans));
System.out.println(subject.getPrincipal()+"是否有role1和role2两个角色:"+
subject.hasAllRoles(Arrays.asList("role1","role2")));
System.out.println(subject.getPrincipal()+"是否有role1和role3两个角色:"+
subject.hasAllRoles(Arrays.asList("role1","role3")));
//check开头方法校验不通过同样抛出异常信息
subject.checkRole("role1");
//做权限的检验
System.out.println(subject.getPrincipal()+"是否具有user:create权限:"+
subject.isPermitted("user:create"));
System.out.println(subject.getPrincipal()+"是否具有user:delete权限:"+
subject.isPermitted("user:delete"));
//check开头的校验方法不通过会抛出异常
subject.checkPermission("user:delete");
}catch (UnknownAccountException e){
System.out.println("账号错误");
}catch (IncorrectCredentialsException e){
System.out.println("密码错误");
}
}
}
结果:
登录的账号密码是:root 123
登录成功
认证状态:true
当前登录的账号是:root
是否具有role1角色:true
当前登录的账号是:root
是否具有role3角色:false
当前登录的账号是:root
当前登录的账号是:root
当前登录的账号是:root
[true, true, false]
当前登录的账号是:root
当前登录的账号是:root
root是否有role1和role2两个角色:true
当前登录的账号是:root
当前登录的账号是:root
root是否有role1和role3两个角色:false
当前登录的账号是:root
当前登录的账号是:root
root是否具有user:create权限:true
当前登录的账号是:root
root是否具有user:delete权限:false
当前登录的账号是:root
342 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
342 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
342 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
342 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
342 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
343 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
343 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
343 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
343 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
343 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
344 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
344 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
344 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
Exception in thread "main" org.apache.shiro.authz.UnauthorizedException: Subject does not have permission [user:delete]
at org.apache.shiro.authz.ModularRealmAuthorizer.checkPermission(ModularRealmAuthorizer.java:323)
at org.apache.shiro.mgt.AuthorizingSecurityManager.checkPermission(AuthorizingSecurityManager.java:137)
at org.apache.shiro.subject.support.DelegatingSubject.checkPermission(DelegatingSubject.java:205)
at com.shiro.test2.main(test2.java:51)