一、Shiro简介
shiro是一个专门用来进行权限管理的框架。 权限管理包括用户身份认证鉴权(授权)两部分,简称认证授权
-
认证:进行登录功能,我们只需要写一个登录的sql语句即可
-
鉴权:判断某个用户登录后有哪些权限
1、shiro认证相当的术语
-
Subject :主体
主体就是当前要登录的人(对象),认证后获得的那个对象就是主体
-
Principal :身份信息
主体(subject)进行身份认证的标识,标识必须具有唯一性,如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal)。
-
credential :凭证信息
是只有主体自己知道的安全信息,如密码、证书等。
2、shiro鉴权
认证成功后才调用鉴权。
-
权限模型
- 主体(账号、密码)
- 资源(资源名称、访问地址)
- 权限(权限名称、资源id)
- 角色(角色名称)
- 角色和权限关系(角色id、权限id)
- 主体和角色关系(主体id、角色id)
通常数据库和权限相当的表有三张:
用户表
角色表
资源权限表
3、权限相关表设计
a、方式一
用户表->角色->资源权限
b 、方式二
用户->资源权限
4、权限控制
a、权限控制有两种方式
-
通过角色授权
登录后获得用户所有的角色,当访问controller资源时判断用户是否拥有某个指定的角色。
-
通过资源授权
RBAC基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问控制
登录后获得用户所有的资源,当访问controller资源时判断访问的controller方法的url是否在资源中存在
b、粗颗粒度和细颗粒度控制
-
粗颗粒度控制
对资源类型的管理称为粗颗粒度权限管理,即只控制到菜单、按钮、方法,控制的是Controller层
-
细颗粒度控制
细颗粒度控制的是业务逻辑,控制的是service层
二、shiro入门案例
1、什么是shiro
Shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架
2、shiro架构
-
Subject 主体
-
SecurityManager 安全管理器
SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。
-
Authenticator 认证器
Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。
-
Authorizer 授权器
Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限
-
realm
相当于datasource数据源,realm是调用dao持久化层获得用户认证和授权所需的数据
-
Cryptography
Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。
3、shiro认证
-
坐标配置
<dependencies>
<!--shiro坐标-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.8.0</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--log4j日志-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies> -
创建shiro配置文件
创建安全管理器所需的数据源
在resource中创建shiro.ini文件,内容配置如下:
[users] //表示下面的数据是用户数据
tom=123 //等号左边的是帐号 等号右边的是密码
jack=123
-
编写认证代码
/**
* shiro测试类
*/
public class MyShiroTest {
/**
* 测试shiro认证
*/
-
shiro常见异常
org.apache.shiro.authc.UnknownAccountException //帐号不存在
org.apache.shiro.authc.IncorrectCredentialsException //密码错误
4、使用自定义Realm 获得据库中的用户信息
-
创建一个自定义Realm类,该类要继承一个授权的父类AuthorizingRealm
/**
* 自定义Realm,用来获得数据查出来的用户数据
*/
public class MyRealm extends AuthorizingRealm {
/**
* 授权,认证成功后,会自动执行该方法
* @param principalCollection
* @return
*/
-
在shiro的配置类中编写认证代码
/**
* 测试shiro认证
*/
5、shiro密码加密
shiro 中提供两种加密方式:MD5和SHA
-
单独对密码加密
String pwd="456";
//sha
/**
* 参数一:加密方式 SHA MD5
* 参数二:要加密的密码
* 参数三:盐值 (在做用户添加的时候一般会为每个用户产生一个随机的盐值,将这个盐值一起保存到数据库)
* 参数四:加密次数
*/
SimpleHash sh=new SimpleHash("SHA",pwd,"woniu",100);
System.out.println(sh);
//md5
SimpleHash sh3=new SimpleHash("MD5",pwd,"woniu",100);
System.out.println(sh3); -
在shiro中认证时对输入的密码加密
在自定realm的认证方法中添加盐
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
在shrio的配置类中创建密码管理器(用来配置密码的加密信息)
//设置密码匹配器
HashedCredentialsMatcher matcher=new HashedCredentialsMatcher();
//设置加密算法
matcher.setHashAlgorithmName("MD5");
//设置加密次数
matcher.setHashIterations(10);
//将密码匹配器加入到自定义Realm中
loginRealm.setCredentialsMatcher(matcher);
三、shiro授权
验证是否有权限访问controller中的方法
1、在shiro.ini中为用户配置权限
角色1 用户的查询 用户的删除
角色2 订单的添加 订单的修改
角色3 用户的添加 订单的删除
[users]
tom=123,res1,res2 #配置用户拥有的角色
jack=456,res3
#配置的角色
[roles]
res1=user:list,user:del #角色拥有的资源权限
res2=order:add,order:update
res3=user:add,order:del
2、shiro判断权限的API
-
Subject 用于判断角色的方法
方法 作用 hasRole(String roleName) 判断是否有该角色访问权, 返回 boolen hasRoles(List<String> roleNames) 判断是否有这些这些角色访问权, 返回 boolean[] hasAllRoles(Collection<String> roleNames) 判断是否有这些这些角色访问权, 返回 boolean checkRole(String roleName) 如果判断失败抛出 AuthorizationException 异常 checkRoles(String... roleNames) 如果判断失败抛出 AuthorizationException 异常 checkRoles(Collection<String> roleNames) 如果判断失败抛出 AuthorizationException 异常 -
Subject 用于判断权限的方法有
方法 作用 isPermitted(String perm) 判断是否有该权限,返回 boolen isPermitted(List<String> perms) 判断是否有这些这些权限,返回 boolean[] isPermittedAll(Collection<String> perms) 判断是否有这些这些权限,返回 boolean checkPermission(String perm) 如果判断失败抛出 AuthorizationException 异常 checkPermissions(String... perms) 如果判断失败抛出 AuthorizationException 异常 checkPermissionsAll(Collection<String> perms) 如果判断失败抛出 AuthorizationException 异常
3、判断主体是否有权限
-
基于角色
System.out.println("用户是否拥有一个角色:" + subject.hasRole("role1"));
//是否有多个角色
System.out.println("用户是否拥有多个角色:" + subject.hasAllRoles(Arrays.asList("role1", "role2")));
subject.checkRole("role1");
// 授权检测,失败则抛出异常
//subject.checkRoles(Arrays.asList("role1", "role3")); -
基于资源
// 基于资源授权
System.out.println("是否拥有某一个权限:" + subject.isPermitted("user:delete"));
System.out.println("是否拥有多个权限:" + subject.isPermittedAll("user:create:1", "user:delete"));
//对应的check方法:
subject.checkPermission("sys:user:delete");
subject.checkPermissions("user:create:1","user:delete");<font color=red>提示:在web项目中,一般不会进行手动编码授权,而是在controller的方法上添加shiro授权注解</font>
4、自定义realm授权
修改自定义Realm中的授权方法
/**
* 模拟根据当前登录人的帐号获得该用户所有的资源权限
* @param account
* @return
*/
public Set<String> getParem(String account){
Set<String> list=new HashSet<>();
if(account.equals("tom")){
list.add("user:list");
list.add("user:update");
}
if(account.equals("jack")){
list.add("order:list");
list.add("order:add");
}
return list;
}
/**
* 授权,认证成功后,会自动执行该方法
* @param principalCollection
* @return
*/