Shiro认证框架

news2024/11/25 0:29:27

目录

概述

认证授权及鉴权

Shiro框架的核心组件

基本流程

spring boot+shiro+mybatisPlus+...实现用户登录

step1:准备工作

(1)坐标

(2)连接数据库

(3)JavaBean

(4)dao数据访问层

 (5)密码工具类 DigestsUtil

 (6)配置类

step2:认证功能

step3:授权鉴权


概述

shiro是apache下一个开源框架,它将软件系统的安全认证功能抽离出来,组成一个通用的安全认证框架,其功能主要有身份认证,授权鉴权,会话管理等功能。

认证授权及鉴权

对于认证授权及鉴权,可以通过这样一个例子来理解,比如说我买了一张去国外的机票,现在要登机,那我在取机票的时候,出示的身份证就是认证的过程,工作人员在核实你确实是本人之后,会把机票给你,这个就是授权,当你拿着张机票去登记的时候,工作人员还要查你的机票,看你是不是我这个航班的机票,这个就是鉴权。理解之后,再看下面的概念就很具体了。

身份认证是指判断一个用户是否为合法用户的过程,最常用的身份认证方式是系统通过判断用户输入的用户名密码和数据库中存储的用户名密码是否一致。由此确认该用户是否为合法用户。

授权也叫访问控制,它是指控制谁能够访问哪些资源。用户认证成功后,系统会为其分配对应的权限,访问资源时,会校验其是否有权限访问这个校验的过程就是鉴权

Shiro框架的核心组件

  • Subject(主体)

即外部应用与subject进行交互,subject将用户作为当前操作的主体,这个主体:可以是一 个通过浏览器请求的用户,也可能是一个运行的程序 。比如说我今天要做一个登录功能的认证,那我的Subject就是当前登录的用户。

  • SecurityManager(权限管理器)

权限管理器是Shiro的核心,负责管理所有Subject并通过Authenticator完成认证,以及Authorizer完成授权。

  • Authenticator(认证器)

登录时进行身份认证

  • Authorizer(授权器)

当用户通过认证,访问资源时,对用户们进行授权操作

  • Realm(数据源)

Realm用于完成数据库的读取,并在其中完成授权校验相关操作

基本流程

1、首先调用Subject.isPermitted/hasRole接口,其会委托给SecurityManager。

2、SecurityManager接着会委托给内部组件Authorizer;

3、Authorizer再将其请求委托给我们的Realm去做;Realm才是真正干活的;

4、Realm将用户请求的参数封装成权限对象。再从我们重写的doGetAuthorizationInfo方法中获取从 数据库中查询到的权限集合。

5、Realm将用户传入的权限对象,与从数据库中查出来的权限对象,进行一一对比。如果用户传入的 权限对象在从数据库中查出来的权限对象中,则返回true,否则返回false。 进行授权操作的前提:用户必须通过认证。

OK,理论结束,实践开始。。。。

spring boot+shiro+mybatisPlus+...实现用户登录

step1:准备工作

在这一部分需要先搭建一个项目的基本框架,与数据库的交互以及数据库表。在数据库方面一共需要五张表,表的结构在级联查询中已经提到,这里不再赘述,链接附在这里CSDN

 需要特别关注一点的是,对于表中密码这部分的数据,我使用sha-1算法进行了加密,一部分原因是为了安全系数更高些,另一部分原因是shiro框架支持多种算法,例如sha系列算法,以及md5Hash,md2Hash,在这里刚好也使用一下。

(1)坐标

首先需要搭建一个springBoot 项目,然后添加需要的坐标,比如数据持久层的mybatisPlus,管理javaBean的lombok,以及shiro框架相关坐标

 <properties>
        <java.version>1.8</java.version>
        <shiro.version>1.3.2</shiro.version>
    </properties>

 <dependencies>
        <!--mybatis-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.28</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>${shiro.version}</version>
        </dependency>
    </dependencies>
(2)连接数据库

在resource资源文件下的application.properties文件中配置4想数据库连接的基本信息

(3)JavaBean

作用:ORM架构中的O,与数据库表中的字段进行一一对应。

在数据库中我们设计了5张表,但在实体类这里,不涉及关系表,关系表只在查数据的时候使用,比如我在判断该用户有没有某种权限的时候,根据登录用户输入的用户名查询用户的基本信息,拿着基本信息中的id查询用户权限,这个时候就需要使用到关系表,

 

(4)dao数据访问层

数据的交互基于两个查询操作,一个是查询基本信息也就是user表总的信息,另一个是查询详细信息,包括角色信息和权限信息

 

 (5)密码工具类 DigestsUtil

作用:获取明文密码的密文和盐值,这是由于数据库中没有数据,需要先造几条数据供使用

对于输入的明文,在获取密文密码前需要先获取盐值,所以调用generateSalt方法生成随机的盐值密文。其次调用show方法进行加密。然后存到map集合中进行返回,到这里我们需要做的就是把数据存到数据库中。

public class DigestsUtil {
//确定要使用的加密算法
    public static final String SHA1 = "SHA-1";
//加密次数
    public static final Integer COUNTS =369;

    /**
     * @Description sha1方法,根据明文和盐值进行加密
     * @param input 需要散列字符串
     * @param salt 盐字符串
     * @return
     */
    public static String show(String input, String salt) {
        return new SimpleHash(SHA1, input, salt,COUNTS).toString();
    }

    /**
     * @Description 随机获得salt字符串
     * @return
     */
    public static String generateSalt(){
        SecureRandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
        return randomNumberGenerator.nextBytes().toHex();
    }


    /**
     * @Description 生成密码字符密文和salt密文
     * @param
     * @return
     */
    public static Map<String,String> entryptPassword(String passwordPlain) {
        Map<String,String> map = new HashMap<>();
        String salt = generateSalt();
        String password =show(passwordPlain,salt);
        map.put("salt", salt);
        map.put("password", password);
        return map;
    }

    public static void main(String[] args) {
//用户测试数据
        String name = "xixi";
        String pwd = "12345";
        Map map = entryptPassword(pwd);
        System.out.println(map.toString());

    }
}
 (6)配置类

作用:定义shiro框架的基本配置,包括拦截规则,登录页面等等,具体功能在方法上有备注

@Configuration
public class ShiroConfiguration {

    /**
     * 1.创建shiro自带cookie对象
     */
    @Bean
    public SimpleCookie sessionIdCookie(){
        SimpleCookie simpleCookie = new SimpleCookie();
        simpleCookie.setName("ShiroSession");
        return simpleCookie;
    }

    //2.创建自定义的realm,用于登录认证和授权
    @Bean
    public MyRealm getRealm() {
        return new MyRealm();
    }

    /**
     * 3.创建会话管理器
     */
    @Bean
    public DefaultWebSessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionValidationSchedulerEnabled(false);
        sessionManager.setSessionIdCookieEnabled(true);
        sessionManager.setSessionIdCookie(sessionIdCookie());
        sessionManager.setGlobalSessionTimeout(3600000);
        return sessionManager;
    }

    //4.创建安全管理器
    @Bean
    public SecurityManager defaultWebSecurityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(getRealm());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }



    /**
     * 5.保证实现了Shiro内部lifecycle函数的bean执行
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 6.开启对shiro注解的支持
     *   AOP式方法级权限检查
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    /**
     * 7.配合DefaultAdvisorAutoProxyCreator实现使用注解进行权限校验,例如:@RequireRoles、@RequirePermissions等
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager());
        return authorizationAttributeSourceAdvisor;
    }




    //8.配置shiro的过滤器工厂再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制
    @Bean
    public ShiroFilterFactoryBean shiroFilter() {
        //1.创建过滤器工厂
        ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
        //2.设置安全管理器
        filterFactory.setSecurityManager(defaultWebSecurityManager());
        //3.通用配置(跳转登录页面,为授权跳转的页面)
        filterFactory.setLoginUrl("/autherror");//跳转url地址
        //4.设置过滤器集合
        //key = 拦截的url地址
        //value = 过滤器类型
        Map<String,String> filterMap = new LinkedHashMap<>();
        //key:请求规则   value:过滤器名称
        filterMap.put("/login","anon");//当前请求地址可以匿名访问
        filterMap.put("/user/**","authc");//当前请求地址必须认证之后可以访问
        //在过滤器工程内设置系统过滤器
        filterFactory.setFilterChainDefinitionMap(filterMap);

        return filterFactory;
    }

}

至此,准备阶段就结束了,可以进行shiro框架功能的测试。

step2:认证功能

认证流程在前文已经叙述过,这里不再赘述,下文主要描述怎么利用shiro实现认证。首先用户在登录界面输入用户名和密码,因为我们在数据库里存的是密文,所以需要先判断该用户名和密码,这个时候,根据我们在配置类中自定义的Realm类中的doGetAuthenticationInfo方法进行认证

 @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//将参数AuthenticationToken 转换为用户名密码令牌
        UsernamePasswordToken token=(UsernamePasswordToken)authenticationToken;
//获取令牌中的用户名
        String username = token.getUsername();
//按照该用户名查询数据库,若数据库中没有数据,说明该用户不存在,返回null,若存在,进行验证
        User user = userService.findUserByName(username);
        if(user!=null){
//SimpleAuthenticationInfo:该类是shiro提供的对象,可以根据传入的参数进行解密操作
/**参数含义
*参数1:安全对象,后续用于鉴权
*参数2:从数据库中查到的密文密码
*参数3:从数据库中查到的盐值的字节数组
*参数4:realm类名
*/
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), ByteSource.Util.bytes(user.getSalt()), "myRealm");
            return info;
        }
        return null;
    }

这个时候就有人有问题了,这个参数authenticationToken里面的数据是怎么来传过来的?🆗,解释一下,在我们访问登录页面输入数据时,在控制层中,会先进行判断,若不为空,将数据封装成UsernamePasswordToken对象,再获取Subject对象,调用login方法进行登录验证,否则处理空异常。

 这个时候,又有人会有问题了,之前设置了加密算法和加密次数,那shiro框架在解密的时候怎么知道你用的是哪个加密算法,加密了多少次啊?🆗再解释一下,在Realm类中其实还有一个方法initCredentialsMatcher(),它是用来指导密码的加密算法和迭代次数的,写完后告诉shiro你的这个实现类是谁,shiro就知道该去回调哪个方法了。

step3:授权鉴权

这个功能主要是用户在访问某个功能鉴权的的时候使用,也就说,每当我访问一个新的功能进行鉴权的时候,都需要重新执行授权方法,该方法同样定义在Realm类中

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取认证通过的用户对象
        User user =  (User) principalCollection.getPrimaryPrincipal();
//根据用户id查询该用户的详细信息
        User userDetail = userService.findUserDetailById(user.getId());
//定义存储角色的集合
        HashSet<String> roles = new HashSet<>();
//定义存储权限的集合
        HashSet<String> perms = new HashSet<>();
//对角色集合进行遍历,将每一个角色对应的的权限存入权限集合
        for (Role role : userDetail.getRoles()) {
            for (Permission perm : role.getPermissions()) {
                perms.add(perm.getCode());
            }
        }
//封装角色权限对象返回
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(perms);
        info.setRoles(roles);
        return info;
    }

 对于后续的鉴权,我使用注解解决

        在资源上使用@RequiresPermissions注解,那么当用户访问该资源时判断用户有没有相应的权限 ,若有,可以访问资源,若无,抛出异常。对于异常情况的处理,使用全局异常处理器@ControllerAdvice进行捕获

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/937514.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

11. 网络模型保存与读取

11.1 网络模型保存(方式一) import torchvision import torch vgg16 torchvision.models.vgg16(pretrainedFalse) torch.save(vgg16,"./model/vgg16_method1.pth") # 保存方式一&#xff1a;模型结构 模型参数 print(vgg16) 结果&#xff1a; VGG((feature…

飞腾FT-2000/4、D2000 log报错指导(2)

在爱好者群中遇见了很多的固件问题,这里总结记录了大家的交流内容和调试心得。主要是飞腾桌面CPU FT-2000/4 D2000相关的,包含uboot和UEFI。希望对大家调试有所帮助。 这个专题会持续更新,凑够一些就发。 12 UEFI现象:主板启动时串口信息停在 s3 flag form ec 处,如下图所…

局域网中电脑共享文件给手机

学习资源&#xff1a; 局域网共享&#xff1a;这样设置&#xff0c;你可以轻松拷贝任何电脑的文件。_哔哩哔哩_bilibili 可以实现什么效果&#xff1f; 连接同一个WIFI&#xff0c;电脑端为服务端&#xff0c;提供共享文件&#xff0c;手机是客户端&#xff0c;可以读取服务端…

Java-泛型

文章目录 Java泛型什么是泛型&#xff1f;在哪里使用泛型&#xff1f;设计出泛型的好处是什么&#xff1f;动手设计一个泛型泛型的限定符泛型擦除泛型的通配符 结论 Java泛型 什么是泛型&#xff1f; Java泛型是一种编程技术&#xff0c;它允许在编译期间指定使用的数据类型。…

Leetcode:238. 除自身以外数组的乘积【题解超详细】

纯C语言实现&#xff08;小白也能看明白&#xff09; 题目 给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数…

8. 损失函数与反向传播

8.1 损失函数 ① Loss损失函数一方面计算实际输出和目标之间的差距。 ② Loss损失函数另一方面为我们更新输出提供一定的依据。 8.2 L1loss损失函数 ① L1loss数学公式如下图所示&#xff0c;例子如下下图所示。 import torch from torch.nn import L1Loss inputs torch.tens…

冷启问题目前常见解决方案

1、冷启动的基本方式 随机冷启 个性化冷启 2、冷启动解决问题 冷启动保量 冷启动保量&#xff0c;保证每个item曝光的次数一样&#xff0c;实时统计已曝光的次数和要曝光的次数 冷启动结束过滤&#xff0c;如果需曝光的量越大&#xff0c;且越接近冷启结束时间&#xff0…

3.2 数据的表现形式及其运算

3.2.1 常量和变量 在计算机高级语言中&#xff0c;数据有两种表现形式&#xff1a;常量和变量。 1.常量 在程序运行过程中&#xff0c;其值不能被改变的量称为常量。如例3.1程序中的5,9,32和例3.2程序中的1000,0.0036,0.0225,0.0198是常量。数值常量就是数学中的常数。 常用的…

三、前端监控之Sentry的介绍

Sentry介绍 Sentry是一个开源的实时事件日志记录和聚合平台。它专门用于监视错误和提取执行适当的事后操作所需的所有信息, 而无需使用标准用户反馈循环的任何麻烦。支持 web 前后端、移动应用以及游戏&#xff0c;支持 Python、OC、Java、Go、Node、Django、RoR 等主流编程语…

【Docker】网络

文章目录 Docker 网络基础Docker网络管理Docker网络架构CNMLibnetwork驱动 常见的网络类型 Docker 网络管理命令docker network createdocker network inspectdocker network connectdocker network disconnectdocker network prunedocker network rmdocker network ls docker …

跳跃游戏 II【贪心算法】

跳跃游戏 II class Solution {public int jump(int[] nums) {int cur 0;//当前最大覆盖路径int next 0;//下一步的最大覆盖路径int res 0;//存放结果&#xff0c;到达终点时最少的跳跃步数for (int i 0; i < nums.length; i) {//遍历数组&#xff0c;以给出数组以一个…

老韦上新

知道韦东山老师的人很多&#xff0c;当然肯定可能也是有人不知道的&#xff0c;比如我今天吃饭的时候&#xff0c;我跟 JP 同学说&#xff0c;你认识韦东山老师吗&#xff1f;JP 同学说&#xff0c;「我不认识&#xff0c;他是不是搞 ODM 的&#xff1f;你知道的&#xff0c;我…

【python】输出高亮信息的内容

背景 日志是定位问题和数据分析的关键手段之一&#xff0c;尤其是在调试阶段&#xff0c;高效的、具有辨识度的日志可以非常快速准确的进行问题定位。shell中的echo命令自带文本格式化输出的功能&#xff0c;我们先来回顾下基本的语法&#xff0c;然后套用到python中即可。 s…

【kubernetes】使用kubepshere部署中间件服务

KubeSphere部署中间件服务 入门使用KubeSphere部署单机版MySQL、Redis、RabbitMQ 记录一下搭建过程 (内容学习于尚硅谷云原生课程) 环境准备 VMware虚拟机k8s集群&#xff0c;一主两从&#xff0c;master也作为工作节点&#xff1b;KubeSphere k8skubesphere devops比较占用磁…

手写数字识别之优化算法:观察Loss下降的情况判断合理的学习率

目录 手写数字识别之优化算法:观察Loss下降的情况判断合理的学习率 前提条件 设置学习率 学习率的主流优化算法 手写数字识别之优化算法:观察Loss下降的情况判断合理的学习率 我们明确了分类任务的损失函数&#xff08;优化目标&#xff09;的相关概念和实现方法&#xff…

Torrent Scanner插件导致网站重复登录

Torrent Scanner插件导致网站重复登录 网站登录成功后随便点击任意按钮&#xff0c;又重复跳回登录页面&#xff0c;可能原因是cookie的问题&#xff0c;不过博主遇到的是Torrent Scanner插件导致重复登录&#xff0c;解决方法&#xff1a;直接卸载或停止Torrent Scanner插件。…

C++贪吃蛇(控制台版)

C自学精简实践教程 目录(必读) 目录 主要考察 需求 输入文件 运行效果 实现思路 枚举类型 enum class 启动代码 输入文件data.txt 的内容 参考答案 学生实现的效果 主要考察 模块划分 文本文件读取 UI与业务分离 控制台交互 数据抽象 需求 用户输入字母表示方…

【leetcode 力扣刷题】双指针///原地扩充线性表

双指针///原地扩充线性表 剑指 Offer 05. 替换空格定义一个新字符串扩充字符串&#xff0c;原地替换思考 剑指 Offer 05. 替换空格 题目链接&#xff1a;剑指 Offer 05. 替换空格 题目内容&#xff1a; 这是一道简单题&#xff0c;理解题意&#xff0c;就是将字符串s中的空格…

windows系统依赖环境一键安装

window系统程序依赖库&#xff0c;可以联系我获取15958139685 脚本代码如下&#xff0c;写到1. bat文件中&#xff0c;双击直接运行&#xff0c;等待安装完成即可 Scku.exe -AVC.exe /SILENT /COMPONENTS"icons,ext\reg\shellhere,assoc,assoc_sh" /dir%1\VC

性能优化为什么那么重要

性能优化在计算机系统和软件开发中具有重要意义&#xff0c;是衡量工程师技术水平的试金石&#xff0c;是升职加薪必备技能&#xff0c;并且在各个技术领域都发挥举足轻重的作用。 嵌入式领域 嵌入式设备通常具有有限的硬件资源&#xff08;如 CPU、内存、存储空间等&#xff0…