Shiro权限框架
—观点:
学习任何的知识,我们首先要知道它是什么,然后通过是什么(定义)来分析它的作用、行为。从而圈定学习的范围。我们将这个过程称为,学习思路!!
1. Shiro概述
1.1. Shiro是什么
官方说明:
href=”#what-is-apache-shiro-” What is Apache Shiro? Apache Shiro is a powerful and flexible open-source security framework that cleanly handles authentication, authorization, enterprise session management and cryptography. |
Shiro是一个非常强大的、易于使用的、开源的、权限框架。它包括了权限校验、权限授予、会话管理、安全加密等组件。
1.2. 为什么需要使用Shiro(兴趣点)
如果你是需要设计RBAC(Role Based Access Control)基础系统,需要编写大量用于权限控制的代码时。那么你需要使用Shiro。因为Shiro已经将RBAC系统大量的代码封装好,可以减少我们大量的工作量。
如:页面的显示的HTML控件根据登录用户的权限不同而不同。使用Shiro可以轻松解决。
1.3. Shiro的下载
shiro的下载路径:http://shiro.apache.org/download.html
—所有包的说明,根据列表的说明,下载我们需要的jar包
包名 | Maven 坐标 | 说明 |
shiro-all | Not Recommended | Includes all binary functionality for Shiro |
shiro-core | <dependency> | Required in all environments. Slf4j’s slf4j-api jar and one of its binding jars is required. commons-beanutils is required only if using INI config.
只要使用shiro必须的核心包,它依赖slf4j 和 commons-beanutils 以及还需要一个INI配置文件 |
shiro-web | <dependency> | Enables support for web-based applications.
支持基于Web的应用 |
shiro-aspectj | <dependency> | Enables AspectJ support for Shiro AOP and Annotations. |
shiro-cas | <dependency> | Enables Jasig CAS support. the Apache Shiro based buji-pac4j project. 对cas单点登录框架的支持 |
shiro-ehcache | <dependency> | Enables Ehcache-based famework caching. 对echche缓存框架的支持 |
shiro-hazelcast | <dependency> | Enables Hazelcast-based famework caching. |
shiro-features | <dependency> | OSGi / Apache Karaf integration. |
shiro-guice | <dependency> | Enables Google Guice integration.
guice 类似spring的ioc框架。 |
shiro-quartz | <dependency> | Enables Quartz-based scheduling for Shiro native session validation.
quartz定时器框架 |
shiro-spring | <dependency> | Enables Spring Framework integration. |
1.4. 包说明
–Shiro常用包
|
1.5. Shiro结构图
|
Authentication:权限校验,每次操作校验用户是否有访问权限
Authorization:授权,用户登录时,授予用户对应的权限
Session Management:会话管理,用于记录用户的登录状态
Cryptography:加密,加密算法的实现(SHA、MD5)
web Support:对Web项目的支持,Shiro的标签!!
2. Shiro入门
2.1. 访问流程图
我们知道学习任何的框架,都是从配置流程开始的。我们我们学习Shiro也是一样。所以首先要了解Shiro的访问流程。从而知道配置的配置,快速入门。
|
- 首先应用访问(可以使用远程调用,可以是Web请求等),Shiro通过一个Subject对象来标识当前访问的身份。这句话告诉我们,第一次访问的时候,Shiro肯定会创建一个Subject对象标签当前请求(用户)的身份。
- SecurityManger容器创建一个Subject对象验证请求的参数,SecurityManager的作用是统一管理Subject。这句话意味着,一个SecurityManager对象管理多个Subject的对象。
- Subject通过SecurityManger获得操作当前用户的权限,在启动的那一刻,SecurityManger就会加载shiro.ini权限配置文件,在用户登录成功后,可以根据shiro配置的信息,获得用户对应的权限。
- shiro配置:是一个权限控制信息文件,里面必须包括用户的验证信息,权限的信息
—登录流程!!!!
2.2. 配置步骤说明
根据访问流程图,我们要使用访问Shiro权限框架的功能。首先需要有一个配置文件shiro.ini配置文件配置了用户的权限认证信息。然后通过SessionManager对象读取配置文件,获得用户身份Subject,
客户端代码通过当前用户访问有权限的操作。
由此,得出配置步骤:
第一步:任何框架都需要导入包
第二步:创建一个shiro.ini配置文件。(文件名任意编写,后缀必须为ini)
第三步:编写测试代码
2.3. 配置步骤
注意事项:shiro框架依赖slf4j以及beanutils包。
2.3.1. 第一步:导入包
2.3.2. 第二步:shiro.ini配置文件
创建一个shiro.ini配置,编写权限认证信息。
注意事项:
- shiro.ini文件名可以任意编写,但后缀必须是ini
- shiro.ini配置文件放在classpath根目录下
shiro.ini规则说明
[main] #用于配置SecurityManager里面的对象 对象名=类全限制名 对象名.属性[.属性…] = 值
[users] #用于配置用户名信息 用户名= 密码, 角色1, 角色2, …, 角色N
[roles] #用于配置角色信息 角色名= 权限1, 权限2, …, 权限N #全部权限使用 * (星号)
[urls] #用于配置路径拦截规则 |
权限格式使用:权限:操作:操作
代码如下:
[users] jim = jim,admin
[roles] admin = * |
2.3.3. 第三步:创建SecurityManager对象
创建一个SecurityManager对象,并且测试校验是否成功
package cn.gzsxt.shiro;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject;
public class ShiroDemo {
public static void main(String[] args) { // 1.获得SecurityManager对象 IniSecurityManagerFactory factory = new IniSecurityManagerFactory(“classpath:shiro.ini”); SecurityManager securityManager = factory.createInstance();
//2.设置SecurityUtils使用的安全管理器是securityManager SecurityUtils.setSecurityManager(securityManager);
//2.获得subject Subject subject = SecurityUtils.getSubject(); AuthenticationToken token=new UsernamePasswordToken(“jim”, “jim”); //3.校验用户名密码是否正确
try { Subject resultSubject = securityManager.login(subject, token); //获得用户名 System.out.println(resultSubject.getPrincipal()); //判断是否拥有admin角色 boolean hasRole = resultSubject.hasRole(“admin”); System.out.println(hasRole); } catch (AuthenticationException e) { System.out.println(“校验失败,用户名或者密码不正确”); e.printStackTrace(); } } } |
2.3.4. 注意事项
以上示例,我们需要记住并且理解入门示例中的几个API的使用
IniSecurityManagerFactory:作用加载ini配置文件获得SecurityManagerFactory对象
SecurityManager:安全管理容器,就是否则整个Shiro框架授权校验对象的管理
SecurityUtils :SecurityManager对象帮助类
Subject:验证通过后用于存储授权信息的身份对象
UsernamePasswordToken :用于设置校验信息的对象
IncorrectCredentialsException :密码出错异常
UnknownAccountException:用户名出错异常
3. Realm的使用
3.1. Realm机制的必要性
以上案例,我们看到,我们的用户验证信息来自于ini配置文件的[users]以及[roles]标记。这样的难以符合我们实际的需求。
我们希望可以将Shiro校验的用户信息存储在数据库里面,在从数据库里面读取出来。
解决方案:Shiro是通过Realm机制,实现将配置文件的校验用户信息存放在数据库、LDAP等数据存储系统里面。
说白了,现在我们要做的事情,就是从数据库里面获得用户的验证信息!!!
3.2. 访问流程图说明
|
如图所示:
- 我们需通过Subject封装访问用户的信息
- 我们需要一个SecurityManager对象来管理所有用户的权限
- 我们需要ini配置文件配置获得Realm对象
- 我们需要在Realm进行权限验证以及授权
3.3. 配置步骤说明
第一步:导入包
第二步:创建shiro.ini配置文件
第三步:创建入口的测试类对象
第四步:创建Realm对象
第五步:配置shiro.ini调用Realm对象
3.4. 权限校验–配置入门
3.4.1. 第一步:导入包
|
3.4.2. 第二步:创建shiro.ini配置文件
[main] #声明realm对象 myRealm=cn.gzsxt.realm.MyRealm
#指定securityManager的realm对象 securityManager.realms=$myRealm |
3.4.3. 第三步:编写一个测试类
package cn.gzsxt.test;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.junit.Test;
public class PermissionTest {
@Test public void authc(){
try { //第一步:获得SecurityManager对象 IniSecurityManagerFactory ismf=new IniSecurityManagerFactory(“classpath:shiro.ini”); SecurityManager securityManager = ismf.createInstance();
//第二步:构造一个subject SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
//第三步:封装用户名密码(身份信息) UsernamePasswordToken token=new UsernamePasswordToken(“zhangsan”, “123456”);
//第四步:校验(登录) Subject resultSubject = securityManager.login(subject, token);
//第五步:验证是否通过 System.out.println(resultSubject.isAuthenticated()); } catch (AuthenticationException e) { e.printStackTrace(); }
} } |
3.4.4. 第四步:编写Realm
package cn.gzsxt.realm;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection;
/** * 自动一个MyRealm类,用于权限验证以及权限授予 * * @author ranger * */ public class MyRealm extends AuthorizingRealm {
/** * 权限验证 所谓的权限验证,就是验证访问者(subject).是否使用有使用权限的身份。 说白了,就验证用户名密码是否正确 * * 如果校验成功,我们就返回AuthenticationInfo对象 如果校验失败,我们需要返回一个异常 * UnknownAccountException * */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println(“用户名:” + token.getPrincipal()); if (token.getPrincipal().equals(“zhangsan”)) { return new SimpleAuthenticationInfo(token.getPrincipal(), “123456”, this.getName());
} else { return null; } }
/** * 授权 根据通过校验的身份(subject),说白了就是登录成功的访问者,我们给予什么权限。 * 将查询到的权限信息封装在AuthorizationInfo里面返回!! */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection collection) { return null; }
}
|
3.4.5. 加密
需求:我们的密码是明文的,我们需要将密码Md5加密。
问题:我们发现我们自己写的Md5的类,无法传递给SimpleAuthenticationInfo对象,作为密码校验。如何解决的这个问题呢?
答:shiro框架自带的密码加密的功能。
- SimpleHash类:用于生成指定的Hash算法。
|
- HashedCredentialsMatcher类:用于让Realm校验时,校验指定的Hash算法
|
- ByteSource 用于给Hash算法加盐的
|
—创建Md5密码
package cn.gzsxt.test;
import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.util.ByteSource;
//注意事项:设置的创建密码的参数必须与校验器的参数保持一致 public class CreatePasswordUtils {
public static void main(String[] args) { //加密方式 String hashAlgorithmName = “MD5”; //明文密码 Object credentials = “123456”; //盐值 Object salt = ByteSource.Util.bytes(“nchu234we”); //迭代加密的次数, int hashIterations = 1; //返回加密的结果 Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations); //加密后的密码 System.out.println(result); }
}
|
—修改配置文件
[main] # 对象名 =类全限制名 ,就是创建一个对象 myRealm = cn.gzsxt.realm.MyRealm
#加密的对象 credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher #设置加密的算法为md5 ,属性对应的是set方法 credentialsMatcher.hashAlgorithmName=md5 #设置md5加密次数为1次 credentialsMatcher.hashIterations=1 #设置md5算法是加盐 credentialsMatcher.hashSalted=true
#将加密对象指定给对应给myRealm对象 myRealm.credentialsMatcher=$credentialsMatcher
#将realm对象加入到securityManager容器里面 #引用 securityManager.realms = $对象名 #securityManager对象名是shiro固定的。用于指定securityManager容器 #对象的引用格式为: 对象名.属性名 = $对象名; securityManager.realms = $myRealm |
—修改校验代码
package cn.gzsxt.realm;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource;
/** * Realm :所有Realm的父接口 * AuthenticatingRealm :只有权限校验,没有权限授权的Realm * AuthorizingRealm : 既有权限校验,权限授权的Realm (主要就是使用它) * JdbcRealm:使用实现的对数据库 操作的代码的Realm。它已经写死了表名,必要要按它定义的表名定义数据库表。(灵活性差) * * 注意事项:使用Realm,将验证认证信息与获得授权信息,都放在一起!!!!! * @author ranger * * */ public class MyRealm extends AuthorizingRealm {
/** * 用于权限校验 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { try { System.out.println(“=权限校验=”);
//第一步:获得请求过来的用户名 Object principal = token.getPrincipal();
//第二步:根据用户查询数据库,返回用户信息 (模拟数据,查询到了“admin“) if(“admin”.equals(principal)){ //第三步:如果用户名相同,校验密码,如果密码正确,不会报异常,如果校验不通过就报异常 //参数1:用户名 //参数2:校验的密码,从数据库查询出来的 //参数3:realm的名字 。随便设置一个唯一字符串
//4). md5加密盐值 ByteSource salt = ByteSource.Util.bytes(“nchu234we”);
SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(principal,“8763d15228c560fed665e1fe73b2f601”,salt,this.getName()); return authenticationInfo; } } catch (Exception e) { e.printStackTrace(); } return null; }
/** * 权限授权 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection token) { return null; } } |
3.4.6. 返回的认证信息为一个实体(JavaBean、Map)
注意:现在校验后返回的认证信息是一个字符串的用户名,而我们如果将Shiro的校验功能用到登录的逻辑里面,我明显需要返回的不是一个用户名,而是用户的信息。
用户的信息,我们只能使用一个实体类来封装。可以是JavaBean或者是Map
答:我们校验方法返回的SimpleAuthenticationInfo的构建方法的第一个参数就是用于指定,返回的用户认证信息的。可以将用户名修改为一个我们指定的实体类对象就可以了!!!
—构建一个实体类
package cn.gzsxt.javabean;
import java.io.Serializable;
/** * 注意:因为Shiro是一个支持缓存的框架,所以实体类需要实现序列化接口 * @author ranger * */ public class ActiveUser implements Serializable{
private static final long serialVersionUID = -1354674546192347496L; private Integer userId; public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } private String username; private String password; private Integer age; private Integer status; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public static long getSerialversionuid() { return serialVersionUID; }
}
|
—修改返回的认证信息
package cn.gzsxt.realm;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource;
import cn.gzsxt.javabean.ActiveUser;
/** * Realm :所有Realm的父接口 * AuthenticatingRealm :只有权限校验,没有权限授权的Realm * AuthorizingRealm : 既有权限校验,权限授权的Realm (主要就是使用它) * JdbcRealm:使用实现的对数据库 操作的代码的Realm。它已经写死了表名,必要要按它定义的表名定义数据库表。(灵活性差) * * 注意事项:使用Realm,将验证认证信息与获得授权信息,都放在一起!!!!! * @author ranger * * */ public class MyRealm extends AuthorizingRealm {
/** * 用于权限校验 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { try { System.out.println(“=权限校验=”);
//第一步:获得请求过来的用户名 Object principal = token.getPrincipal();
//第二步:根据用户查询数据库,返回用户信息 (模拟数据,查询到了“admin“) if(“admin”.equals(principal)){ //第三步:如果用户名相同,校验密码,如果密码正确,不会报异常,如果校验不通过就报异常 //参数1:用户名 //参数2:校验的密码,从数据库查询出来的 //参数3:realm的名字 。随便设置一个唯一字符串
ActiveUser user=new ActiveUser(); user.setUserId(1); user.setAge(20); user.setPassword(“8763d15228c560fed665e1fe73b2f601”); user.setUsername(“admin”); user.setStatus(0);
//4). md5加密盐值 ByteSource credentialsSalt = ByteSource.Util.bytes(“nchu234we”); //参数1:用于设置认证信息,返回给调用对象的 //参数2:校验的密码 //参数3:如果配置了Md5加密,而且设置了需要加盐,该参数就是密码的盐 //参数4:指定realm的名字,随便写个唯一的字符串就可以。建议直接使用this.getName(); SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(user,”8763d15228c560fed665e1fe73b2f601″,credentialsSalt,this.getName()); return authenticationInfo; } } catch (Exception e) { e.printStackTrace(); } //如果校验成功,返回null return null; }
/** * 权限授权 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection token) { return null; } } |
3.5. 权限授予–配置入门
3.5.1. 第一步:检验Subject对象的权限校验方法
—校验代码
package cn.gzsxt.test;
import java.util.ArrayList; import java.util.List;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject;
/** * 注意:根据示例 * 1.身份信息是放在Subject这个身份对象里面的 * 2.Shiro的所有对象是通过securityManager来管理 * 3.可以在配置文件配置,认证的信息(数据库里面的表数据) * @author ranger * */ public class ShiroTest {
public static void main(String[] args) { //第一步:读取配置文件 IniSecurityManagerFactory factory= new IniSecurityManagerFactory(“classpath:shiro.ini”); //第二步:获得安全管理器 SecurityManager securityManager = factory.createInstance(); //第三步:通过安全管理器帮助类构建一个Subject(身份) SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject();
//第四步:构建身份信息(用户名/密码) UsernamePasswordToken token = new UsernamePasswordToken(“admin”,”123456″);
//第五步:校验是否成功,将一个空的身份,以及身份信息通过登录方法,如果成功返回带身份信息的Subject对象。如果不成功就报异常 try { Subject result = securityManager.login(subject, token); System.out.println(“认证成功,用户名“+result.getPrincipals());
boolean flag = result.isAuthenticated(); //使用校验方法,校验用户对应的角色 boolean hasRole = result.hasRole(“roleAdmin”); System.out.println(“是否有指定的角色:“+hasRole); List<String> hasRoles=new ArrayList<>(); hasRoles.add(“roleAdmin”); hasRoles.add(“roleEdu”); boolean allRoles = result.hasAllRoles(hasRoles); System.out.println(“是否都包括集合的所有角色:”+allRoles);
//校验用户是否有对应的权限 boolean permitted = result.isPermitted(“user:add”); System.out.println(“是否有user:add权限:“+permitted);
boolean permitted2 = result.isPermitted(“modular:add”); System.out.println(“是否有modular:add权限:“+permitted2);
System.out.println(flag); } catch (AuthenticationException e) { System.out.println(“用户名或者密码出错“); e.printStackTrace(); } } } |
—配置文件
#users标签:用于指定用户信息,以及用户的角色 #注意:shiro的支持一个用于有多个角色的 #用户名= 密码, 角色1, 角色2, …, 角色N #如果出现多个角色,用于取的是角色的并集 [users] admin = 123456,roleAdmin,roleEdu #roles标签:用于设置角色的信息,包括的角色以及权限 #权限字符串的格式为:模块名:操作:操作… 类似我们的子权限的设置 [roles] roleAdmin = user:query,user:add,user:delete,modular:* roleEdu = edu:query,edu:add |
3.5.2. 第二步:在Realm授权
根据以上代码,我们确认,在配置文件配置的角色以及权限只可以通过权限校验的。
问题:我们以后开发程序是不可以将程序的权限以及角色写在配置文件里面的。而是写在数据库里面!!!我们如何将数据库里面的权限设置给我们对象的认证者呢?
答:在我们AuthorizingRealm提供了权限授予的方法doGetAuthorizationInfo可以用于授权。替换在配置文件的权限、角色信息。
–JavaBean
package cn.gzsxt.javabean;
import java.io.Serializable;
/** * 注意:因为Shiro是一个支持缓存的框架,所以实体类需要实现序列化接口 * @author ranger * */ public class ActiveUser implements Serializable{
private static final long serialVersionUID = -1354674546192347496L; private Integer userId; public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } private String username; private String password; private Integer age; private Integer status;
public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public static long getSerialversionuid() { return serialVersionUID; }
}
|
–Realm
package cn.gzsxt.realm;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource;
import cn.gzsxt.javabean.ActiveUser; import cn.gzsxt.javabean.Role;
/** * Realm :所有Realm的父接口 * AuthenticatingRealm :只有权限校验,没有权限授权的Realm * AuthorizingRealm : 既有权限校验,权限授权的Realm (主要就是使用它) * JdbcRealm:使用实现的对数据库 操作的代码的Realm。它已经写死了表名,必要要按它定义的表名定义数据库表。(灵活性差) * * 注意事项:使用Realm,将验证认证信息与获得授权信息,都放在一起!!!!! * @author ranger * * */ public class MyRealm extends AuthorizingRealm {
/** * 用于权限校验 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { try { System.out.println(“=权限校验=”);
//第一步:获得请求过来的用户名 Object principal = token.getPrincipal();
//第二步:根据用户查询数据库,返回用户信息 (模拟数据,查询到了“admin“) if(“admin”.equals(principal)){ //第三步:如果用户名相同,校验密码,如果密码正确,不会报异常,如果校验不通过就报异常 //参数1:用户名 //参数2:校验的密码,从数据库查询出来的 //参数3:realm的名字 。随便设置一个唯一字符串
ActiveUser user=new ActiveUser(); user.setUserId(1); user.setAge(20); user.setPassword(“8763d15228c560fed665e1fe73b2f601”); user.setUsername(“admin”); user.setStatus(0);
//4). md5加密盐值 ByteSource credentialsSalt = ByteSource.Util.bytes(“nchu234we”); //参数1:用于设置认证信息,返回给调用对象的 //参数2:校验的密码 //参数3:如果配置了Md5加密,而且设置了需要加盐,该参数就是密码的盐 //参数4:指定realm的名字,随便写个唯一的字符串就可以。建议直接使用this.getName(); SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(user,”8763d15228c560fed665e1fe73b2f601″,credentialsSalt,this.getName()); return authenticationInfo; } } catch (Exception e) { e.printStackTrace(); }
return null; }
/** * 权限授权,必须在通过验证后才执行的方法 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection token) {
System.out.println(“===权限授予====”); //获得当前通过验证的用户,为什么可以获得校验后的用户信息呢? //答:因为就是如果通不过校验,肯定就会有有授权,进入了授权肯定就有校验的认证用户了
ActiveUser user=(ActiveUser) token.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
authorizationInfo.addStringPermission(“user:query”); authorizationInfo.addStringPermission(“user:add”); authorizationInfo.addStringPermission(“user:delete”); authorizationInfo.addStringPermission(“user:edit”); authorizationInfo.addStringPermission(“modular:*”);
authorizationInfo.addRole(“roleAdmin”);
//将权限设置角色里面 Role role=new Role(); role.setPermisssion(authorizationInfo.getStringPermissions()); user.setRole(role);
return authorizationInfo; } } |
—配置文件
[main] # 对象名 =类全限制名 ,就是创建一个对象 myRealm = cn.gzsxt.realm.MyRealm
#加密的对象 credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher #设置加密的算法为md5 credentialsMatcher.hashAlgorithmName=md5 #设置md5加密次数为1次 credentialsMatcher.hashIterations=1 #设置md5算法是加盐 credentialsMatcher.hashSalted=true
#将加密对象指定给对应给myRealm对象 myRealm.credentialsMatcher=$credentialsMatcher
#将realm对象加入到securityManager容器里面 #引用 securityManager.realms = $对象名 #securityManager对象名是shiro固定的。用于指定securityManager容器 #对象的引用格式为: 对象名.属性名 = $对象名; securityManager.realms = $myRealm |
—测试代码
package cn.gzsxt.test;
import java.util.ArrayList; import java.util.List;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject;
import cn.gzsxt.javabean.ActiveUser; import cn.gzsxt.javabean.Role;
/** * 注意:根据示例 * 1.身份信息是放在Subject这个身份对象里面的 * 2.Shiro的所有对象是通过securityManager来管理 * 3.可以在配置文件配置,认证的信息(数据库里面的表数据) * @author ranger * */ public class ShiroTest {
public static void main(String[] args) { //第一步:读取配置文件 IniSecurityManagerFactory factory= new IniSecurityManagerFactory(“classpath:shiro.ini”); //第二步:获得安全管理器 SecurityManager securityManager = factory.createInstance(); //第三步:通过安全管理器帮助类构建一个Subject(身份) SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject();
//第四步:构建身份信息(用户名/密码) UsernamePasswordToken token = new UsernamePasswordToken(“admin”,”123456″);
//第五步:校验是否成功,将一个空的身份,以及身份信息通过登录方法,如果成功返回带身份信息的Subject对象。如果不成功就报异常 try { Subject result = securityManager.login(subject, token); System.out.println(“认证成功,用户名“+result.getPrincipals());
boolean flag = result.isAuthenticated(); //使用校验方法,校验用户对应的角色 boolean hasRole = result.hasRole(“roleAdmin”); System.out.println(“是否有指定的角色:“+hasRole); List<String> hasRoles=new ArrayList<>(); hasRoles.add(“roleAdmin”); hasRoles.add(“roleEdu”); boolean allRoles = result.hasAllRoles(hasRoles); System.out.println(“是否都包括集合的所有角色:”+allRoles);
//校验用户是否有对应的权限 boolean permitted = result.isPermitted(“user:add”); System.out.println(“是否有user:add权限:“+permitted);
boolean permitted2 = result.isPermitted(“modular:add”); System.out.println(“是否有modular:add权限:“+permitted2);
System.out.println(flag);
} catch (AuthenticationException e) { System.out.println(“用户名或者密码出错“); e.printStackTrace(); } } } |
4. 综合案例
在数据库里面设计,权限信息表。然后通过shiro读取数据库的信息。实现验证与授权。
4.1. 第一步:数据库设计
— 导出 shiro-1115 的数据库结构 DROP DATABASE IF EXISTS `shiro-1115`; CREATE DATABASE IF NOT EXISTS `shiro-1115` /*!40100 DEFAULT CHARACTER SET utf8 */; USE `shiro-1115`;
— 导出 表 shiro-1115.tb_admin 结构 DROP TABLE IF EXISTS `tb_admin`; CREATE TABLE IF NOT EXISTS `tb_admin` ( `admin_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘管理员编号‘, `admin_name` varchar(50) DEFAULT NULL COMMENT ‘管理员名‘, `admin_account` varchar(50) DEFAULT NULL COMMENT ‘登录账号‘, `admin_pwd` varchar(50) DEFAULT NULL COMMENT ‘登录密码‘, `create_date` datetime DEFAULT NULL COMMENT ‘创建日期‘, `admin_status` int(11) DEFAULT NULL COMMENT ‘管理员状态 0可用,1禁用‘, `role_id` bigint(20) DEFAULT NULL COMMENT ‘角色编号‘, PRIMARY KEY (`admin_id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT=’管理员‘;
— 正在导出表 shiro-1115.tb_admin 的数据:~1 rows (大约) DELETE FROM `tb_admin`; /*!40000 ALTER TABLE `tb_admin` DISABLE KEYS */; INSERT INTO `tb_admin` (`admin_id`, `admin_name`, `admin_account`, `admin_pwd`, `create_date`, `admin_status`, `role_id`) VALUES (1, ‘配置管理员‘, ‘admin’, ‘123456’, ‘2019-04-02 16:33:39’, 0, 1); /*!40000 ALTER TABLE `tb_admin` ENABLE KEYS */;
— 导出 表 shiro-1115.tb_dictionary 结构 DROP TABLE IF EXISTS `tb_dictionary`; CREATE TABLE IF NOT EXISTS `tb_dictionary` ( `dictionary_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘字典编号‘, `dictionary_name` varchar(200) DEFAULT NULL COMMENT ‘字典名‘, `dictionary_value` varchar(500) DEFAULT NULL COMMENT ‘字典值‘, `dictionary_type_code` bigint(20) DEFAULT NULL COMMENT ‘字段类型编码‘, `dictionary_type_name` varchar(200) DEFAULT NULL COMMENT ‘字段类型名称‘, PRIMARY KEY (`dictionary_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=’tb_dictionary’;
— 正在导出表 shiro-1115.tb_dictionary 的数据:~0 rows (大约) DELETE FROM `tb_dictionary`; /*!40000 ALTER TABLE `tb_dictionary` DISABLE KEYS */; /*!40000 ALTER TABLE `tb_dictionary` ENABLE KEYS */;
— 导出 表 shiro-1115.tb_modular 结构 DROP TABLE IF EXISTS `tb_modular`; CREATE TABLE IF NOT EXISTS `tb_modular` ( `modular_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘模块编号‘, `modular_name` varchar(200) DEFAULT NULL COMMENT ‘模块名‘, `modular_sort` bigint(20) DEFAULT NULL COMMENT ‘排序顺序‘, PRIMARY KEY (`modular_id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT=’模块表‘;
— 正在导出表 shiro-1115.tb_modular 的数据:~0 rows (大约) DELETE FROM `tb_modular`; /*!40000 ALTER TABLE `tb_modular` DISABLE KEYS */; INSERT INTO `tb_modular` (`modular_id`, `modular_name`, `modular_sort`) VALUES (1, ‘系统管理模块‘, 0); /*!40000 ALTER TABLE `tb_modular` ENABLE KEYS */;
— 导出 表 shiro-1115.tb_permission 结构 DROP TABLE IF EXISTS `tb_permission`; CREATE TABLE IF NOT EXISTS `tb_permission` ( `permission_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘权限编号‘, `permission_name` varchar(200) DEFAULT NULL COMMENT ‘权限名‘, `permission_action` varchar(500) DEFAULT NULL COMMENT ‘权限路径,匹配路径是否有权限‘, `permission_is_show` int(11) DEFAULT NULL COMMENT ‘是否显示‘, `permission_key` varchar(500) DEFAULT NULL COMMENT ‘父菜单编号,0表示顶级菜单‘, `modular_id` bigint(20) DEFAULT NULL COMMENT ‘模块编号‘, PRIMARY KEY (`permission_id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT=’权限表‘;
— 正在导出表 shiro-1115.tb_permission 的数据:~2 rows (大约) DELETE FROM `tb_permission`; /*!40000 ALTER TABLE `tb_permission` DISABLE KEYS */; INSERT INTO `tb_permission` (`permission_id`, `permission_name`, `permission_action`, `permission_is_show`, `permission_key`, `modular_id`) VALUES (1, ‘管理员管理‘, ‘/admin/toAdminList’, 0, ‘admin:list’, 1), (2, ‘管理员管理-To增加‘, ‘/admin/toAdminAdd’, 0, ‘admin:list:to_add’, 1), (3, ‘管理员管理–增加‘, ‘/admin/addAdmin’, 0, ‘admin:list:add’, 1); /*!40000 ALTER TABLE `tb_permission` ENABLE KEYS */;
— 导出 表 shiro-1115.tb_role 结构 DROP TABLE IF EXISTS `tb_role`; CREATE TABLE IF NOT EXISTS `tb_role` ( `role_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘角色编号‘, `role_name` varchar(50) DEFAULT NULL COMMENT ‘角色名‘, `role_permissions` varchar(1000) DEFAULT NULL COMMENT ‘权限集,权限编号使用逗号分隔‘, PRIMARY KEY (`role_id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT=’角色‘;
— 正在导出表 shiro-1115.tb_role 的数据:~1 rows (大约) DELETE FROM `tb_role`; /*!40000 ALTER TABLE `tb_role` DISABLE KEYS */; INSERT INTO `tb_role` (`role_id`, `role_name`, `role_permissions`) VALUES (1, ‘超级管理员‘, ‘1,2,3’); /*!40000 ALTER TABLE `tb_role` ENABLE KEYS */; |
4.2. 第二步:配置Shiro入门示例
—配置文件
[users] admin = 123456,roleAdmin [roles] roleAdmin = user:list,user:list:add,user:list:to_add |
—测试入口
package cn.gzsxt.test;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject;
public class ShiroTest {
public static void main(String[] args) { //第一步:读取配置文件,创建SecurityManager容器 IniSecurityManagerFactory factory=new IniSecurityManagerFactory(“classpath:shiro.ini”); SecurityManager securityManager = factory.createInstance();
//第二步:通过SecurityUtil获得一个身份对象 SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject();
//第三步:构建一个身份信息(Token) UsernamePasswordToken token=new UsernamePasswordToken(“admin”,”123456″); //验证信息,获得配置文件的身份信息以及权限
Subject resultSubject = securityManager.login(subject, token);
System.out.println(“用户信息:“+resultSubject.getPrincipal()); }
} |
4.3. 第三步:编写实体类
–Admin
package cn.gzsxt.pojo;
import java.io.Serializable;
public class Admin implements Serializable {
private static final long serialVersionUID = -4165903044029644075L;
private Long adminId;//BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT ‘管理员编号‘, private String adminName;//VARCHAR(50) NULL DEFAULT NULL COMMENT ‘管理员名‘, private String adminAccount;//VARCHAR(50) NULL DEFAULT NULL COMMENT ‘登录账号‘, private String adminPwd;//VARCHAR(50) NULL DEFAULT NULL COMMENT ‘登录密码‘, private String createDate;//DATETIME NULL DEFAULT NULL COMMENT ‘创建日期‘, private Integer adminStatus;//INT(11) NULL DEFAULT NULL COMMENT ‘管理员状态 0可用,1禁用‘, private Long roleId;//BIGINT(20) NULL DEFAULT NULL COMMENT ‘角色编号‘,
//一个管理员只能是有一个角色 private Role role;
public Role getRole() { return role; } public void setRole(Role role) { this.role = role; } public Long getAdminId() { return adminId; } public void setAdminId(Long adminId) { this.adminId = adminId; } public String getAdminName() { return adminName; } public void setAdminName(String adminName) { this.adminName = adminName; } public String getAdminAccount() { return adminAccount; } public void setAdminAccount(String adminAccount) { this.adminAccount = adminAccount; } public String getAdminPwd() { return adminPwd; } public void setAdminPwd(String adminPwd) { this.adminPwd = adminPwd; } public String getCreateDate() { return createDate; } public void setCreateDate(String createDate) { this.createDate = createDate; } public Integer getAdminStatus() { return adminStatus; } public void setAdminStatus(Integer adminStatus) { this.adminStatus = adminStatus; } public Long getRoleId() { return roleId; } public void setRoleId(Long roleId) { this.roleId = roleId; } } |
–Role
package cn.gzsxt.pojo;
import java.io.Serializable; import java.util.List;
public class Role implements Serializable {
private static final long serialVersionUID = 7439278538619165462L;
private Long roleId;//BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT ‘角色编号‘, private String roleName;//VARCHAR(50) NULL DEFAULT NULL COMMENT ‘角色名‘, private String rolePermissions;//VARCHAR(1000) NULL DEFAULT NULL COMMENT ‘权限集,权限编号使用逗号分隔‘, //一个就是有多个权限 private List<Permission> permissions; public List<Permission> getPermissions() { return permissions; } public void setPermissions(List<Permission> permissions) { this.permissions = permissions; } public Long getRoleId() { return roleId; } public void setRoleId(Long roleId) { this.roleId = roleId; } public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } public String getRolePermissions() { return rolePermissions; } public void setRolePermissions(String rolePermissions) { this.rolePermissions = rolePermissions; }
} |
–Permission
package cn.gzsxt.pojo;
import java.io.Serializable;
public class Permission implements Serializable {
private static final long serialVersionUID = -8720560773258570437L;
private Long permissionId;//BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT ‘权限编号‘, private String permissionName;//VARCHAR(200) NULL DEFAULT NULL COMMENT ‘权限名‘, private String permissionAction;//VARCHAR(500) NULL DEFAULT NULL COMMENT ‘权限路径,匹配路径是否有权限‘, private Integer permissionIsShow;//INT(11) NULL DEFAULT NULL COMMENT ‘是否显示‘, private String permissionKey;//VARCHAR(500) NULL DEFAULT NULL COMMENT ‘父菜单编号,0表示顶级菜单‘, private Long modularId;//BIGINT(20) NULL DEFAULT NULL COMMENT ‘模块编号‘, public Long getPermissionId() { return permissionId; } public void setPermissionId(Long permissionId) { this.permissionId = permissionId; } public String getPermissionName() { return permissionName; } public void setPermissionName(String permissionName) { this.permissionName = permissionName; } public String getPermissionAction() { return permissionAction; } public void setPermissionAction(String permissionAction) { this.permissionAction = permissionAction; } public Integer getPermissionIsShow() { return permissionIsShow; } public void setPermissionIsShow(Integer permissionIsShow) { this.permissionIsShow = permissionIsShow; } public String getPermissionKey() { return permissionKey; } public void setPermissionKey(String permissionKey) { this.permissionKey = permissionKey; } public Long getModularId() { return modularId; } public void setModularId(Long modularId) { this.modularId = modularId; } } |
4.4. 第四步:编写数据库连接
package cn.gzsxt.utils;
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException;
public class DbUitls {
//1.获得数据库连接 public static Connection getConnection(){ //四要素 String driver=”org.gjt.mm.mysql.Driver”; String url=”jdbc:mysql://localhost:3306/shiro-1115″; String user=”root”; String password=”123456″;
//加载驱动 try { Class.forName(driver); return DriverManager.getConnection(url, user, password); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return null; }
public static void main(String[] args) { System.out.println(DbUitls.getConnection()); }
} |
4.5. 第五步:实现权限验证
—权限验证代码
package cn.gzsxt.realm;
import java.sql.SQLException; import java.util.List;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource;
import cn.gzsxt.dao.AdminDAO; import cn.gzsxt.dao.PermissionDAO; import cn.gzsxt.dao.RoleDAO; import cn.gzsxt.pojo.Admin; import cn.gzsxt.pojo.Permission; import cn.gzsxt.pojo.Role;
public class MyRealm extends AuthorizingRealm {
private AdminDAO adminDAO = new AdminDAO(); private RoleDAO roleDAO=new RoleDAO(); private PermissionDAO permissionDAO =new PermissionDAO();
/** * 权限验证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println(“=权限验证==”);
// 连接数据库,通过用户名查询用户信息 // 1.获得认证信息,账号名 String adminAccount = (String) token.getPrincipal(); // 2.通过账号名查询查询指定的管理员记录 try { Admin admin = adminDAO.findByAccount(adminAccount); //密码加盐 ByteSource salt=ByteSource.Util.bytes(“asdfwera”+admin.getAdminSalt()); SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(admin,admin.getAdminPwd(),salt,this.getName()); return authenticationInfo; } catch (SQLException e) { e.printStackTrace(); }
return null; }
/** * 权限授权,授予必须依赖权限验证通过 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
}
}
|
—配置文件修改
#[users] # admin = 123456,roleAdmin #[roles] # roleAdmin = user:list,user:list:add,user:list:to_add
[main] #自定义Realm myRealm = cn.gzsxt.realm.MyRealm #创建一个Hash算法的对象 credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher #设置hash算法是Md5算法 credentialsMatcher.hashAlgorithmName=md5 #设置迭代加密的次数 credentialsMatcher.hashIterations=2 #设置是否加盐 credentialsMatcher.hashSalted=true
#指定自定义Realm支持hash算法加密的密码 myRealm.credentialsMatcher=$credentialsMatcher securityManager.realms=$myRealm |
4.6. 第六步:实现权限授予
package cn.gzsxt.realm;
import java.sql.SQLException; import java.util.List;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource;
import cn.gzsxt.dao.AdminDAO; import cn.gzsxt.dao.PermissionDAO; import cn.gzsxt.dao.RoleDAO; import cn.gzsxt.pojo.Admin; import cn.gzsxt.pojo.Permission; import cn.gzsxt.pojo.Role;
public class MyRealm extends AuthorizingRealm {
private AdminDAO adminDAO = new AdminDAO(); private RoleDAO roleDAO=new RoleDAO(); private PermissionDAO permissionDAO =new PermissionDAO();
/** * 权限验证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println(“=权限验证==”);
// 连接数据库,通过用户名查询用户信息 // 1.获得认证信息,账号名 String adminAccount = (String) token.getPrincipal(); // 2.通过账号名查询查询指定的管理员记录 try { Admin admin = adminDAO.findByAccount(adminAccount); //密码加盐 ByteSource salt=ByteSource.Util.bytes(“asdfwera”+admin.getAdminSalt()); SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(admin,admin.getAdminPwd(),salt,this.getName()); return authenticationInfo; } catch (SQLException e) { e.printStackTrace(); }
return null; }
/** * 权限授权,授予必须依赖权限验证通过 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //获得当前验证通过的管理员 Admin admin=(Admin) principalCollection.getPrimaryPrincipal(); //通过当前管理员获得角色编号 Long roleId = admin.getRoleId(); try { //通过角色编号查询唯一的角色(单角色权限验证) Role role = roleDAO.findByRoleId(roleId); //将角色信息放在管理员的实体类里面 admin.setRole(role);
//通过就是的权限ID集,获得角色的权限 String rolePermissions = role.getRolePermissions(); List<Permission> permissions = permissionDAO.findByIds(rolePermissions); //将角色的权限赋予角色对象 role.setPermissions(permissions);
//创建一个权限授予对象 SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo(); //将角色设置给该对象 authorizationInfo.addRole(role.getRoleName()); //将权限设置给Shiro的权限对象 for (Permission permission : permissions) { authorizationInfo.addStringPermission(permission.getPermissionKey()); } //返回 return authorizationInfo;
} catch (SQLException e) { e.printStackTrace(); }
return null; }
}
|
5. 总结
- Shiro是什么
一个权限控制框架。
- Shiro的作用是什么
就是在实现Rbac系统的时候,使用它来做权限验证,可以减少我们的开发的代码。
- 我们将使用的API记住
IniSecurityManagerFactory : 用于加载配置文件,创建SecurityManager对象
SecurityManager :就是整个Shiro的控制对象
SecurityUtils :SecurityManager 工具类,用于获得Subject对象
Subject :身份类,存储返回的数据信息、提供了校验的权限的方法
UsernamePasswordToken 身份信息构建类 (Token 令牌,作用就是传入校验参数)
AuthorizingRealm 支持校验与授权的Realm
AuthenticationInfo 校验成功返回的信息的父接口
SimpleAuthenticationInfo 校验成功返回信息类
Md5Hash Md5加密类
ByteSource 字节码处理工具类,我们在构造Md5加盐时使用到。
HashedCredentialsMatcher Md5算法校验器,用于支持Md5校验
AuthorizationInfo 授权成功返回的信息类的父接口
PrincipalCollection 授予是获得验证信息的类
SimpleAuthorizationInfo 授权成功返回的信息类的实现类
|
6. Shiro整合SpringMVC
6.1. 说明
上面的知识,我们已经了解Shiro的权限授权、权限校验、Md5的加密密码的配置。
但是,我们的需求将Shiro框架用到Web项目。
所以,我们需要使用Shiro整合SpringMVC使用!!!
6.2. 整合步骤
6.2.1. 第一部分:SpringMVC框架的配置
6.2.1.1. 步骤说明
- 导入依赖的Jar包
- 构建一个请求页面
- 创建业务控制器
- 配置web.xml的核心控制器
- 创建配置文件
- 构建一个返回页面
6.2.1.2. 第一步:导入依赖的Jar包
|
6.2.1.3. 第二步:构建一个请求页面
<%@ page language=“java” contentType=“text/html; charset=UTF-8” pageEncoding=“UTF-8”%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=“Content-Type” content=“text/html; charset=UTF-8”> <title>Insert title here</title> </head> <body> <a href=“${pageContext.request.contextPath }/admin/addAdmin”>addAdmin</a> </body> </html> |
6.2.1.4. 第三步:创建业务控制器
package cn.gzsxt.controller;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.context.annotation.SessionScope;
@Controller @SessionScope @RequestMapping(value=”admin”) public class AdminController {
@RequestMapping(value=”/addAdmin”) public String addAdmin(){ System.out.println(“=增加管理员=”); return “/index.jsp”; }
}
|
6.2.1.5. 第四步:配置web.xml的核心控制器
<?xml version=“1.0” encoding=“UTF-8”?> <web-app xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns=“http://xmlns.jcp.org/xml/ns/javaee” xsi:schemaLocation=“http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd” id=“WebApp_ID” version=“3.1”> <display-name>shiro-demo-08-springmvc</display-name>
<!– 配置核心控制器。拦截器所有的请求 –> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!– 指定配置文件的路径 –> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-*.xml</param-value> </init-param> <!– 配置启动的时候就创建对象 –> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
<welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> |
6.2.1.6. 第五步:创建配置文件
–SpringMVC配置文件,spring-mvc.xml
<?xml version=“1.0” encoding=“UTF-8”?> <beans xmlns=“http://www.springframework.org/schema/beans” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:mvc=“http://www.springframework.org/schema/mvc” xsi:schemaLocation=“http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd”>
<!– 启动默认注解支持 –> <mvc:annotation-driven /> <!– 放开静态资源访问 –> <mvc:default-servlet-handler/>
</beans>
|
–Spring容器配置文件,spring-context.xml
<?xml version=“1.0” encoding=“UTF-8”?> <beans xmlns=“http://www.springframework.org/schema/beans” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:context=“http://www.springframework.org/schema/context” xsi:schemaLocation=“http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd”>
<!– 扫描组件配置 –> <context:component-scan base-package=“cn.gzsxt” /> </beans>
|
6.2.2. 第二部分:Shiro框架的配置
6.2.2.1. 配置步骤说明
- 导入依赖的Jar包
- 创建Shiro.ini配置文件
- 创建一个入口测试类
- 创建一个Realm
6.2.2.2. 第一步:导入依赖的Jar包
|
6.2.2.3. 第二步:创建Shiro.ini配置文件
[users] admin = 123456,adminRole [roles] adminRole = admin:list,admin:add,admin:delete,admin:edit |
6.2.2.4. 第三步:创建一个入口测试类
package cn.gzsxt.shiro.test;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject;
public class ShiroTest {
public static void main(String[] args) { //第一步:加载ini配置文件,获得SecurityManagerFactory对象 IniSecurityManagerFactory factory=new IniSecurityManagerFactory(“classpath:shiro.ini”); //第二步:获得SecurityManager对象 SecurityManager securityManager = factory.createInstance(); //第三步:获得Subject身份对象 SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject();
//第四步:构建一个身份信息对象(Token) UsernamePasswordToken token=new UsernamePasswordToken(“admin”,”123456″);
//第五步:login操作。校验权限 Subject resultSubject = securityManager.login(subject, token);
//第六步:校验是否成功 boolean isAuth= resultSubject.isAuthenticated(); System.out.println(isAuth);
}
}
|
6.2.2.5. 第四步:创建一个Realm
—修改配置文件
#[users] # admin = 123456,adminRole #[roles] # adminRole = admin:list,admin:add,admin:delete,admin:edit
[main] myRealm = cn.gzsxt.realm.MyRealm securityManager.realms=$myRealm |
–MyRealm类
package cn.gzsxt.realm;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection;
public class MyRealm extends AuthorizingRealm {
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println(“=权限校验=”); try { if (token.getPrincipal().equals(“admin”)) { SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(token.getPrincipal(), “123456”, this.getName()); return info; } } catch (Exception e) { e.printStackTrace(); }
return null; }
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(); info.addRole(“role_admin”); info.addStringPermission(“user:add”); info.addStringPermission(“user:list”); //info.addStringPermission(“user:edit”);
return info;
}
}
|
6.2.2.6. 第五步:测试结果
—运行ShiroTest测试类,结果为:
|
6.2.3. 第三部分:SpringMVC整合Shiro
6.2.3.1. 说明
- 权限控制是对谁的控制?,就是对url访问权限的控制。
- Filter的优先级别高于Servlet。而Shiro是使用Filter拦截的,SpringMVC是使用Servlet拦截请求的。那么我们如何让Shiro交给SpringMVC代理呢?
答:Spring提供了一个Filter代理类,可以让Spring容器代理Filter的操作,DelegatingFilterProxy。实现了在过滤里面可以调用Spring容器的对象,把我们原来配置在web.xml的过滤器配置在Spring配置文件里面。
6.2.3.2. Shiro提供的过滤器说明
在org.apache.shiro.web.filter.mgt.DefaultFilter枚举里面定义可以使用的过滤器以及对于的枚举值
|
anon : 匿名过滤器,该过滤器的作用就是用于配置不需要授权就可以直接访问的路径。
authc: 校验过滤器,该过滤器的作用就是用于必须经过校验才可以访问的路径(url)
logout: 注销过滤器, 该过滤器的作用就是用于注销退出登录。
perms:权限验证过滤器,用于权限验证的
6.2.3.3. 配置步骤说明
- 在web.xml配置过滤器代理类DelegatingFilterProxy,让Filter交个Spring容器代理
- 创建一个spring-shiro.xml配置文件
6.2.3.4. 第一步:配置web.xml
<filter> <filter-name>securityFilterBean</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <!– 将生命周期交给Spring代理 –> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>securityFilterBean</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
6.2.3.5. 第二步:创建Shiro.xml配置文件
<?xml version=“1.0” encoding=“UTF-8”?> <beans xmlns=“http://www.springframework.org/schema/beans” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xsi:schemaLocation=“http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd”>
<!– 配置拦截器调用的对象 注意事项:bean的名字必须要和web.xml的DelegatingFilterProxy的过滤的name属性一一对应。 –> <bean name=“securityFilterBean” class=“org.apache.shiro.spring.web.ShiroFilterFactoryBean”> <!– 指定跳转到校验不成功的路径 ,不指定会跳转上一次访问菜单页面 –> <!– <property name=”unauthorizedUrl” value=”/undo”></property> –> <!– 指定依赖的securityManager对象 –> <property name=“securityManager” ref=“securityManager”></property> <!– 指定 登录请求的路径 –> <property name=“loginUrl” value=“/admin/loginAdmin”></property> <!– 登录成功跳转的路径 –> <property name=“successUrl” value=“/index”></property> <!– 用于指定执行的过滤器链 ,配置多个过滤器连接对的url –> <property name=“filterChainDefinitions”> <value> <!– 指定url与过滤器的关系 –> <!– 所有的路径都要拦截 –> /admin/toLogin = anon /** = authc </value> </property>
</bean>
<!– 2.指定securityManager的对象 –> <bean name=“securityManager” class=“org.apache.shiro.web.mgt.DefaultWebSecurityManager”>
<property name=“realm” ref=“myRealm” />
</bean>
<!– 3.创建一个Realm对象 –> <bean name=“myRealm” class=“cn.gzsxt.realm.MyRealm”></bean>
<!– 4.Spring容器对shiro生命周期的管理 ,基于注解权限拦截需要配置–> <bean name=“lifecycleBeanPostProcessor” class=“org.apache.shiro.spring.LifecycleBeanPostProcessor”></bean> <bean class=“org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator” depends-on=“lifecycleBeanPostProcessor”/>
</beans> |
6.2.3.6. 第三步:权限控制器标签的使用
<%@ page language=“java” contentType=“text/html; charset=UTF-8” pageEncoding=“UTF-8”%> <%@taglib prefix=“shiro” uri=“http://shiro.apache.org/tags” %> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=“Content-Type” content=“text/html; charset=UTF-8”> <title>Insert title here</title> </head> <body> 主页 <a href=“${pageContext.request.contextPath }/logout”>退出</a><br/> <shiro:hasPermission name=“user:list”> <!– 如果有user:list权限才显示 用户列表 –> <a href=“#”> 用户列表</a><br/> </shiro:hasPermission> <shiro:hasPermission name=“user:add”> <!– 如果有user:add权限才显示 用户增加 –> <a href=“#”> 用户增加</a><br/> </shiro:hasPermission> <shiro:hasPermission name=“user:edit”> <!– 如果有user:edit权限才显示 用户编辑 –> <a href=“#”> 用户编辑</a><br/> </shiro:hasPermission> <shiro:hasPermission name=“user:delete”> <!– 如果有user:delete权限才显示 用户删除 –> <a href=“#”> 用户删除</a><br/> </shiro:hasPermission> </body> </html> |
7. Shiro整合SpringMVC基于注解
7.0.1. 配置流程说明
7.0.2. 第一步:配置web.xml
<filter> <filter-name>securityFilterBean</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <!– 将生命周期交给Spring代理 –> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>securityFilterBean</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
7.0.3. 第二步:配置Shiro配置类
package cn.gzsxt.config;
import java.util.LinkedHashMap; import java.util.Map;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
import cn.gzsxt.realm.ShiroRealm;
@Configuration public class ShiroConfig {
//1.配置securityFilterBean对象 @Bean(name=”securityFilter”) public Object getShiroFilterFactory() { ShiroFilterFactoryBean factory=new ShiroFilterFactoryBean();
//设置属性 factory.setLoginUrl(“/login”); factory.setSuccessUrl(“/index”); factory.setSecurityManager(this.getSecurityManager()); //基于配置的权限过滤器,顺序执行,所以使用LinkedHashMap Map<String, String> filterChainDefinition=new LinkedHashMap<>(); filterChainDefinition.put(“/**”, “authc”); factory.setFilterChainDefinitionMap(filterChainDefinition);
Object securityFilter=null; try { securityFilter = factory.getObject(); } catch (Exception e) { e.printStackTrace(); } return securityFilter; }
//2.获得securityManager对象 @Bean(name=”securityManager”) public DefaultWebSecurityManager getSecurityManager() { DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager(); //设置需要的realm对象 securityManager.setRealm(this.getRealm()); return securityManager; }
//3.配置realm对象 @Bean public ShiroRealm getRealm(){ ShiroRealm realm=new ShiroRealm(); return realm; } } |
相关文章