Spring MVC 整合 Shiro 权限控制

news2024/11/19 9:29:43

Apache Shiro 是一个功能强大且灵活的开放源代码安全框架,可以细粒度地处理认证 (Authentication),授权 (Authorization),会话 (Session) 管理和加密 (cryptography) 等企业级应用中常见的安全控制流程。 Apache Shiro 的首要目标是易于使用和理解。 有时候安全性的流程控制会非常复杂,对开发人员来说是件很头疼的事情,但并不一定如此。 框架就应该尽可能地掩盖复杂性,并公开一个简洁而直观的 API,从而简化开发人员的工作,确保其应用程序安全性。这次我们聊一聊如何在 Spring Web 应用中使用 Shiro 实现权限控制。

功能

Apache Shiro 是一个具有许多功能的综合型应用程序安全框架。 下图为 Shiro 中的最主要的几个功能:

Shiro 的主要目标是“应用安全的四大基石” - 认证,授权,会话管理和加密:

  • 身份验证:也就是通常所说的 “登录”,为了证明用户的行为所有者。
  • 授权:访问控制的过程,即确定什么用户可以访问哪些内容。
  • 会话管理:即使在非 Web 应用程序中,也可以管理用户特定的会话,这也是 Shiro 的一大亮点。
  • 加密技术:使用加密算法保证数据的安全,非常易于使用。

架构

从整体概念上理解,Shiro 的体系架构有三个主要的概念:Subject (主体,也就是用户),Security Manager (安全管理器)和 Realms (领域)。 下图描述了这些组件之间的关系:

这几大组件可以这样理解:

  • Subject (主体):主体是当前正在操作的用户的特定数据集合。主体可以是一个人,也可以代表第三方服务,守护进程,定时任务或类似的东西,也就是几乎所有与该应用进行交互的事物。
  • Security Manager (安全管理器):它是 Shiro 的体系结构的核心,扮演了类似于一把 “伞” 的角色,它主要负责协调内部的各个组件,形成一张安全网。
  • Realms (领域):Shiro 与应用程序安全数据之间的 “桥梁”。当需要实际与用户帐户等安全相关数据进行交互以执行认证和授权时,Shiro 将从 Realms 中获取这些数据。

数据准备

在 Web 应用中,对安全的控制主要有角色、资源、权限(什么角色能访问什么资源)几个概念,一个用户可以有多个角色,一个角色也可以访问多个资源,也就是角色可以对应多个权限。落实到数据库设计上,我们至少需要建 5 张表:用户表、角色表、资源表、角色-资源表、用户-角色表,这 5 张表的结构如下: 用户表:

idusernamepassword
1张三123456
2李四666666
3王五000000

角色表:

idrolename
1管理员
2经理
3员工

资源表:

idresname
1/user/add
2/user/delete
3/compony/info

角色-资源表:

idroleidresid
111
212
323

用户-角色表:

iduseridroleid
111
212
313

对应的 POJO 类如下:

/**
 * 用户
 */
public class User {
 private Integer id;
 private String username;
 private String password;//getter & setter...
} 
/**
 * 角色
 */
public class Role {private String id;private String rolename;
} 
/**
 * 资源
 */
public class Resource {private String id;private String resname;
} 
/**
 * 角色-资源
 */
public class RoleRes {private String id;private String roleid;private String resid;
} 
/**
 * 用户-角色
 */
public class UserRole {private String id;private String userid;private String roleid;
} 

Spring 与 Shiro 整合的详细步骤,请参阅我的博客 《 Spring 应用中整合 Apache Shiro 》。 这里补充一下:需要提前引入 Shiro 的依赖,打开 mvnrepository.com,搜索 Shiro,我们需要前三个依赖,也就是 Shiro-Core、Shiro-Web 以及 Shiro-Spring,以 Maven 项目为例,在 pom.xml 中的 <dependencies> 节点下添加如下依赖:

<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.4.0</version>
</dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>1.4.0</version>
</dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.0</version>
</dependency> 

application-context.xml 中需要这样配置 shiroFilter bean:

<!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"><property name="securityManager" ref="securityManager"/><!-- 登录页面 --><property name="loginUrl" value="/login"/><!-- 登录成功后的页面 --><property name="successUrl" value="/index"/><!-- 非法访问跳转的页面 --><property name="unauthorizedUrl" value="/403"/><!-- 权限配置 --><property name="filterChainDefinitions"><value><!-- 无需认证即可访问的静态资源,还可以添加其他 url -->/static/** = anon<!-- 除了上述忽略的资源,其他所有资源都需要认证后才能访问 -->/** = authc</value></property>
</bean> 

接下来就需要定义 Realm 了,自定义的 Realm 集成自 AuthorizingRealm 类:

public class MyRealm extends AuthorizingRealm {
 @Autowired
 private UserService userService;
 /*** 验证权限*/
 @Override
 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {String loginName = SecurityUtils.getSubject().getPrincipal().toString();if (loginName != null) { String userId = SecurityUtils.getSubject().getSession().getAttribute("userSessionId").toString(); // 权限信息对象,用来存放查出的用户的所有的角色及权限 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 用户的角色集合 ShiroUser shiroUser = (ShiroUser) principalCollection.getPrimaryPrincipal(); info.setRoles(shiroUser.getRoles()); info.addStringPermissions(shiroUser.getUrlSet()); return info;}return null;
 }
 /*** 认证回调函数,登录时调用*/
 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {String username = (String) token.getPrincipal();User user = new User();sysuser.setUsername(username);try { List<SysUser> users = userService.findByNames(user);List<String> roleList= userService.selectRoleNameListByUserId(users.get(0).getId()); if (users.size() != 0) {String pwd = users.get(0).getPassword();// 当验证都通过后,把用户信息放在 session 里Session session = SecurityUtils.getSubject().getSession();session.setAttribute("userSession", users.get(0));session.setAttribute("userSessionId", users.get(0).getId());session.setAttribute("userRoles", org.apache.commons.lang.StringUtils.join(roleList,","));return new SimpleAuthenticationInfo(username,users.get(0).getPassword()); } else {// 没找到该用户throw new UnknownAccountException(); }} catch (Exception e) { System.out.println(e.getMessage());}return null;
 }
 /** * 更新用户授权信息缓存. */
 public void clearCachedAuthorizationInfo(PrincipalCollection principals) {super.clearCachedAuthorizationInfo(principals);
 }
 /** * 更新用户信息缓存. */
 public void clearCachedAuthenticationInfo(PrincipalCollection principals) {super.clearCachedAuthenticationInfo(principals);
 }
 /*** 清除用户授权信息缓存.*/
 public void clearAllCachedAuthorizationInfo() {getAuthorizationCache().clear();
 }
 /*** 清除用户信息缓存.*/
 public void clearAllCachedAuthenticationInfo() {getAuthenticationCache().clear();
 }
 /*** 清空所有缓存*/
 public void clearCache(PrincipalCollection principals) {super.clearCache(principals);
 }
 /*** 清空所有认证缓存*/
 public void clearAllCache() {clearAllCachedAuthenticationInfo();clearAllCachedAuthorizationInfo();
 }
} 

最后定义一个用户登录的控制器,接受用户的登录请求:

@Controller
public class UserController {/** * 用户登录 */@PostMapping("/login")public String login(@Valid User user,BindingResult bindingResult,RedirectAttributes redirectAttributes){try {if(bindingResult.hasErrors()){return "login";}//使用权限工具进行认证,登录成功后跳到 shiroFilter bean 中定义的 successUrlSecurityUtils.getSubject().login(new UsernamePasswordToken(user.getUsername(), user.getPassword()));return "redirect:index";} catch (AuthenticationException e) {redirectAttributes.addFlashAttribute("message","用户名或密码错误");return "redirect:login";}}/** * 注销登录 */@GetMapping("/logout")public String logout(RedirectAttributes redirectAttributes ){SecurityUtils.getSubject().logout();return "redirect:login";}
} 

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

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

相关文章

python实现excel和csv中的vlookup函数

本篇博客会介绍如何使用python在excel和csv里实现vlookup函数的功能&#xff0c;首先需要简单了解一下python如何操作excel 1. python处理excel 1.1 删除excel中指定行 在文件夹里创建了一个excel文件&#xff0c;可以看到里面放的是三国人物的数据 会发现在【蜀】里&#…

ESP-IDF:在C++环境下实现动态数组例程,包括初始化,添加数值,查找,删除,打印等功能

代码&#xff1a; /* 动态数组 */ struct DYNAMICARRAY { int *pAddress; int size; int capacity; }; DYNAMICARRAY *initialDynamicArray() { DYNAMICARRAY *p (DYNAMICARRAY *)malloc(sizeof(DYNAMICARRAY)); p->size 0; p->capacity 5; p->pAddress (int *)…

数据结构与算法--python版-01

文章目录基本概念数据存储方式算法案例基本概念 程序 数据结构 算法 数据结构&#xff0c;数据的存储&#xff08;物理结构&#xff09;及相互间的关联关系(逻辑结构)&#xff1b; 线性表&#xff08;顺序表、链表、队列、栈&#xff09;&#xff0c;一个前驱&#xff0c;一…

口语语言理解最新进展及前沿

口语语言理解 作为任务型对话系统的核心组件&#xff0c;目的是为了获取用户询问语句的框架语义表示信息&#xff0c;进而将这些信息为对话状态的追踪模块DSTDSTDST, 以及自然语言生成模块NLGNLGNLG所使用 SLUSLUSLU任务通常包含以下两个任务: 意图识别任务: intent detectio…

企企购商城 | 破解降本难题,非生采购“利器”来了

近年来&#xff0c;疫情反复无常&#xff0c;全球市场环境动荡不安&#xff0c;在瞬息万变的环境下&#xff0c;很多企业都处于降本处境&#xff0c;采购降本成了其中的重要一环。其中&#xff0c;非生采购作为贯穿企业各个职能板块的采购活动&#xff0c;众多国际先进的企业已…

应用PID算法对ABS系统的仿真和分析

1、内容简介略635-论文V2 可以交流、咨询、答疑2、内容说明摘要&#xff1a;本文旨在设计一种利用模糊控制理论优化的pid控制器&#xff0c;控制abs系统&#xff0c;达到对滑移率最佳控制范围的要求 &#xff0c;所提出的方案采用级联控制架构&#xff1a;设计用于外环中的车轮…

零基础开始QT绘图(6)

前面我们开始提到了GraphicView的使用&#xff0c;也提到GraphicsItem的使用&#xff0c;并且开始了自定义GraphicsItem的编写&#xff0c;而且上次我们的GraphicsItem也是可以拖动了&#xff0c;但这离我们需要的效果还有一定的距离。 所以&#xff0c;这次&#xff0c;我们进…

工程电磁设备在2018年杭州电磁大会的报告

1 方法原理 本篇是对多频电磁方法,应用的解读。 本汇报讲述了EMI传感器的基本情况,以及用手持多频电磁仪进行实际探测应用的例子。 什么是电磁感应?用发射装置(TX)激发谐波,产生一次场(Primary field),地下导体目标会相应产生涡流电磁场,产生二次场,被接收装置(RX…

「网络安全渗透」如果你还不懂CSRF?这一篇让你彻底掌握

1 什么是 CSRF 面试的时候的著名问题&#xff1a;"谈一谈你对 CSRF 与 SSRF 区别的看法" 这个问题&#xff0c;如果我们用非常通俗的语言讲的话&#xff0c;CSRF 更像是钓鱼的举动&#xff0c;是用户攻击用户的&#xff1b;而对于 SSRF 来说&#xff0c;是由服务器…

14.0、Linux-yum方式安装 docker

14.0、Linux-yum方式安装 docker 第一步&#xff1a; 检测 CentOS7 -> cat /etc/redhat-release 第二步&#xff1a; 用以下命令安装准备环境 -> yum -y install gcc yum -y install gcc-c 第三步&#xff1a; 如果之前安装过 docker 的&#xff0c;先用以下命令删…

【每日一题】【LeetCode】【第十三天】移动零

解决之路 题目描述 测试案例&#xff08;部分&#xff09; 第一次 看到这题目&#xff0c;第一时间还真想到了双指针法&#xff08;暴力解法是个会写代码的都能第一时间想到&#xff0c;所以自己学会了尽量不第一时间用暴力解法&#xff09; right的初始位置元素若为0&…

Jenkins-Slave分布式架构搭建

♥ Jenkins的分布式构建&#xff0c;在Jenkins的配置中叫做节点&#xff0c;分布式构建能够让同一套代码或项目在不同的环境(如&#xff1a;Windows和Linux系统)中编译、部署等。 ♥ 将jenkins项目发布在不同服务器上&#xff08;分布jenkins工作空间&#xff0c;部署项目到不同…

java 简单了解spring之(IOC容器)

IOC 容器 是spring的一个重要组成部分 IOC 翻译过来叫控制反转 他在做的事是 降低计算机代码的耦合度 这个过程叫IOC 通过控制反转 对象在被创建时 由一个调控系统内所有对象的外界实体将将其所依赖的对象引用传递给他&#xff0c;也可以说&#xff0c;依赖被注入到对象中 说简…

[oeasy]python0048_取整_int_float_浮点型_cast_扮演_tab_制表键_制表符

转化为10进制 回忆上次内容 上次 把其他进制 转化回 十进制 用的是 int 函数 int 来自于 integer 同源词 还有 integrateentire意思都是完整的 完整的 和 零散的 相对 可以把 零散的小数 转化为 完整的整数吗&#xff1f;&#x1f914; 取整 可以 把一个浮点型的小数 取…

【Proteus仿真】【51单片机】酒精浓度检测系统设计

文章目录一、功能简介二、软件设计三、实验现象联系作者一、功能简介 本项目使用Proteus8仿真51单片机控制器&#xff0c;使用LCD1602显示模块、按键模块、LED和蜂鸣器、MQ-3酒精传感器模块等。 主要功能&#xff1a; 系统运行后&#xff0c;LCD1602显示酒精浓度值和阈值&…

【自学Docker】Docker命令

Docker命令 Docker命令分类 Docker环境信息 docker [info|version]Docker容器生命周期管理 docker [create|exec|run|start|stop|restart|kill|rm|pause|unpause]Docker容器操作运维 docker [ps|inspect|top|attach|wait|export|port|rename|stats]Docker容器rootfs命令 …

AMBA--AHB总线协议学习及Verilog实现与仿真(一)

1、AHB总线概述 AHB&#xff1a;Advanced High-performance Bus,即高级高性能总线。AHB总线是SOC芯片中应用最为广泛的片上总线。下图是一个典型的基于AMBA AHB总线的微控制器系统&#xff1a; 基于AMBA AHB的设计中可以包含一个或多个总线主机&#xff0c;通常一个系统里至少…

MySQL——如何正确的显示随机消息

在之前的文章中有介绍order by语句的几种执行模式。考虑如下场景&#xff1a;有一个APP&#xff0c;有一个随机显示英语单词的功能&#xff0c;也就是根据每个用户的级别有一个单词表&#xff0c;然后这个用户每次访问首页的时候&#xff0c;都会随机滚动显示三个单词。会发现随…

PhotoZoom2023专业的图片无损放大软件最新V8版功能介绍

PhotoZoom 8 全新版本 震撼来袭。 一款划时代的、技术上产生革命性影响的数码图片放大工具。 我们获取图片的方法&#xff0c;一般是从度娘图片和各个图库里找素材。但一般网上搜索到的很多图片像素都非常小&#xff0c;普通方法放大就像打了马赛克一样&#xff0c;根本没法直接…

基于STM32的室内环境监测系统、基于STM32的智能家居系统

程序源码无删减 整个开发用到的所有资料如下&#xff1a; 开发软件MDK JLINK驱动安装包及相关文件 stm32开发资料-stm32f103c8t6 PCtoLCD2002 取模软件 stm32配套的液晶屏资料以及相关程序 Stm32f103c8t6 UCOSII 各传感器的数据手册以及程序、BMP180、DHT11温度模块、HC-SR04 …