Shiro入门一:基于java项目,初识Shiro
在看了两天的《跟我学Shiro》之后,针对大佬的博客源码来梳理一下对Shiro的简单理解。
《跟我学Shiro》pdf上传两次都失败了,上传成功之后再贴出来分享给需要的同学。
首先是shiro-permission.ini文件的配置:
里面包含了用户和权限
[users]
zhangsan=123,role1,role2
wang=123,role2
#权限
[roles]
#角色role1对资源user拥有create、update权限
role1=user:create,user:update
role2=user:create,user:delete
role3=user:create
下面是测试代码:
package com.hust.shiro.authorization;
import java.util.Arrays;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
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 org.junit.Test;
/**
* 授权测试
*
* @author Administrator
*/
public class Authorization {
// 角色授权、资源授权测试
@Test
public void testAuthorization() {
// 创建SecurityManager工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-permission.ini");
// 创建SecurityManager
SecurityManager securityManager = factory.getInstance();
// 将SecurityManager设置到系统中
SecurityUtils.setSecurityManager(securityManager);
// 创建subject
Subject subject = SecurityUtils.getSubject();
// 创建token令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
// 执行认证
try {
subject.login(token);
} catch (UnknownAccountException e) {
System.out.println("用户名未注册");
} catch (IncorrectCredentialsException e) {
System.out.println("密码不正确");
} catch (AuthenticationException e) {
e.printStackTrace();
}
System.out.println("认证状态:" + subject.getPrincipal());
// 认证通过后执行授权
// 基于角色的授权(在配置文件中) zhang=123,role1,role2
//boolean isHasRole = subject.hasRole("role1");
//System.out.println("isHasRole:" + isHasRole);
if (subject.hasRole("role1")){
System.out.println("zhangsan拥有role1");
}else{
System.out.println("zhangsan没有role1");
}
// 判断拥有角色:role1 and role2
boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("role1","role2"));
System.out.println("hasAllRoles:" + hasAllRoles);
// 判断拥有角色:role1 不同的是当判断为假时抛出AuthorizationException异常
try {
subject.checkRole("role1");
} catch (AuthorizationException e) {
e.printStackTrace();
}
// 基于资源的授权
boolean isPermitted = subject.isPermitted("user:create");
System.out.println("isPermitted:" + isPermitted);
//使用check方法判断授权,如果授权不通过抛出异常
try {
subject.checkPermission("user:create");
} catch (AuthorizationException e) {
e.printStackTrace();
}
}
}
运行之后可以看到控制台打印的信息,可以看到是如何一步一步读取这个ini配置文件的。
Shiro 提供了 hasRole/hasRoles
用于判断用户是否拥有某个角色/某些权限;但是没有提供如 hashAnyRole 用于判断是否有某些权限中的某一个。
Shiro 提供的 checkRole/checkRoles
和 hasRole/hasAllRoles
不同的地方是它在判断为假的情 况下会抛出 UnauthorizedException 异常
另外一种就是 基于角色的访问控制
(即隐式角色)叫做粗粒度判断
,这种方式的缺点就是如果很多地方进 行了角色判断,但是有一天不需要了那么就需要修改相应代码把所有相关的地方进行删除; 这就是粗粒度造成的问题
Shiro 提供了 isPermitted
和 isPermittedAll
用于判断用户是否拥有某个权限或所有权限,也 没有提供如 isPermittedAny 用于判断拥有某一个权限的接口。
Shiro 提供的checkPermission
和checkPermissions
但是失败的情况下会抛出 UnauthorizedException 异常.
基于资源的访问控制
(显示角色),也可以叫基于权限的访问控制(细粒度判断
),这种方式的一般规则是“资源标识符:操作”,即是资源级别的粒度;这种方式的好处就是如果 要修改基本都是一个资源级别的修改,不会对其他模块代码产生影响,粒度小。但是实现 起来可能稍微复杂点,需要维护“用户——角色,角色——权限(资源:操作)”之间的 关系
用户可以自己定义Realm
shiro-realm.ini配置:
[main]
#自定义realm
customRealm=com.hust.shiro.realm.CustomRealm
#将realm设置到SecurityManager,相当于注入
securityManager.realm=$customRealm
自定义Realm:
package com.hust.shiro.realm;
import java.util.ArrayList;
import java.util.List;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class CustomRealm extends AuthorizingRealm {
@Override
public void setName(String name) {
// TODO Auto-generated method stub
super.setName("customRealm");
}
// 用于认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//第一步就是从token中取用户身份信息
String userCode = (String) token.getPrincipal();
//如果查询不到返回null
//模拟在数据库中找不到用户
if (!userCode.equals("zhangsansan")) {
return null;
}
//第二步根据用户输入的userCode从数据库查询
String password = "111111";
//如果查询到返回AuthenticationInfo
SimpleAuthenticationInfo simpleAuthenticationInfo =
new SimpleAuthenticationInfo(userCode, password, this.getName());
return simpleAuthenticationInfo;
}
// 用于授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//principals获取主身份信息
//将getPrimaryPrincipal方法返回值强转为真实身份类型
String userCode = (String) principals.getPrimaryPrincipal();
//根据身份信息获取权限信息
//模拟数据
List<String> permissions = new ArrayList<String>();
permissions.add("user:create");
permissions.add("items:update");
//查到权限数据,返回
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addStringPermissions(permissions);
return simpleAuthorizationInfo;
}
}
测试方法:
// 自定义realm
@Test
public void testCustomRealm() {
// 创建securityManager工厂,通过ini文件创建securityManager工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-realm.ini");
// 创建securityManager
SecurityManager securityManager = factory.getInstance();
// 将securityManager设置当前的运行环境中
SecurityUtils.setSecurityManager(securityManager);
// SecurityUtils里面创建一个subject
Subject subject = SecurityUtils.getSubject();
// 在认证提交前要准备token(令牌)
UsernamePasswordToken token = new UsernamePasswordToken("zhangsansan", "111111");
// 执行认证提交
try {
subject.login(token);
} catch (UnknownAccountException e) {
System.out.println("用户名未注册");
} catch (IncorrectCredentialsException e) {
System.out.println("密码不正确");
} catch (AuthenticationException e) {
e.printStackTrace();
}
// 是否认证通过
boolean isAuthenticated = subject.isAuthenticated();
System.out.println("是否认证通过: " + isAuthenticated);
// 判断权限
boolean isPermission = subject.isPermitted("user:create");
System.out.println("isPermission:" + isPermission);
// 退出操作
subject.logout();
// 是否认证通过
isAuthenticated = subject.isAuthenticated();
System.out.println("退出后是否认证通过: " + isAuthenticated);
}
使用md5加密处理:
shiro-realm-md5.ini
[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次数
credentialsMatcher.hashIterations=1
#自定义realm
customRealm=com.hust.shiro.realm.CustomRealmMD5
customRealm.credentialsMatcher=$credentialsMatcher
#将realm设置到SecurityManager,相当于注入
securityManager.realms=$customRealm
CustomRealmMD5.java
package com.hust.shiro.realm;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
public class CustomRealmMD5 extends AuthorizingRealm {
@Override
public void setName(String name) {
super.setName("customRealmMD5");
}
// 用于认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//第一步就是从token中取用户身份信息
String userCode = (String) token.getPrincipal();
//如果查询不到返回null
//模拟在数据库中找不到用户
if (!userCode.equals("zhangsansan")) {
return null;
}
//第二步根据用户输入的userCode从数据库查询
String password = "98cd6f146bdec65a48dedc505aaf2b80";
String salt = "sadsgg";
//如果查询到返回AuthenticationInfo
SimpleAuthenticationInfo simpleAuthenticationInfo =
new SimpleAuthenticationInfo(userCode, password, ByteSource.Util.bytes(salt),this.getName());
return simpleAuthenticationInfo;
}
// 用于授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
}
测试代码:
// 自定义realm实现散列匹配
@Test
public void testCustomRealmMD5() {
// 创建securityManager工厂,通过ini文件创建securityManager工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-realm-md5.ini");
// 创建securityManager
SecurityManager securityManager = factory.getInstance();
// 将securityManager设置当前的运行环境中
SecurityUtils.setSecurityManager(securityManager);
// SecurityUtils里面创建一个subject
Subject subject = SecurityUtils.getSubject();
// 在认证提交前要准备token(令牌)
UsernamePasswordToken token = new UsernamePasswordToken("zhangsansan", "123456");
// 执行认证提交
try {
subject.login(token);
} catch (UnknownAccountException e) {
System.out.println("用户名未注册");
} catch (IncorrectCredentialsException e) {
System.out.println("密码不正确");
} catch (AuthenticationException e) {
e.printStackTrace();
}
// 是否认证通过
boolean isAuthenticated = subject.isAuthenticated();
System.out.println("是否认证通过: " + isAuthenticated);
// 判断权限
boolean isPermission = subject.isPermitted("user:create");
System.out.println("isPermission:" + isPermission);
// 退出操作
subject.logout();
// 是否认证通过
isAuthenticated = subject.isAuthenticated();
System.out.println("是否认证通过: " + isAuthenticated);
}