本文基于shiro的web环境,用宏观(也就是不精确)的角度去理解shiro的工作流程,先看shiro官方的一张图。

和应用程序直接交互的对象是Subject,securitymanager为Subject服务。可以把Subject看成一个用户,你的所有的代码都由用户来执行。suject.execute(callable),这个callable里面就是你的代码。

一、shiro如何介入你的webapp

它是如何初始化的?servletContextListener。它是如何在每个http请求中介入的?ServletFilter.

<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>

EnvironmentLoaderListener会根据你在web.xml中的配置读取对应的配置文件(默认读取/WEB-INF/shiro.ini,或者classroot的对应文件),构建一个shiro环境,该环境保存在servletcontext中,所有的filter都可以获取。里面就包括一个单例的securityManager,该securityManager已经根据ini的内容进行了配置。

再看shiroFilter:

@Override
public void init() throws Exception {
WebEnvironment env = WebUtils.getRequiredWebEnvironment(getServletContext()); setSecurityManager(env.getWebSecurityManager()); FilterChainResolver resolver = env.getFilterChainResolver();
if (resolver != null) {
setFilterChainResolver(resolver);
}
}

这样filter里面就可以使用securityManager了。

下面的一段代码就是本文开头提到的Subject(用户)的创建了,因为是web环境所以每次请求都需要创建一个subject对象,在filter里面给你准备好,在你的servlet里面就可以直接使用了。

    final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
final ServletResponse response = prepareServletResponse(request, servletResponse, chain); final Subject subject = createSubject(request, response); //noinspection unchecked
subject.execute(new Callable() {
public Object call() throws Exception {
updateSessionLastAccessTime(request, response);
executeChain(request, response, chain);
return null;
}
});

看一下subject.execute的javadoc,写道:

Associates the specified Callable with this Subject instance and then executes it on the currently running thread.  If you want to execute the Callable on a different thread, it is better to use the associateWith(Callable)} method instead.

将callable(你的所有代码都在里面执行)和当前的subject实例相关联,并且在当前的thread中执行...

二、securityManage如何为subject服务

请注意看上面最后一段java代码,里面有一个createSubject(request,response)方法,也在filter里面,它的代码如下:

  protected WebSubject createSubject(ServletRequest request, ServletResponse response) {
return new WebSubject.Builder(getSecurityManager(), request, response).buildWebSubject();
}

看到没有?subject的构造用到了securityManager,所有shiro的魔法都被隐藏在securityManager里面了。接下来提一些问题,试着发现securityManager需要完成哪些工作。

  • 如果保证每次http请求得到同一个(确切说应该是一样的)subject?这里关系到session管理了吧。

  • 如何登陆用户,subject.login?这里关系到认证,授权,用户realm了吧。

  • ....

三、clojure-ring使用shiro的可行方案

clojure-ring SPEC中没有提供servlet环境,它的Adapters仅仅是封装了request和response,已经处于请求的最末端。所以shiro提供的servletfilter无法使用。来看看jetty-adapter的代码:

[handler options]
(let [^Server s (create-server (dissoc options :configurator))
^QueuedThreadPool p (QueuedThreadPool. ^Integer (options :max-threads 50))]
(.setMinThreads p (options :min-threads 8))
(when-let [max-queued (:max-queued options)]
(.setMaxQueued p max-queued))
(when (:daemon? options false)
(.setDaemon p true))
(doto s
(.setHandler (proxy-handler handler))
(.setThreadPool p))
(when-let [configurator (:configurator options)]
(configurator s))
(.start s)
(when (:join? options true)
(.join s))
s))

其中.setHandler,是一个实现了jetty的AbstractHandler的handler.

(defn- proxy-handler
"Returns an Jetty Handler implementation for the given Ring handler."
[handler]
(proxy [AbstractHandler] []
(handle [_ ^Request base-request request response]
(let [request-map (servlet/build-request-map request)
response-map (handler request-map)]
(when response-map
(servlet/update-servlet-response response response-map)
(.setHandled base-request true))))))

ring结构和SPEC都非常简洁,上面的代码中将jetty server修改成servletcontext,然后通过一个servlet来实现这个adapter,就可以了。

代码如下:

 public void useServlet() throws Exception {
Server server = new Server(8080); final ServletContextHandler servletContext = new ServletContextHandler(ServletContextHandler.SESSIONS);
servletContext.setClassLoader(Thread.currentThread().getContextClassLoader()); servletContext.addLifeCycleListener(new LifeCycle.Listener() { @Override
public void lifeCycleStopping(LifeCycle arg0) {
} @Override
public void lifeCycleStopped(LifeCycle arg0) {
} @Override
public void lifeCycleStarting(LifeCycle arg0) {
} @Override
public void lifeCycleStarted(LifeCycle arg0) {
servletContext.setContextPath("/");
servletContext.addServlet(new ServletHolder(new HelloServlet()), "/*");
servletContext.addServlet(new ServletHolder(new HelloServlet("Buongiorno Mondo")), "/it/*");
servletContext.addServlet(new ServletHolder(new HelloServlet("Bonjour le Monde")), "/fr/*");
servletContext.callContextInitialized(new EnvironmentLoaderListener(), new ServletContextEvent(servletContext.getServletContext()));
servletContext.addFilter(ShiroFilter.class, "/*", EnumSet.of(DispatcherType.INCLUDE,DispatcherType.REQUEST,DispatcherType.FORWARD,DispatcherType.ERROR));
} @Override
public void lifeCycleFailure(LifeCycle arg0, Throwable arg1) {
}
}); server.setHandler(servletContext);
server.start();
server.join();
}

使用上面的代码,就完整的使用了shiro的web模块,但是上面的方法需要注意几个问题:

1、session和cookie和ring本身的机制不一样,需要特别处理。

2、这样个性化的adapter无法融入ring的生态圈,比如lein-ring

基于上面的考虑,不如不使用shiro的web模块,直接使用shiro-core,然后用ring-middleware的方式来实现。

apache shiro的工作流程分析的更多相关文章

  1. Struts2的工作流程分析

    Struts2的工作流程分析 Posted on 2011-02-22 09:32 概述 本章讲述Struts2的工作原理. 读者如果曾经学习过Struts1.x或者有过Struts1.x的开发经验, ...

  2. 【转】Hostapd工作流程分析

    [转]Hostapd工作流程分析 转自:http://blog.chinaunix.net/uid-30081165-id-5290531.html Hostapd是一个运行在用户态的守护进程,可以通 ...

  3. 第2章 rsync算法原理和工作流程分析

    本文通过示例详细分析rsync算法原理和rsync的工作流程,是对rsync官方技术报告和官方推荐文章的解释. 以下是本文的姊妹篇: 1.rsync(一):基本命令和用法 2.rsync(二):ino ...

  4. [国嵌笔记][030][U-Boot工作流程分析]

    uboot工作流程分析 程序入口 1.打开顶层目录的Makefile,找到目标smdk2440_config的命令中的第三项(smdk2440) 2.进入目录board/samsung/smdk244 ...

  5. rsync算法原理和工作流程分析

    本文通过示例详细分析rsync算法原理和rsync的工作流程,是对rsync官方技术报告和官方推荐文章的解释.本文不会介绍如何使用rsync命令(见rsync基本用法),而是详细解释它如何实现高效的增 ...

  6. nodejs的Express框架源码分析、工作流程分析

    nodejs的Express框架源码分析.工作流程分析 1.Express的编写流程 2.Express关键api的使用及其作用分析 app.use(middleware); connect pack ...

  7. Mysql工作流程分析

    Mysql工作流程图 工作流程分析 1. 所有的用户连接请求都先发往连接管理器 2. 连接管理器    (1)一直处于侦听状态    (2)用于侦听用户请求 3. 线程管理器    (1)因为每个用户 ...

  8. u-boot分析(二)----工作流程分析

    u-boot分析(二) 由于这两天家里有点事,所以耽误了点时间,没有按时更新,今天我首先要跟大家说说我对于u-boot分析的整体的思路,然后呢我以后的博客会按照这个内容更新,希望大家关注. 言归正传, ...

  9. rsync(三)算法原理和工作流程分析

    在开始分析算法原理之前,简单说明下rsync的增量传输功能. 假设待传输文件为A,如果目标路径下没有文件A,则rsync会直接传输文件A,如果目标路径下已存在文件A,则发送端视情况决定是否要传输文件A ...

随机推荐

  1. ASP.NET中MessageBox的实现

    asp.net中没有MessageBox这个控件,固然可以插入Winform里的MessageBox,但一般不提倡,所以只能变通实现,主要有这几种方法: 1.直接利用javascript的alert和 ...

  2. BZOJ开荒记

    2019/4/16 1:04 使用Yinku2017提交了第一发,当然是A+B Problem. 看一下排行榜,算一下区域赛还有180多天吧?先用30天过50道题(含A+B Problem)怎么样?

  3. 利用YYLabel 进行图文混排+高度计算

    利用YYLabel 进行图文混排+高度计算 1.项目需求: 用一个控件显示图片和文字,并且依据图片和文字动态计算控件的高度. 2.方案: 利用YYLabel控件和属性字符串处理. 注:(在使用YYLa ...

  4. Git之忽略提交 .gitignore

    1.touch .gitignore  创建忽略文件,若此文件已存在,请忽略这一步 2.vi .gitignore   编辑忽略文件,将需要忽略的文件添加到忽略文件中 3.git status 发现忽 ...

  5. lightoj 1085【离散化+树状数组】

    题意: 求所有的上升子序列种数: 思路: 我想先离散化一下,然后用树状数组维护一下. 最终答案就是sum(n) ? 卧槽,好像是:然后就过了.. #include <bits/stdc++.h& ...

  6. 51nod 1051【基础】

    思路: 找题4级做做...然后找了题最水的.. = =感动...居然是一下子[记]得了做法... dp一下,枚举列的起点和终点,然后求和这一段,然后对这一大列就是求个最大字段和: #include & ...

  7. springMVC常用传参总结

    本文介绍了springMVC常用的传参方式和一些注意的事项,页面表单主要以ajax的形式提交. 本帅是个菜鸡,水平有限,若有什么讲得不对或有补充的地方欢迎各位提意见. 一.传递String类型   1 ...

  8. SimpleDateFormat线程不安全(转)

    有三种方法可以解决以上安全问题.  1).使用同步 package com.bijian.study.date; import java.text.ParseException; import jav ...

  9. 用css固定textarea文本域大小尺寸

    textarea元素在chrome等浏览器下可以被拖拉从而改变大小,对于查看textarea里面的内容来说相当方便,但是有时候 我们为了保持网页的美观,不得不想要禁掉这个功能,禁止用户随意拉动text ...

  10. java利用myeclipse自带三大框架搭建三大框架(Hibernate+Struts2+Spring)过程详解

    搭建过程因人而异,我的搭建过程大致是这样的: 1.创建一个javaweb项目: 2.导入Spring框架,上图: 2.1: 2.2: 2.3: 3.导入struts2框架,上图: 3.1: 3.2: ...