BOS物流管理系统-第八天

BOS物流管理系统-第八天-权限系统设计—Shiro

回顾:

两大块业务:基础设置模块,业务派单模块。

两天:权限控制。

拦截器大的范围的、纯登录级别(认证级别)大颗粒的权限控制的一种技术。

Shiro:多种权限方式控制,可以细粒度控制,可以配置的。

主要内容:

  1. 权限控制的方式(URL级别-粗粒度,方法级别-细粒度,页面级别-自定义标签、数据级别)
  2. 权限系统的数据表设计(有原则)
  3. Shiro的系统集成(Spring整合)
  4. Apache Shiro框架的运行原理
  5. 自定义Shiro Realm,实现认证功能(基于数据库)
  6. 自定义Shiro Realm,实现授权功能(代码中伪实现,不连接数据库)

学习目标:

  • 权限控制的方式(会选择不同的方式)
  • 表的设计
  • Shiro的认证功能(登录)
  • Shiro的授权功能(url和方法)
  1. 权限管理系统分析设计

    1. 权限控制的方式

从类别上分,有两大类:

  • 认证:你是谁?--识别用户身份。
  • 授权:你能做什么?--限制用户使用的功能。

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

  • URL级别-粗粒度
  • 方法级别-细粒度
  • 页面级别-自定义标签
  • 数据级别-最细化的
  1. URL级别的权限控制-粗粒度

(url:统一资源定位符)

场景:要访问一个链接》路径:

http://localhost/mavenbos25/user_login.action

http://localhost/mavenbos25/staff_save.action

问题:如何控制用户是否有权限访问该路径?

方案:

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

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

过滤器代码:

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

设计表:

用户表:

Id name

101 rose

权限用户对应关系表:

Id 权限名称 权限路径 用户id外键

1 用户登录 user_login.action 101

2 员工保存 staff_save.action 101

判断逻辑:拿当前session中的用户id,到数据库权限数据,判断,不为空,通过,有权限;

原理:

BOS物流管理系统-第八天

为什么说url级别控制是粗粒度的?

因为 url级别控制,每次请求过程中只控制一次 ,相比方法级别权限控制 是粗粒度的 !

URL级别权限控制,基于Filter实现

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

以前:spring的aop的切面编程。

场景:url是粗粒度的,比如保存一个员工url操作,里面可以有多个方法,先查询员工存在不存在,。。。。。。。。保存。。。。很多方法。可能某个方法,当前户没有操作权限。此时url就过滤就做不到了。

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

如何进行权限判断?

如果你方法权限控制要在action层控制,可以直接在数据库中配置aciton的名字和用户关系。

但,如果,你配置service配置,配置具体的方法名和用户关系。

BOS物流管理系统-第八天

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

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

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

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

场景:

Rose进来,不允许她保存派送员的操作。如何在页面限制她呢?

方案:将按钮"隐藏"起来,不显示。
<s:if>权限判断

如果有权限

<input type="button" value="保存"/>

</s:if>

提示:代码是服务端代码,客户端根本就没有按钮元素。

BOS物流管理系统-第八天

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

  1. 数据级别的权限控制

场景:Rose不能查询一张表中的A类数据,而Jack也不能查询B类的数据。情况:不同的人对一张表的数据有不同的权限。

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

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

BOS物流管理系统-第八天

  1. 权限管理数据表的设计

  • 资源:用户要访问的目标,通常是服务中的程序或文件
  • 权限:用户具有访问某资源的能力
  • 角色:权限的集合,为了方便给用户授权。
  • 用户:访问系统的'人'。

BOS物流管理系统-第八天

用户、权限、角色之间的关系。

思考:需要几张表:

表对象实体:

  • 用户(User)表:访问系统的用户,比如用户登录要用
  • 权限(Function)表:系统某个功能允许访问而对应的权限
  • 角色(Role)表:角色是权限的集合(权限组),方便用户授权。

如果没有角色,如何授权?

甲员工,客服人员,授权:记录通知单、快速录单、调度。就在权限表中一个一个设置。3条对应。

乙员工,客服人员,还需要设置3个权限。

解决:设置一个角色,角色中拥有这3个权限。再来新员工丙,只需要将这个一个角色给他就可以了。因为角色中拥有这三个权限。

为了简化权限设计,通常我们要求,用户要有权限,必须通过角色来配置,间接的获得权限。

BOS物流管理系统-第八天

表对象之间的关系:

  • 用户和角色关系表:一个用户对应N个角色,一个角色可以授予N个用户—》多对多关系
  • 角色和权限关系表:一个角色包含N个权限,一个权限可以属于N个角色—》多对多关系

完整的权限相关表:

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

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

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

课前资料:(回去自己画一下)

BOS物流管理系统-第八天

角色和权限表都具有的共有字段:

  • Id:编号
  • Name:名称,中文,用于显示识别,如管理员
  • Code:编码名称,英文,编程时使用,如admin
  • Description:描述,用户描述该角色或权限的用途

权限表的设计:

BOS物流管理系统-第八天

(下面几个字段主要用来生成动态菜单)

  • 路径:每一个url(struts2:xxx.action)都设置一个权限(路径的字段)。
  • 是否生成菜单

xxx.action是否都是菜单?

不是,大部分的action,是功能性aciton,比如staff_save.action。

菜单的action:page_staff_.action

所以,是否生成菜单,来识别url是否要在菜单上展示。

因为下一次课程中的菜单要生成动态菜单。现在的菜单json写死。

到时候,我们直接在数据库查询路径作为菜单。查询的时候,加一个条件,是否生成菜单:1。,在页面展示。

  • 优先级:排序,用来控制菜单顺序。Order by 优先级字段。
  • 父权限编号:树形结构设计,该菜单在哪个父菜单下面。

    第一级,父节点0,(如基础数据)

    第二级:子节点:第一级别id(如取派员设置)

    两级的菜单。(扩展出来N级)

BOS物流管理系统-第八天

  1. 数据库设计和实体类生成

    1. 数据库建模设计和数据表的生成

BOS物流管理系统-第八天

BOS物流管理系统-第八天

执行生成的脚本,生成数据库对象:

BOS物流管理系统-第八天

t_auth_function:权限表

t_auth_role:角色表

t_auth_role_function:角色权限关系表

t_auth_user_role:用户角色关系表

t_user:用户表

扩展:

脚本执行时控制台的提示信息:

BOS物流管理系统-第八天

  1. 反转生成实体类(hibernate3-maven-plugin插件)

5张表3个实体。

修改src/main/resources/reveng.xml

<schema-selection
match-table="T_AUTH_.*"
match-schema="MYBOS"/>

<table
name="T_AUTH_FUNCTION"
schema="MYBOS"

class="cn.itcast.bos.domain.auth.Function">

<primary-key>

<generator
class="uuid"></generator>

</primary-key>

</table>

<table
name="T_AUTH_ROLE"
schema="MYBOS"

class="cn.itcast.bos.domain.auth.Role">

<primary-key>

<generator
class="uuid"></generator>

</primary-key>

</table>

执行:

BOS物流管理系统-第八天

BOS物流管理系统-第八天

【补充】

如果是Oracle11g的数据库,反转可能会报错,

解决方案1:需要在hibernate.properties中配置Oracle方言。

hibernate.dialect=org.hibernate.dialect.Oracle10Dialect

hibernate.connection.driver_class=oracle.jdbc.driver.OracleDriver

hibernate.connection.url=jdbc:oracle:thin:@localhost:1521:xe

hibernate.connection.username=mybos

hibernate.connection.password=mybos

如果还不行,则需修改反转配置文件reveng.xml的内容:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-reverse-engineering SYSTEM "http://hibernate.sourceforge.net/hibernate-reverse-engineering-3.0.dtd" >

<hibernate-reverse-engineering>

<table-filter match-schema="AMES" match-name="ATTACH"/>

<table-filter match-schema="AMES" match-name="ATTACH_GROUP"/>

......

</hibernate-reverse-engineering>

注意:match-schema就是用户名,match-name就是表名,注意大小写的区分

查看生成的实体内容。

将三个对象都复制到工程中:

BOS物流管理系统-第八天

注意User覆盖前要将原来user中的东东复制到新的User中。

BOS物流管理系统-第八天

  1. Apache Shiro权限控制

  1. Shiro简介和环境搭建

在企业中一般使用什么技术方案进行权限控制呢?

一般两种方式:

  • 自定义一个权限框架

实现方式:URL级别 Filter实现,方法级别 自定义注解 Spring AOP 反射实现, 页面显示 自定义标签实现

  • 使用已有的权限控制系统
  • Spring Security 安全框架

BOS物流管理系统-第八天

BOS物流管理系统-第八天

缺点:使用复杂、不够灵活(必须依赖于spring), Spring 官方项目 使用Apache Shiro 进行权限控制

  • Apache Shiro 比较新 ,很多企业了解到shiro 因为 spring side 项目

BOS物流管理系统-第八天

什么是Apache Shiro ?

BOS物流管理系统-第八天

BOS物流管理系统-第八天

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

官网:

http://shiro.apache.org/

BOS物流管理系统-第八天

BOS物流管理系统-第八天

Authentication: 认证 --- 用户登录

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

区别:

皇宫后宫三千佳丽。

认证:

要得到佳丽,认证。登录到后宫(进入后宫),用户认证,皇帝输入用户名和密码进入后宫。随意找佳丽。

问题:如果有后宫令牌(可以登录进去),里面的佳丽没法限制。

授权:

不管有没有认证身份,不管谁进入到后宫,每个佳丽的身上都有一把锁,你必须有钥匙才能才行。

通过引入Maven坐标导入shiro:

<shiro.version>1.2.4</shiro.version>

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-all</artifactId>

<version>${shiro.version}</version>

</dependency>

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

BOS物流管理系统-第八天

官方推荐根据需要单独导入jar。

  1. Shiro基本原理分析

可参考:

BOS物流管理系统-第八天

BOS物流管理系统-第八天

Shiro的框架的体系结构:

BOS物流管理系统-第八天

Shiro权限控制流程的原理:

BOS物流管理系统-第八天

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

BOS物流管理系统-第八天

官方说法:

BOS物流管理系统-第八天

BOS物流管理系统-第八天

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

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

BOS物流管理系统-第八天

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

小结:如何开发Shiro?

通过以上分析,程序员使用shiro只需要

1、 应用程序代码中调用Subject(用户)的API ;

2、 定义编写Realm连接安全数据(获取权限数据)

  1. URL级别的权限控制—Shiro与Spring整合

Url的级别控制,有两种方式:

认证和授权。

User_login.action---->:

  1. 登录:认证,可以拥有所有的action的访问权----shiro的配置+编码
  2. 授权:授权,更具体化了。谁可以访问哪个action。
  1. 配置

配置过滤器web.xml:

<!-- shiro权限过滤器 -->

<filter>

<!-- 这里的 filter-name 要和 spring 的 applicationContext-shiro.xml 里的 org.apache.shiro.spring.web.ShiroFilterFactoryBean

的 bean name 相同 -->

<filter-name>shiroSecurityFilter</filter-name>

<!-- spring的代理过滤器类:以前的过滤器 -->    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

<init-param>

<param-name>targetFilterLifecycle</param-name>

<param-value>true</param-value>

</init-param>

</filter>

<filter-mapping>

<filter-name>shiroSecurityFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

注意:要放在struts的前端控制器之前配置!但放在openEntitymanage之后。

以前:

web的过滤器谁初始化的?

Servlet容器初始化的。

现在:

org.springframework.web.filter.DelegatingFilterProxy提供了让过滤器的具体对象等交给spring初始化,它本身并不是具体的干活的filter。

filter-name

必须和一会要配置spring bean的名字一致,才能自动找到。

这个Filter 是 spring提供 ,DelegationFilterProxy 是代理Filter

(会自动找 和<filter-name> 同名的 <bean> 对象 )

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

<!-- shiro权限控制过滤器bean -->

<bean
id="shiroSecurityFilter"
class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">

<!-- shiro
的核心安全接口 -->

<property
name="securityManager"
ref="securityManager"
/>

<!-- 要求登录时的链接 -->

<property
name="loginUrl"
value="/login.jsp"
/>

<!-- 登陆成功后要跳转的连接 -->

<property
name="successUrl"
value="/index.jsp"
/>

<!-- 未授权时要跳转的连接,权限不足的跳转路径 -->

<property
name="unauthorizedUrl"
value="/unauthorized.jsp"
/>

<!-- shiro
连接约束配置(URL级别的权限控制),即URL和filter的关系,URL控制规则:路径=规则名 -->

<property
name="filterChainDefinitions">

<value>

/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

</value>

</property>

</bean>

<!-- shiro安全管理器 -->

<bean
id="securityManager"
class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">

<!-- 注入 Realm连接安全数据-->

</bean>

配置shiroFilter后,可以应用 10种过滤规则

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

BOS物流管理系统-第八天

详解见:

BOS物流管理系统-第八天

常用:

认证

  • anon不用认证(登录)就能访问(单词注意大小写)
  • authc: 需要认证(登录)才能使用,例如/admins/user/**=authc,没有参数

授权:

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

BOS物流管理系统-第八天

认证和授权是两码事.

Anon可以不登陆访问;authc必须登录才能访问。

假如登录上了,没有配置具体权限的url,可以访问。

但如果配置了具体权限的url(门有锁),就必须遵循这个权限了。

测试:

BOS物流管理系统-第八天

小结:通过配置就可以URL级别的权限控制。

经过测试,发现无法达到首页。

原因是:你现在的认证是你自己的认证。

你要想让配置规则有效,认证能进去,必须走shiro的认证。

操作:去掉strut2的认证。

关于通配符:

BOS物流管理系统-第八天

BOS物流管理系统-第八天

BOS物流管理系统-第八天

BOS物流管理系统-第八天

重新登录演示。

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

  • 传统登录逻辑:

用户输入用户名和密码 ---- 传递数据库查询 ---- 返回user ---- 判断如果user不为null, 登录成功, 将user加入session ----- 如果 user为null ,调回登录页面

  • Shiro实现登录逻辑

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

BOS物流管理系统-第八天

BOS物流管理系统-第八天

  1. 编写Shiro的认证登录逻辑

UserAction的login登录方法:

//shiro:登录逻辑

//获取认证对象(用户)包装对象

Subject subject = SecurityUtils.getSubject();

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

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

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

//认证过程...("自己认证")

//用户登录

try {

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

subject.login(authenticationToken);

// 成功,首页

return
SUCCESS;

} catch (AuthenticationException e) {

// 认证失败

e.printStackTrace();

// 登录页面

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

return
LOGIN;

}

重启服务测试:

控制台异常:

BOS物流管理系统-第八天

认证错误的异常。可以用来判断登录是否成功,进行页面调整逻辑。

BOS物流管理系统-第八天

说明了没有realm。

  1. 编写Realm,给SecurityManager提供。

BOS物流管理系统-第八天

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

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

我们的,dao:spring data jpa:

自定义realm:

BOS物流管理系统-第八天

BosRealm:

/**

* 实现认证和授权功能

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

* @author BoBo

*

*/

@Component("bosRealm")

public
class BosRealm extends AuthorizingRealm{

//注入用户Service

@Autowired

private UserService userService;

@Override

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

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

System.out.println("授权中。。。");

return
null;

}

@Override

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

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

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

System.out.println("开始认证了。。。。。");

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

UsernamePasswordToken upToken = (UsernamePasswordToken)token;

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

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

//判断用户是否存在

if(null==user){

//用户名不存在

return
null;//会自动抛出异常

}else{

//用户存在

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

:凭证(密码):密码校验:校验的动作交给shiro

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

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

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

}

}

}

ApplicatonContext.xml:

<!-- service需要spring扫描 -->

<context:component-scan
base-package="cn.itcast.bos.service,cn.itcast.bos.web,cn.itcast.bos.auth"
/>

<!-- shiro安全管理器 -->

<bean
id="securityManager"
class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">

<!-- 注入 Realm连接安全数据-->

<property
name="realm"
ref="bosRealm"></property>

</bean>

启动测试:

控制台:

用户不存在的异常:

BOS物流管理系统-第八天

BOS物流管理系统-第八天

密码错误的异常:

BOS物流管理系统-第八天

因此,action层可以根据异常的不同,来更加细化给用户的提示信息:

UserAction:

//shiro:登录逻辑

//获取认证对象(用户)包装对象

Subject subject = SecurityUtils.getSubject();

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

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

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

//认证过程...("自己认证")

try {

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

subject.login(authenticationToken);

// 成功,首页

return
SUCCESS;

} catch (UnknownAccountException e) {

//e.printStackTrace();

//提示用户名不存在

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

// 登录页面

return
LOGIN;

} catch (IncorrectCredentialsException e) {

//e.printStackTrace();

//提示密码不正确

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

// 登录页面

return
LOGIN;

}
catch (AuthenticationException e) {

// 认证失败

e.printStackTrace();

// 登录页面

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

return
LOGIN;

}

国际化文件:Messages.properties

略。。。。

  1. 用户认证(退出)

UserAction:

//登出

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

public String loginout(){

//传统的退出:销毁session的对象

//        ServletActionContext.getRequest().getSession().invalidate();

//shiro退出:调用自己的退出

Subject subject = SecurityUtils.getSubject();

subject.logout();

//跳转到登录页面

return
NONE;

}

提示:修改密码的逻辑也需要发生变化,得从shiro中获取当前登录用户了。

BOS物流管理系统-第八天

  1. 用户授权(权限)—自定义Ream

测试:

BOS物流管理系统-第八天

BOS物流管理系统-第八天

  1. 准备测试数据

<property
name="filterChainDefinitions">

<value>

/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

</value>

</property>

在t_auth_user、t_auth_role、t_auth_function、t_auth_role_function、t_auth_user_role 五张表插入一些测试数据:

直接导入课前资料中的脚本:

BOS物流管理系统-第八天

T_AUTH_FUNCTION:

BOS物流管理系统-第八天

T_AUTH_ROLE

BOS物流管理系统-第八天

T_AUTH_ROLE_FUNCTION

BOS物流管理系统-第八天

T_USER

BOS物流管理系统-第八天

新增的用户的密码都为1。

提示:密码可以通过自定义md5 函数生成

T_AUTH_USER_ROLE

BOS物流管理系统-第八天

select t.*, t.rowid from T_AUTH_FUNCTION t;

select t.*, t.rowid from T_AUTH_ROLE t;

select t.*, t.rowid from T_AUTH_ROLE_FUNCTION t;

select t.*, t.rowid from T_AUTH_USER_ROLE t;

select t.*, t.rowid from T_USER t;

  1. Realm的授权代码的实现

cn.itcast.bos.auth.realm.BosRealm:

//注入角色dao

@Autowired

private RoleDAO roleDAO;

//注入功能的dao

@Autowired

private FunctionDAO functionDAO;

@Override

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

//授权:回调方法

//如果返回null,说明没有权限,shiro会自动跳到<property name="unauthorizedUrl" value="/unauthorized.jsp" />

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

//给授权提供数据的

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

//每一次授权,都一定会调用这里的方法,来获取授权的信息

System.out.println("开始授权了。。。。。。。。。。。。。。。。。。。。。。。。。。。");

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

//强行给当前用户权限(写死)

SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();

//1)功能权限

//        authorizationInfo.addStringPermission("user");

//2)角色权限

//        authorizationInfo.addRole("operator");

//对于shiro的授权底层:就是两个集合,分别存放功能权限字符串和角色权限字符串,二者没有必然关系,各自对比各自的

//====获取当前用户的名字

//两种方式:

:工具类来获取(首长-)

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

:通过参数获取首长(推荐)

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

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

//得从数据库查询当前用户的授权情况

//分析得知:在系统中有两类用户:超管-普通用户

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

//超管:不管有没有配置角色或权限,它必须都有才行

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

List<Role> roleList = roleDAO.findAll();

for (Role role : roleList) {

authorizationInfo.addRole(role.getCode());

}

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

List<Function> functionList = functionDAO.findAll();

for (Function function : functionList) {

authorizationInfo.addStringPermission(function.getCode());

}

}else{

//普通用户:需要根据数据库数据来看有哪些角色和权限

//获取当前用户的拥有的角色

List<Role> roleList = roleDAO.findByUsers(user);

for (Role role : roleList) {

authorizationInfo.addRole(role.getCode());

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

Set<Function> functionList = role.getFunctions();

for (Function function : functionList) {

authorizationInfo.addStringPermission(function.getCode());

}

}

}

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

//        return null;//当前用户没有授权的权限

}

cn.itcast.bos.dao.auth.RoleDAO:

//角色的dao

public
interface RoleDAO extends JpaRepository<Role, String>{

/**

* 根据用户编号查询角色列表

* @param userId

* @return

*/

@Query("from Role r inner join fetch r.users u where u.id = ?")

public List<Role> findByUserId(String userId);

/**

* 根据用户查询角色列表

* @param user

* @return

*/

public List<Role> findByUsers(User user);

}

cn.itcast.bos.dao.auth. FunctionDAO

//功能dao

public
interface FunctionDAO extends JpaRepository<Function, String> {

}

  1. Shrio的运行原理

底层

先有认证,再授权

BOS物流管理系统-第八天

  1. 方法级别的权限控制

实现原理

  1. 定义注解(shiro已经提供了)
  2. Spring AOP动态代理配置(开启注解等功能,aop)
  3. 反射机制获取注解信息(shiro帮你做了)

我们要做的是什么?

  1. 配置开启shiro注解功能
  2. 在方法上加注解

开发步骤:

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

ApplicationContext.xml

<!-- 开启权限控制的注解功能并且配置aop -->

<!-- 后处理器:通过动态代理在某bean实例化的前增强。:自己去找权限注解 -->

<bean
id="lifecycleBeanPostProcessor"
class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

<!-- 切面自动代理:相当于以前的AOP标签配置

advisor:切面 advice:通知

-->

<bean
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"

depends-on="lifecycleBeanPostProcessor">

</bean>

<!-- Advisor切面配置:授权属性的切面 -->

<bean
class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">

<!-- 注入安全管理器 -->

<property
name="securityManager"
ref="securityManager"/>

</bean>

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

@RequiresAuthentication 需要用户登录

subject.isAuthenticated() 必须返回true

@ RequiresUser

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

"Remember Me"服务:

认证机制 基于 session

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

面试题: 认证和记忆的区别

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

@ RequiresRoles 需要角色

@RequiresPermissions 需要权限

可参考:

BOS物流管理系统-第八天

Action层的方法权限控制(在需要权限控制的方法上面加上述两个注解之一即可):

分区保存的action方法上

BOS物流管理系统-第八天

BOS物流管理系统-第八天

错误信息1:

BOS物流管理系统-第八天

原因分析:Spring的动态代理优先对接口进行代理,默认的接口Action中只有execute方法,没有listpage()方法。

BOS物流管理系统-第八天

解决方案:

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

<!-- 切面自动代理:相当于以前的AOP标签配置 -->

<bean
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"

depends-on="lifecycleBeanPostProcessor"
>

<!-- 设置aop的代理使用CGLIB代理 -->

<property
name="proxyTargetClass"
value="true"/>

</bean>

【扩展—终极方案】

BOS物流管理系统-第八天

错误信息2:

BOS物流管理系统-第八天

BOS物流管理系统-第八天

递归向上寻找泛型的类型。

//递归向上查找

Class
actionClass =this.getClass();

//向父类递归寻找泛型

while(true){

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

Type type = actionClass.getGenericSuperclass();

if(type
instanceof ParameterizedType){

//转换为参数化类型

ParameterizedType parameterizedType = (ParameterizedType) type;

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

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

//实例化模型对象

try {

model=modelClass.newInstance();

} catch (InstantiationException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

}

break;

}

//寻找父类

actionClass=actionClass.getSuperclass();

}

错误信息3:

BOS物流管理系统-第八天

原因分析:Service没有注入进来,注入的时候根据类型注入失败了。

因为基于SubareanAction生成的为SubareanAction$$$xxx,再初始化SubareanAction$$$xxx的时候,无法将父类的私有属性继承过来,因此,也无法根据属性的autowaired的注解来生成对应的setter方法。因此也就无法注入service。

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

SubareaAction:

//注入service

private SubareaService subareaService;

@Autowired

public
void setSubareaService(SubareaService subareaService) {

this.subareaService = subareaService;

}

BOS物流管理系统-第八天

解决方案2:

@Autowire还放到私有声明上,

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

BOS物流管理系统-第八天

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

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

BOS物流管理系统-第八天

扩展:如果要对Action使用AOP,除了上面的配置外,还要将代理的方式改为cglib代理:

<aop:aspectj-autoproxy proxy-target-class="true"/>

错误信息4(正常了)

BOS物流管理系统-第八天

BOS物流管理系统-第八天

web.xml:

BOS物流管理系统-第八天

小结:

Shiro的Url级别权限控制和方法级别权限控制(注解)的无权限的处理结果不同:

Url级别权限控制:页面自动跳转到:

方法级别权限控制(注解):直接抛出异常。

  1. 重点和作业

  1. 权限控制的几种方式 ? 实现原理?

URL 、 方法注解、 页面显示标签控制,数据级别

  1. 画出 权限控制数据表结构
  2. Shiro权限控制好处, 为什么要使用Apache Shiro ?

使用简单,灵活 数据可以采用自定义Realm 连接安全数据 (没有数据来源要求 , 来自文件、 数据库、网络 … )

4、 Shiro 运行原理 :画图 整理 shiro 认证和授权,调用过程

应用代码 ---- Subject --- SecurityManager ----- Realm

5.Url级别的权限控制:认证和授权(通过自定义realm提供权限数据)

补充作业: JdbcRealm 实现认证和授权

6、 方法级别的控制(注解5个(认证3,授权2))

代码部分 :

Shiro 会配置,会修改 (不需要去记忆 )

  • Subject 代码调用
  • 自定义Realm
  • 修改 URL访问规则
  • 在目标方法使用shiro注解

Eclise工具校验的问题:

BOS物流管理系统-第八天

上一篇:php-5.3 zend opcache 的设置


下一篇:spring文件上传