Shiro Authorization
- 授权 : 给身份认证通过的人,授予他可以访问某些资源的权力
- 权限粒度 : 分为粗粒度和细粒度,粗粒度:对表的操作.细粒度:对记录的操作.shiro 一般管理的是粗粒度的权限,比如: 菜单,按钮,url .一般细粒度的权限是通过业务来控制的
- 角色 : 权限的集合.
- 权限的表示规则 : 资源:操作:实例. 可以用通配符表示.如:
user:add 表示对user有添加的权限
user:* 表示对user具有所有操作的权限
user:delete:100 表示对user标识为100的记录有删除的权限
测试代码:
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","1111");
try{
//认证
subject.login(token);
}catch (AuthenticationException e){
System.out.println("认证不通过");
}
//基于角色的授权
boolean result = subject.hasRole("role1");
System.out.println(result);
//可以通过checkRole来检测是否具有某个角色,如果没有则抛出UnAuthorizedException
subject.checkRoles("role1");
//基于资源的授权
result = subject.isPermitted("user:delete");
System.out.println(result);
System.out.println(subject.isPermittedAll("user:dd","user:add","user:update"));
shiro.ini
[users]
zhangsan=1111,role1
lisi=1234,role2
[roles]
role1=user:add,user:update,user:delete
role2=user:*
shiro 中权限检查的方式有3种
- 编程式
if(subject.hasRole("role1")){
//操作某个资源
}
- 注解式
@RequiresRoles("role1")
public void list(){
//查询数据
}
- 标签
<shiro:hasPermission name="user:delete">
<a>delete</a>
</shiro:hasPermission>
需要在jsp中先引入标签
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
授权流程
- 获取subject主体
- 判断主体是否通过认证
- 调用subject.isPermitted*/hosRole* 来进行权限的判断
subject是由其实现类DelegatingSubject来调用方法的.该类将处理交给了SecurityManager.SecurityManager 的实现类AuthorizingSecurityManager将处理交给了authorizer(授权器),Authorozer 由其实现类ModularRealmAuthorizer来处理,该类可以调用对应的Realm来获取数据.
自定义Realm实现授权
仅仅通过配置文件指定权限不够灵活.在实际应用中都是将客户信息,角色信息,权限信息保存到数据库中所以需要从数据库中去获取相关的数据信息.可以通过shiro提供的JdbcRealm来实现,也可以自定义realm来实现.
自定义realm:
package cn.ckh2019.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.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import java.util.ArrayList;
import java.util.List;
/**
* @author Chen Kaihong
* 2019-06-09 21:48
*/
public class UserRealm extends AuthorizingRealm {
@Override
public String getName() {
return "userRealm";
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
String usernaeme = principal.getPrimaryPrincipal().toString();
//根据用户名到数据库查询该用户对应的权限信息
List<String> permissions = new ArrayList<>();
permissions.add("user:add");
permissions.add("user:delete");
permissions.add("user:update");
permissions.add("user:find");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
for(String perm : permissions){
info.addStringPermission(perm);
}
return info;
}
/**
* 完成身份认证(从数据库中取数据)
* 返回认证信息,如果认证失败返回null
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取用户输入的用户名
String username = (String) token.getPrincipal();
//根据用户名到数据库查询密码信息
//假定从数据库获取的密码是1234
String password = "6fbf9e1b65c1bef784eafff34b93b482";
String salt = "ckh";
//将从数据库查询的信息封装到SimpleAuthenticationInfo中
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,password,ByteSource.Util.bytes(salt),getName());
return info;
}
}
shiro.ini:
[main]
credentialsMatcher= org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=md5
credentialsMatcher.hashIterations=10
userRealm= cn.ckh2019.realm.UserRealm
userRealm.credentialsMatcher=$credentialsMatcher
securityManager.realm=$userRealm
测试代码:
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("张三","1234");
//6.进行用户验证
try {
subject.login(token);
//7.通过subject来判断用户是否通过验证
if (subject.isAuthenticated()) {
System.out.println("登录成功");
}
}catch(IncorrectCredentialsException e) {
System.out.println("密码错误");
}catch(UnknownAccountException e){
System.out.println("用户名错误");
}
System.out.println(subject.isPermitted("user:add","user:delete"));