Shiro框架——shiro的认证

 基本使用

1.环境搭建

引入pom依赖

说明:Shiro获取权限相关信息可以通过数据库获取,也可以通过ini配置文件获取

这里演示从ini文件中获取。

  • 在resources目录下创建ini文件
  • 注:这里等号左边的(如:zhangsan),就代表用户名。等号右边的(123456)就代表对应的密码。
  • 2.登录认证
    2.1登录认证概念
    ① 身份验证:就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。

    ② 在shiro中,用户需要提供principals(身份)和credentials(证明)给shiro,从而应用能验证用户身份;

    ③ principals:身份,即主体的标识属性,可以是任何属性,如用户名、邮箱等,唯一即可。一个主体可以有多个principals,但只有一个主身份 ( Primary principals ),一般是用户名/邮箱/手机号。

    ④ credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证书等。

    最常见的principals和credentials组合就是用户名/密码

    2.2登录认证基本流程

  • ① 收集用户身份/凭证,即如用户名/密码

    ② 调用 Subject.login 进行登录,如果失败将得到相应 的 AuthenticationException异常,根据异常提示用户 错误信息;否则登录成功

    ③ 创建自定义的 Realm 类,继承 org.apache.shiro.realm.AuthenticatingRealm类,实现 doGetAuthenticationInfo() 方法

  • 2.3demo样例

    四步走:

  • 初始化获取SecurityManager
  • 获取subject对象
  • 创建token对象,web应用用户名密码从页面传递
  • 完成登录
  •     public static void main(String[] args) {
            //1.创建安全管理器对象
            DefaultSecurityManager securityManager = new DefaultSecurityManager();
            //2.给安全管理器设置realm
            securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
            //3.SecurityUtils给全局安全工具类设置安全管理器
            SecurityUtils.setSecurityManager(securityManager);
            //4.关键对象subject主体
            Subject subject = SecurityUtils.getSubject();
            //5.创建令牌
            UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","123456");
            try {
                System.out.println("认证状态"+subject.isAuthenticated());//false
                //用户认证
                subject.login(token);
                System.out.println("登陆成功");
                System.out.println("认证状态"+subject.isAuthenticated());
            }catch (UnknownAccountException e){
                e.printStackTrace();
                System.out.println("认证失败,用户名不存在");
            }catch (IncorrectCredentialsException e){
                e.printStackTrace();
                System.out.println("认证失败,密码错误");
            }
        }

    常见的异常类型

    DisabledAccountException(帐号被禁用)
    LockedAccountException(帐号被锁定)
    ExcessiveAttemptsException(登录失败次数过多)
    ExpiredCredentialsException(凭证过期)等

  • 2.4自定义Realm

    通过分析源码可得:

  • 自定义Realm的作用:放弃使用.ini文件,使用数据库查询

    上边的程序使用的是Shiro自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm。

    1.shiro提供的Realm

  • 2.根据认证源码认证使用的是SimpleAccountRealm

  • SimpleAccountRealm的部分源码中有两个方法一个是认证一个是授权

  • 源码部分:

  • public class SimpleAccountRealm extends AuthorizingRealm {
    		//.......省略
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            UsernamePasswordToken upToken = (UsernamePasswordToken) token;
            SimpleAccount account = getUser(upToken.getUsername());
     
            if (account != null) {
     
                if (account.isLocked()) {
                    throw new LockedAccountException("Account [" + account + "] is locked.");
                }
                if (account.isCredentialsExpired()) {
                    String msg = "The credentials for account [" + account + "] are expired";
                    throw new ExpiredCredentialsException(msg);
                }
     
            }
     
            return account;
        }
     
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            String username = getUsername(principals);
            USERS_LOCK.readLock().lock();
            try {
                return this.users.get(username);
            } finally {
                USERS_LOCK.readLock().unlock();
            }
        }
    }

    3.自定义realm

  • package com.lut.realm;
     
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
     
    /**
     * 自定义Realm 将认证/授权的数据的来源转为数据库的实现
     */
    public class CustomerRealm extends AuthorizingRealm {
        //授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            System.out.println("==================");
            return null;
        }
     
        //认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            //在token中获取 用户名
            String principal = (String) token.getPrincipal();
            System.out.println(principal);
     
            //实际开发中应当 根据身份信息使用jdbc mybatis查询相关数据库
            //在这里只做简单的演示
            //假设username,password是从数据库获得的信息
            String username="zhangsan";
            String password="123456";
            if(username.equals(principal)){
                //参数1:返回数据库中正确的用户名
                //参数2:返回数据库中正确密码
                //参数3:提供当前realm的名字 this.getName();
                SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal,password,this.getName());
                return simpleAuthenticationInfo;
            }
            return null;
        }
    }
     

    注意:真正项目中,这里重写的认证方法应该要根据token中传入的用户名到数据库中查询对应的密码。然后通过SimpleAuthenticationInfo 的构造器放入SimpleAuthenticationInfo 对象中,返回给安全管理器,安全管理器会帮我们把用户输入的密码和数据库中的密码进行对比验证。

  • 4.使用自定义Realm认证

    使用我们刚刚自定义Realm完成从数据库中获取认证数据源

  • package com.lut.test;
     
    import com.lut.realm.CustomerRealm;
    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.mgt.DefaultSecurityManager;
    import org.apache.shiro.subject.Subject;
     
    /**
     * 测试自定义的Realm
     */
    public class TestAuthenticatorCusttomerRealm {
     
        public static void main(String[] args) {
            //1.创建安全管理对象 securityManager
            DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
     
            //2.给安全管理器设置realm(设置为自定义realm获取认证数据)
            defaultSecurityManager.setRealm(new CustomerRealm());
            //IniRealm realm = new IniRealm("classpath:shiro.ini");
     
            //3.给安装工具类中设置默认安全管理器
            SecurityUtils.setSecurityManager(defaultSecurityManager);
     
            //4.获取主体对象subject
            Subject subject = SecurityUtils.getSubject();
     
            //5.创建token令牌
            UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
            try {
                subject.login(token);//用户登录
                System.out.println("登录成功~~");
            } catch (UnknownAccountException e) {
                e.printStackTrace();
                System.out.println("用户名错误!!");
            }catch (IncorrectCredentialsException e){
                e.printStackTrace();
                System.out.println("密码错误!!!");
            }
     
        }
    }

    2.5使用MD5+Salt+Hash
    补充:MD5算法

    作用:一般用来加密或者签名(校验和)
    特点:MD5算法不可逆如何内容相同无论执行多少次md5生成结果始终是一致
    生成结果:始终是一个16进制32位长度字符串
    使用场景:比较两个文件内容是否相同。可以对两个文件进行校验,如果校验之后两个生成的值是一样的,说明两个文件内容完全一致。

注意:

这里为了保证用户的安全,用户注册时:不能直接把密码明文存储在数据库中。要在密码上先拼接上salt,再使用MD5对整个字符串进行校验。最后把结果存储在数据库中。
用户登录时:也要在密码相同的位置(比如说注册和登录时都是在密码最后拼接salt)拼接salt。然后再进行MD5校验。
注册时和登录时使用的salt必须要是同一个。可以写在后端的配置文件中,也可以随机生成一个,然后跟注册时一起存到数据库中。总之必须保证注册时和登陆时拼接的salt是同一个。

MD5的基本使用

package com.lut.test;
 
import org.apache.shiro.crypto.hash.Md5Hash;
 
public class TestShiroMD5 {
    
    public static void main(String[] args) {
 
        //使用md5
        Md5Hash md5Hash = new Md5Hash("123");
        System.out.println(md5Hash.toHex());
 
        //使用MD5 + salt处理
        Md5Hash md5Hash1 = new Md5Hash("123", "X0*7ps");
        System.out.println(md5Hash1.toHex());
 
        //使用md5 + salt + hash散列(参数代表要散列多少次,一般是 1024或2048)
        Md5Hash md5Hash2 = new Md5Hash("123", "X0*7ps", 1024);
        System.out.println(md5Hash2.toHex());
 
    }
}
 
输出结果:
202cb962ac59075b964b07152d234b70
bad42e603db5b50a78d600917c2b9821
7268f6d32ec8d6f4c305ae92395b00e8

 自定义md5+salt的realm
 

package com.lut.realm;
 
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
 
/**
 * 使用自定义realm 加入md5 + salt +hash
 */
public class CustomerMd5Realm extends AuthorizingRealm {
 
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
 
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取 token中的 用户名
        String principal = (String) token.getPrincipal();
 
        //假设这是从数据库查询到的信息
        String username="zhangsan";
        String password="7268f6d32ec8d6f4c305ae92395b00e8";//加密后
 
        //根据用户名查询数据库
        if (username.equals(principal)) {
            //参数1:数据库用户名
            //参数2:数据库md5+salt之后的密码
            //参数3:注册时的盐
            //参数4:realm的名字
            return new SimpleAuthenticationInfo(principal,
                    password,
                    ByteSource.Util.bytes("@#$*&QU7O0!"),
                    this.getName());
        }
        return null;
    }
}
 

使用md5+salt 认证

package com.lut.test;
 
import com.lut.realm.CustomerMd5Realm;
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.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
 
import java.util.Arrays;
 
public class TestCustomerMd5RealmAuthenicator {
 
    public static void main(String[] args) {
 
        //1.创建安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
 
        //2.注入realm
        CustomerMd5Realm realm = new CustomerMd5Realm();
 
        //3.设置realm使用hash凭证匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //声明:使用的算法
        credentialsMatcher.setHashAlgorithmName("md5");
        //声明:散列次数
        credentialsMatcher.setHashIterations(1024);
        realm.setCredentialsMatcher(credentialsMatcher);
        defaultSecurityManager.setRealm(realm);
 
        //4.将安全管理器注入安全工具
        SecurityUtils.setSecurityManager(defaultSecurityManager);
 
        //5.通过安全工具类获取subject
        Subject subject = SecurityUtils.getSubject();
 
        //6.认证
        UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
 
        try {
            subject.login(token);
            System.out.println("登录成功");
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用户名错误");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密码错误");
        }
    }
}
 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32--基于STM32F103C8T6的OV7670摄像头显示

本文介绍基于STM32F103C8T6实现的OV7670摄像头显示设计(完整资源及代码见文末链接) 一、简介 本文实现的功能:基于STM32F103C8T6实现的OV7670摄像头模组实时在2.2寸TFT彩屏上显示出来 所需硬件: STM32F103C8T6最小系统板、OV76…

学习docker第三弹------Docker镜像以及推送拉取镜像到阿里云公有仓库和私有仓库

docker目录 1 Docker镜像dockers镜像的进一步理解 2 Docker镜像commit操作实例案例内容是ubuntu安装vim 3 将本地镜像推送至阿里云4 将阿里云镜像下载到本地仓库5 后记 1 Docker镜像 镜像,是docker的三件套之一(镜像、容器、仓库)&#xff0…

大模型~合集14

我自己的原文哦~ https://blog.51cto.com/whaosoft/12286799 # Attention as an RNN Bengio等人新作:注意力可被视为RNN,新模型媲美Transformer,但超级省内 , 既能像 Transformer 一样并行训练,推理时内存需求又不随 token 数线性…

基于因果推理的强对流降水临近预报问题研究

我国地域辽阔,自然条件复杂,灾害性天气种类繁多,地区差异性大。雷雨大风、冰雹、短时强降水等强对流天气是造成经济损失、危害生命安全最严重的一类灾害性天气。由于强对流降水具有高强度、小空间尺度等特点,一直是气象预报领域的…

vue组件传值之$attrs

1.概述:$attrs用于实现当前组件的父组件,向当前组件的子组件通信(祖-》孙) 2.具体说明:$attrs是一个对象,包含所有父组件传入的标签属性。 注意:$attrs会自动排除props中声明的属性&#xff0…

矩阵系统哪家好~矩阵短视频运营~怎么矩阵OEM

一、引言 在当今的数字化时代,矩阵系统在众多领域中发挥着至关重要的作用,如视频监控、信号切换、自动化控制等。然而,如何判断一个矩阵系统是否好用成为了许多用户面临的问题。本文将从多个方面探讨矩阵系统好用与否的判断标准,希…

Python | Leetcode Python题解之第492题构造矩形

题目: 题解: class Solution:def constructRectangle(self, area: int) -> List[int]:w int(sqrt(area))while area % w:w - 1return [area // w, w]

QtCreator14调试Qt5.15出现 Launching Debugger 错误

1、问题描述 使用QtCreator14调试程序,Launching Debugger 显示红色,无法进入调试模式。 故障现象如下: 使能Debugger Log窗口,显示: 325^error,msg"Error while executing Python code." 不过&#xff…

SpringCloud学习:Seata总结与回顾

SpringCloud学习:Seata总结与回顾 文章目录 SpringCloud学习:Seata总结与回顾1. Seata实战:测试2. Seate原理总结和面试题3. Seata总结与回顾4. 易混点 1. Seata实战:测试 测试问题 未启用分布式事务 若不使用分布式事务&#xf…

基于PSO粒子群优化的CNN-GRU-SAM网络时间序列回归预测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) PSO优化过程: PSO优化前后,模型训练对比: 数据预测对比: 误差回归对比&a…

以太网交换安全:MAC地址漂移与检测(实验:二层环路+网络攻击)

一、什么是MAC地址漂移? MAC地址漂移是指网络中设备的MAC地址在运行过程中发生变化的现象。 MAC地址是用于唯一标识网络中的设备。 MAC地址漂移是指交换机上一个VLAN内有两个端口学习到同一个MAC地址,后学习到的MAC地址表项覆盖原MAC地址表项的现象。…

I2C的学习

I2C通信的基本概念 双线制:I2C通信采用两条线进行数据传输: SDA(Serial Data Line):数据线,用于传输数据。SCL(Serial Clock Line):时钟线,用于同步数据传输。…

软件测试工程师:如何写出好的测试用例?

软件测试用例(Test Case)是软件测试过程中的一种详细文档或描述,用于描述在特定条件下,对软件系统或组件进行测试的步骤、输入数据、预期输出和预期行为。编写高质量的测试用例是确保软件质量的关键步骤之一。以下是一些编写优秀测试用例的建议&#xff…

FLexsim 实体

发生器 属性列表 到达方式 ①到达时间间隔:根据数学分布,自定义到达时间间隔 ②到达时间表 到达时间:发生器产生实体的时间;实体名称:对应时间产生的临时实体的名字 ③到达序列:批量产生多少实体。 暂存区 …

【C++贪心 单调栈】1727. 重新排列后的最大子矩阵|1926

本文涉及知识点 C贪心 C单调栈 LeetCode1727. 重新排列后的最大子矩阵 给你一个二进制矩阵 matrix ,它的大小为 m x n ,你可以将 matrix 中的 列 按任意顺序重新排列。 请你返回最优方案下将 matrix 重新排列后,全是 1 的子矩阵面积。 示例…

计算机毕业设计 零食批发商仓库管理系统的设计与实现 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…

【CS常见问题】你用的是VS2019,最高支持.NET5.0,但是项目将.NET6.0设为目标无法运行,怎么办?

.NET版本问题 报错示例报错分析最简单的方法步骤 报错示例 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 NETSDK1045 当前 .NET SDK 不支持将 .NET 6.0 设置为目标。请将 .NET 5.0 或更低版本设置为目标,或使用支持 .NET 6.0 的 .NET SDK 版本。 ABFview C:\x…

计算机组成原理与系统结构——外部存储器

笔记内容及图片整理自XJTUSE “计算机组成原理与系统结构” 课程ppt,仅供学习交流使用,谢谢。 磁盘 磁盘是一个由非磁性材料构成的圆形盘片(称为基片),上面涂抹可磁化材料。传统的基片一直是铝制或铝合金的&#xff0…

【STL】string类的使用

🌟🌟作者主页:ephemerals__ 🌟🌟所属专栏:C、STL 目录 string类的介绍--为什么学习string类 一、string类的默认成员函数 构造函数(constructor) 析构函数(destructor) 赋值运算符重载operator 二…

DAY38 ||62.不同路径 |63. 不同路径 II

62.不同路径 题目:62. 不同路径 - 力扣(LeetCode) 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图…