Shiro认证(Authentication)
Shiro简介:特性和架构
Apache Shiro是一个功能强大且易于使用的Java安全(权限)框架,提供了认证、授权、会话管理、加密、与Web集成、缓存等功能。Shiro不仅可以在JavaSE环境中使用,也可以在JavaEE环境中使用。
特性
- Authentication(认证):身份认证/登录,验证用户是否拥有相应的身份。
- Authorization(授权):权限验证,验证某个已认证的用户是否拥有某个权限。
- Session Management(会话管理):管理用户登录后的会话信息。
- Cryptography(加密):保护数据的安全性,如密码加密存储到数据库。
- Web Support:非常容易集成到Web环境。
- Caching:缓存用户信息、角色和权限,提高效率。
- Concurrency:支持多线程应用的并发验证。
- Remember Me:记住我功能,一次登录后下次无需再次登录。
架构
Shiro的架构从外部和内部来看可以分为两个部分:
-
外部架构:
- Subject:代表当前“用户”,与应用交互的任何东西都可以是Subject,如网络爬虫、机器人等。所有Subject都绑定到SecurityManager。
- SecurityManager:安全管理器,所有与安全有关的操作都会与SecurityManager交互,且它管理着所有Subject。它是Shiro的核心,负责与Shiro的其他组件进行交互。
- Realm:域,Shiro从Realm获取安全数据(如用户、角色、权限)。SecurityManager要验证用户身份,需要从Realm获取相应的用户进行比较以确定用户身份是否合法。
-
内部架构:
- Authenticator:认证器,负责主体认证。
- Authorizer:授权器,决定主体是否有权限进行相应的操作。
- SessionManager:管理会话的生存周期。
- SessionDAO:数据访问对象,用于会话的CRUD操作。
- CacheManager:缓存控制器,管理用户、角色、权限等的缓存。
- Cryptography:密码模块,提供常见的加密组件用于加密/解密操作。
认证
Token
在Shiro中,认证指的是识别和证明操作者是一个合法用户。用户如果想要通过认证,需要提供Principal(身份)和Credentials(凭证),从而应用能验证用户身份。这些身份和凭证信息在Shiro框架中以Token(令牌)的概念进行封装。
快速上手
添加依赖:
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>2.0.1</version>
</dependency>
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope>
</dependency>
配置shiro.ini:在resource目录下创建shiro.ini文件配置用户认证数据。
# 对用户信息进行匹配
[users]
# 用户账号和密码
admin=123456
czkt=111111
认证测试:
public class ShiroTester { @Test public void testShiro() { IniRealm realm = new IniRealm("classpath:shiro.ini"); DefaultSecurityManager securityManager = new DefaultSecurityManager(); securityManager.setRealm(realm); SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456"); try { subject.login(token); } catch (Exception e) { System.out.println("认证异常:"); e.printStackTrace(); } System.out.println("是否认证成功:" + subject.isAuthenticated()); System.out.println("身份信息:" + subject.getPrincipal()); }
}
认证流程
- 调用
Subject.login(Token)
进行登录,其会自动委托给SecurityManager
。 SecurityManager
负责真正的身份验证逻辑,它会委托给Authenticator
进行身份验证。Authenticator
可能会委托给相应的AuthenticationStrategy
进行多Realm身份验证。Authenticator
会把相应的Token传入Realm,从Realm获取身份信息,如果没有返回或抛出异常表示身份验证失败。
记住我 vs 认证
“记住我”功能允许用户在一次登录后,下次访问时无需再次登录。这与普通的认证流程有所不同,因为“记住我”功能通常会在用户的浏览器中存储一个持久化的Cookie,用于在用户下次访问时自动进行身份验证。
注销Logout
用户可以通过调用Subject.logout()
方法进行注销操作。这会委托给SecurityManager
进行实际的注销逻辑处理。
SpringBoot + Shiro 认证
1. 基础代码调整
首先,你需要在Spring Boot项目中添加Shiro的依赖。这可以通过在pom.xml
文件中添加以下依赖来完成:
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.7.1</version> <!-- 请根据需要选择最新版本 -->
</dependency>
2. 自定义Realm
Realm是Shiro与数据源之间的桥梁,用于获取安全数据(如用户、角色和权限)。你需要创建一个自定义的Realm类,并实现AuthorizingRealm
接口。
import org.apache.shiro.authc.*;
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.springframework.beans.factory.annotation.Autowired; // 假设你有一个UserService用于处理用户相关的业务逻辑
@Component
public class CustomRealm extends AuthorizingRealm { @Autowired private UserService userService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = (String) principals.getPrimaryPrincipal(); // 从数据库中获取用户的角色和权限信息 Set<String> roles = userService.getRolesByUsername(username); Set<String> permissions = userService.getPermissionsByUsername(username); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); authorizationInfo.setRoles(roles); authorizationInfo.setStringPermissions(permissions); return authorizationInfo; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken upToken = (UsernamePasswordToken) token; String username = upToken.getUsername(); // 从数据库中获取用户信息 User user = userService.findByUsername(username); if (user == null) { throw new UnknownAccountException("用户不存在"); } // 将用户信息封装到SimpleAuthenticationInfo中,并返回 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( username, user.getPassword(), // 假设密码已经加密存储 ByteSource.Util.bytes(user.getSalt()), // 假设使用了盐值加密 getName() ); return authenticationInfo; }
}
3. 配置Shiro相关对象
你需要在Spring Boot的配置类中配置Shiro的相关对象,如SecurityManager
和ShiroFilterFactoryBean
。
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map; @Configuration
public class ShiroConfig { @Bean public SecurityManager securityManager(CustomRealm customRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(customRealm); return securityManager; } @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setLoginUrl("/login"); // 登录页面URL shiroFilterFactoryBean.setSuccessUrl("/index"); // 登录成功后的跳转页面 shiroFilterFactoryBean.setUnauthorizedUrl("/403"); // 未授权页面URL // 定义过滤器链 Map<String, String> filterChainDefinitionMap = new HashMap<>(); filterChainDefinitionMap.put("/static/**", "anon"); // 静态资源无需认证 filterChainDefinitionMap.put("/login", "anon"); // 登录页面无需认证 filterChainDefinitionMap.put("/**", "authc"); // 其他URL都需要认证 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public FilterRegistrationBean<DelegatingFilterProxy> delegatingFilterProxy() { FilterRegistrationBean<DelegatingFilterProxy> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new DelegatingFilterProxy("shiroFilter")); registrationBean.addUrlPatterns("/*"); return registrationBean; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; }
}
4. IndexController重写登录方法
你需要创建一个控制器来处理登录请求。
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam; @Controller
public class IndexController { @GetMapping("/login") public String login() { return "login"; // 返回登录页面 } @PostMapping("/login") public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model) { Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); try { subject.login(token); return "redirect:/index"; // 登录成功后重定向到首页 } catch (Exception e) { model.addAttribute("error", "用户名或密码错误"); return "login"; // 登录失败返回登录页面 } } @GetMapping("/index") public String index() { return "index"; // 返回首页 } @GetMapping("/403") public String accessDenied() { return "403"; // 返回未授权页面 }
}
5. 测试认证登录
现在,你可以启动Spring Boot应用程序,并访问/login
页面来测试登录功能。输入正确的用户名和密码后,你应该会被重定向到/index
页面。如果输入错误的用户名或密码,你应该会看到登录页面上的错误信息。
确保你的UserService
和数据库配置正确,以便能够正确地获取和验证用户信息。此外,你还需要配置一个视图解析器(如Thymeleaf或JSP)来渲染登录页面、首页和未授权页面。
相关文章:

Shiro认证(Authentication)
Shiro简介:特性和架构 Apache Shiro是一个功能强大且易于使用的Java安全(权限)框架,提供了认证、授权、会话管理、加密、与Web集成、缓存等功能。Shiro不仅可以在JavaSE环境中使用,也可以在JavaEE环境中使用。 特性 …...

Qt和c++面试集合
目录 Qt面试 什么是信号(Signal)和槽(Slot)? 什么是Meta-Object系统? 什么是Qt的MVC模式? 1. QT中connect函数的第五个参数是什么?有什么作用? 3. 在QT中ÿ…...

Spark 3.3.x版本中的动态分区裁剪(DPP,Dynamic Partition Pruning)的实现及应用剖析
文章目录 Dynamic Partition Pruning(DPP)的作用DPP生效的一些要点DPP生效的简单SQL示例DPP生效SQL的解析示例Deduplicate Correlated SubqueryRewrite Predicates as JoinRewrite Join With Dynamic SubqueryRewrite Dynamic Subquery as Dynamic Expre…...

Android 各国语言value文件夹命名规则
中文 values-zh英语values-en 阿拉伯语 values-ar 保加利亚语 values-bg加泰罗尼亚语values-ca 捷克语 values-cs 丹麦语 values-da 德语 values-de 希腊语 values-el 西班牙语 values-es 芬兰语 values-fi 法语 values-fr 希伯来语 values-iw 印地语 values-hi 克罗里亚语 …...

深入理解Redis锁与Backoff重试机制在Go中的实现
文章目录 流程图Redis锁的深入实现Backoff重试策略的深入探讨结合Redis锁与Backoff策略的高级应用具体实现结论 在构建分布式系统时,确保数据的一致性和操作的原子性是至关重要的。Redis锁作为一种高效且广泛使用的分布式锁机制,能够帮助我们在多进程或分…...

uniapp-小程序开发0-1笔记大全
uniapp官网: https://uniapp.dcloud.net.cn/tutorial/syntax-js.html uniapp插件市场: https://ext.dcloud.net.cn/ uviewui类库: https://www.uviewui.com/ 柱状、扇形、仪表盘库: https://www.ucharts.cn/v2/#/ CSS样式&…...

Go语言数据库操作深入讲解
go操作MySQL 使用第三方开源的mysql库: github.com/go-sql-driver/mysql (mysql驱动)github.com/jmoiron/sqlx (基于mysql驱动的封装) 命令行输入 : go get github.com/go-sql-driver/mysqlgo get github.com/jmoiron/sqlx Insert操作 登录后复制 // 连接Mysql data…...

搜维尔科技:SenseGlove Nova 2触觉反馈手套开箱测评
SenseGlove Nova 2触觉反馈手套开箱测评 搜维尔科技:SenseGlove Nova 2触觉反馈手套开箱测评...

步步精科技诚邀您参加2024慕尼黑华南电子展
尊敬的客户: 我们诚挚地邀请您参加即将于2024年10月14日至10月16日在深圳国际会展中心 (宝安新馆)举办的慕尼黑华南电子展(electronica South China)。本届将聚焦人工智能、数据中心、新型储能、无线通信、硬件安全、新能源汽车、第三代半导…...

OPC UA与PostgreSQL如何实现无缝连接?
随着工业4.0的推进,数据交换和集成在智能制造中扮演着越来越重要的角色。OPC UA能够实现设备与设备、设备与系统之间的高效数据交换。而PostgreSQL则是一种强大的开源关系型数据库管理系统,广泛应用于数据存储和管理。如何将OPC UA与PostgreSQL结合起来&…...

C语言[斐波那契数列2]
本篇文章讲述前一篇文章的细节,方便大家进行代码的运算。 本次代码题为: 输出斐波那契数列的前20位数,每行4位数。 详细解释: 在 main 函数中,首先定义了循环变量 i 和用于存储斐波那契数列项的三个长整型变量 f1 、 f2 和 temp 。其…...

八、Linux之实用指令
1、指定运行级别 1.1 基本介绍 运行级别说明 0 :关机 1 :单用户【找回丢失密码】 2:多用户状态没有网络服务(用的非常少) 3:多用户状态有网络服务(用的最多) 4:系统未使…...

2024_E_100_连续字母长度
连续字母长度 题目描述 给定一个字符串,只包含大写字母,求在包含同一字母的子串中,长度第 k 长的子串的长度,相同字母只取最长的那个子串。 输入描述 第一行有一个子串(1<长度<100),只包含大写字母。 第二行为…...

清空redo导致oracle故障恢复---惜分飞
客户由于空间不足,使用> redo命令清空了oracle的redo文件 数据库挂掉之后,启动报错 Fri Oct 04 10:32:57 2024 alter database open Beginning crash recovery of 1 threads parallel recovery started with 31 processes Started redo scan Errors in file /home/oracle…...

VAE(与GAN)
VAE 1. VAE 模型概述 变分自编码器(Variational Autoencoder, VAE)是一种生成模型,主要用于学习数据的潜在表示并生成新样本。它由两个主要部分组成:编码器和解码器。 编码器:将输入数据映射到潜在空间,…...

【高等数学】多元微分学(二)
隐函数的偏导数 二元方程的隐函数 F ( x , y ) 0 F(x,y)0 F(x,y)0 推出隐函数形式 y y ( x ) yy(x) yy(x). 欲求 d y d x \frac{d y}{d x} dxdy 需要对 F 0 F0 F0 两边同时对 x x x 求全导 0 d d x F ( x , y ( x ) ) ∂ F ∂ x d x d x ∂ F ∂ y d y d x ∂ F…...

.NET 中的 Web服务(Web Services)和WCF(Windows Communication Foundation)
一、引言 在当今数字化时代,不同的软件系统和应用程序之间需要进行高效、可靠的通信与数据交换。.NET 框架中的 Web 服务和 WCF(Windows Communication Foundation)为此提供了强大的技术支持。它们在构建分布式应用程序、实现跨平台通信以及…...

Linux小知识2 系统的启动
我们在上文中介绍了文件系统,提到了Linux的文件系统存在一个块的概念,其中有一个特殊的块:引导块。这和我们这里要讲的系统启动有关。 BIOS 基本输入输出系统,基本上是一个操作系统最早实现也是最早运行的第一个程序。是一个比较…...

Oracle-19g数据库的安装
简介 Oracle是一家全球领先的数据库和云解决方案提供商。他们提供了一套完整的技术和产品,包括数据库管理系统、企业级应用程序、人工智能和机器学习工具等。Oracle的数据库管理系统是业界最受欢迎和广泛使用的数据库之一,它可以管理和存储大量结构化和…...

Dubbo快速入门(二):第一个Dubbo程序(附源码)
文章目录 一、生产者工程0.目录结构1.依赖2.配置文件3.启动类4.生产者服务 二、消费者工程0.目录结构1.依赖2.配置文件3.启动类4.服务接口5.controller接口 三、测试代码 本博客配套源码:gitlab仓库 首先,在服务器上部署zookeeper并运行,可以…...

不同数据类型转换与转义的对比差异
(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu) 在C和C语言中,类型转换与转义是有点像的,有时可能被误解,这块需要仔细辨别。 类型转换形如,把不同字节数或相同字节数的类型值进行转换,强调的是数值转换过去&…...

Kylin系统安装VMwareTools工具
如下图所示,安装好Kylin系统之后,还未安装VMwareTools工具,导致系统画面无法填充虚拟机 正常安装了VMwareTools工具后的系统画面 所以,接下来我们介绍一下如何在Kylin系统下安装VMwareTools工具 首先,点击VMware工具栏…...

uni-app 拍照图片添加水印
获取图片信息 uni.chooseImage({count: 6, //默认9sizeType: ["original", "compressed"], //可以指定是原图还是压缩图,默认二者都有sourceType: ["camera"], //从相册选择success: async function (result: any) {if (!props.isMar…...

Docker-registry私有镜像仓库的安装
Docker-registry私有镜像仓库的安装 我在这里的镜像仓库搭建在ip为192.168.3.23的虚机中。 安装docker-registry 1.拉取镜像 # docker pull registry 2.查看镜像 # docker images REPOSITORY TAG IMAGE ID CREATE…...

在vue3中实现祖组件给后代组件传参,可以跨域几层。
使用provide和inject就可以 下面是祖组件代码: //这是祖组件// 引入provide import { provide } from "vue";//定义数据 const projectId ref("");// 给后代组件传参 provide("projectId", projectId); 下面是后代组件代码&#…...

【优选算法】——双指针(上篇)!
🌈个人主页:秋风起,再归来~🔥系列专栏:C刷题算法总结🔖克心守己,律己则安 目录 前言:双指针 1. 移动零(easy) 2. 复写零(easy) 3…...

【C语言】数据输出格式控制
数据的输出格式修饰 常用两种: 整型中,输出数据左对齐、右对齐、占m位、不足m位前补0。浮点型中,默认通过四舍五入保留小数点后6位,通过参数设置保留小数点后n位。 #include <stdio.h> #define PI 3.14159 /* 功能&#x…...

Qt-界面优化选择器的用法(70)
目录 描述 使用 类型选择器 ID 选择器 并集选择器 子控件选择器 伪控制器 描述 QSS 的选择器⽀持以下⼏种 选择器⽰例说明全局选择器*选择所有的 widget.类型选择器 (type selector)QPushButton选择所有的 QPushButton 和其⼦类的控件.类选择器 (class selector).QPus…...

全国职业技能大赛——信息安全管理与评估第一阶段BC、FW、WAF题目详细解析过程
💗需要职业技能大赛环境+WP,请联系我!🍬 博主介绍 👨🎓 博主介绍:大家好,我是 一个想当文人的黑客 ,很高兴认识大家~ ✨主攻领域:【渗透领域】【应急响应】 【edusrc漏洞挖掘】 【VulnHub靶场复现】【面试分析】 🎉欢迎关注💗一起学习👍一起讨论⭐️一起…...

基于Vite创建项目
vite 是新一代前端构建工具,官网地址:https://vitejs.cn,vite的优势如下: 轻量快速的热重载(HMR),能实现极速的服务启动。对 TypeScript、JSX、CSS 等支持开箱即用。真正的按需编译,…...