shiro 权限控制框架

一。 shiro简介

Apache Shiro(发音为“shee-roh”,日语“堡垒(Castle)”的意思)是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可为任何应用提供安全保障 - 从命令行应用、移动应用到大型网络及企业应用。
Shiro为解决下列问题(我喜欢称它们为应用安全的四要素)提供了保护应用的API:

  • 认证 - 用户身份识别,常被称为用户“登录”;
  • 授权 - 访问控制;
  • 密码加密 - 保护或隐藏数据防止被偷窥;
  • 会话管理 - 每用户相关的时间敏感的状态。
Shiro还支持一些辅助特性,如Web应用安全、单元测试和多线程,它们的存在强化了上面提到的四个要素

从2003年至今,框架选择方面的情况已经改变了不少,但今天仍有令人信服的理由让你选择Shiro。其实理由相当多,Apache Shiro:

  • 易于使用 - 易用性是这个项目的最终目标。应用安全有可能会非常让人糊涂,令人沮丧,并被认为是“必要之恶”【译注:比喻应用安全方面的编程。】。若是能让它简化到新手都能很快上手,那它将不再是一种痛苦了。
  • 广泛性 - 没有其他安全框架可以达到Apache Shiro宣称的广度,它可以为你的安全需求提供“一站式”服务。
  • 灵活性 - Apache Shiro可以工作在任何应用环境中。虽然它工作在Web、EJB和IoC环境中,但它并不依赖这些环境。Shiro既不强加任何规范,也无需过多依赖。
  • Web能力 - Apache Shiro对Web应用的支持很神奇,允许你基于应用URL和Web协议(如REST)创建灵活的安全策略,同时还提供了一套控制页面输出的JSP标签库。
  • 可插拔 - Shiro干净的API和设计模式使它可以方便地与许多的其他框架和应用进行集成。你将看到Shiro可以与诸如Spring、Grails、Wicket、Tapestry、Mule、Apache Camel、Vaadin这类第三方框架无缝集成。

支持 - Apache Shiro是Apache软件基金会成员,这是一个公认为了社区利益最大化而行动的组织。项目开发和用户组都有随时愿意提供帮助的友善成员。像Katasoft这类商业公司,还可以给你提供需要的专业支持和服务。


二、shiro的核心概念

        Subject,SecurityManager和Realms 

Subject主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;

SecurityManager安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;

Realm域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

也就是说对于我们而言,最简单的一个Shiro应用(带有图解):

       1、应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager;

       2、我们需要给Shiro的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断。

                              


从以上也可以看出,Shiro不提供维护用户/权限,而是通过Realm让开发人员自己注入。

    接下来我们来从Shiro内部来看下Shiro的架构,如下图所示:

                     

Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;

SecurityManager:相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。

Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得Shiro默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;

Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;

Realm:可以有1个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC实现,也可以是LDAP实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的Realm;

SessionManager:如果写过Servlet就应该知道Session的概念,Session呢需要有人去管理它的生命周期,这个组件就是SessionManager;而Shiro并不仅仅可以用在Web环境,也可以用在如普通的JavaSE环境、EJB等环境;所有呢,Shiro就抽象了一个自己的Session来管理主体与应用之间交互的数据;这样的话,比如我们在Web环境用,刚开始是一台Web服务器;接着又上了台EJB服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到Memcached服务器);


javaSE的案例
     创建一个maven项目 引入依赖
		<!-- shiro权限管理所需jar包 -->
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-core</artifactId>
	<version>1.4.0</version>
</dependency>
<!-- 登陆所需jar包 -->
<dependency>
	<groupId>commons-logging</groupId>
	<artifactId>commons-logging</artifactId>
	<version>1.2</version>
</dependency>



因为在javaSE的环境下  需要去文件中读取信心 shiro读取的文件为ini文件   在resource资源目录下创建一个ini文件

  

# =======================  
# Shiro INI configuration  
# =======================  
  
[main]  
  
[users]  
# 设置用户信息  
# 语法是 username = password, roleName1, roleName2, …, roleNameN  
jiaozi = 123456,role1  
  
[roles]  
# 角色信息和角色拥有的权限  
#语法是 rolename = permissionDefinition1, permissionDefinition2, …, permissionDefinitionN  
#权限的语法 * 表示所有权限  一般语法是 权限类型.权限动作.权限的资源id  比如  user:delete:1 表示拥有删除1号用户的权限  user:delete:*表示删除所有用户权限  
admin = *  
role1 = user:query:*, user:delete:1  
  
[urls]  
# web中的url过滤  

main方法启动

package cn.et.lesson01;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;

public class TestShiro {

	public static void main(String[] args) {
		
		//从配置文件中读取用户的权限信息    用来测试不用管是否过期
        Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:my.ini");  
        org.apache.shiro.mgt.SecurityManager securityManager = (org.apache.shiro.mgt.SecurityManager)factory.getInstance();  
        SecurityUtils.setSecurityManager(securityManager);  
		//获取当前用户
		Subject currentUser = SecurityUtils.getSubject();
		//获取当前用户的会话
		//Session session = currentUser.getSession();
		/**
		 * 用户包括两个部分
		 * 	principals and credentials
		 * 		principals(本人)表示用户的标识信息 比如用户名  用户地址
		 * 		credentials(凭证) 表示用户用于登陆的凭证 比如密码  证书等
		 * 
		 */
		//判断是否登陆
		if ( !currentUser.isAuthenticated() ) {
			//使用用户的登录信息创建令牌
		    UsernamePasswordToken token = new UsernamePasswordToken("jiaozi", "123456");
		    //登录时抓取错误  账号密码正取会登录成功
		    try {
		    	 currentUser.login(token);
		    	 System.out.println("登陆成功");
		    	 //检查登录后的用户名是否拥有某个角色
				    if(currentUser.hasRole("role1")){
				    	System.out.println("拥有role1角色");
				    }
				    //检查登陆是否的用户是否有某个权限
				    if(currentUser.isPermitted("user:query:1")){
				    	System.out.println("有查询1号用户的权限");
				    }
		    } catch ( UnknownAccountException uae ) {
		       System.out.println("账号错误");
		    } catch ( IncorrectCredentialsException ice ) {
		       System.out.println("密码不匹配");
		    } catch ( LockedAccountException lae ) {
		    	System.out.println("账号锁定");
		    }catch ( AuthenticationException ae ) {
		       System.out.println("未知异常");
		    }
		}
	}
}
控制台如下图 测试成功


          



javaEE案例(还是用的是ini文件)

   引入所需的jar包

<!-- shiro权限管理所需jar包 -->
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-core</artifactId>
	<version>1.4.0</version>
</dependency>

<!-- 登陆所需jar包 -->
<dependency>
	<groupId>commons-logging</groupId>
	<artifactId>commons-logging</artifactId>
	<version>1.2</version>
</dependency>

<!-- shiro集成web 所需jar包 -->
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-web</artifactId>
	<version>1.4.0</version>
</dependency>

在WEB-INF中创建一个shiro.ini文件

# =======================  
# Shiro INI configuration  
# =======================  
  
[main]
  #认证不通过(用户输入密码账号登陆)
  authc.loginUrl = /login.html
  #授权不通过(没有某个角色和权限)
  roles.loginUrl=/login.html
  roles.unauthorizedUrl=/no.html
  
  perms.loginUrl=/login.html
  perms.unauthorizedUrl=/no.html
[users]  
# 设置用户信息  
# 语法是 username = password, roleName1, roleName2, …, roleNameN  
jiaozi = 123456,role1  
  
[roles]  
# 角色信息和角色拥有的权限  
#语法是 rolename = permissionDefinition1, permissionDefinition2, …, permissionDefinitionN  
#权限的语法 * 表示所有权限  一般语法是 权限类型.权限动作.权限的资源id  比如  user:delete:1 表示拥有删除1号用户的权限  user:delete:*表示删除所有用户权限  
admin = *  
role1 = user:query:*, user:delete:1  



# web中的url过滤    
#語法是  某個路徑  = 怎麼樣過濾1,過濾2   常用的過濾有  
#  anon 匿名用戶   
#  authc 表示用戶和密碼驗證過濾 類 org.apache.shiro.web.filter.authc.FormAuthenticationFilter 沒有登錄自動跳轉到登錄頁  
#  perms 是否擁有某些權限過濾 類 org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter 用法 perms["remote:invoke"]  
#   roles是否擁有某個角色   org.apache.shiro.web.filter.authz.RolesAuthorizationFilter  用法roles[administrator]  
#  user 是否是某個用戶 org.apache.shiro.web.filter.authc.UserFilter   
# 也可以在main中自定義filter url就可以應用 參考http://shiro.apache.org/web.html#programmatic-support  

[urls]  
# web中的url过滤  
/login.html=anon
/suc.jsp=authc
#访问role.html需要role2的角色
/role.html=roles[role2]
#/suc.jsp=perms[user:delete:2]

创建一个servlet

package cn.et.lesson01.controller;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;

/**
 * Servlet implementation class LogonController
 */
public class LogonController extends HttpServlet {
	private static final long serialVersionUID = 1L;

    /**
     * Default constructor. 
     */
    public LogonController() {
        // TODO Auto-generated constructor stub
    }

	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//获取前台的账号密码
		String username=request.getParameter("userNamo");
		String pwd=request.getParameter("possword");
		
		//获取当前用户
		Subject currentUser = SecurityUtils.getSubject();
		//获取当前用户的会话
		Session session = currentUser.getSession();
		UsernamePasswordToken token = new UsernamePasswordToken(username, pwd);
		try {
	    	 currentUser.login(token);
	    	 
	    	 //跳转到成功页面
	    	 request.getRequestDispatcher("/suc.jsp").forward(request, response);
	    	 
	    } catch ( UnknownAccountException uae ) {
	       System.out.println("账号错误");
	    } catch ( IncorrectCredentialsException ice ) {
	       System.out.println("密码不匹配");
	    } catch ( LockedAccountException lae ) {
	    	System.out.println("账号锁定");
	    }catch ( AuthenticationException ae ) {
	       System.out.println("未知异常");
	    }
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}
}


web.xml的配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name>ShiroLesson</display-name>
  <!-- 添加shiro支持的过滤器和ini文件路径配置参数 -->
  <context-param>
    <param-name>shiroConfigLocations</param-name>
    <param-value>/WEB-INF/shiro.ini</param-value>
  </context-param>
  <listener>
    <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
  </listener>
  <filter>
    <filter-name>ShiroFilter</filter-name>
    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>ShiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>ERROR</dispatcher>
  </filter-mapping>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
    <description></description>
    <display-name>LogonController</display-name>
    <servlet-name>LogonController</servlet-name>
    <servlet-class>cn.et.lesson01.controller.LogonController</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>LogonController</servlet-name>
    <url-pattern>/LogonController</url-pattern>
  </servlet-mapping>
</web-app>


登录html的页面

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form action="LogonController" method="post">  
    	 用戶名 :<input type="text" name="userName"/>  
  		 密碼:<input type="text" name="password"/>  
   		<input type="submit">  
	</form> 
</body>
</html>


其他页面自行创建(失败,成功等页面)


javaEE(读取数据库)

   维护数据库


                                  用户表

   

                                     角色表

    

                                      权限表

        

                                    菜单表

             

                          用户与角色的关系表

     

                    角色与权限关系表

              


             用户与菜单的关系表

                         

         

    内部的树据构部分的数据库结构 请参考 http://blog.csdn.net/panhaigang123/article/details/78820550

    

  整个学习的代码链接 https://github.com/panhaigang/ShiroLesson



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值