当前位置: 首页 > article >正文

springboot+shiro 权限管理

一、为什么要了解权限框架
        权限管理框架属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则用户可以访问而且只能访问自己被授权的资源。

        目前常见的权限框架有Shiro和Spring Security,本篇文章记录springboot整合shiro,实现简单的权限控制。

二、shiro介绍
        Shiro全称是Apache Shiro,是一款灵活、强大的安全框架。方便简洁的处理身份认证、授权、加密等。

        shiro的三个组件:

        Subject 【主体】:指代当前用户【人、爬虫等】

        SecurityManager【安全管理器】:管理所有的Subject、具体安全操作的真正执行者。

        Reamls:本质是一个安全的DTO,用于进行权限、认证信息;

        通过Subject来进行认证和授权,而Subject又委托给SecurityManager; 需要给Shrio的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断。

三、环境准备
模拟场景【基于权限】:

                1、可以跳转到add页面,说明拥有add权限。

                2、可以跳转到update页面,说明拥有update权限。

                3、拥有add权限只展示add的链接、拥有update权限只展示update的链接;

模拟场景【基于角色】:

               1、拥有admin身份进入add、update,select页面,

               2、拥有user身份只可以进入select页面。

实现效果:

环境:

jdk 17

Maven 3.8.6

Mysql 8.x

IDEA2021

springboot 2.7.0

3.1 数据库表以及相关数据

        一共五张表:用户表、角色表、权限表、用户角色表、角色权限表。初始化SQL脚本在最后的传送门。

 

当前数据库数据:【用户->身份->权限】

        张三,角色是admin 拥有权限有add

        李四,角色是user,拥有权限有update

3.2、shiro环境准备

1、导入必要依赖、导入springboot-shiro的整合相关依赖依赖

        <!--  boot-shiro整合依赖-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.10.0</version>
        </dependency>
 
        <!--shiro缓存-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.7.1</version>
        </dependency>
 
        <!--   mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
    <!--        mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
 
    <!-- hutool工具包-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.11</version>
        </dependency>
    <!--thymeleaf模板引擎-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
            <version>2.6.4</version>
        </dependency>
    <!--    thymeleaf-shiro整合依赖    -->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

2、配置文件的一一些必要参数

2、配置文件的一一些必要参数

server:
  port: 8080
spring:
  thymeleaf:
    mode: HTML
    cache: false
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3308/boot_mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&AllowPublicKeyRetrieval=True
    username: root
    password: root
 
debug: true
 
mybatis:
  mapperLocations: mapper/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  #日志输出
    map-underscore-to-camel-case: true  #开启驼峰映射

3、 页面资源

         准备页面资源:index.html、login.html。启动项目来到首页、不用登录登录情况下可以访问任意页。

项目目录结构:

四、自定义登录

第一步:需要先完成一些简单的配置:

1、新建UserReam类,继承AuthorizingRealm ,并重写他的认证和授权方法、实现自定义授权认证。

/**
 * 自定义UserRealm,用户认证授权登录
 */
public class UserRealm extends AuthorizingRealm {
 
    @Autowired
    private UserService userService;
 
 
 
 
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token ) throws AuthenticationException {
        System.err.println("执行了+==========>认证AuthenticationInfo");
        // 用户名密码数据库里取
        UsernamePasswordToken userToken =(UsernamePasswordToken) token;
        IUser queryUser = new IUser();
        queryUser.setUserName(userToken.getUsername());
 
        List<IUser> userList = userService.selectUser(queryUser);
 
 
        if(CollectionUtils.isEmpty(userList)){
            return null;
        }else {
            IUser user = userList.get(0);
            System.err.println("user:"+user);
 
            // 密码认证 简单的equals比较
            return new SimpleAuthenticationInfo(user.getUserName(), user.getPassWord(),"");
        }
 
    }
 
 
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.err.println("执行了+==========>授权doGetAuthenticationInfo");
        String username = (String)principals.getPrimaryPrincipal();
        System.err.println("username"+username);
        IUser queryUser = new IUser();
        queryUser.setUserName(username);
//        根据用户名获取身份、再由身份获取权限
        List<IRole> roles = userService.selectRolesByUser(queryUser);
        if(CollectionUtils.isEmpty(roles)){
            return null;
        }else {
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            roles.forEach(role -> {
                simpleAuthorizationInfo.addRole(role.getName());
                //权限信息
                List<IPermission> perms = userService.selectPermsByRole(role);
                if (!CollectionUtils.isEmpty(perms)) {
                    perms.forEach(permission -> {
                        simpleAuthorizationInfo.addStringPermission(permission.getPermission());
                    });
                }
            });
            return simpleAuthorizationInfo;
        }
    }
}

2、新建ShiroConfiguration配置类,

        配置类里创建了工厂对象、安全对象、自定Ream等bean对象、  shiro内置了五个过滤器,可对资源、请求接口等进行拦截

/**
 * shiro配置类
 */
@Configuration
public class ShiroConfiguration {
 
    /**
     * 工厂对象3
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //给filter设置安全管理
        bean.setSecurityManager(defaultWebSecurityManager);
 
 
        /**
         *
         * 基于路径拦截资源
         *   anon 无需认证
         *   authc 必须认证
         *   user 记住我功能
         *   perms 拥有对某个资源
         *   roles 用某个角色权限
         */
 
 
        Map<String,String> map = new HashMap<>();
        map.put("/index","authc");
        map.put("/toLogin","anon");
        map.put("/","authc");
        map.put("/toAdd","perms[add]");
        map.put("/toUpdate","perms[update]");
        map.put("/toSelect", "roles[admin]");
        //更改默认的登录请求路径
        bean.setLoginUrl("/toLogin");
        //未授权请求路径
        bean.setUnauthorizedUrl("/unauthorized");
        bean.setFilterChainDefinitionMap(map);
        return bean;
    }
    /**
     * 安全对象2
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 管理realm
        securityManager.setRealm(userRealm);
        return securityManager;
 
    }
 
 
 
    /**
     * 创建realm对象,先创建,再接管1
     */
    @Bean(name = "userRealm")
    public UserRealm userRealm(){
        return new UserRealm();
    }
 
 
    /**
     * 页面的Shiro标签生效
     * @return
     */
    @Bean
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }
 
}

 

   shiro登录过程

                传统登录功能:

shiro拿到用户信息后。不是直接调用业务逻辑层的方法。现由SecurityUtils拿到登录对象、由对象取执行login方法,由于UserRealm 继承了AuthorizingRealm、所以登录操作被拦截、完成认证操作。login方法会抛出需要认证失败的异常。根据异常信息可以给前端对应的提示。

第二步: 自定义登录方法

/**
 * 自定义UserRealm,用户认证授权登录
 */
public class UserRealm extends AuthorizingRealm {
 
    @Autowired
    private UserService userService;
 
 
 
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token ) throws AuthenticationException {
        System.err.println("执行了+==========>认证AuthenticationInfo");
        SimpleAuthenticationInfo info =null;
        String username = token.getPrincipal().toString();
        IUser queryUser = new IUser();
        queryUser.setUserName(username);
        List<IUser> dbUserList = userService.selectUser(queryUser);
        if(CollectionUtils.isNotEmpty(dbUserList)){
            IUser dbUser = dbUserList.get(0);
                // 将注册时保存的随机盐构造ByteSource对象
             info = new SimpleAuthenticationInfo(dbUser.getUserName(),dbUser.getPassWord(),this.getName());
//             info = new SimpleAuthenticationInfo(dbUser.getUserName(),dbUser.getPassWord(),new SimpleByteSource(dbUser.getSalt()),this.getName());
        }
        return info;
    }
 
 
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.err.println("执行了+==========>授权doGetAuthenticationInfo");
        String username = (String)principals.getPrimaryPrincipal();
        System.err.println("username"+username);
        IUser queryUser = new IUser();
        queryUser.setUserName(username);
//        根据用户名获取身份、再由身份获取权限
        List<IRole> roles = userService.selectRolesByUser(queryUser);
        if(CollectionUtils.isEmpty(roles)){
            return null;
        }else {
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            roles.forEach(role -> {
                simpleAuthorizationInfo.addRole(role.getName());
                //权限信息
                List<IPermission> perms = userService.selectPermsByRole(role);
                if (!CollectionUtils.isEmpty(perms)) {
                    perms.forEach(permission -> {
                        simpleAuthorizationInfo.addStringPermission(permission.getPermission());
                    });
                }
            });
            return simpleAuthorizationInfo;
        }
    }
}

完成了简单资源授权,为了前端展示,把对应的数据存到model

            IUser user = new IUser();
            user.setUserName(username);
            List<IRole> roles = userService.selectRolesByUser(user);
            List<IPermission> perms = userService.selectPermsByRole(roles.get(0));
            model.addAttribute("role",roles.get(0).getName());
            model.addAttribute("perms",perms);
 效果如下:


http://www.kler.cn/a/380627.html

相关文章:

  • 中国雕塑、
  • C#-类:声明类、声明类对象
  • Armv8的安全启动
  • 云轴科技ZStack在CID大会上分享VF网卡热迁移技术
  • EHOME视频平台EasyCVR萤石设备视频接入平台视频诊断技术可以识别哪些视频质量问题?
  • net core Autofac 替换默认的服务容器 DI,微软自动的容器 不支持命名选项的
  • 【前端基础】盒子模型
  • 华为HarmonyOS打造开放、合规的广告生态 - 开屏广告
  • 【双指针】【数之和】 LeetCode 633.平方数之和
  • CSS中的背景色和前景色
  • 软件测试面试题个人总结
  • 齐次线性微分方程的解的性质与结构
  • 《YOLO 目标检测》—— YOLO v4 详细介绍
  • el-talble selection行 初始默认勾选
  • TypeScript中的类型注解、Interface接口、泛型
  • 2025郑州国际台球及配套设施展会,台球盛宴,产业新篇
  • 制造业大模型应用案例赏析
  • 【论文速读】| PathSeeker:使用基于强化学习的越狱攻击方法探索大语言模型的安全漏洞
  • 高效作业跟踪:SpringBoot作业管理系统
  • leetcode203. Remove Linked List Elements
  • 【AI】【提高认知】深度学习与反向传播:理解AI的基础
  • mutable用法
  • FastAPI 目录结构推荐
  • 了解神经网络中的激活函数
  • 【VSCode / Source Insight 4】设置关键字高亮的插件 | Highlight Word
  • AutoCAD2019