Guns第十二节shiro和权限系统

大家设想一下,假如说一个后台管理系统,我们是多用户的,很多人登陆,我们有这样一个需求,每个人登陆上去根据他的角色不同,登陆上去之后看到的内容不同,那么这样的需要我们怎么做呢?这个时候就需要集成一个权限系统,这个系统可以把不同的用户根据角色区分出来,然后不同的角色对应不同的资源,不同的资源就是不用的权限。

那么说一下guns权限系统的介绍,guns权限系统主要是分为三个模块,第一个模块是用户,第二个模块是角色,第三个模块是菜单(就是权限或者是资源)。

之前介绍过,用户对应角色,角色对应资源,权限系统是这样来协调完成的

为了完成权限系统而用到的安全框架。它是一个安全框架。利用shiro我们可以把普通用户看成一个普通资源的一个需求,shiro管理之后,shiro把这个用户,角色,资源都存起来,我每次登陆的时候请求一次shiro,shiro就知道这些用户,这些角色都有哪些资源

如何集成shiro

Guns第十二节shiro和权限系统_第1张图片

,大家看到它的maven依赖,在这个pom里面,

Guns第十二节shiro和权限系统_第2张图片首先它引入了shiro的核心包shiro-core,shiro-spring是shiro和spring交互的包,shiro-ehcache,因为我们一些的session,cookie之类的东西,我们的一些session是存在缓存2里面的,我们有一个shiro-ehcache这样的一个依赖,然后还有一个ehcache的核心包,然后大家可以看到这里面有一些排除依赖,Guns第十二节shiro和权限系统_第3张图片,这个依赖会跟我们项目中的依赖会冲突,所以说进行了一个排除,比如说这个shiro包里面,它这里面有slf4j-api的这个包,可能这个commons-io里面也有这个包,然后这两个包版本都不一样,但是我需要的是commons-io里面集成的slf4j-api这个包,那么我就需要在shiro里面把slf4j-api这个包排除掉,exclusion就是排除的意思。

 

我们的一些session是存到缓存里面的,所以我们有这样一个依赖Guns第十二节shiro和权限系统_第4张图片,然后还有一个ehcache的核心包,排除依赖,就是说会跟我们项目中引用的依赖冲突Guns第十二节shiro和权限系统_第5张图片,所以说进行了一个排除,大家如何看哪些包有冲突呢?这个idea有一个工具Guns第十二节shiro和权限系统_第6张图片Guns第十二节shiro和权限系统_第7张图片依赖图,当有冲突的时候,他会显示红色的一些线,我这个里面是把冲突排除掉了,现在是没有冲突的,加入有一些冲突的包需要引用的话,我们需要用红色的线,现在是一个完整的,没有冲突的。

 

下一步就是配置shiro,shiro的配置在ShiroConfig里面,首先引用的是DefaultWebSecurityManager,这个是shiro的核心配置Guns第十二节shiro和权限系统_第8张图片

这是一个主要的类,但是这里面配置了shiro需要用到的其他的bean,

这个是spring基于config的用法,当大家往这个里面写参数的时候,这个参数就代表了一个bean,当我们执行它的时候Guns第十二节shiro和权限系统_第9张图片,spring会加载这些bean,加载完之后呢,它会把bean传递给参数,然后再进行相应的设置,我们加载它的时候,spring相当于加载了这三个bean了,这三个bean可以在下边找到,比如说下面这个也一样,当它加载cookieRememberMeManager的时候,他需要传递一个参数,然后这个参数也是一个bean,并且在加载这个bean的时候,先加载这个bean,大家可以看到有这个beanGuns第十二节shiro和权限系统_第10张图片这个bean加载好了之后,会把这个bean作为参数传到这个bean里面Guns第十二节shiro和权限系统_第11张图片,从而再加载CookieRememberManager

接下来 spring session管理器,

Guns第十二节shiro和权限系统_第12张图片如果我们的项目在多台机器上面,需要session共享的话,就要把这个开关打开,我们的就是用到了这个bean

Guns第十二节shiro和权限系统_第13张图片

 

单机环境,多机环境的配置有一个文档

Guns第十二节shiro和权限系统_第14张图片

需要把相应的dependency打开。然后把yml里面设成true,然后多级环境需要把session存到redis里面,所以说需要开启一个redis,然后配置这个host,port,password。这个类里面注释打开,

@ConditionalOnProperty判断yml里面有没有这个属性和这个值,从而会激活这段代码,或者这段代码Guns第十二节shiro和权限系统_第15张图片

以上是session管理器的配置,

 

缓存管理器的配置Guns第十二节shiro和权限系统_第16张图片,它是需要从这个bean里面获取一个缓存的对象,然后放到ehcachemanager里面,然后返回一个bean

Guns第十二节shiro和权限系统_第17张图片这里定义了Realm这样的一个bean,

 

rememeberMe管理器

Guns第十二节shiro和权限系统_第18张图片rememberMe就是记住密码的意思。就是记住密码的功能。它会把用户的一些信息保存到cookie里面。所以说你在次登陆的时候是不需要登陆账号密码的。这个rememberMe这个功能,它有一个manager.setCipherKey,这个就是是一个密钥,因为这个cookie是经过加密的,不经过加密的话,别人可以轻易的把它破解,可以轻易的从这个电脑里面获取它的信息。所以说它是经过这样一个加密的,然后它的加密需要生成一个密钥,需要把这个密钥进行一个编码,这个编码就是用的Base64编码Guns第十二节shiro和权限系统_第19张图片。我们的这个密钥是guns这四个字符,shiro记住密码采用的是AES加密,AES的key需要十六位,这个方法呢生成了一个16位的key。

生成一下是,为什么需要16位,这个是shiro的一个配置。所以说我们需要把这个东西,经过base64位加密,才可以使用这个记住密码的功能。如果不进行这一步的话,不进行编码的话,我们直接在这里输入Guns第十二节shiro和权限系统_第20张图片,大家可以看到它的参数是一个byte,我们直接输入'guns'.getBytes(),这个shiro rememberMe就不能开启,它默认有这样一个配置,所以我们需要遵守一下Guns第十二节shiro和权限系统_第21张图片,可以做一个简单的测试,,它的编码打出这个getBytes之后,它是补了0了,它可以数一下,,它应该是16位,16位的编码,它会补零。因为如果不补的话,它生成的这个密钥是4位的,或者说你是生成的别的密钥的话,它加个1,它就是5位的,我们需要把她变成16位,所以说我们需要把它进行一个base64的转化,base64会把缺的这些位补上。它就是满足了AES加密需要16为的这个标准。所以说我们的shiro rememberMe才可以生效。

记住密码cookie的一些设置

Guns第十二节shiro和权限系统_第22张图片,默认设置了7天,然后设置了HttpOnly,这个HttpOnly主要是为了防止xss攻击,这里为true的话,就是不能通过js获取当前浏览器的cookie,必须通过http传输的方式获取这个cookie。

 

下面是一个shiro过滤器链的配置。

Guns第十二节shiro和权限系统_第23张图片

,比如说我们输入localhost,他会跳转到哪一个页面,

,我们点登陆的时候,把账号和密码交给shiro,shiro判断,假如说验证通过的话,它会跳转到哪个url,

,当我们访问一个资源,我们登陆 进去,没有这个资源的权限,我们会跳转到一个页面。

 

下面是配置一个shiro拦截器链的一个配置

Guns第十二节shiro和权限系统_第24张图片

大家可以看到它是一个LinkedHashMap,这个LinkedHashMap是有顺序的,而HashMap是没有顺序的,但是拦截器必须保证一定的顺序,大家可以看到这个map里面放了很多的这个映射,shiro的这些映射呢就是相应的这些url,这些url对应的访问权限,当一个用户访问这个的时候,它的访问权限是是除了不需要验证的那些路径这两都需要认证的,他们有一个区别,authc不接受rememberMe验证,只接受账号密码验证。

假如说加一个页面它不需要访问的话就可以登陆,可以在最上面加上

关于代理方法的

Guns第十二节shiro和权限系统_第25张图片

MethodInvokingFactoryBean这个bean的作用就是当DefaultWebSecurityManager这个bean初始化之后,它会自动注入MethodInvokingFactoryBean的参数里面,当它完成之后,它会执行这一段代码,就相当于spring帮我们执行了这个类,然后调用了这个方法,就相当于,我们的这个方法被spring代理执行了,帮我们把方法的参数设置成它,大家可以看到这个这个是设置方法名,这个是设置方法参数,关于代理方法的一个说明。

 

 

 

当我们shiro和spring结合的时候,就可以起到一个什么效果呢?比如说实现了Initializable接口 shirobean 例如ShiroDbRealm继承了实现了接口,是shiro的一个接口,这个配置的意思就是当我们初始化ShiroDbRealm之后,spring会帮我们自动调用这个接口的方法,这个init()方法。等同的效果就是spring帮我们执行了这句话。

大家可能有疑问,问什么我们不自己写一个init呢?为什么要用spring来给我们执行呢?

因为还有很多的bean实现了这个接口,不是光它一个类实现了这个接口,那么这个功能呢就是帮助所有实现了这个Initializable接口的类都会初始化之后执行这个Initializable里面的方法。

相应的Destroyable这个接口呢,shiro销毁bean的时候spring自动给我们调用destroy方法,例如这个类里面实现了destroyable()方法,那么当这个相应的bean销毁的时候呢,spring就会帮我们自动调用shiro的Destroyable接口。

 

下面介绍一下

Guns第十二节shiro和权限系统_第26张图片大家可以看到他是一个切面,这个切面的作用就是开启了aop方式的权限检查,这个是shiro自带的一个aop方法。大家可以看到我们的权限检查是通过permission进行权限检查,是我们自己实现的一个aop。两个aop我们都可以使用。有两种方式,另一种呢,我记得我写过一个例子,另一种就是shiro自带的权限检查,这两个效果是相同的,

如果你用我们自己实现的呢,就这样写,如果你用shiro提供的就这样写,这样写的意思就是这段代码需要这个权限。我们这个权限是以url形式来区别这个权限的,所以说我们请求的url跟这个权限是一致的,所以说我们的aop里面就省略了权限的名称,我们直接在permission里面写他需要的角色就行了。

这段代码就是实现了这个切面,

Guns第十二节shiro和权限系统_第27张图片

然后开启了shiro自带的权限检查的注解。

 

引入pom,然后写上这些配置,shiro就集成进来了。

 

然后还有一个很重要的,这个也是非常重要的,这个realm其实就是一个数据库和shiro交互的接口,我们把数据从数据库里面夺取出来,然后告诉shiro,告诉shiro哪些东西呢?比如说第一个接口Guns第十二节shiro和权限系统_第28张图片,就是把相关的账号密码告诉shiro我们的用户的密码应该是多少,大家都知道登陆的时候,账号密码走的都是shiro的过滤器,shiro会收集到这个账号密码,然后存储到shiro的内存里面,然后我们只需要读取到用户名和对应的密码是多少,我们告诉shiro正确的密码是多少。

封装了用户登录的账号密码。Shirofactory其实就是一系列的dao操作,封装到factory类里面shiro相关的读取数据库的操作。然后下面可以看到shiroFactory.userz(),Guns第十二节shiro和权限系统_第29张图片就是通过账号获取数据库里面对应的user对象的实体。如果user不存在,我们就直接在这抛出一个一场。然后shiro就会反馈这个异常账号或者密码错误。

 

这一步之后又通过user对象,获取到shirouser,相当于shiro可识别的对象,shirouser里面包含这些东西

Guns第十二节shiro和权限系统_第30张图片

获取到这些东西之后Guns第十二节shiro和权限系统_第31张图片,我们生成一个这样的东西,调用shiroFactory里面的info方法,传递了shiroUser,user,super.getNmae(),getName()这里是一个realmName(),Guns第十二节shiro和权限系统_第32张图片这里生成SimpleAuthenticationInfo需要一个realmName(),realmName就是AuthorizingRealm的name,super.getNmae()是类的一个属性,info就是把我们上面相关查到的一些东西返回一个SimpleAuthenticationInfo对象。这个对象就是跟shiro进行一个交互,然后这个里面Guns第十二节shiro和权限系统_第33张图片呢和shiro交互的时候判断密码是否正确。所以说这一步就是告诉shiro真正的密码是什么。这个密码就在user里面,然后通过user告诉它。你们可以看到这个密码加盐处理,然后进行md5加密,然后shiro下一步的工作就是找到这个登陆时的账号和密码,和它对比一下是否正确,正确的话执行下一步,登陆成功。如果不正确的话,就会返回密码错误。

 

 

下面这个方法就是数据库和shiro进行的第二次交互,这一次交互主要时为了告诉shiro这个用户有哪些角色,哪些权限。Guns第十二节shiro和权限系统_第34张图片当我们使用来控制资源的权限的时候,那么shiro就知道这个用户有哪些角色和哪些权限。然后通过拦截注解判断它是否该访问这个资源,从而达到权限管理的目的。这样shiro才能控制权限的管理。然后通过这个注解判断它是否访问这个资源,从而达到权限管理的目的。在查出有哪些角色和哪些权限之后,就生成一个SimpleAuthorizationInfo对象,这个对象就是返回给shiro,告诉shiro我们的权限和角色都包含在info里面了。

 

 

Guns第十二节shiro和权限系统_第35张图片我们设置的时md5的加密方式,通过md5的方式来加密密码,通过盐来混淆md5的值。

 

aop切面权限,比较简单就是写了一个切面Guns第十二节shiro和权限系统_第36张图片,他是拦截permission这个注解,所有的带permission的这个注解都会被PermissionAop拦截到。大家看一下拦截的业务,首先获取他的方法,然后通过方法获取了他的注解,然后获取这个注解的value,注解的value就是注解的参数。

大家可以看到这个注解的参数是一个字符串数组,这个value就是角色的意思,当我们用permission的时候,如果加上字符串就是,下面的资源只有admin才能访问

Guns第十二节shiro和权限系统_第37张图片

这个permissionaop有两个参数来决定我们时如何检查的,

Guns第十二节shiro和权限系统_第38张图片,如果没有参数,我们就遍历检查一下它有没有这个权限。大家可以看到这个checkAll()方法。它是如何判断的,

Guns第十二节shiro和权限系统_第39张图片

首先,获取当前登录的用户,当前登录的用户里面就有所有的角色,判断user存不存在,

通过shirokit.hasPermission()来判断当前登录用户是否有这个资源,Guns第十二节shiro和权限系统_第40张图片,通过验证资源就去调用ShiroKit里面的方法,ShiroKit又会调用shiro原生的getSubject()方法。这个permission就可以检查到这个资源,以上时checkAll的实现。

这一步就是检查当前用户有没有资源的权限Guns第十二节shiro和权限系统_第41张图片。如果有就可以继续访问,如果没有就抛出异常,下面这个Guns第十二节shiro和权限系统_第42张图片就是当permisssion有参数的话,它就执行下面的逻辑,有参数,它就会验证当前用户是否有这个角色,

Guns第十二节shiro和权限系统_第43张图片

Guns第十二节shiro和权限系统_第44张图片首先获取到shiroUser,如果当前用户是空的话,那么直接return false,如果不为空,则获取一个permissions,然后通过这个方法Guns第十二节shiro和权限系统_第45张图片,如果有一个角色包含在这个用户里面,join就是拼接了一下,就是把这个数组,通过用逗号拼接成一个字符串,然后通过它Guns第十二节shiro和权限系统_第46张图片当前用户是否拥有该角色,如果有这个角色的话,我们把返回结果返回true,循环遍历完之后,如果没有这个角色,hasAnyRole依然返回false。

然后通过判断Guns第十二节shiro和权限系统_第47张图片,如果有这个角色,就继续执行,继续执行就是放过这个aop,继续执行下面的代码,Guns第十二节shiro和权限系统_第48张图片就可以走这个业务了。如果没有就抛出异常Guns第十二节shiro和权限系统_第49张图片。以上就是我们的权限检查。

 

 

 

shiro可以直接在beetl里面调用,前端的按钮通过shirokit的方法来判断有没有这个权限,有这个权限,就显示这个button,没有这个权限就不显示这个button。beetl把shiro关键字绑定成一个工具 类,工具类就是shiro工具类。

Guns第十二节shiro和权限系统_第50张图片

为一个业务增加权限控制的话,需要哪些步骤,

首先通过管理系统添加相应的资源

Guns第十二节shiro和权限系统_第51张图片

然后为角色赋予相应的权限,

Guns第十二节shiro和权限系统_第52张图片

然后用户要配置这个角色

Guns第十二节shiro和权限系统_第53张图片,这个角色就会拥有相应的权限。

拥有了权限之后,我们可以在这个controller里面加上权限控制,不加的话,用户登录进来,所有的资源都可以访问。

Guns第十二节shiro和权限系统_第54张图片

加上之后@Permission之后,通过shiro管理起来了,通过shiro的一个检查,它会检查当前用户有没有这个资源,有的话才让你访问,没有的话,跳转到错误页面。

Guns第十二节shiro和权限系统_第55张图片

你可能感兴趣的:(Guns)