Shiro权限框架

--观点:html

学习任何的知识,咱们首先要知道它是什么,而后经过是什么(定义)来分析它的做用、行为。从而圈定学习的范围。咱们将这个过程称为,学习思路!!java

 

 

1. Shiro概述

1.1. Shiro是什么

官方说明:mysql

href="#what-is-apache-shiro-" What is Apache Shiro?git

Apache Shiro is a powerful and flexible open-source security framework that cleanly handles authentication, authorization, enterprise session management and cryptography.github

 

Shiro是一个很是强大的、易于使用的、开源的、权限框架。它包括了权限校验权限授予、会话管理、安全加密等组件。web

 

1.2. 为何须要使用Shiro(兴趣点)

若是你是须要设计RBACRole Based Access Control)基础系统,须要编写大量用于权限控制的代码时。那么你须要使用Shiro。由于Shiro已经将RBAC系统大量的代码封装好,能够减小咱们大量的工做量。算法

 

如:页面的显示的HTML控件根据登陆用户的权限不一样而不一样。使用Shiro能够轻松解决。spring

1.3. Shiro的下载

shiro的下载路径:http://shiro.apache.org/download.htmlsql

 

--全部包的说明,根据列表的说明,下载咱们须要的jar数据库

包名

Maven 坐标

说明

shiro-all

Not Recommended

Includes all binary functionality for Shiro

shiro-core

<dependency> 
  <groupId>org.apache.shiro</groupId>    
  <artifactId>shiro-core</artifactId>  
  <version>1.3.2</version> 
</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>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-web</artifactId>
  <version>1.3.2</version>
</dependency>

Enables support for web-based applications.

 

支持基于Web的应用

shiro-aspectj

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-aspectj</artifactId>
  <version>1.3.2</version>
</dependency>

Enables AspectJ support for Shiro AOP and Annotations.

shiro-cas

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-cas</artifactId>
  <version>1.3.2</version>
</dependency>

Enables Jasig CAS support.
 NOTE:
Shiro-CAS support is deprecated, support has been moved to

the Apache Shiro based buji-pac4j project.

cas单点登陆框架的支持

shiro-ehcache

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-ehcache</artifactId>
  <version>1.3.2</version>
</dependency>

Enables Ehcache-based famework caching.

echche缓存框架的支持

shiro-hazelcast

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-hazelcast</artifactId>
  <version>1.3.2</version>
</dependency>

Enables Hazelcast-based famework caching.

shiro-features

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-features</artifactId>
  <version>1.3.2</version>
</dependency>

OSGi / Apache Karaf integration.

shiro-guice

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-guice</artifactId>
  <version>1.3.2</version>
</dependency>

Enables Google Guice integration.

 

guice 相似springioc框架。

shiro-quartz

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-quartz</artifactId>
  <version>1.3.2</version>
</dependency>

Enables Quartz-based scheduling for Shiro native session validation.

 

quartz定时器框架

shiro-spring

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring</artifactId>
  <version>1.3.2</version>
</dependency>

Enables Spring Framework integration.

 

1.4. 包说明

--Shiro经常使用包

 

 

 

1.5. Shiro结构图

 

 

 

Authentication:权限校验,每次操做校验用户是否有访问权限

Authorization:受权,用户登陆时,授予用户对应的权限

Session Management:会话管理,用于记录用户的登陆状态

Cryptography:加密,加密算法的实现(SHAMD5

web Support:对Web项目的支持,Shiro的标签!!

 

 

 

 

2. Shiro入门

2.1. 访问流程图

咱们知道学习任何的框架,都是从配置流程开始的。咱们咱们学习Shiro也是同样。因此首先要了解Shiro的访问流程。从而知道配置的配置,快速入门。

 

 

 

 

  1. 首先应用访问(可使用远程调用,能够是Web请求等),Shiro经过一个Subject对象来标识当前访问的身份。这句话告诉咱们,第一次访问的时候,Shiro确定会建立一个Subject对象标签当前请求(用户)的身份。
  2. SecurityManger容器建立一个Subject对象验证请求的参数,SecurityManager的做用是统一管理Subject。这句话意味着,一个SecurityManager对象管理多个Subject的对象。
  3. Subject经过SecurityManger得到操做当前用户的权限,在启动的那一刻,SecurityManger就会加载shiro.ini权限配置文件,在用户登陆成功后,能够根据shiro配置的信息,得到用户对应的权限。
  4. 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配置,编写权限认证信息。

 

注意事项:

  1. shiro.ini文件名能够任意编写,但后缀必须是ini
  2. 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. 访问流程图说明

 

 

 

 

 

如图所示:

  1. 咱们需经过Subject封装访问用户的信息
  2. 咱们须要一个SecurityManager对象来管理全部用户的权限
  3. 咱们须要ini配置文件配置得到Realm对象
  4. 咱们须要在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框架自带的密码加密的功能。

 

  1. SimpleHash类:用于生成指定的Hash算法。

 

 

 

  1. HashedCredentialsMatcher类:用于让Realm校验时,校验指定的Hash算法

 

 

  1. 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:校验的密码,从数据库查询出来的

//参数3realm的名字 。随便设置一个惟一字符串

     

//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. 返回的认证信息为一个实体(JavaBeanMap

注意:如今校验后返回的认证信息是一个字符串的用户名,而咱们若是将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:校验的密码,从数据库查询出来的

//参数3realm的名字 。随便设置一个惟一字符串

 

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:校验的密码,从数据库查询出来的

//参数3realm的名字 。随便设置一个惟一字符串

 

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. 总结

  1. Shiro是什么

一个权限控制框架。

  1. Shiro的做用是什么

就是在实现Rbac系统的时候,使用它来作权限验证,能够减小咱们的开发的代码。

  1. 咱们将使用的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. 步骤说明

  1. 导入依赖的Jar
  2. 构建一个请求页面
  3. 建立业务控制器
  4. 配置web.xml的核心控制器
  5. 建立配置文件
  6. 构建一个返回页面

 

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. 配置步骤说明

  1. 导入依赖的Jar
  2. 建立Shiro.ini配置文件
  3. 建立一个入口测试类
  4. 建立一个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. 说明

  1. 权限控制是对谁的控制?,就是对url访问权限的控制。
  2. 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. 配置步骤说明

  1. web.xml配置过滤器代理类DelegatingFilterProxy,让Filter交个Spring容器代理
  2. 建立一个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.xmlDelegatingFilterProxy的过滤的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;

}

}

相关文章
相关标签/搜索