一。 shiro简介
Apache Shiro(发音为“shee-roh”,日语“堡垒(Castle)”的意思)是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可为任何应用提供安全保障 - 从命令行应用、移动应用到大型网络及企业应用。
Shiro为解决下列问题(我喜欢称它们为应用安全的四要素)提供了保护应用的API:
- 认证 - 用户身份识别,常被称为用户“登录”;
- 授权 - 访问控制;
- 密码加密 - 保护或隐藏数据防止被偷窥;
- 会话管理 - 每用户相关的时间敏感的状态。
从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服务器);
<!-- 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