Shiro之授权

2024-01-07 19:36:44

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
Shiro之授权


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

在当今的应用开发中,安全是至关重要的一环。而 Shiro 作为一个强大而灵活的安全框架,为我们提供了一种简单而有效的方式来管理应用程序的授权。
在这篇博客中,我将带领大家深入了解 Shiro 的授权功能,以及如何使用 Shiro 来保护我们的应用程序。
通过实际的案例和代码示例,我将向你展示如何使用 Shiro 来控制对资源的访问,以及如何实现细粒度的权限控制。
无论你是刚刚开始接触 Shiro,还是已经有一定经验的开发者,这篇博客都将为你提供宝贵的知识和实用的技巧。让我们一起探索 Shiro 的授权世界,为我们的应用程序增添一层强大的安全保护。


提示:以下是本篇文章正文内容,下面案例可供参考

一、Shiro之授权

当用户认证后就可以正常访问,但并不是所有的资源都可以访问,假如你不是VIP,那么你就不能访问VIP才能访问的资源。如果你想访问VIP的资源,你必须拥有访问VIP资源的权限,这就是授权的应用范围。

权限表的设计

CREATE TABLE `role` (
 `rid` int(11) NOT NULL AUTO_INCREMENT,
 `roleName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 `roleDesc` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 PRIMARY KEY (`rid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `role` VALUES (1, '总经理', '管理整个公司');
INSERT INTO `role` VALUES (2, '股东', '参与公司决策');
INSERT INTO `role` VALUES (3, '财务', '管理公司资产');


CREATE TABLE `permission` (
 `pid` int(11) NOT NULL AUTO_INCREMENT,
 `permissionName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 `url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `permission` VALUES (1, '查询报表', '/reportform/find');
INSERT INTO `permission` VALUES (2, '查询工资', '/salary/find');
INSERT INTO `permission` VALUES (3, '查询税务', '/tax/find');


CREATE TABLE `users_role` (
 `uid` int(255) NOT NULL,
 `rid` int(11) NOT NULL,
 PRIMARY KEY (`uid`, `rid`) USING BTREE,
 INDEX `rid`(`rid`) USING BTREE,
 CONSTRAINT `users_role_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `users` (`uid`) ON DELETE RESTRICT ON UPDATE RESTRICT,
 CONSTRAINT `users_role_ibfk_2` FOREIGN KEY (`rid`) REFERENCES `role` (`rid`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `users_role` VALUES (1, 2);
INSERT INTO `users_role` VALUES (1, 3);


CREATE TABLE `role_permission` (
 `rid` int(11) NOT NULL,
 `pid` int(11) NOT NULL,
 PRIMARY KEY (`rid`, `pid`) USING BTREE,
 INDEX `pid`(`pid`) USING BTREE,
 CONSTRAINT `role_permission_ibfk_1` FOREIGN KEY (`rid`) REFERENCES `role` (`rid`) ON DELETE RESTRICT ON UPDATE RESTRICT,
 CONSTRAINT `role_permission_ibfk_2` FOREIGN KEY (`pid`) REFERENCES `permission` (`pid`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `role_permission` VALUES (1, 1);
INSERT INTO `role_permission` VALUES (2, 1);
INSERT INTO `role_permission` VALUES (1, 2);
INSERT INTO `role_permission` VALUES (3, 2);
INSERT INTO `role_permission` VALUES (1, 3);
INSERT INTO `role_permission` VALUES (2, 3);

数据库查询权限

1.编写用户、角色、权限实体类

@Data
public class Users implements Serializable {
  private Integer uid;
  private String username;
  private String password;
  private String salt;
}


// 角色
@Data
public class Role implements Serializable{
  private Integer rid;
  private String roleName;
  private String roleDesc;
}


// 权限
@Data
public class Permission implements Serializable{
  private Integer pid;
  private String permissionName;
  private String url;
}

2.修改UsersMapper接口

// 根据用户id查询权限
List<Permission> findPermissionById(Integer id);

3.在resources目录中编写UsersMapper的映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itbaizhan.myshiro1.mapper.UsersMapper">
  <select id="findPermissionById" resultType="com.itbaizhan.myshiro1.domain.Permission">
     SELECT DISTINCT permission.pid,permission.permissionName,permission.url FROM
       users
         LEFT JOIN users_role on users.uid = users_role.uid
         LEFT JOIN role on users_role.rid = role.rid
         LEFT JOIN role_permission on role.rid = role_permission.rid
         LEFT JOIN permission on role_permission.pid = permission.pid
     where users.uid = #{uid}
  </select>
</mapper>

在Realm进行授权

// 自定义授权方法
@Override 
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  //1.拿到用户认证信息
  Users users = (Users) principalCollection.getPrimaryPrincipal();
  //2.从数据库中查询权限
  List<Permission> permissions = usersMapper.findPermissionById(users.getUid());
  //3.遍历权限对象,将所有权限名交给Shiro管理
  SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
  for (Permission permission : permissions) {
    authorizationInfo.addStringPermission(permission.getUrl());
   }
  return authorizationInfo;
}

过滤器配置鉴权

Shiro可以根据用户拥有的权限,控制具体资源的访问,这一过程称为鉴权。在Shiro中,可以通过过滤器进行鉴权配置:

过滤器说明
roles角色授权过滤器,验证用户是否拥有某角色
perms权限授权过滤器,验证用户是否拥有某权限
port端口过滤器,表示可以通过的端口,如果配置端口为80,而用户访问该页面是非80,自动将请求端口改为80并重定向到该80端口
restrest风格过滤器,自动根据请求方法构建权限字符串
sslSSL过滤器,只有请求协议是https才能通过,否则自动跳转会https端口(443)

1.配置过滤器

// 配置过滤器
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
  // 1.创建过滤器工厂
  ShiroFilterFactoryBean filterFactory=new ShiroFilterFactoryBean();
  // 2.过滤器工厂设置SecurityManager
  filterFactory.setSecurityManager(securityManager);
  // 3.设置shiro的拦截规则
  Map<String,String> filterMap=new HashMap<>();
  // 不拦截的资源
  filterMap.put("/login.html","anon");
  filterMap.put("/fail.html","anon");
  filterMap.put("/user/login","anon");
  filterMap.put("/css/**","anon");


  // 鉴权过滤器,要写在/**之前,否则认证都无法通过
  filterMap.put("/reportform/find", "perms[/reportform/find]");
  filterMap.put("/salary/find", "perms[/salary/find]");
  filterMap.put("/staff/find", "perms[/staff/find]");


  // 其余资源都需要认证,authc过滤器表示需要认证才能进行访问; user过滤器表示配置记住我或认证都可以访问
  //     filterMap.put("/**","authc");
  filterMap.put("/user/pay","authc");
  filterMap.put("/**", "user");
  
  // 4.将拦截规则设置给过滤器工厂
  filterFactory.setFilterChainDefinitionMap(filterMap);
  // 5.登录页面
  filterFactory.setLoginUrl("/login.html");
  return filterFactory;
}


// 编写测试控制器
@RestController
public class MyController {
  @GetMapping("/reportform/find")
  public String findReportform(){
    return "查询报表";
   }


  @GetMapping("/salary/find")
  public String findSalary(){
    return "查询工资";
   }


  @GetMapping("/staff/find")
  public String findStaff(){
    return "查询员工";
   }
}

2.编写权限不足页面

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>权限不足</title>
</head>
<body>
<h1>您的权限不足,请联系管理员!</h1>
</body>
</html>

3.配置权限不足的跳转页面

// 配置过滤器
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
  // 1.创建过滤器工厂
  ShiroFilterFactoryBean filterFactory=new ShiroFilterFactoryBean();
  // 2.过滤器工厂设置SecurityManager
  filterFactory.setSecurityManager(securityManager);
  // 3.设置shiro的拦截规则
  Map<String,String> filterMap=new HashMap<>();
  // 不拦截的资源
  filterMap.put("/login.html","anon");
  filterMap.put("/fail.html","anon");
  filterMap.put("/noPermission.html","anon");
  filterMap.put("/user/login","anon");
  filterMap.put("/css/**","anon");


  // 鉴权过滤器
  filterMap.put("/reportform/find", "perms[/reportform/find]");
  filterMap.put("/salary/find", "perms[/salary/find]");
  filterMap.put("/staff/find", "perms[/staff/find]");




  // 其余资源都需要认证,authc过滤器表示需要认证才能进行访问; user过滤器表示配置记住我或认证都可以访问
  //     filterMap.put("/**","authc");
  filterMap.put("/user/pay","authc");
  filterMap.put("/**", "user");
  // 4.将拦截规则设置给过滤器工厂
  filterFactory.setFilterChainDefinitionMap(filterMap);
  // 5.登录页面
  filterFactory.setLoginUrl("/login.html");
  // 6.权限不足访问的页面
  filterFactory.setUnauthorizedUrl("/noPermission.html");
  return filterFactory;
}

注解配置鉴权

Shiro支持注解配置鉴权

注解说明
@RequiresGuest不认证即可访问的资源
@RequiresUser通过登录方式或“记住我”方式认证后可以访问资源
@RequiresAuthentication通过登录方式认证后可以访问资源
@RequiresRoles认证用户拥有特定角色才能访问的资源
@RequiresPermissions认证用户拥有特定权限才能访问的资源

1.在配置类开启Shiro注解

// 开启shiro注解的支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
  AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
  advisor.setSecurityManager(securityManager);
  return advisor;
}


// 开启aop注解支持
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
  DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
  defaultAAP.setProxyTargetClass(true);
  return defaultAAP;
}

2.在控制器方法上添加鉴权注解

@GetMapping("/test/index")
@RequiresGuest
public String testIndex() {
  return "访问首页";
}


@GetMapping("/test/user")
@RequiresUser
public String testUser() {
  return "用户中心";
}


@GetMapping("/test/pay")
@RequiresAuthentication
public String testPay() {
  return "支付中心";
}


@GetMapping("/tax/find")
@RequiresPermissions("/tax/find")
public String taxFind() {
  return "查询税务";
}


@GetMapping("/address/find")
@RequiresPermissions("/address/find")
public String addressFind() {
  return "查询地址";
}

@RequiresRoles("admin")
@RequiresPermissions("/admin")
    public String adminMethod() {
        return "admin页面"
    }

缓存权限

每次鉴权都需要去数据库查询,这样非常浪费数据库资源,因为用户的权限短时间内是不会改变的,所以我们可以把权限保存在缓存中,这样我们就可以不用每次都去查询数据库了,可以提高系统性能。

Shiro整合ehcache

ehcache是用来管理缓存的一个工具,其缓存的数据可以放在内存中,也可以放在硬盘上。ehcache的核心是CacheManager,一切的ehcache的应用都是从CacheManager开始的。

1.引入shiro和ehcache整合包

<!-- shiro和ehcache整合包 -->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-ehcache</artifactId>
  <version>1.9.0</version>
</dependency>

2.创建配置文件shiro-ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
     updateCheck="false">
  <diskStore path="java.io.tmpdir/Tmp_EhCache"/>
  <!--
    默认缓存设置
    maxElementsInMemory:缓存最大数目
    maxEntriesLocalHeap:指定允许在内存中存放元素的最大数量。
    timeToIdleSeconds:一个元素在不被请求的情况下允许在缓存中存活的最大时间。0表示永久有效。
    timeToLiveSeconds:无论一个元素闲置与否,其允许在Cache中存活的最大时间。0表示永久有效。
    diskExpiryThreadIntervalSeconds:检查元素是否过期的线程多久运行一次
   -->
  <defaultCache
      maxElementsInMemory="10000"
      timeToIdleSeconds="120"
      timeToLiveSeconds="120"
      diskExpiryThreadIntervalSeconds="120"/>


  <!-- 授权缓存设置 -->
  <cache name="authorizationCache"
      maxEntriesLocalHeap="2000"
      timeToIdleSeconds="0"
      timeToLiveSeconds="0">
  </cache>
</ehcache>

3.在配置文件创建CacheManager

// 创建CacheManager
@Bean
public EhCacheManager ehCacheManager() {
  EhCacheManager ehCacheManager = new EhCacheManager();
  ehCacheManager.setCacheManagerConfigFile("classpath:shiro-ehcache.xml");
  return ehCacheManager;
}

4.在SecurityManager中配置CacheManager

@Bean
public DefaultWebSecurityManager securityManager(MyRealm myRealm,
                         MyRealm2 myRealm2,
                         SessionManager sessionManager,
                         CookieRememberMeManager rememberMeManager,
                         EhCacheManager ehCacheManager){
  DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
  // 自定义Realm放入SecurityManager中
  //     securityManager.setRealm(myRealm);
  // 设置Realm管理者(需要设置在Realm之前)
  securityManager.setAuthenticator(modularRealmAuthenticator());
  List<Realm> realms = new ArrayList();
  realms.add(myRealm);
  //     realms.add(myRealm2);
  securityManager.setRealms(realms);
  securityManager.setSessionManager(sessionManager);
  securityManager.setRememberMeManager(rememberMeManager);
  securityManager.setCacheManager(ehCacheManager);
  return securityManager;
}


总结

提示:这里对文章进行总结:

总的来说,Shiro 为我们提供了一个强大而灵活的授权解决方案。通过深入理解 Shiro 的授权概念和流程,我们可以更好地利用其特性来保护我们的应用程序。

文章来源:https://blog.csdn.net/liubopro666/article/details/135442374
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。