shiro原理
一、web.xml中过滤器配置1、过滤器DelegatingFilterProxyDelegatingFilterProxy做的事情是代理Filter的方法,从application context里获得bean。spring bean实现了Filter接口,但默认情况下,是由spring容器来管理其生命周期的(不是由servlet容器来管理)。如果设 置"targetFilterL.
一、web.xml中过滤器配置
1、过滤器DelegatingFilterProxy
DelegatingFilterProxy做的事情是代理Filter的方法,从application context里获得bean。spring bean实现了Filter接口,但默认情况下,是由spring容器来管理其生命周期的(不是由servlet容器来管理)。如果设 置"targetFilterLifecycle"为True,则spring来管理Filter.init()和Filter.destroy()。
DelegatingFilterProxy作用是自动到spring容器查找名字为shiroFilter(filter-name)的bean并把所有Filter的操作委托给它。
二、shiro sessionManager
1、web中的session
Session机制
Session机制采用的是在服务器端保持状态的方案。
当用 户访问到一个服务器,服务器就要为该用户创建一个SESSION,在创建这个SESSION的时候,服务器首先检查这个用户发来的请求里是否包含了一个 SESSIONID,如果包含了一个SESSIONID则说明之前该用户已经登陆过并为此用户创建过SESSION,那服务器就按照这个 SESSIONID把这个SESSION在服务器的内存中查找出来(如果查找不到,就有可能为他新创建一个),如果客户端请求里不包含有 SESSIONID,则为该客户端创建一个SESSION并生成一个与此SESSION相关的SESSIONID。这个SESSIONID是唯一的、不重 复的、不容易找到规律的字符串,这个SESSIONID将被在本次响应中返回到客户端保存,而保存这个SESSIONID的正是COOKIE,这样在交互 过程中浏览器可以自动的按照规则把这个标识发送给服务器。
2、web中的cookie
Cookie机制
Cookie机制采用的是在客户端保持状态的方案。
Cookie 机制,就是当服务器对访问它的用户生成了一个Session的同时服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的 cookie,保存在客户端,里面记录着用户当前的信息,当用户再次访问服务器时,浏览器检查所有存储的cookie,如果某个cookie所声明的作用 范围大于等于将要请求的资源所在的位置也就是对应的Cookie文件。 若存在,则把该cookie附在请求资源的HTTP请求头上发送给服务器(比如sessionID),我们知道,cookie的保存有临 时性的和持久性的,大多都是临时性的,也就是cookie只保存在客户端的内存中,而没有保存在硬盘上,当关闭浏览器,cookie也就销毁。以下是有关 cookie机制的一些具体说明:
cookie的内容主要包括:名字,值,过期时间,路径和域。
3、shiro sessionManager
sessionManager用于为应用中的Subject管理session,比如创建、删除、失效或者验证等。和Shiro中的其他核心组件一样,他由SecurityManager维护。
Shiro有两种session会话管理器:
Servlet容器会话管理
在Web环境中,Shiro默认的会话管理器SessionManager 的实现是ServletContainerSessionManager。这个实现只是简单的封装了Servlet容器,包括会话集群功能。它的本质是Shiro Session API与Servlet容器之间的一个桥梁。
使用这个默认实现的好处是,应用程序将使用现有的servlet容器的会话配置,例如超时,基于特定容器的集群机制等。缺点是你的应用程序依赖于特定servlet容器的会话处理将不适合移植。
如果使用缺省的servlet容器配置,在Web.xml文件里可配置Session超时。例如:
<session-config> <!-- web.xml expects the session timeout in minutes: --> <session-timeout>30</session-timeout> </session-config> |
本地会话管理
如果你想要特定的会话管理/集群功能,并且可以在不同的servlet容器移植。你可以Shiro本地的会话管理。“本地”的意思是Shiro自己实现的企业级会话管理支持所有的Subject和HttpServletRequest会话。但请放心,Shiro实现的会话管理符合servlet规范,现有的Web/HTTP相关代码不需要修改。
三、securityManager
在容器中securityManager只有一个实例,首先在spring配置文件中注入到Securityutils中:
<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
<property name="arguments" ref="securityManager"/>
</bean>
然后通过SecurityUtils.getSecurityManager()获取对象。
securityManager对象和Subject对象放在ThreadContext类中,如果securityManager为null则抛出异常。
四、Subject
基本上是当前正与软件进行交互的任何东西。
创建的subject的工作还是由 SecurityManager 来完成的。Buidler是在 Subject的内类,在Buidler中通过SecurityUtils 获取到了 SecurityManager ,调用buildSubject()。在buildSubject()中调用了 securityManager.createSubject()方法( 由SecurityManager 的子类DefaultSecurityManager 的 实现createSubject()),完成subject创建的。
每次经过AbstractShiroFilter的doFilterInternal方法(具体的类也就是上面的内部类ShiroFilter)都会创建一个新的Subject,具体分析里面的代码可以发现,这个Subject的数据会从SubjectContext或Session中获取过来。这意味着每次经过Shiro过滤器的HTTP请求,都会创建一次新的Subject.
Suject里面的数据,主要是从SubjectContext中获取,但是获取方式不一样,如SecurityManager总是从SubjectContext中直接获取,而其他数据则主要从Session中获取。只有在登录操作的时候数据会都从SubjectContext上下文中获取。因为登录成功后还会有一个绑定操作,它会把当前用户的相关信息写入Session中去。
getSubject()方法调用一个独立的应用程序,该应用程序可以返回一个在应用程序特有位置上基于用户数据的Subject,在服务器环境中(如,Web 应用程序),它基于与当前线程或传入的请求相关的用户数据上获得Subject。
DefaultWebSubjectFactory:
public Subject createSubject(SubjectContext context) {
if (!(contextinstanceof WebSubjectContext)) {
returnsuper.createSubject(context);
}
WebSubjectContext wsc = (WebSubjectContext) context;
SecurityManager securityManager = wsc.resolveSecurityManager();
Session session = wsc.resolveSession();
booleansessionEnabled = wsc.isSessionCreationEnabled();
PrincipalCollection principals = wsc.resolvePrincipals();
booleanauthenticated = wsc.resolveAuthenticated();
String host = wsc.resolveHost();
ServletRequest request = wsc.resolveServletRequest();
ServletResponse response = wsc.resolveServletResponse();
returnnew WebDelegatingSubject(principals, authenticated, host, session, sessionEnabled, request, response,securityManager);
}
五、LifecycleBeanPostProcessor
LifecycleBeanPostProcessor用于在实现了Initializable接口的Shiro bean初始化时调用Initializable接口回调,在实现了Destroyable接口的Shiro bean销毁时调用 Destroyable接口回调。
即让spring管理shiro中实现了Initializable接口和Destroyable接口的bean的生命周期。
Spring that automatically calls the init() and/or destroy() methods on Shiro objects that implement the org.apache.shiro.util.Initializable or org.apache.shiro.util.Destroyable interfaces,
六、remember me
Remembered vs. Authenticated(记住我 vs认证):
Remembered(记住我):一个记住我的Subject 不是匿名的,而且有一个已知的身份ID(也就是subject.getPrincipals()是非空的)。
但是这个被记住的身份ID 是在之前的session 中被认证的。
如果subject.isRemembered()返回true,则Subject 被认为是被记住的。
Authenticated(已认证):一个已认证的Subject 是指在当前Session 中被成功地验证过了(也就是说,login方法被调用并且没有抛出异常)。如果subject.isAuthenticated()返回true 则认为Subject 已通过验证。
Remembered 和Authenticated 是互斥的——若其中一个为真则另一个为假,反之亦然。
八、realm
Realms 担当Shiro 和你的应用程序的安全数据之间的“桥梁”或“连接器”, Realms 来连接一些安全数据源 ,Realm 本质上是一个特定安全的DAO:它封装了数据源的连接详细信息。
九、token类型
最基本和常用的令牌是 UsernamePasswordToken,用以指定用户的用户名和密码。 UsernamePasswordToken 类实现了 AuthenticationToken 接口,它提供了一种获得凭证和用户的主体(帐户身份)的方式。 UsernamePasswordToken 适用于大多数应用程序,并且您还可以在需要的时候扩展 AuthenticationToken 接口来将您自己获得凭证的方式包括进来。
AuthenticationToken两个实现类:
HostAuthenticationToken:
RememberMeAuthenticationToken:
十、shiro的core,spring,web
Web包是对core包的扩展,使shiro支持在web中的应用,比如增加JSP/GSP标签库等
七、filterChainDefinitions
1)Shiro验证URL时,URL匹配成功便不再继续匹配查找(所以要注意配置文件中的URL顺序,尤其在使用通配符时) 故filterChainDefinitions的配置顺序为自上而下,以最上面的为准
2)当运行一个Web应用程序时,Shiro将会创建一些有用的默认Filter实例,并自动地在[main]项中将它们置为可用
自动地可用的默认的Filter实例是被DefaultFilter枚举类定义的,枚举的名称字段就是可供配置的名称
anon---------------org.apache.shiro.web.filter.authc.AnonymousFilter
authc--------------org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic---------org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
logout-------------org.apache.shiro.web.filter.authc.LogoutFilter
noSessionCreation--org.apache.shiro.web.filter.session.NoSessionCreationFilter
perms--------------org.apache.shiro.web.filter.authz.PermissionAuthorizationFilter
port---------------org.apache.shiro.web.filter.authz.PortFilter
rest---------------org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles--------------org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
ssl----------------org.apache.shiro.web.filter.authz.SslFilter
user---------------org.apache.shiro.web.filter.authz.UserFilter
3)通常可将这些过滤器分为两组
anon,authc,authcBasic,user是第一组认证过滤器
perms,port,rest,roles,ssl是第二组授权过滤器
注意user和authc不同:当应用开启了rememberMe时,用户下次访问时可以是一个user,但绝不会是authc,因为authc是需要重新认证的
user表示用户不一定已通过认证,只要曾被Shiro记住过登录状态的用户就可以正常发起请求,比如rememberMe
说白了,以前的一个用户登录时开启了rememberMe,然后他关闭浏览器,下次再访问时他就是一个user,而不会authc
4)举几个例子
/admin=authc,roles[admin] 表示用户必需已通过认证,并拥有admin角色才可以正常发起'/admin'请求
/edit=authc,perms[admin:edit] 表示用户必需已通过认证,并拥有admin:edit权限才可以正常发起'/edit'请求
/home=user 表示用户不一定需要已经通过认证,只需要曾经被Shiro记住过登录状态就可以正常发起'/home'请求
5)各默认过滤器常用如下(注意URL Pattern里用到的是两颗星,这样才能实现任意层次的全匹配)
/admins/**=anon 无参,表示可匿名使用,可以理解为匿名用户或游客
/admins/user/**=authc 无参,表示需认证才能使用
/admins/user/**=authcBasic 无参,表示httpBasic认证
/admins/user/**=user 无参,表示必须存在用户,当登入操作时不做检查
/admins/user/**=ssl 无参,表示安全的URL请求,协议为https
/admins/user/**=perms[user:add:*]
参数可写多个,多参时必须加上引号,且参数之间用逗号分割,如/admins/user/**=perms["user:add:*,user:modify:*"]
当有多个参数时必须每个参数都通过才算通过,相当于isPermitedAll()方法
/admins/user/**=port[8081]
当请求的URL端口不是8081时,跳转到schemal://serverName:8081?queryString
其中schmal是协议http或https等,serverName是你访问的Host,8081是Port端口,queryString是你访问的URL里的?后面的参数
/admins/user/**=rest[user]
根据请求的方法,相当于/admins/user/**=perms[user:method],其中method为post,get,delete等
/admins/user/**=roles[admin]
参数可写多个,多个时必须加上引号,且参数之间用逗号分割,如/admins/user/**=roles["admin,guest"]
当有多个参数时必须每个参数都通过才算通过,相当于hasAllRoles()方法
<property name="unauthorizedUrl" value="/user/unauthorized"/>:
已登录去访问没权限的链接会跳转到该链接
<property name="loginUrl" value="/user/gologin"/>:
没有登录访问需要身份认证authc的链接会跳转到该地址
Ddd
注意1:
使用shiro的自动登录功能,form格式:
<form action="" method="post">
<ul>
<li>姓 名:<input type="text" name="username"/> </li>
<li>密 码:<input type="password" name="password"/> </li>
<li><input type="checkbox" name="rememberMe" value="true"/>Remember Me?</li>
<li><input type="submit" value="确认" /> </li>
</ul>
</form>
过滤器配置:
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- shiro的核心安全接口 -->
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login.jsp"/>
<property name="successUrl" value="/book/home"/>
<property name="unauthorizedUrl" value="/login.jsp"/>
<property name="filterChainDefinitions">
<value>
/login.jsp = authc
/book/deleteBook=authc,roles[admin] <!-- 需登录且是admin,才能删除 -->
/book/list=authc <!-- 需要登录才能查询 -->
</value>
</property>
</bean>
在点击登录提交form时会自动寻找到realm中的doGetAuthenticationInfo方法进行验证。
注意2:
启动注解配置,在与springMVC整合时必须放在springMVC的配置文件中。
Shiro在注解模式下,登录失败,与没有权限均是通过抛出异常。并且默认并没有去处理或者捕获这些异常。在springMVC下需要配置捕获相应异常来通知用户信息,如果不配置异常会抛出到页面。
自定义权限处理类:
/**
* 自定义权限异常处理
*
*/
@Component
publicclass MyHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object object,
Exception exception) {
// 是否为ajax请求
final String requestType = request.getHeader("X-Requested-With");
if (exceptioninstanceof AuthorizationException) {
response.setStatus(413);// 无权限异常主要用于ajax请求返回
response.addHeader("Error-Json", "{code:413,msg:'nopermission',script:''}");
response.setContentType("text/html;charset=utf-8");
if ("XMLHttpRequest".equals(requestType)) {
returnnew ModelAndView();
}
returnnew ModelAndView("../nopower");
}
returnnull;
}
}
欢迎关注公众号:
更多推荐
所有评论(0)