shiro简介使用 与 web集成
shiro是apache的一个开源框架,是一个权限管理的框架,实现 用户认证、用户授权。spring中有spring security (原名Acegi),是一个权限框架,它和spring依赖过于紧密,没有shiro使用简单。shiro不依赖于spring,shiro不仅可以实现 web应用的权限管理,还可以实现c/s系统,分布式系统权限管理,shiro属于轻量框架,越来越多企业项目开始使用shi
1. shiro简介
shiro是apache的一个开源框架,是一个权限管理的框架,实现 用户认证、用户授权。spring中有spring security (原名Acegi),是一个权限框架,它和spring依赖过于紧密,没有shiro使用简单。 shiro不依赖于spring,shiro不仅可以实现 web应用的权限管理,还可以实现c/s系统,分布式系统权限管理,shiro属于轻量框架,越来越多企业项目开始使用shiro。
应用场景:
-
在独立应用中使用
-
在web中使用
-
在spring框架中集成。
shiro 解决应用安全的四要素:
-
认证 - 用户身份识别,常被称为用户“登录”
-
授权 - 访问控制
-
密码加密 - 保护或隐藏数据防止被偷窥
-
会话管理 - 与用户相关的时间敏感的数据
2. shiro优势
-
易用: 相当于其他安全框架,shiro比较简单易用
-
广泛: 使用非常广泛,资料好找
-
灵活: 可以工作在很多工作环境,web,ejb,ioc等等
-
web支持: 对web的支持好,允许你基于应用 URL 和 Web 协议(如 REST)创建灵活的安全策略,同时还提供了一套控制页面输出的 JSP 标签库
-
支持:应用广泛,是 Apache 软件基金会成员
3. 核心概念
Shiro的核心概念有三个:Subject,SecurityManager 和 Realms。
3.1 Subject
subject 被Shiro 描述为一个主体,这个主要可以是具体的用户(即:人),可以是一个系统,例如:我们开发的系统需要通过支付宝支付,那么系统对于支付宝来说就是一个主体。所以主体是一个抽象的概念。
在考虑安全时,最常考虑的两个问题,1)当前用户是谁(即,认证) 2)当前用户能被允许做什么?(即:授权)。 考虑应用安全的最自然方式就是基于当前用户。Shiro 的 API 用它的 Subject 概念从根本上体现了这种思考方式
在应用系统中,使用shiro可以方便的获取当前操作的主体:
//获取当前主体
Subject currentUser =SecurityUtils.getSubject();
//判断主体是否具有指定角色。
currentUser.hasRole("administrator")
在获取到Subject后,通过这个对象,可以对其执行绝大多数的安全操作:登录,退出,执行授权检查等。
Shiro 的api非常直观,它反映了开发者以“每个用户” 思考安全控制的自然趋势
3.2 SecurityManager
如果说Subject关注当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
SecurityManager 是 Shiro 框架的核心,充当“保护伞”引用了多个内部嵌套安全组件,它们形成了对象图。但是,一旦 SecurityManager 及其内部对象图配置好,它就会退居幕后,应用开发人员几乎把他们的所有时间都花在 Subject API 调用上。
一个应用只需要一个 SecurityManager,是一个单例对象。缺省实现是POJO,可以用POJO兼容的任何配置机制进行配置:普通的Java代码、Spring xml、YAML、和 ini 文件等。
SecurityManager是通过java代码或配置文件生成,用来管理所有用户的安全操作的对象,是单例的。
以ini配置文件为例:
// 1.装入 INI 配置
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2. 创建 SecurityManager
SecurityManager securityManager = factory.getInstance();
//3. 使其可访问
SecurityUtils.setSecurityManager(securityManager);
3.3 Realm
Realm 充当了 Shiro 与应用安全数据间的“桥梁”或者“连接器"。 从某种意义上讲,Realm 实际上就是一个安全相关的 DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给 Shiro。 Shiro 内置了一些Realm ,支持多种数据源的连接,如JDBC、LDAP、INI文件的连接等。另外,可以自定义Realm 实现,方便个性化的应用场景。
3.4 工作原理示意图
shiro用户认证时序图:
shiro访问授权时序图
注: subject: 提供针对具体用户的执行安全操作 SecurityManager: 配置所有用户的安全规则 Realm: 为SecurityManager提供数据源,这个数据源可以是配置文件,数据库,或第三方系统(例如需要使用QQ账号进行认证登录,则QQ为第三方系统,一般通过接口提供认证功能)
4. 典型安全场景
4.1 认证
一般的web应用认证就是指登录功能(占了大部分),也就是说,当用户使用应用进行认证时,他们就在证明他们就是自己所说的那个人(证明我是谁)。
认证典型的三个步骤: 1)获取用户信息,作为当事人的身份证明依据,shiro中称之为证书 2)将身份证明依据提交系统 3)如果身份证明有效则通过认证,否则认证失败。
4.2 授权
授权实质上就是访问控制, 控制已认证的用户能够访问应用的哪些资源。 多数用户执行访问控制是通过 角色 + 权限 的概念来完成的。角色是所有用户个体的一个分组,如管理员、普通用户、商家等;而权限 则表示具体能够操作的行为,比如查询所有用户、删除某些用户、修改信息等等,是与具体应用资源直接挂钩的。
用户 ---> 角色 ---> 权限
用户、角色、权限三者之间一般通过角色进行连接,用户和权限之间不直接绑定。
用户角色: 用户名=密码,角色1,角色2 角色权限: 角色=资源标识:操作 或者 角色=资源标识:操作:对象实例ID
导入所需要的依赖
<!-- shiro核心包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- 添加shiro web支持 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
shiro.ini配置文件 配置文件放入 resources
[users]
zs=123
ls=456
ww=789
shiro-role.ini 角色管理
[users]
zs=123,role1,role2,role3
ls=123,role2
ww=123,role3
演示代码
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.subject.Subject;
public class Test {
@org.junit.Test
public void test1() {
System.out.println("test1");
//1、获取SecurityManager工厂,此处使用Ini配置文件初始化
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2. 得到SecurityManager实例 并绑定给SecurityUtils
org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
//3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("ww", "789");
try {
//4、登录,即身份验证
subject.login(token);
} catch (AuthenticationException e) {
//5、身份验证失败
e.printStackTrace();
}
System.out.println(subject.isAuthenticated());
//6、退出
subject.logout();
}
@org.junit.Test
public void test2() {
System.out.println("test2");
//1、获取SecurityManager工厂,此处使用Ini配置文件初始化
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro-role.ini");
//2. 得到SecurityManager实例 并绑定给SecurityUtils
org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
//3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
Subject subject = SecurityUtils.getSubject();
//登录账号 测试
UsernamePasswordToken token = new UsernamePasswordToken("zs", "123");
try {
//4、登录,即身份验证
subject.login(token);
} catch (AuthenticationException e) {
//5、身份验证失败
}
System.out.println(subject.isAuthenticated());
//System.out.println(subject.hasRole("role1"));
//System.out.println(subject.hasRole("role2"));
//System.out.println(subject.hasRole("role3"));
//判断是否拥有莫权限
try {
subject.checkRole("role1");
//abcd
//233
} catch (AuthorizationException e) {
//e.printStackTrace();
System.out.println("11111111111");
}
try {
subject.checkRole("role2");
} catch (AuthorizationException e) {
//e.printStackTrace();
System.out.println("2222");
}
try {
subject.checkRole("role3");
} catch (AuthorizationException e) {
//e.printStackTrace();
System.out.println("3333");
}
try {
subject.checkRoles("role1", "role2", "role3");
//abcdeeee
} catch (AuthorizationException e) {
// e.printStackTrace();
System.out.println("444");
}
//6、退出
subject.logout();
}
@org.junit.Test
public void test3() {
System.out.println("test3");
//1、获取SecurityManager工厂,此处使用Ini配置文件初始化
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro-permission.ini");
//2. 得到SecurityManager实例 并绑定给SecurityUtils
org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
//3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("ww", "123");
try {
//4、登录,即身份验证
subject.login(token);
} catch (AuthenticationException e) {
//5、身份验证失败
}
try {
subject.checkPermission("user:update");
//subject.checkPermission("user:delete");
} catch (AuthorizationException e) {
e.printStackTrace();
}
//6、退出
subject.logout();
}
}
5. shiro与web集成
5.1 集成
1)配置文件shiro-web.ini
[main]
#定义身份认证失败后的请求url映射,loginUrl是身份认证过滤器中的一个属性
authc.loginUrl=/login.do
#定义角色认证失败后的请求url映射,unauthorizedUrl是角色认证过滤器中的一个属性
roles.unauthorizedUrl=/unauthorized.jsp
#定义权限认证失败后请求url映射,unauthorizedUrl是角色认证过滤器中的一个属性
perms.unauthorizedUrl=/unauthorized.jsp
[users]
zs=123,role1
ls=123,role2
ww=123,role3
zdm=123,admin
[roles]
role1=user:create
role2=user:create,user:update
role3=user:create,user:update,user:delete,user:view,user:load
admin=user:*
#定义请求的地址需要做什么验证
[urls]
#请求login的时候不需要权限,游客身份即可(anon)
/login.do=anon
#请求/user/updatePwd.jsp的时候,需要身份认证(authc)
/user/updatePwd.jsp=authc
#请求/student的时候,需要角色认证,必须是拥有teacher角色的用户才行
/admin/*.jsp=roles[admin]
#请求/teacher的时候,需要权限认证,必须是拥有user:create权限的角色的用户才行
/teacher=perms["user:create"]
2)通过监听器EnvironmentLoaderListener读取配置文件,来创建相应的WebEnvironment
可通过shiroConfigLocations参数,指定shiro的配置文件 shiroConfigLocations 默认是“/WEB-INF/shiro.ini”IniWebEnvironment默认是先从/ WEB-INF/shiro.ini加载, 如果没有就默认加载 classpath:shiro.ini。
3) 配置过滤器,放在web.xml的最前面。该过滤器拦截所有的请求进行安全验证。
<context-param>
<param-name>shiroConfigLocations</param-name>
<param-value>classpath:shiro-web.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>
</filter-mapping>
//登录
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.zking.test.controller.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login.do</url-pattern>
</servlet-mapping>
//退出
<servlet>
<servlet-name>LogoutServlet</servlet-name>
<servlet-class>com.zking.test.controller.LogoutServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LogoutServlet</servlet-name>
<url-pattern>/logout.do</url-pattern>
</servlet-mapping>
//默认跳转
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
5.2 url过滤器
shiro通过url过滤器链功能支持特定的过滤规则。示例:
[urls]
/assets/** = anon
/user/signup = anon
/user/** = user
/rpc/rest/** = perms[rpc:invoke], authc
/** = authc
对于每一行,等号左边的值表示相对上下文的 Web 应用路径。等号右边的值定义了过滤器链 - 一个逗号分隔的有序 Servlet 过滤器列表,它会针对给出的路径进行执行。 shiro内置的安全过滤器:
过滤器 | 示例 | 作用 |
---|---|---|
anon | /admins/**=anon | 表示可以匿名使用,如登录页面 |
authc | /admins/user/**=authc | 表示需要认证才可使用 |
roles | /admins/user/**=roles[admin] | 参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如/admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法 |
perms | /admins/user/*=perms[user:add:] | perms参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/*=perms["user:add:,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法 |
rest | /admins/user/**=rest[user] | 根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等 |
authcBasic | /admins/user/**=authcBasic | 没有参数表示httpBasic认证 |
过滤器 | 实现类 |
---|---|
anon | org.apache.shiro.web.filter.authc.AnonymousFilter |
authc | org.apache.shiro.web.filter.authc.FormAuthenticationFilter |
roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter |
perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter |
rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter |
authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter |
1.实例代码!
登录 servlet
package com.zking.test.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("login doPost");
String username = request.getParameter("username");
String password = request.getParameter("password");
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);
request.getSession().setAttribute("username", username);
request.getRequestDispatcher("/main.jsp").forward(request, response);
} catch (AuthenticationException e) {
request.getSession().setAttribute("message", "账号或密码错误");
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
退出service
package com.zking.test.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LogoutServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//request.getSession().invalidate();
System.out.println("logout...");
Subject subject = SecurityUtils.getSubject();
subject.logout();
response.sendRedirect(request.getContextPath()+"/login.jsp");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
登录界面 login.jsp
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2018/7/24
Time: 22:56
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>用户登陆</h1>
<div style="color: red">${message}</div>
<form action="login.do" method="post">
帐号:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="确定">
<input type="reset" value="重置">
</form>
</body>
</html>
主界面 mian.jsp
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2018/7/24
Time: 22:56
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="r" uri="http://shiro.apache.org/tags" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>主界面<%=System.currentTimeMillis()%>,欢迎您:[${sessionScope.username}]</h1>
<ul>
系统功能列表
<li>
<a href="admin/addUser.jsp">用户新增</a>
</li>
<li>
<a href="admin/listUser.jsp">用户查询</a>
</li>
<li>
<a href="admin/resetPwd.jsp">重置用户密码</a>
</li>
<li>
<a href="admin/updateUser.jsp">用户修改</a>
</li>
<li>
<a href="user/updatePwd.jsp">个人密码修改</a>
</li>
<li>
<a href="logout.do">退出系统</a>
</li>
</ul>
<ul>
shiro标签
<li>
<r:hasPermission name="user:create">
<a href="admin/addUser.jsp">用户新增</a>
</r:hasPermission>
</li>
<li>
<a href="admin/listUser.jsp">用户查询</a>
</li>
<li>
<a href="admin/resetPwd.jsp">重置用户密码</a>
</li>
<li>
<r:hasPermission name="user:update">
<a href="admin/updateUser.jsp">用户修改</a>
</r:hasPermission>
</li>
<li>
<a href="user/updatePwd.jsp">个人密码修改</a>
</li>
<li>
<a href="logout.do">退出系统</a>
</li>
</ul>
</body>
</html>
授权 未通过 跳转界面
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2018/7/24
Time: 23:08
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>未授权的操作</h1>
<div>
未授权的操作,请与管理员联系,或切换帐号重新<a href="logout.do">登陆</a>后再试!
</div>
</body>
</html>
别的测试界面 集
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)