设为首页 收藏本站
查看: 2371|回复: 0

[经验分享] apache shiro的工作流程分析

[复制链接]

尚未签到

发表于 2017-12-24 17:36:38 | 显示全部楼层 |阅读模式
  本文基于shiro的web环境,用宏观(也就是不精确)的角度去理解shiro的工作流程,先看shiro官方的一张图。
DSC0000.png

  和应用程序直接交互的对象是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的方式来实现。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-427588-1-1.html 上篇帖子: Flink实战(1) 下篇帖子: Ubuntu安装Apache + mod
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表