shiro 方法级别细粒度权限控制_Shiro的认证和权限控制

从类别上分,有两大类:

- 认证:你是谁?–识别用户身份。

- 授权:你能做什么?–限制用户使用的功能。

权限的控制级别

从控制级别(模型)上分:

- URL级别-粗粒度

- 方法级别-细粒度

- 页面级别-自定义标签(显示)

- 数据级别-最细化的(数据)

URL级别的权限控制-粗粒度

在web.xml中配置一个过滤器filter,在过滤器中,对请求的地址进行解析,字符串截取:

url.substring()…把上下文前面的路径都截取掉,剩下user_login.action。

过滤器代码:

以通过查询数据库,来判断,当前登录用户,是否可以访问user_login.action。

url级别控制,每次请求过程中只控制一次 ,相比方法级别权限控制 是粗粒度的 !URL级别权限控制,基于Filter实现。

方法级别的权限控制-细粒度

aop面向切面的编程,在方法执行之前,进行权限判断,如果没有权限,抛出异常,终止方法的继续运行。

自定义注解 在需要权限控制方法上, 添加需要的权限信息

代理 (Spring AOP ),在目标方法运行时 进行增强 ,通过反射技术获取目标方法上注解中权限 , 查询数据库获取当前登陆用户具有权限,进行比较。

相比URL级别权限控制, 可以控制到服务器端执行的每个方法,一次请求中可以控制多次。

页面(显示)级别的权限控制-自定义标签

页面显示的权限控制,通常是通过 自定义标签来实现

数据级别的权限控制

在每条数据上增加一个字段,该字段记录了权限的值。数据和权限绑定。

代码,你在查询数据的时候,需要去权限和用户对应表中,通过当前登录用户的条件,查询出你的数据权限。然后再将数据权限作为一个条件,放到业务表中进行查询。从而限制了数据的访问。

权限系统的数据表设计

资源:用户要访问的目标,通常是服务中的程序或文件

权限:用户具有访问某资源的能力

角色:权限的集合,为了方便给用户授权。

用户:访问系统的’人’。

表对象实体:

- 用户(User)表:访问系统的用户,比如用户登录要用

- 权限(Function)表:系统某个功能允许访问而对应的权限

- 角色(Role)表:角色是权限的集合(权限组),方便用户授权。

表对象之间的关系:

- 用户和角色关系表:一个用户对应N个角色,一个角色可以授予N个用户—》多对多关系

- 角色和权限关系表:一个角色包含N个权限,一个权限可以属于N个角色—》多对多关系

完整的权限相关表:

URL级别权限控制包含:资源表、权限表、角色表、用户表,以及相关关系(都是多对多),共7张表。

方法级别的权限控制包含:功能权限、角色、用户,以及相关关系(都是多对多),共5张表。

但Apache Shiro框架支持的URL级别权限控制,是将资源和资源权限对应关系配置到了配置文件中,不需要表的支撑,只需要5张表了。

Apache Shiro权限控制

Apache Shiro 可以不依赖任何技术使用, 可以直接和web整合,通常在企业中和Spring 结合使用。

Authentication: 认证 — 用户登录

Authorization : 授权 —- 功能权限管理

通过引入Maven坐标导入shiro

官方建议:不推荐直接引入shiro-all,依赖比较多,原因怕有jar冲突。官方推荐根据需要单独导入jar。

Shiro基本原理

Shiro的框架的体系结构:

Shiro权限控制流程的原理:

应用代码 —- 调用Subject (shiro的Subject 就代表当前登陆用户) 控制权限 —- Subject 在shiro框架内部 调用 Shiro SecurityManager 安全管理器 —– 安全管理器调用 Realm (程序和安全数据连接器 )。

Subject要进行任何操作,都必须要调用安全管理器(对我们来说是自动的)。

而安全管理器会调用指定的Realms对象,来连接安全数据。

Realms用来编写安全代码逻辑和访问安全数据,是连接程序和安全数据的桥梁。

URL级别的权限控制

配置整合和url级别认证

配置过滤器web.xml:放在struts的前端控制器之前配置,但放在openEntitymanage之后。

shiroSecurityFilter

org.springframework.web.filter.DelegatingFilterProxy

targetFilterLifecycle

true

shiroSecurityFilter

/*

配置ApplicationContext.xml:(shiro权限控制过滤器+ shiro安全管理器)

/login.jsp = anon

/validatecode.jsp = anon

/js/** = anon

/css/** = anon

/images/** = anon

/user_login.action* = anon

/page_base_staff.action = anon

/page_base_region.action = perms["user"]

/page_base_subarea.action = roles["operator"]

/** = authc

配置shiroFilter 其实是一个过滤器链,含有10个Filter(校验功能)。

常用:

认证

- anon不用认证(登录)就能访问(单词注意大小写)

- authc: 需要认证(登录)才能使用,例如/admins/user/**=authc,没有参数。

授权:

- perms:需要拥有某权限才能使用,如具体允许的权限:/page_base_region.action =perms[“user”],如果要访问该action,当前登录用户必须拥有user名字的权限。

- roles:需要拥有某角色才能使用,如具体允许的角色:/page_base_subarea.action = roles[“operator”]如果要访问该action,当前用户必须拥有operator权限。

用户认证(登录)—自定义Realm

Shiro实现登录逻辑

用户输入用户名和密码 —- 应用程序调用Subject的login方法 —- Subject 调用SecurityManager的方法 —- SecurityManager 调用Realm的认证方法 —- 认证方法根据登录用户名查询密码 ,返回用户的密码 —- SecurityManager 比较用户输入的密码和真实密码是否一致 。

编写Shiro的认证登录逻辑

@Action(value="user_login",results={@Result(name=SUCCESS,type="redirect",location="/index.jsp"),@Result(name=LOGIN,location="/login.jsp")})

@InputConfig(resultName="login")

public String login() throws Exception {

//shrio:登陆逻辑

//获取认证对象的包装对象

Subject subject = SecurityUtils.getSubject();

//获取一个认证的令牌:

//直接获取页面的用户和密码进行校验

AuthenticationToken authenticationToken = new UsernamePasswordToken(model.getUsername(),MD5Utils.md5(model.getPassword()));

//认证过程

try {

// 如果成功,就不抛出异常,会自动将用户放入session的一个属性

subject.login(authenticationToken);

//成功,返回首页

return SUCCESS;

}catch(UnknownAccountException e){

//用户名错误

addActionError(getText("UserAction.usernamenotfound"));

//返回登陆页面

return LOGIN;

}catch (IncorrectCredentialsException e) {

//密码错误

addActionError(getText("UserAction.passwordinvalid"));

//返回登陆页面

return LOGIN;

}

catch (AuthenticationException e) {

//认证失败

e.printStackTrace();

//页面上进行提示

addActionError(getText("UserAction.loginfail"));

//返回登陆页面

return LOGIN;

}

}

编写Realm,给SecurityManager提供

JdbcRealm和jndiLdapRealm,直接连接jdbc或jndi或ldap。

相当于dao和reaml整合了,能直接读取数据库,逻辑代码都实现好了。

/**

* 实现认证和授权功能

*自定义的realm,作用从数据库查询数据,并返回数据库认证的信息

*/

@Component("bosRealm")

public class BosRealm extends AuthorizingRealm{

//注入ehcache的缓存区域

@Value("BosShiroCache")//注入缓存具体对象的名字,该名字在ehcache.xml中配置的

public void setSuperAuthenticationCacheName(String authenticationCacheName){

super.setAuthenticationCacheName(authenticationCacheName);

}

//注入service

@Autowired

private UserService userService;

//注入角色dao

@Autowired

private RoleDao roleDao;

//注入功能的dao

@Autowired

private FunctionDao functionDao;

//授权方法:获取用户的权限信息

//授权:回调方法

//如果返回null,说明没有权限,shiro会自动跳到

//如果不返回null,根据配置/page_base_subarea.action = roles["weihu"],去自动匹配

//给授权提供数据的

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

//给当前用户授权的权限(功能权限、角色)

SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

//两种方式:

//方式1:工具类来获取(首长-)

// User user=(User)SecurityUtils.getSubject().getPrincipal();

//方式2:通过参数获取首长(推荐)

User user = (User) principals.getPrimaryPrincipal();

//实际:需要根据当前用户的角色和功能权限来构建一个授权信息对象,交给安全管理器

if (user.getUsername().equals("admin")) {

//如果是超级管理员

//查询出所有的角色,给认证信息对象

List roleList = roleDao.findAll();

for (Role role : roleList) {

authorizationInfo.addRole(role.getCode());

}

//查询出所有的功能权限,给认证对象

List functionList = functionDao.findAll();

for (Function function : functionList) {

authorizationInfo.addStringPermission(function.getCode());

}

} else {

//如果是普通用户

List roleList = roleDao.findByUsers(user);

for (Role role : roleList) {

authorizationInfo.addRole(role.getCode());

//导航查询,获取某角色的拥有的功能权限

Set functions = role.getFunctions();

for (Function function : functions) {

authorizationInfo.addStringPermission(function.getCode());

}

}

}

return authorizationInfo;//将授权信息交给安全管理器接口。

}

//认证:回调,认证管理器会将认证令牌放到这里(action层的令牌AuthenticationToken)

//发现如果返回null,抛出用户不存在的异常UnknownAccountException

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

//用户名密码令牌(action传过来)

UsernamePasswordToken upToken = (UsernamePasswordToken) token;

//调用业务层来查询(根据用户名来查询用户,无需密码)

User user = userService.findByUsername(upToken.getUsername());

//判断用户是否存在

if (user == null) {

//用户不存在

return null;//抛出异常

} else {

//用户名存在

//参数1:用户对象,将来要放入session,数据库查询出来的用户

//参数2:凭证(密码):密码校验:校验的动作交给shiro

//参数3:当前使用的Realm在Spring容器中的名字(bean的名字,自动在spring容器中寻找)

SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), super.getName());

return authenticationInfo;//密码校验失败,会自动抛出IncorrectCredentialsException

}

}

}

ApplicatonContext.xml:

用户认证(退出)以及修改密码

/**

* 用户退出登录

*@return

*@throws Exception

*/

@Action(value="user_logout",results={@Result(name=LOGIN,type="redirect",location="/login.jsp")})

public String logout() throws Exception {

//shiro退出

Subject subject = SecurityUtils.getSubject();

subject.logout();

//跳转登陆页面

return LOGIN;

}

/**

* 用户修改密码

*@return

*@throws Exception

*/

// @Action(value="user_editPassword",results={@Result(name=JSON,type=JSON)})

@Action("user_editPassword")

public String editPassword() throws Exception {

//获取Principal就是获取当前用户

User loginUser = (User) SecurityUtils.getSubject().getPrincipal();

model.setId(loginUser.getId());

//页面结果

HashMap resultMap = new HashMap();

try {

//调用service进行修改密码

userService.updateUserPassword(model);

//修改成功

resultMap.put("result", true);

} catch (Exception e) {

e.printStackTrace();

//修改失败

resultMap.put("result", false);

}

//将结果压入栈顶

ActionContext.getContext().getValueStack().push(resultMap);

//转换为json

return JSON;

}

用户授权(授权)—自定义Ream

数据库数据添加,applicationContext.xml配置

/login.jsp = anon

/validatecode.jsp = anon

/js/** = anon

/css/** = anon

/images/** = anon

/user_login.action = anon

/page_base_staff.action = anon

/page_base_region.action = perms["region"]

/page_base_subarea.action = roles["weihu"]

/page_qupai_noticebill_add.action = perms["noticebill"]

/page_qupai_quickworkorder.action = roles["kefu"]

/** = authc

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

代码在上面的BosRealm的中,protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)

RoleDao省略。

方法级别的权限控制

启用Shiro注解

需要 Shiro 的 Spring AOP 集成来扫描合适的注解类以及执行必要的安全逻辑。

ApplicationContext.xml

depends-on="lifecycleBeanPostProcessor">

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

在需要权限控制的目标方法上面使用shiro的注解:

@RequiresAuthentication 需要用户登录

subject.isAuthenticated() 必须返回true

@ RequiresUser

subject.isAuthenticated() 返回true 或者subject.isRemembered() 返回true

“Remember Me”服务:

认证机制 基于 session

被记忆机制 基于 cookie (subject.isAuthenticated() 返回 false )

@ RequiresGuest 与 @RequiresUser 相反,不能认证也不能被记忆。

@ RequiresRoles 需要角色

@RequiresPermissions 需要权限

异常

动态代理异常

解决方案:

配置ApplicationContext.xml,设置代理为cglib代理(对目标类代理)

depends-on="lifecycleBeanPostProcessor" >

1

2

3

4

5

6

方案二:

1

类型转换异常

解决方案:递归向上寻找泛型的类型。

//递归向上 查找

Class actionClass =this.getClass();

//向父类递归寻找泛型

while(true){

//得到带有泛型的类型,如BaseAction

Type type = actionClass.getGenericSuperclass();

if(type instanceof ParameterizedType){

//转换为参数化类型

ParameterizedType parameterizedType = (ParameterizedType) type;

//获取泛型的第一个参数的类型类,如Userinfo

Class modelClass = (Class) parameterizedType.getActualTypeArguments()[0];

//实例化模型对象

try {

model=modelClass.newInstance();

} catch (InstantiationException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

}

break;

}

//寻找父类

actionClass=actionClass.getSuperclass();

}

空指针异常

解决方案1:使用public 的Setter方法上的注解直接注入Service。

SubareaAction:

//注入service

private SubareaService subareaService;

@Autowired

public void setSubareaService(SubareaService subareaService) {

this.subareaService = subareaService;

}

解决方案2:

@Autowire还放到私有声明上,

在struts.xml中覆盖常量(开启自动装配策略):

值默认是false,struts2默认注入采用的是构造器注入(从spring中寻找的bean)

改成true,struts2会采用setter方法注入

页面标签(实现页面内容定制显示)

页面拿Session中的user对象: 代表user对象。

程序中拿Session中的user对象:SecurityUtils.getSubject().getPrincipal()

资源通配符和权限通配符可便捷开发。

代码级别

使用代码编程的方式,直接在程序中使用Subject对象,调用内部的一些API。(有代码侵入)

//代码级别的权限控制(授权):功能权限和角色权限:两套机制:boolean判断,异常判断

//授权的权限控制

//====布尔值判断

//功能权限

if(subject.isPermitted("staff")){

//必须拥有staff功能权限才能执行代码

System.out.println("我是一段代码。。。。。");

}

//角色权限

if(subject.hasRole("weihu")){

//必须拥有staff功能权限才能执行代码

System.out.println("我是一段代码。。。。。");

}

//====异常判断

//功能权限

try {

subject.checkPermission("staff");

//有权限

} catch (AuthorizationException e) {

// 没权限

e.printStackTrace();

}

//角色权限

try {

subject.checkRole("weihu");

//有权限

} catch (AuthorizationException e) {

// 没权限

e.printStackTrace();

}

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

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

相关文章

python运行外部程序_在Python中运行外部程序(可执行文件)?

在Python中运行外部程序(可执行文件)? 我刚刚开始研究Python,我一直在尝试从Python运行外部可执行文件。 我有一个用Fortran编写的程序的可执行文件。 假设可执行文件的名称是flow.exe。 我的可执行文件位于C:\Documents and Sett…

word里画的流程图怎么全选_怎么用word画流程图

word是我们最常用的文档编辑软件了,他除了可以制作文字图片的编辑之外,其实它还可以轻松制作出一些简单的流程图,那么又是如何操作的呢?让我们一起看下去吧!一、word画流程图虽说word不是专门使用来画流程图的&#xf…

python中值滤波去除椒盐噪声_Python实现图像去噪方式(中值去噪和均值去噪)

实现对图像进行简单的高斯去噪和椒盐去噪。 代码如下: import numpy as np from PIL import Image import matplotlib.pyplot as plt import random import scipy.misc import scipy.signal import scipy.ndimage from matplotlib.font_manager import FontProperti…

二次扩增产物条带弥散_PCR实验操作常见解决方法

1. cDNA产量的很低可能的原因:*RNA模板质量低*对mRNA浓度估计过高*反应体系中存在反转录酶抑制剂或反转录酶量不足*同位素磷32过期*反应体积过大,不应超过50μl2. 扩增产物在电泳分析时没有条带或条带很浅*常见的原因在于您的反应体系是PCR的反应体系而不…

java 关注公众号没有调接口_深入理解Java继承、封装、多态的实现原理

点击关注上方“Java技术江湖”,设为“置顶或星标”,第一时间送达技术干货。作者:黄小斜文章来源:微信公众号【Java技术江湖】目录从JVM结构开始谈多态JVM 的结构Java 的方法调用方式常量池(constant pool)图 2. 常量池各表的关系方…

wordpress友联_Wordpress 友情链接页面终极版 – Fatesinger

之前写过一篇带头像的友情链接页面,当时有朋友说怎么能支持分类,我让他仿照以前不带头像的旧方法修改下,以前旧方法是用SQL语句获取的分类,其实完全可以通过Wordpress 自带的函数来实现,原因你懂的。而且还有一部分朋友…

系统相机裁剪比例_如何正确设置相机:6个最常见的错误,你还在犯错吗?

你是否在摄影中出现这样的设置错误?查看相机的这6个设置,并按照以下自定义提示操作,以增强照片质量并提高专业摄影水平。1.白平衡绝大多数照片是在自动白平衡模式下拍摄的。这是一个简单的选择,在大多数情况下是合理的,但这不是1…

java 正则首位8或者9的8位数字_正则简单手机号和邮箱练习

正则表达式的组成规则在java中,正则表达式的编译是类java.util.regex.Pattern正则表达式的构造摘要1.字符x包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号的普通字符。比如之前的例子可以将[1-9][0-9]{4,12}改成1[0-9]{4,12}或者a[0-9]{4,12}此时就…

inputstreamreader 编码不完整_素材编码对剪辑效率的影响

这次说说在项目的剪辑阶段关于编码的一些注意事项。压缩编码会拖慢剪辑速度大多数中低端摄像机都使用其于时间压缩的编码来进行录制,也称为Long GOP压缩。这种编码对于缩小文件体积很有用,但是计算机不得不耗费更多的性能来进行剪辑。简单来讲&#xff0…

python定义三维数组不指定长度_插值/调整三维数组大小

ndimage.zoom 这可能是最好的方法,zoom method正是为这类任务而设计的。from scipy.ndimage import zoom new_array zoom(array, (0.5, 0.5, 2)) 按指定的因子更改每个维度中的大小。如果数组的原始形状是(40, 50, 60),那么新的数组将是(20, 25, 120)。…

django默认缓存是多大_Django之缓存 - osc_fd8vzgsi的个人空间 - OSCHINA - 中文开源技术交流社区...

一、缓存由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中&#xff0c…

mac xampp连接mysql数据库_请问在mac下xampp无法读取mysql的数据

报错信息如下:Fatal error: Uncaught Error: Call to undefined function mysql_connect() in /Applications/XAMPP/xamppfiles/htdocs/test/db.class.php:51 Stack trace: #0 /Applications/XAMPP/xamppfiles/htdocs/test/db.class.php(31): ms_new_mysql->conn…

python调用shell脚本的参数_使用python执行shell脚本 并动态传参 及subprocess的使用详解

最近工作需求中 有遇到这个情况 在web端获取配置文件内容 及 往shell 脚本中动态传入参数执行shell脚本这个有多种方法 最后还是选择了subprocess这个python标准库subprocess这个模块可以非常方便的启动一个子进程,并且控制其输入和输出Class Popen(args&#xff0c…

mysql replication延迟_深入mysql主从复制延迟问题的详解

面试mysqldba的时候遇到一个题:描述msyql replication 机制的实现原理,如何在不停掉mysql主库的情况下,恢复数据不一致的slave的数据库节点?MySQL的复制(replication)是一个异步的复制,从一个MySQL instace(称之为Mast…

gitlab 安装gitlabrunner 无法连接tiller_谈一谈GitLab Runner是个什么东东?

概念我从官网搜了一下,GitLab Runner 是一个开源项目, 它用来运行你定制的任务(jobs)并把结果返回给 GitLab。 GitLab Runner 配合GitLab CI(GitLab 内置的持续集成服务) 协调完成任务。gitlab想要了解 GitLab Runner之前,我们先要知道或者说…

mysql百万级去重_mysql优化小技巧之去除重复项(百万级数据)

mysql优化小技巧之去除重复项(百万级数据)发布时间:2018-06-11 11:54,浏览次数:482, 标签:mysql说到这个去重,脑仁不禁得一疼,尤其是出具量比较大的时候。毕竟咱不是专业的DB,所以嘞,只能自己弄…

高中必背88个数学公式_高中数学:必修+选修全部知识点精华!附高考必背203个公式...

童鞋们好呀,2021高考将至,但是很多高三同学还是处于一个很不好的状态感觉每一科都很紧张,学校学习的节奏快,自己基础又没跟上,还要补基础总觉得后一步还没跟上,就要迈下一步了,不知道从哪学&…

mysql获取逻辑日志_Mysql 逻辑架构图及日志系统

1、Mysql逻辑架构图场景一:一条SQL语句如何执行?如图显示一条SQL语句的执行过程:执行器的执行流程:2、Mysql日志系统说到日志系统,需要了解几个概念:creash-safe、redo log、binlog、WAL技术。Redo log用于…

专科学python真的不好_专科生转行做python运维靠谱吗?

看了下问题,专科做运维,的确在找工作的时候没有优势,but,这并不代表运维行业对学历要求高,而是你找别的工作,专科也没有什么优势。知乎水平动辄985,专科就显得那么的微不足道。但没有竞争力不太表找不到相关…

mysql实例详解_MySQL 多实例详解

MySQL多实例配置方法1、单一配置文件2、多配置文件。二、实战步骤:1、同步时间2、准备mysql依赖包3、环境准备3.1添加mysql用户3.2编译安装mysql4、安装MySQL多实例5、准备配置文件6、启动MySQL7、登录mysql与一些安全措施8、关闭MySQL9,、提供简易管理脚本10、为my…