Shiro提供了与Web集成的支持,其通过一个ShiroFilter入口来拦截须要安全控制的URL。然后进行对应的控制,ShiroFilter相似于如Strut2/SpringMVC这样的web框架的前端控制器,其是安全控制的入口点,其负责读取配置(如ini配置文件),然后推断URL是否须要登录/权限等工作。

7.1 准备环境

1、创建webapp应用

此处我们使用了jetty-maven-plugin和tomcat7-maven-plugin插件。这样能够直接使用“mvn jetty:run”或“mvn tomcat7:run”直接执行webapp了。然后通过URLhttp://localhost:8080/chapter7/訪问就可以。

2、依赖

Servlet3

Java代码

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>

Servlet3的知识能够參考https://github.com/zhangkaitao/servlet3-showcase及Servlet3规范http://www.iteye.com/blogs/subjects/Servlet-3-1

shiro-web

Java代码

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.2</version>
</dependency>

其它依赖请參考源代码的pom.xml。

7.2 ShiroFilter入口

1、Shiro 1.1及曾经版本号配置方式

Java代码

<filter>
<filter-name>iniShiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.IniShiroFilter</filter-class>
<init-param>
<param-name>configPath</param-name>
<param-value>classpath:shiro.ini</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>iniShiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

1、使用IniShiroFilter作为Shiro安全控制的入口点,通过url-pattern指定须要安全的URL;

2、通过configPath指定ini配置文件位置。默认是先从/WEB-INF/shiro.ini载入,假设没有就默认载入classpath:shiro.ini,即默认相对于web应用上下文根路径;

3、也能够通过例如以下方式直接内嵌ini配置文件内容到web.xml

Java代码

<init-param>
<param-name>config</param-name>
<param-value>
ini配置文件贴在这
</param-value>
</init-param>

2、Shiro 1.2及以后版本号的配置方式

从Shiro 1.2開始引入了Environment/WebEnvironment的概念,即由它们的实现提供对应的SecurityManager及其对应的依赖。

ShiroFilter会自己主动找到Environment然后获取对应的依赖。

Java代码 收藏代码



org.apache.shiro.web.env.EnvironmentLoaderListener



通过EnvironmentLoaderListener来创建对应的WebEnvironment,并自己主动绑定到ServletContext,默认使用IniWebEnvironment实现。

能够通过例如以下配置改动默认实现及其载入的配置文件位置:

Java代码

<context-param>
<param-name>shiroEnvironmentClass</param-name>
<param-value>org.apache.shiro.web.env.IniWebEnvironment</param-value>
</context-param>
<context-param>
<param-name>shiroConfigLocations</param-name>
<param-value>classpath:shiro.ini</param-value>
</context-param>

shiroConfigLocations默认是“/WEB-INF/shiro.ini”,IniWebEnvironment默认是先从/WEB-INF/shiro.ini载入。假设没有就默认载入classpath:shiro.ini。

3、与Spring集成

Java代码

<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

DelegatingFilterProxy作用是自己主动到spring容器查找名字为shiroFilter(filter-name)的bean并把全部Filter的操作托付给它。然后将ShiroFilter配置到spring容器就可以:

Java代码

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!—忽略其它,详见与Spring集成部分 -->
</bean>

最后不要忘了使用org.springframework.web.context.ContextLoaderListener载入这个spring配置文件就可以。

由于我们如今的shiro版本号是1.2的,因此之后的測试都是使用1.2的配置。

7.3 Web INI配置

ini配置部分和之前的相比将多出对url部分的配置。

Java代码

[main]
#默认是/login.jsp
authc.loginUrl=/login
roles.unauthorizedUrl=/unauthorized
perms.unauthorizedUrl=/unauthorized
[users]
zhang=123,admin
wang=123
[roles]
admin=user:*,menu:*
[urls]
/login=anon
/unauthorized=anon
/static/**=anon
/authenticated=authc
/role=authc,roles[admin]
/permission=authc,perms["user:create"]

当中最重要的就是[urls]部分的配置,其格式是: “url=拦截器[參数],拦截器[參数]”;即假设当前请求的url匹配[urls]部分的某个url模式,将会执行其配置的拦截器。比方anon拦截器表示匿名訪问(即不须要登录就可以訪问);authc拦截器表示须要身份认证通过后才干訪问。roles[admin]拦截器表示须要有admin角色授权才干訪问;而perms[“user:create”]拦截器表示须要有“user:create”权限才干訪问。

url模式使用Ant风格模式

Ant路径通配符支持?

、*,注意通配符匹配不包含文件夹分隔符“/”:

?:匹配一个字符,如”/admin?”将匹配/admin1,但不匹配/admin或/admin2。

匹配零个或多个字符串。如/admin将匹配/admin、/admin123,但不匹配/admin/1;

:匹配路径中的零个或多个路径。如/admin/将匹配/admin/a或/admin/a/b。

url模式匹配顺序

url模式匹配顺序是依照在配置中的声明顺序匹配,即从头開始使用第一个匹配的url模式对应的拦截器链。

如:

Java代码

/bb/**=filter1
/bb/aa=filter2
/**=filter3

假设请求的url是“/bb/aa”。由于依照声明顺序进行匹配。那么将使用filter1进行拦截。

拦截器将在下一节具体介绍。接着我们来看看身份验证、授权及退出在web中怎样实现。

1、身份验证(登录)

1.1、首先配置须要身份验证的url

Java代码

/authenticated=authc
/role=authc,roles[admin]
/permission=authc,perms["user:create"]

即訪问这些地址时会首先推断用户有没有登录。假设没有登录默会跳转到登录页面,默认是/login.jsp。能够通过在[main]部分通过例如以下配置改动:

Java代码

authc.loginUrl=/login  

1.2、登录Servlet(com.github.zhangkaitao.shiro.chapter7.web.servlet.LoginServlet)

Java代码

@WebServlet(name = "loginServlet", urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String error = null;
String username = req.getParameter("username");
String password = req.getParameter("password");
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);
} catch (UnknownAccountException e) {
error = "用户名/密码错误";
} catch (IncorrectCredentialsException e) {
error = "用户名/密码错误";
} catch (AuthenticationException e) {
//其它错误,比方锁定。假设想单独处理请单独catch处理
error = "其它错误:" + e.getMessage();
}
if(error != null) {//出错了,返回登录页面
req.setAttribute("error", error);
req.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(req, resp);
} else {//登录成功
req.getRequestDispatcher("/WEB-INF/jsp/loginSuccess.jsp").forward(req, resp);
}
}
}

1、doGet请求时展示登录页面;

2、doPost时进行登录。登录时收集username/password參数,然后提交给Subject进行登录。假设有错误再返回到登录页面;否则跳转到登录成功页面(此处应该返回到訪问登录页面之前的那个页面,或者没有上一个页面时訪问主页)。

3、JSP页面请參考源代码。

1.3、測试

首先输入http://localhost:8080/chapter7/login进行登录,登录成功后接着能够訪问http://localhost:8080/chapter7/authenticated来显示当前登录的用户:

Java代码

${subject.principal}身份验证已通过。

当前实现的一个缺点就是,永远返回到同一个成功页面(比方首页),在实际项目中比方支付时假设没有登录将跳转到登录页面,登录成功后再跳回到支付页面;对于这样的功能大家能够在登录时把当前请求保存下来,然后登录成功后再重定向到该请求就可以。

Shiro内置了登录(身份验证)的实现:基于表单的和基于Basic的验证,其通过拦截器实现。

2、基于Basic的拦截器身份验证

2.1、shiro-basicfilterlogin.ini配置

Java代码

[main]
authcBasic.applicationName=please login
………省略users
[urls]
/role=authcBasic,roles[admin]

1、authcBasic是org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter类型的实例,其用于实现基于Basic的身份验证。applicationName用于弹出的登录框显示信息使用,如图:

2、[urls]部分配置了/role地址须要走authcBasic拦截器。即假设訪问/role时还没有通过身份验证那么将弹出如上图的对话框进行登录,登录成功就可以訪问。

2.2、web.xml

把shiroConfigLocations改为shiro-basicfilterlogin.ini就可以。

2.3、測试

输入http://localhost:8080/chapter7/role,会弹出之前的Basic验证对话框输入“zhang/123”就可以登录成功进行訪问。

3、基于表单的拦截器身份验证

基于表单的拦截器身份验证和【1】相似。可是更简单,由于其已经实现了大部分登录逻辑;我们仅仅须要指定:登录地址/登录失败后错误信息存哪/成功的地址就可以。

3.1、shiro-formfilterlogin.ini

Java代码

[main]
authc.loginUrl=/formfilterlogin
authc.usernameParam=username
authc.passwordParam=password
authc.successUrl=/
authc.failureKeyAttribute=shiroLoginFailure [urls]
/role=authc,roles[admin]

1、authc是org.apache.shiro.web.filter.authc.FormAuthenticationFilter类型的实例,其用于实现基于表单的身份验证。通过loginUrl指定当身份验证时的登录表单。usernameParam指定登录表单提交的用户名參数名。passwordParam指定登录表单提交的密码參数名。successUrl指定登录成功后重定向的默认地址(默认是“/”)(假设有上一个地址会自己主动重定向带该地址);failureKeyAttribute指定登录失败时的request属性key(默认shiroLoginFailure)。这样能够在登录表单得到该错误key显示对应的错误消息。

3.2、web.xml

把shiroConfigLocations改为shiro- formfilterlogin.ini就可以。

3.3、登录Servlet

Java代码

@WebServlet(name = "formFilterLoginServlet", urlPatterns = "/formfilterlogin")
public class FormFilterLoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String errorClassName = (String)req.getAttribute("shiroLoginFailure");
if(UnknownAccountException.class.getName().equals(errorClassName)) {
req.setAttribute("error", "用户名/密码错误");
} else if(IncorrectCredentialsException.class.getName().equals(errorClassName)) {
req.setAttribute("error", "用户名/密码错误");
} else if(errorClassName != null) {
req.setAttribute("error", "未知错误:" + errorClassName);
}
req.getRequestDispatcher("/WEB-INF/jsp/formfilterlogin.jsp").forward(req, resp);
}
}

在登录Servlet中通过shiroLoginFailure得到authc登录失败时的异常类型名,然后依据此异常名来决定显示什么错误消息。

4、測试

输入http://localhost:8080/chapter7/role,会跳转到“/formfilterlogin”登录表单,提交表单假设authc拦截器登录成功后,会直接重定向会之前的地址“/role”。假设我们直接訪问“/formfilterlogin”的话登录成功将直接到默认的successUrl。

4、授权(角色/权限验证)

4.1、shiro.ini

Java代码

[main]
roles.unauthorizedUrl=/unauthorized
perms.unauthorizedUrl=/unauthorized
[urls]
/role=authc,roles[admin]
/permission=authc,perms["user:create"]

通过unauthorizedUrl属性指定假设授权失败时重定向到的地址。roles是org.apache.shiro.web.filter.authz.RolesAuthorizationFilter类型的实例,通过參数指定訪问时须要的角色。如“[admin]”,假设有多个使用“,”切割,且验证时是hasAllRole验证,即且的关系。Perms是org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter类型的实例。和roles相似。仅仅是验证权限字符串。

4.2、web.xml

把shiroConfigLocations改为shiro.ini就可以。

4.3、RoleServlet/PermissionServlet

Java代码

@WebServlet(name = "permissionServlet", urlPatterns = "/permission")
public class PermissionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Subject subject = SecurityUtils.getSubject();
subject.checkPermission("user:create");
req.getRequestDispatcher("/WEB-INF/jsp/hasPermission.jsp").forward(req, resp);
}
}

Java代码

@WebServlet(name = "roleServlet", urlPatterns = "/role")
public class RoleServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Subject subject = SecurityUtils.getSubject();
subject.checkRole("admin");
req.getRequestDispatcher("/WEB-INF/jsp/hasRole.jsp").forward(req, resp);
}
}

4.4、測试

首先訪问http://localhost:8080/chapter7/login,使用帐号“zhang/123”进行登录,再訪问/role或/permission时会跳转到成功页面(由于其授权成功了);假设使用帐号“wang/123”登录成功后訪问这两个地址会跳转到“/unauthorized”即没有授权页面。

5、退出

5.1、shiro.ini

Java代码

[urls]
/logout=anon

指定/logout使用anon拦截器就可以,即不须要登录就可以訪问。

5.2、LogoutServlet

Java代码

@WebServlet(name = "logoutServlet", urlPatterns = "/logout")
public class LogoutServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
SecurityUtils.getSubject().logout();
req.getRequestDispatcher("/WEB-INF/jsp/logoutSuccess.jsp").forward(req, resp);
}
}

直接调用Subject.logout就可以,退出成功后转发/重定向到对应页面就可以。

5.3、測试

首先訪问http://localhost:8080/chapter7/login,使用帐号“zhang/123”进行登录。登录成功后訪问/logout就可以退出。

Shiro也提供了logout拦截器用于退出,其是org.apache.shiro.web.filter.authc.LogoutFilter类型的实例。我们能够在shiro.ini配置文件里通过例如以下配置完毕退出:

Java代码 收藏代码

[main]
logout.redirectUrl=/login [urls]
/logout2=logout

通过logout.redirectUrl指定退出后重定向的地址;通过/logout2=logout指定退出url是/logout2。这样当我们登录成功后然后訪问/logout2就可以退出。

演示样例源代码:https://github.com/zhangkaitao/shiro-example

Shiro学习(7)与Web整合的更多相关文章

  1. Apache Shiro学习-2-Apache Shiro Web Support

     Apache Shiro Web Support  1. 配置 将 Shiro 整合到 Web 应用中的最简单方式是在 web.xml 的 Servlet ContextListener 和 Fil ...

  2. Shiro学习笔记(5)——web集成

    Web集成 shiro配置文件shiroini 界面 webxml最关键 Servlet 測试 基于 Basic 的拦截器身份验证 Web集成 大多数情况.web项目都会集成spring.shiro在 ...

  3. Spring学习笔记:spring整合web之spring-web架包的引用(WebApplicationContextUtils注入容器)

    WebApplicationContextUtils 一.Spring整合web之前 案例:给部门列表添加新部门 import org.apache.log4j.Logger; import org. ...

  4. SpringMVC+Apache Shiro+JPA(hibernate)整合配置

    序: 关于标题: 说是教学,实在愧不敢当,但苦与本人文笔有限,实在找不到更合理,谦逊的词语表达,只能先这样定义了. 其实最真实的想法,只是希望这个关键词能让更多的人浏览到这篇文章,也算是对于自己写文章 ...

  5. Shiro学习

    Shiro学习资源 Shiro官网,http://shiro.apache.org/index.html 学习网站链接,http://blog.java1234.com/blog/articles/4 ...

  6. springboot 学习之路 8 (整合websocket(1))

    目录:[持续更新.....] spring 部分常用注解 spring boot 学习之路1(简单入门) spring boot 学习之路2(注解介绍) spring boot 学习之路3( 集成my ...

  7. 1、Shiro 安全框架与Spring 整合详解

    Apache Shiro 是一个安全认证框架,和 Spring Security 相比,在于他使用了比较简洁易懂的认证和授权方式.其提供的 native-session(即把用户认证后的授权信息保存在 ...

  8. springboot+shiro+redis(单机redis版)整合教程-续(添加动态角色权限控制)

    相关教程: 1. springboot+shiro整合教程 2. springboot+shiro+redis(单机redis版)整合教程 3. springboot+shiro+redis(集群re ...

  9. shiro学习笔记

    一.概念: shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证.权限授权.加密.会话管理等功能,组成了一个通用的安全认证框架. (一)shiro的功能 ...

随机推荐

  1. REDIS基础笔记

    Redis基础笔记 资源链接 简介 简介 安装 五种数据类型及相应命令 1. 字符串类型 2. 散列类型 3. 列表类型 4. 集合类型 5. 有序集合 其他 事务 SORT 生存时间 任务队列 发布 ...

  2. Android获取系统时间yyyyMMddHHmmssSSS

    代码改变世界 public String testTime1() throws ParseException { String DEFAULT_TIME_FORMAT = "yyyy-MM- ...

  3. 第五篇:python基础_5

    本篇内容 协程函数 递归 二分法 import语句 from...import语句 模块搜索路径 包的导入 软件开发规范 logging模块的使用 一. 协程函数 1.定义 协程函数就是使用了yiel ...

  4. 游戏(game)

    游戏(game) 题目描述 这个游戏是这样的,你有一个初始序列S ,你每次可以选择一段任意长度的连续区间,把他们+1 再膜k,给定目标序列,你需要尝试用尽量少的操作次数将初始序列变为目标序列.作为一名 ...

  5. [暑假集训--数论]poj2262 Goldbach's Conjecture

    In 1742, Christian Goldbach, a German amateur mathematician, sent a letter to Leonhard Euler in whic ...

  6. ip地址正则表达式

    p = re.compile('^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$') if p.match(dom ...

  7. 【HDOJ5555】Immortality of Frog(状压DP)

    题意:给你一个NxN的网格,第N行的每一列都有个青蛙,这些青蛙只会往上走, 上帝会在每个膜中随机等概率放一个长生不老的药, 一共有N个膜,每个膜覆盖一些区间,如果这个区间恰好为N那么就是好膜,否则是坏 ...

  8. CSDN数据库下载地址 CSDN 用户名密码泄漏,600万数据下载

    原文发布时间为:2011-12-21 -- 来源于本人的百度文章 [由搬家工具导入] 12月21日消息,下午有网友爆料称国内最大的开发者社区CSDN.NET的安全系统遭到黑客攻击,CSDN数据库中的6 ...

  9. 【Visual Studio】解决错误 fatal error C1010: unexpected end of file while looking for precompiled head(转)

    原文转自 http://blog.csdn.net/liuqiyao_01/article/details/38867145 在编译VS时候,出现fatal error C1010: unexpect ...

  10. java 基于tcp客户端服务端发送接收数据

    客户端: package demo03; import java.io.IOException; import java.io.InputStream; import java.io.OutputSt ...