一、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(principalsauthenticatedhostsessionsessionEnabledrequestresponse,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;
}
}

 

欢迎关注公众号:

 

 

 

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐