Shiro 学习应用(续)

前面的文章中为大家介绍了 Shrio 的基础概念,可能比较笼统,没有深入到开发过程的一些问题。现在集中在本帖中归纳一下有关问题。

FormAuthenticationFilter 表单过滤器

表单过滤器的问题,是本人在实现验证码组件时候遇到的,亦曾经一度让我“抓狂”。虽然有便捷的方法实现验证码,例如在控制器中就可以判断验证码逻辑了,但是那有违 Shiro 结构体系的思想;本人也想籍此了解 Shiro 扩展实现方法,——如果都走捷径,那么学习的目的就达不到了。于是,本人就把遇到问题逐一罗列出来。

首先啰嗦下,为什么不写在控制器?

Shiro 提供丰富的过滤器,FormAuthenticationFilter 就是一种过滤器。既然使用了安全框架,那么我们就应该按照框架的方法去编码。我们知道,经典 Web MVC 中,过滤器是优于 Servlet 执行的,也是一般用作安全检测、权限校验之用的。所以 Shiro 组件基于过滤器的思想就“名正言顺”了(所以说 Filter 能做的,Shiro 也能做,Filter 做不了的或者不适合做的, Shiro 过滤器也不会僭越)。实际从作用功效上,过滤器与 Servlet 彼此替代可能性蛮大的,也就是说,Servlet 的逻辑用在 Filter 之上亦可以,效果看起来不会差太多——反之亦然。不过,我们还是要回顾下它们之间的区别:首先是前面说的,Filter 优于 Servlet 执行,Filter 拦截了,Servlet 也不会执行;其次,编码风格上 Filter 基于责任链式模式,Servlet 却不是;最后就是 Filter 不像 Servlet,它不能产生一个请求或者响应,Filter 通常只是返回 true/false。当然,它们是如此地相像,以至于把 Filter 认为是 Servlet 变种的一种亦未尝不可。

所以,我们会看到 Shiro 的一些案例中,既有 Filter,又有 Controller。例如下面我们绑定会员登录的 url 到 FormAuthenticationFilter,然后又声明有控制器。

CaptchaFormAuthenticationFilter 是扩展 FormAuthenticationFilter 的验证码过滤器。/service/user/access/login = authcaptcha 这句就是绑定了验证码过滤器。这样如此,应该是不用编码就可以调用 CaptchaFormAuthenticationFilter 了。

这个完整的请求还有 Controller 部分:

@RequestMapping(value = "/login", method = RequestMethod.POST)
public void loginAction(User user, HttpServletRequest request, HttpServletResponse response) {
	String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
	Throwable exObj = (Throwable) request.getAttribute("exObj");

	String error = null;
	LOGGER.info("客户端 " + user.getName() + "登录..." + exceptionClassName);

	if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
		error = "未知用户错误";
	} else if (IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
		error = "用户名/密码错误";
	} else if (IncorrectCaptchaException.class.getName().equals(exceptionClassName)) {
		error = "验证码错误";
	} else if (exObj != null && exObj.getClass().getName().equals(IllegalArgumentException.class.getName())) {
		error = exObj.getMessage(); // 缺少某个字段
	} else if (exceptionClassName != null) {
		error = "其他错误:" + exceptionClassName;
	}

	// 输出 JSON
	ResponseHelper rsp = new ResponseHelper(response);
	boolean isOk = error == null;
	String msg = error == null ? "登录成功!" : error + "具体原因:" + (exObj != null ? exObj.getMessage() : "N/A");
	rsp.outputAction(isOk, msg);
}

可见,这个控制器好像“打杂”的,作用比较简单,像是一些善后的工作。

但是,为什么过滤器执行不起来?

遗憾的是,声明过滤器之后却执行不起来,不能正确跳转。访问这个 url,即使输入合法也跳到“登录”页面(也就是未登录的页面)。然后反复搜索相关资料和例子,得知表单是 AccessControlFilter 的子类,如果访问了一定回 isAccessAllowed() 方法,试了下果然可以,表示是否允许访问,然后我下意识地调用验证码的 executeLogin() 方法,也就是封装的 getSubject().login(token); 的方法。难道是这样调用控制器的吗?我覺得我这种方法有点“简单粗暴”,框架不至于推荐这么做——后来的测试也证明此法不可行,报了一个两次 redirect 重定向的异常。——这肯定是我的方法不对!

百搜不得其解下,我十分灰心,甚至想放弃,后来我想到把 Shiro 源码和文档附加到 jar 中,看看里面的源码和注释。果然人家就说明需要 POST 请求,而且要指定登陆 URL(这个是 Action 接受请求的地址,如果不设置默认是 /login.jsp,这就是导致很多人不能成功调用过滤器的原因!)才能调用过滤器,有条件限制的,而且根本不需要执行 isAccessAllowed() 方法。

后来又发现个小插曲,——偶尔登录成功后,在尝试登录是不会执行过滤器。因此 Shiro 定义是“一次执行的”,具体原理我就没有深究了只是知道有这么一个机制。测试对应的方法也很简单,把浏览器缓存清掉,令其 session 去掉。

另外表单 POST 字段的映射,FormAuthenticationFilter 也考虑到了,参见配置中的 <property name="usernameParam" value="name" />:

	<bean id="captchaFormAuthenticationFilter"
		class="com.ajaxjs.framework.user.captcha.CaptchaFormAuthenticationFilter">
		<property name="usernameParam" value="name" />
		<property name="loginUrl" value="/service/user/access/login" />
	</bean>

这里的是 Spring 的配置方法。

小结一下 Shiro 认证

Shiro 就是这样,提供现成的组件方便我们调用,我们也可以从中了解其机制,——反正都是开源的。过滤器是认证的第一板斧,是创建 token 的那一步。有了 token 才能执行 AuthenticatingRealm.doGetAuthenticationInfo(AuthenticationToken authcToken),最后到控制器。网上有些例子把创建 token 那一步写在控制器中,尽管做法有点简单粗暴,但道理还是一样的。

时间: 2024-11-08 23:13:33

Shiro 学习应用(续)的相关文章

那些年,我还在学习C# 学习笔记续_C#教程

那些年,我还在学习C#续 那些年学习C#,就是对C#相关的一些知识有一个了解,等到要用时才不会找不到方向,比如说扩展方法,开始时怎么觉得没有用,后来了解到asp.net MVC,它可以用来扩展Html类,比如做一个分页的方法:所以对一门语言了解宽一些是没有坏处的:C#中还有一些在上文中没有提到的,如:读取文件.网络(socket)编程.序列化等,他们都是非常重要的啊,下面再来看一看吧! 一.读取文件 在文件读取学习时,一般都会提到字节流与字符流,前都按字节读取,后都按字符读取:我们通过FileS

Shiro 学习应用

和 Spring Security 一样,Shiro 也属于权限安全框架.和 Spring Security 相比,Shiro 更简单,学习曲线更低.关于 Shiro 的一系列特征及优点,很多文章已有列举,这里不再逐一赘述.这里记下学习 Spring 4.x + Shiro  1.2 的过程,可能有水平不够的地方,敬请指正. 一点概念 所有操作其实离不开理论.基础概念.虽然有点啰嗦.晦涩,但出于真正掌握的目的,仍是要强调其价值的.Shiro 为 Java 程序提供了认证(Authenticati

学习日记--续

真惨,这么久才第二次登陆! 这几天学习了pb,刚刚了解了easerver,尝试进行多层开发.只是大体有了认识,具体实现还有很多问题. ea server作为一个应用中间层,允许多种方式的客户端连接.比如pb,jsp(asp).实现方式: 1.首先建立应用组件(component 应用的主要实现部分,实现数据库的连接,数据的处理等),并创建对应的方法,然后发布 2.然后建立应用代理(proxy) 这个代理包含了服务器组件方法的接口部分,主要是方便进行客户端开发,实现对服务器组件的方法的调用 3.客

由浅入深学习Flash制作高射炮游戏(续)

接着上篇:由浅入深学习Flash制作高射炮游戏我们制作一个完整的游戏. 上篇讲到了,可以设置一定角度发炮弹了!这时接着做,首先我们把炮弹去掉,只要炮弹出来舞台左.右和下我们就将该MC去掉. 代码: Mouse.hide();gravity = 2;attachMovie("crosshair", "crosshair", 1);attachMovie("tank", "tank", 2, {_x:230, _y:350});c

ExtJS5学习之TreePanel(续)

    承接着上一篇,在TreePanel的节点上应用CellEditor插件对节点进行修改,上一篇<ExtJS5学习之TreePanel>中节点数据修改是通过弹出一个Window实现的,这次我们来试试使用CellEditor插件来完成同样的功能,关键代码如下:        Js代码   /*****************ExtJS TreePanel面板*********************/   Ext.define("OA.view.TreePanel",{ 

Flash/Flex学习笔记(5):捕获摄像头(续)--在线抓屏并保存到客户端本地

必须有摄像头上面的演示才能正常播放. 思路 使用摄像头以及在线抓屏在上一节Flash/Flex学习笔记(2)捕获摄像头 里已经讲过了就不重复粘贴了至于在客户端保存文件Flash里用起来也很简单:直接调用 FileReference 即可另外为了减少图片大小还可能借助AS3.0的扩展库项目地址http://code.google.com/p/as3corelib/把bmp格式的位置转换成jpeg再保存   扩展 结合本文的方法再配合Flash/Flex学习笔记(4)如何打开网页及Get/Post数

BootStrap学习系列之Bootstrap Typeahead 组件实现百度下拉效果(续)_javascript技巧

接着上篇的内容,在上篇给大家介绍了Bootstrap学习系列之使用 Bootstrap Typeahead 组件实现百度下拉效果 Bootstrap,来自 Twitter,是目前最受欢迎的前端框架.Bootstrap 是基于 HTML.CSS.JAVASCRIPT 的,它简洁灵活,使得 Web 开发更加快捷. 官方:http://twitter.github.io/typeahead.js/ 示例:http://twitter.github.io/typeahead.js/examples/(本

Lucene5学习之Facet(续)

     默认Facet是统计落入某一组域值的总数的,然后按照总数从大到小排序,判定规则是域值是否相同,其实还可以根据域值是否在某个范围内来判定是否落入某一个分组.这里说的范围就是通过Range定义的,比如: Java代码          /**1小时之前的毫秒数*/   final LongRange PAST_HOUR = new LongRange("Past hour", this.nowSec - 3600L,           true, this.nowSec, tr

SpringBoot学习:整合shiro(身份认证和权限认证),使用EhCache缓存

项目下载地址:http://download.csdn.NET/detail/aqsunkai/9805821 (一)在pom.xml中添加依赖:   [html] view plain copy   <properties>       <shiro.version>1.3.2</shiro.version>   </properties>   [html] view plain copy   <!--shiro start-->