Shiro 授权(Authorization)
简介
授权
授权,也叫访问控制,她可以控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。
授权的几个关键对象:
- 主体(Subject)
- 资源(Resource)
- 资源(Permission)
- 角色(R)
主体
主体,即访问应用的用户,在Shiro 中使用Subject 代表该用户。用户只有授权后才允许访问相应的资源。
资源
在应用中用户可以访问的任何东西,比如访问JSP页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只有授权后才能访问。
资源
安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:访问用户列表页面、查看/新增/修改/删除用户数据(即很多时候都是CRUD (增查改删)式权限控制)、打印文档等等。
如上可以看出,权限代表了用户有没有操作某个资源的权力,即反映在某个资源上的操作允不允许,不反映谁去执行这个操作。所以后续还需要把权限赋予给用户,即定义哪个用户允许在某个资源上做什么操作(权限),Shiro 不会去做这件事情,而是由实现人员提供。
Shiro支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权限,即实例级别的)。
角色
角色代表了操作集合,可以理解为权限的集合,-般情况下我们会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有-组不同的权限。
授权(Authorization)
快速上手
在resources下配置shiro.ini
[users]
#配置规则:用户账号=密码,角色1,角色2
admin=123456,管理员
czkt=111111,客户经理
[roles]
#配置规则:角色=权限1,权限2
管理员=user:*,role:*
客户经理=user:list,user:view
授权测试
@Test
public void testShiro(){
IniRealm realm=new IniRealm("classpath:shiro.ini");
DefaultSecurityManager securityManger=new DefaultSecurityManager();
securityManger.setRealm(realm);
SecurityUtils.setSecurityManager(securityManger);
Subject subject=SecurityUtils.getSubject();
UsernamePasswordToken token=new UsernamePasswordToken("admin","123456");
subject.login(token);
System.out.println("是否通过认证:"+subject.isAuthenticated());
System.out.println("是否为管理员角色:"+subject.hasRole("管理员"));
System.out.println("是否能操作用户查看功能:"+subject.isPermitted("user:view"));
subject.checkPermission("user:view");
}
运行效果:
授权流程
- 首先调用Subject.isPermitted*/hasRole*接口,其会委托给SecurityManager,而SecurityManager接着会委托给Authorizer;
- Authorizer是真正的授权者,如果我们调用如isPermitted(“user:view”),其首先会通过PermissionResolver把字符串转换成相应的Permission实例;
- 在进行授权之前,其会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限;
- Authorizer会判断Realm的角色/权限是否和传入的匹配,如果有多个Realm,会委托给ModularRealmAuthorizer进行循环判断,如果匹配如isPermitted*/hasRole*会返回true,否则返回false表示授权失败。
授权方式
Shiro支持四种方式的授权验证(权限控制):
- 代码级别权限控制
Subject subject=new Subject();
if(subject.hasRole("管理员")){
//有权限
}else{
//无权限
}
- 页面标签权限控制
<shiro:hasRole name="管理员">
<!-- 有权限 -->
</shiro:hasRole >
- 方法注解权限控制
@RequiresRoles("管理员")
public String add(Model model){
}
- URL拦截权限控制
fileMap.put("/user/dologin","anon");
fileMap.put("/user/logout","logout");
fileMap.put("/user/list","perms[用户列表]");
fileMap.put("/user/add","perms[用户添加]");
fileMap.put("/user/edit/**","perms[用户编辑]");
fileMap.put("/user/del/**","perms[用户删除]");
Spring Boot+Shiro 授权
静态授权
自定义Realm授予期限
public class MyShiroRealm extends AuthorizingRealm{
@Resource
private IUserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("调用MyShiroRealm.doGetAuthorizationInfo获取权限信息");
User user=(User)principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
//静态授权
info.addRole(user.getRole().getRoleName());
info.addStringPermission("用户列表");
if (user.getRole().getRoleName().equals("管理员")){
info.addStringPermission("用户添加");
info.addStringPermission("用户编辑");
info.addStringPermission("用户删除");
}
return info;
}
配置ShiroConfig拦截URL
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactory=new ShiroFilterFactoryBean();
shiroFilterFactory.setSecurityManager(securityManager);
shiroFilterFactory.setLoginUrl("/user/login");
shiroFilterFactory.setSuccessUrl("/main");
shiroFilterFactory.setUnauthorizedUrl("/403");
Map<String,String> fileMap=new LinkedHashMap<String,String>();
fileMap.put("/css/**","anon");
fileMap.put("/fonts/**","anon");
fileMap.put("/images/**","anon");
fileMap.put("/js/**","anon");
fileMap.put("/localcss/**","anon");
fileMap.put("/localjs/**","anon");
fileMap.put("/user/dologin","anon");
fileMap.put("/user/logout","logout");
//静态授权
fileMap.put("/user/list","perms[用户列表]");
fileMap.put("/user/add","perms[用户添加]");
fileMap.put("/user/edit/**","perms[用户编辑]");
fileMap.put("/user/del/**","perms[用户删除]");
fileMap.put("/**","authc");
shiroFilterFactory.setFilterChainDefinitionMap(fileMap);
return shiroFilterFactory;
}
测试授权控制
在IndexController控制器添加unauthorized方法:
@RequestMapping(value = "/403")
public String unauthorized(){
return "403";
}
修改403页面
<body class="nav-md footer_fixed">
<div class="container body">
<div class="main_container">
<div th:include="demo/common/header"></div>
<div class="col-md-12">
<div class="x_panel">
<div class="x_title">
<h2 style="color: red">
权限不足
<i class="fa fa-remove"></i>
<small style="color: red"><span th:text="${session.userSession.userName}">登录用户</span>
- 对不起,你没有操作改功能的权限! ^_^</small></h2>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
<!-- page content -->
<div th:include="demo/common/footer"></div>
动态授权
基础代码开发
- 添加实体类Right
@Entity
@Table(name = "sys_right")
@JsonIgnoreProperties(value = {"hibernateLazyInitializer","handler"})
public class Right implements Serializable {
@Id
@Column(name = "right_code")
private String rightCode;
@Column(name = "right_parent_code")
private String rightParentCode;
@Column(name = "right_type")
private String rightType;
@Column(name = "right_test")
private String rightTest;
@Column(name = "right_url")
private String rightUrl;
@Column(name = "right_tip")
private String rightTip;
//多对多
@ManyToMany(targetEntity = Role.class,mappedBy = "rights")
@JsonIgnore
private Set<Role> roles = new HashSet<Role>(0);
//省略get/set方法,改造方法
}
- 修改Role实体类,添加与Right类的多对多关联
@ManyToMany(targetEntity = Right.class,fetch = FetchType.LAZY)
@JoinTable(name = "sys_role_right",joinColumns = {@JoinColumn(name = "rf_role_id")}
,inverseJoinColumns = {@JoinColumn(name = "rf_right_code")})
@OrderBy(value = "rightCode")
private Set<Right> rights = new HashSet<Right>(0);
- 开发RightRepository
public interface RightRepository extends JpaRepository<Right,String> {
List<Right> findRightsByRolesOrderByRightCode(Role role);//根据角色
}
- 添加新的方法
public interface IRoleService {
public List<Role> findAllRoles();
public List<Right> findAllRights();
public List<Right> findRightsByRole(Role role);
}
调整IndexController
@RequestMapping("/user/dologin")
public String dologin(String userName, String userPassword, Model model, HttpSession session){
try {
UsernamePasswordToken token=new UsernamePasswordToken(userName,userPassword);
Subject subject= SecurityUtils.getSubject();
subject.login(token);
User user=(User) subject.getPrincipal();
Role role=user.getRole();
List<Right> rights=roleService.findRightsByRole(role);
role.getRights().addAll(rights);
session.setAttribute("userSession",user);
return "demo/main";
}
调整自定义Realm
public class MyShiroRealm extends AuthorizingRealm{
@Resource
private IUserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("调用MyShiroRealm.doGetAuthorizationInfo获取权限信息");
User user=(User)principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
//静态授权
// info.addRole(user.getRole().getRoleName());
// info.addStringPermission("用户列表");
// if (user.getRole().getRoleName().equals("管理员")){
// info.addStringPermission("用户添加");
// info.addStringPermission("用户编辑");
// info.addStringPermission("用户删除");
// }
//动态授权
Role role=user.getRole();
if (role!=null){
info.addRole(user.getRole().getRoleName());
Set<Right> rights=role.getRights();
if (rights!=null && rights.size()>0){
rights.forEach(right -> info.addStringPermission(right.getRightCode()));
}
}
return info;
}
}
调整配置对象ShiroConfig
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactory=new ShiroFilterFactoryBean();
shiroFilterFactory.setSecurityManager(securityManager);
shiroFilterFactory.setLoginUrl("/user/login");
shiroFilterFactory.setSuccessUrl("/main");
shiroFilterFactory.setUnauthorizedUrl("/403");
Map<String,String> fileMap=new LinkedHashMap<String,String>();
fileMap.put("/css/**","anon");
fileMap.put("/fonts/**","anon");
fileMap.put("/images/**","anon");
fileMap.put("/js/**","anon");
fileMap.put("/localcss/**","anon");
fileMap.put("/localjs/**","anon");
fileMap.put("/user/dologin","anon");
fileMap.put("/user/logout","logout");
//静态授权
// fileMap.put("/user/list","perms[用户列表]");
// fileMap.put("/user/add","perms[用户添加]");
// fileMap.put("/user/edit/**","perms[用户编辑]");
// fileMap.put("/user/del/**","perms[用户删除]");
//动态授权
List<Right> rights=roleService.findAllRights();
for (Right right:rights){
if (right.getRightUrl()!=null && !right.getRightUrl().trim().equals("")){
fileMap.put(right.getRightUrl()+"/**","perms["+right.getRightCode()+"]");
}
}
fileMap.put("/**","authc");
shiroFilterFactory.setFilterChainDefinitionMap(fileMap);
return shiroFilterFactory;
}