Shiro动态权限(整合Spring Boot)

在写这篇博客之前,我在网上找了很多资料,关于shiro的动态权限管理,网上的资料要么写的不是很详细,要么就是写的比较复杂,对于初学者来说极其不友好,于是我就琢磨出了一个相对比较简单的实现方法,希望能对大家有所帮助,如果有什么不足之处,还请在评论区留言,毕竟我技术一般,考虑不周实属正常。


目录

1、说明
2、数据库
3、代码
4、测试
5、小结


说明

动态权限是程序运行期间,对用户的权限进行更改而不需要修改源码。在shiro中,实现动态权限只需要实现一个自定义的过滤器即可,这里使用的方法是继承PathMatchingFilter

需要注意的是,任何的动态权限都会损耗服务器的资源,因为所谓的动态权限就是根据数据库的变化,实时的将情况反应到客户端,即每次请求都需要对数据库发送请求,因为需要的是实时的数据,所以没办法使用Redis解决这个问题,因为即使存储到Redis中,Redis也需要去向数据库发送请求来更新数据,甚至因为请求经过了Redis,还会增加Redis的损耗。


数据库

用户表
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `salt` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'aa', '894b3913a4a13b25dc6186d11835c209', 'abc');
INSERT INTO `user` VALUES (2, 'bb', '894b3913a4a13b25dc6186d11835c209', 'abc');
INSERT INTO `user` VALUES (3, 'cc', '894b3913a4a13b25dc6186d11835c209', 'abc');
角色表
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'user');
INSERT INTO `role` VALUES (2, 'admin');
INSERT INTO `role` VALUES (3, 'guest');
用户角色表
-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `user_id` int NULL DEFAULT NULL,
  `role_id` int NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1, 1);
INSERT INTO `user_role` VALUES (2, 2, 2);
INSERT INTO `user_role` VALUES (3, 3, 3);
权限表
-- ----------------------------
-- Table structure for access
-- ----------------------------
DROP TABLE IF EXISTS `access`;
CREATE TABLE `access`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of access
-- ----------------------------
INSERT INTO `access` VALUES (1, 'v1', '/v1');
INSERT INTO `access` VALUES (2, 'v2', '/v2');
INSERT INTO `access` VALUES (3, 'v3', '/v3');
角色权限表
-- ----------------------------
-- Table structure for role_access
-- ----------------------------
DROP TABLE IF EXISTS `role_access`;
CREATE TABLE `role_access`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `role_id` int NULL DEFAULT NULL,
  `access_id` int NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of role_access
-- ----------------------------
INSERT INTO `role_access` VALUES (1, 1, 1);
INSERT INTO `role_access` VALUES (2, 2, 2);
INSERT INTO `role_access` VALUES (3, 3, 3);

代码

导入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<!--引入shrio-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-starter</artifactId>
    <version>1.5.3</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.1</version>
</dependency>
<!--导入这个依赖的目的是使用里面一个随机生成字符串的工具类,这个随机字符串可以当作我们的盐值-->
<dependency>
    <groupId>commons-lang</groupId>
    <artifactId>commons-lang</artifactId>
    <version>2.6</version>
</dependency>
yml配置
spring:
  datasource:
    password: 123456
    username: root
    url: jdbc:mysql:///rbac?serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
  thymeleaf:
    cache: false

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
pojo层

实体类依据表进行创建,使用Lombok的@Data注解,以下是一个具体的例子,剩余四个实体类大家可以照此例进行创建。

package com.th.rbac.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.springframework.security.core.userdetails.UserDetails;

@Data
public class User{

  private long id;
  private String name;
  private String password;

}
dao层

由于使用的是Mybatis-plus,所以这里只需要继承BaseMapper就可以了,下面是一个具体的例子:

package com.th.rbac.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.th.rbac.pojo.User;

public interface UserMapper extends BaseMapper<User> {
}
service层

由于只是进行简单的测试,所以并没有创建接口,而是直接写的实现类,需要继承mybatisplus中的ServiceImpl,示例如下:

package com.th.rbac.service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.th.rbac.mapper.UserMapper;
import com.th.rbac.pojo.User;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> {
}
controller层
package com.th.shiro.controller;

import com.th.shiro.pojo.User;
import com.th.shiro.pojo.UserRole;
import com.th.shiro.service.UserRoleService;
import com.th.shiro.service.UserService;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
public class SecurityController {
    @Resource
    private UserService userService;

    @Resource
    private UserRoleService userRoleService;
    
    //跳转到登录页面
    @RequestMapping("/toLogin")
    public String toLogin() {
        return "login";
    }
    //跳转到注册页面
    @RequestMapping("/toRegist")
    public String toRegist() {
        return "regist";
    }
    //登录
    @RequestMapping("/login")
    public String login(String name, String password, Model model) {
        try {
            //使用账号密码生成token
            UsernamePasswordToken token = new UsernamePasswordToken(name,password);
            SecurityUtils.getSubject().login(token);
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            model.addAttribute("error","用户名错误~");
            return "error";
        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            model.addAttribute("error","密码错误~");
            return "error";
        } catch (Exception e) {
            e.printStackTrace();
            model.addAttribute("error","系统异常");
            return "error";
        }
        return "index";
    }
    //注册
    @RequestMapping("/regist")
    public String regist(User user) {
        //生成随机的字符串,即我们要用到的盐值
        String salt = RandomStringUtils.randomAlphanumeric(6);
        //使用MD5加密
        Md5Hash password = new Md5Hash(user.getPassword(), salt, 1024);
        //将加密后的密码和盐值放入到实体类中
        user.setPassword(String.valueOf(password));
        user.setSalt(salt);
        //新增用户
        userService.save(user);

        /**
         * 接下来需要给用户赋予一个角色才行,这就需要拿到该用户的id
         * 我这里是直接根据用户名去查询的
         * 如果是mybatis,这里应该会有一个反射机制,在新增的时候,实体类的id值就已经有了
         * 但是mybatis-plus在这里似乎并没有使用到这个反射机制
         * 总之user对象的id是0,懒得去研究源码了,直接简单粗暴的查询
         */
        Map<String, Object> map = new HashMap<>();
        map.put("name", user.getName());
        List<User> users = userService.listByMap(map);
        User user1 = users.get(0);

        //设置好值之后就可以新增了
        UserRole userRole = new UserRole();
        userRole.setUserId(user1.getId());
        userRole.setRoleId(1);
        userRoleService.save(userRole);
        return "login";
    }
    //退出登录
    @RequestMapping("/logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "login";
    }
    //没有权限
    @RequestMapping("/unAuth")
    @ResponseBody
    public String unAuth(){
        return "没有权限哦~";
    }
    //返回v1
    @RequestMapping("/v1")
    @ResponseBody
    public String v1() {
        return "v1";
    }
    //返回v2
    @RequestMapping("/v2")
    @ResponseBody
    public String v2() {
        return "v2";
    }
    //返回v3
    @RequestMapping("/v3")
    @ResponseBody
    public String v3() {
        return "v3";
    }
}
首页

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <a href="/v1">v1</a><br/>
    <a href="/v2">v2</a><br/>
    <a href="/v3">v3</a><br/>
    <a href="/toRegist">注册</a><br/>
    <a href="/logout">退出</a><br/>
</body>
</html>
登录页

与spring security不同的是,shiro并没有自带一个登录页面,这需要我们自己去写

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/login" method="post">
        账号:<input type="text" name="name"/><br/>
        密码:<input type="text" name="password"/><br/>
        <input type="submit" value="登录">
    </form>
</body>
</html>
注册页
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/regist" method="post">
    账号:<input type="text" name="name"/><br/>
    密码:<input type="text" name="password"/><br/>
    <input type="submit" value="注册">
</form>
</body>
</html>
错误页
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1 th:text="${error}"></h1>
</body>
</html>
启动类
package com.th.shiro;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan(basePackages = "com.th.shiro.mapper")
public class ShiroApplication {
    public static void main(String[] args) {
        SpringApplication.run(ShiroApplication.class, args);
    }
}

至此,这个案例的基本框架就搭建好了,但是需要注意的是,这个时候是运行不了的,运行了也会报错,因为shiro是第三方框架,而不是spring旗下的,所以整合起来难免会有些问题。

接下来我们将shiro整合到spring boot中,并且实现shiro的动态权限管理,只需要编写三个类即可:

  1. UserRealm:这个类用来连接数据库,需要继承AuthorizingRealm
  2. ShiroConfig:这个类是shiro的配置类,用它来配置一些东西
  3. ShiroFilter:这个类是最关键的,它需要实现shiro的PathMatchingFilter类中的onPreHandle方法,进行动态权限管理
UserRealm
package com.th.shiro.mapper;

import com.th.shiro.pojo.*;
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.ByteSource;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class UserRealm extends AuthorizingRealm {

    @Resource
    private UserMapper userMapper;

    /**
     * 授权
     * 这里不需要将用户的权限和角色放入到SimpleAuthorizationInfo中
     * 就算写了,也没用,因为动态代理是需要从数据库去查询的
     * 而这里仅仅只会在用户登录的时候执行一次,即在用户登录的时候就把权限和角色定死了
     * 即使在数据库修改了用户的权限和角色,只要用户没有退出登录,那么用户就还保有角色和权限
     * 直到用户重新登陆的时候才会从数据库拿到被修改后的角色和权限
     */

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //在token中获取用户名
        String name = (String)authenticationToken.getPrincipal();
        //从数据库查询用户信息
        Map<String, Object> userMap = new HashMap<>();
        userMap.put("name", name);
        List<User> users = userMapper.selectByMap(userMap);
        
        if (users.size() == 0) {
            throw new UnknownAccountException("没有此账号");
        }else{
            User user = users.get(0);
            return new SimpleAuthenticationInfo(name,       //用户名
                    user.getPassword(),                     //加密后的密码
                    ByteSource.Util.bytes(user.getSalt()),  //随机盐
                    getName());                             //当前realm的名称
        }
    }
}
ShiroFilter
package com.th.shiro.filter;

import com.th.shiro.mapper.*;
import com.th.shiro.pojo.*;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.PathMatchingFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ShiroFilter extends PathMatchingFilter {

    @Resource
    private UserMapper userMapper;
    @Resource
    private RoleMapper roleMapper;
    @Resource
    private UserRoleMapper userRoleMapper;
    @Resource
    private AccessMapper accessMapper;
    @Resource
    private RoleAccessMapper roleAccessMapper;

    /**
     * 这个方法会在用户发送请求时执行
     * 如果有兴趣,可以在AccessControlFilter类中的onPreHandle方法打一个断点调试
     * @param request 请求
     * @param response 响应
     * @param mappedValue 映射值,映射的是访问该路径所需要的权限
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        Subject subject = SecurityUtils.getSubject();

        // 如果没有登录,就跳转到登录页面
        if (!subject.isAuthenticated()) {
            WebUtils.issueRedirect(request, response, "/toLogin");
            return false;
        }

        /**
         * 接下来就是从数据库查询用户的权限,与访问该路径需要的权限进行对比
         * 如果用户没有该权限,则无法访问,如果有,则可以访问
         * 因为使用的是mybatis-pulse,所以这里的查询代码会显得繁杂
         */
       String name = subject.getPrincipal().toString();
        //从数据库查询用户信息
        Map<String, Object> userMap = new HashMap<>();
        userMap.put("name", name);
        List<User> users = userMapper.selectByMap(userMap);
        User user = users.get(0);

        //查询角色关系表
        Map<String, Object> map = new HashMap<>();
        map.put("user_id", user.getId());
        List<UserRole> userRoles = userRoleMapper.selectByMap(map);

        //查询角色信息
        List<Role> roleList = new ArrayList<>();
        for (UserRole userRole : userRoles) {
            Role role = roleMapper.selectById(userRole.getRoleId());
            roleList.add(role);
        }

        //查询角色权限表
        List<List<RoleAccess>> roleAccessList = new ArrayList<>();
        for (Role role : roleList) {
            Map<String, Object> roleAccessMap = new HashMap<>();
            roleAccessMap.put("role_id",role.getId());
            roleAccessList.add(roleAccessMapper.selectByMap(roleAccessMap));
        }

        //查询权限信息
        List<Access> accessList = new ArrayList<>();
        for (List<RoleAccess> roleAccesses : roleAccessList) {
            for (RoleAccess roleAccess : roleAccesses) {
                Access access = accessMapper.selectById(roleAccess.getAccessId());
                accessList.add(access);
            }
        }

        //查询到用户的权限后,就可以开始进行判断了
        String[] perms = (String[]) mappedValue;
        for (String perm : perms) {
            for (Access access : accessList) {
                if (perm.equals(access.getName())) {
                    return true;
                }
            }
        }
        //如果用户没有权限,则跳转到无权限页面
        WebUtils.issueRedirect(request, response, "/unAuth");
        return false;
    }
}
ShiroConfig
package com.th.shiro.config;

import com.th.shiro.filter.ShiroFilter;
import com.th.shiro.mapper.AccessMapper;
import com.th.shiro.mapper.UserRealm;
import com.th.shiro.pojo.Access;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Configuration
public class ShiroConfig {

    @Resource
    private AccessMapper accessMapper;

    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //给ShiroFilter配置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //从数据库中读取权限
        List<Access> accesses = accessMapper.selectList(null);
        //自定义拦截器
        Map<String, Filter> filtersMap = new LinkedHashMap();
        filtersMap.put("requestURL", shiroFilter());
        shiroFilterFactoryBean.setFilters(filtersMap);
        //配置系统受限资源和公共资源,可以直接在数据库中读取
        Map<String, String> map = new HashMap<String, String>();
        for (Access access : accesses) {
            /**
             * 这里需要使用到自己的自定义拦截器
             * 由于它是一个字符串,所以这里使用StringBuffer进行连接
             * 字符串的规则:过滤器[权限]
             * 一般情况下,这个字符串是这样的:roles[admin],或者perms[v1]
             * 这里就是用到了shiro自带的过滤器,roles和perms,前者是角色过滤器,后者是权限过滤器
             * 而使用我们自己的过滤器,是这样的requestURL[v1]
             * 如果你的过滤器名称不叫requestURL,请写上自己的名称
             * 另外,这里值得一提的是,在ShiroFilter类的onPreHandle方法中
             * mappedValue属性正是中括号中的字符串
             * 如果你仅仅只是写上requestURL,而没有在后方加上[权限],则mappedValue属性是null
             */
            StringBuffer perms = new StringBuffer();
            perms.append("requestURL[");
            perms.append(access.getName());
            perms.append("]");
            map.put(access.getUrl(), perms.toString());
        }
        // 设置认证界面路径
        shiroFilterFactoryBean.setLoginUrl("/toLogin");
        shiroFilterFactoryBean.setUnauthorizedUrl("/unAuth");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }
    //创建安全管理器
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;
    }
    //创建自定义Realm
    @Bean
    public Realm getRealm() {
        //给自定义的realm设置密码匹配器
        UserRealm userRealm = new UserRealm();
        userRealm.setCredentialsMatcher(md5HashedCredentialsMatcher());
        return userRealm;
    }

    //密码匹配器
    @Bean
    public HashedCredentialsMatcher md5HashedCredentialsMatcher(){
        //获取hash凭证匹配器
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //设置hash凭证匹配器使用的算法(MD5)
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //如果使用散列(加密次数),则需要设置散列的次数:1024次,与注册时的加密次数是一致的
        hashedCredentialsMatcher.setHashIterations(1024);
        return hashedCredentialsMatcher;
    }

    //自定义的拦截器
    @Bean
    public ShiroFilter shiroFilter() {
        return new ShiroFilter();
    }

    /**
     * 如果不写这个配置,那么在启动程序的时候,会出现jdk动态代理与CGLIB代理混乱的问题
     * 而这个配置的意思,就是指定代理为CGLIB代理
     * 至于这两种代理,可以去百度,这里就不进行赘述了
     * 这是shiro和spring boot整合所产生的问题
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }
}

测试

启动程序,访问localhost:8080端口

在这里插入图片描述

点击v1,会跳到登录页面

在这里插入图片描述

在数据库中,我给了三个默认的用户,密码是123,当然也可以自己注册一个用户,回到首页,点击注册,进入到注册页面

在这里插入图片描述

在这里插入图片描述

注册成功后会跳到登录页,登录即可,登录之后是只能访问v1的,因为新用户默认只有进入v1的权限,如下:

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


赋予新角色

接下来进行动态权限的管理,比如说要让新用户拥有访问v2的权限,这里可以给新用户赋予新的角色,修改数据库,在用户角色关系表中增加一条数据,如下:

INSERT INTO `user_role` VALUES (5, 4, 2);

在这里插入图片描述

接下来不需要重新登录,也不需要重启项目,直接访问v2,是可以访问成功的

在这里插入图片描述


赋予角色权限

另外也可以动态的修改角色所拥有的权限,比如说我们希望user角色能够访问v3,那么只需要在角色权限关系表中新增一条数据即可

INSERT INTO `role_access` VALUES (4, 1, 3);

在这里插入图片描述

直接访问v3,如下:

在这里插入图片描述

如果要删除用户的角色,或者删除角色的权限,只需要删除数据库对应的记录即可


小结

至此关于shiro的动态权限管理就结束了,需要注意的是,如果我们修改了权限规则,例如将/v3路径的权限改为v1,则需要重启程序才行,因为权限规则只会在程序运行的时候,从数据库查询一次,之后就不会再次从数据库查询了,当然,这种缺陷是可以解决的,例如写个定时器,定时从数据库中查询,或者重写shiro的方法等等,有兴趣的可以自己去试试。

另外,shiro是可以和Spring Security进行对比学习的,有兴趣的话,可以去看看我的另一篇博客:Spring Security

另外,上边的代码都发布到gitee上面去了有兴趣可以拉取下来看看:https://gitee.com/thlyj/shiro

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值