Shiro的拦截器

发表时间:2018-03-20 11:36:51 浏览量( 20 ) 留言数( 0 )

学习目标:

1、了解Shiro的默认过滤器的功能

2、Shiro的自定义过滤


学习过程:

    完成上一节课例子,你是不是会觉得这个登陆很神奇,为什么可以自动获得页面的用户名密码,然后什么登陆判断都不需要就完成了登陆了呢。这就是用到了Shiro的默认过滤器功能。

    Shiro内置了很多默认的过滤器,比如身份验证、授权等相关的。默认拦截器可以参考org.apache.shiro.web.filter.mgt.DefaultFilter中的枚举拦截器:  

说明(括号里的表示默认值)

public enum DefaultFilter {  

    anon(AnonymousFilter.class),  

    authc(FormAuthenticationFilter.class),  

    authcBasic(BasicHttpAuthenticationFilter.class),  

    logout(LogoutFilter.class),  

    noSessionCreation(NoSessionCreationFilter.class),  

    perms(PermissionsAuthorizationFilter.class),  

    port(PortFilter.class),  

    rest(HttpMethodPermissionFilter.class),  

    roles(RolesAuthorizationFilter.class),  

    ssl(SslFilter.class),  

    user(UserFilter.class);  

}   


一、基础的Filter

    Shiro对Servlet容器的FilterChain进行了代理,即ShiroFilter在继续Servlet容器的Filter链的执行之前,通过ProxiedFilterChain对Servlet容器的FilterChain进行了代理;即先走Shiro自己的Filter体系,然后才会委托给Servlet容器的FilterChain进行Servlet容器级别的Filter链执行;Shiro的ProxiedFilterChain执行流程:1、先执行Shiro自己的Filter链;2、再执行Servlet容器的Filter链(即原始的Filter)。

   Shiro定义了一些比较基础的Filter,上面的大部分Filter都继承与这些基础的Filter。

1、NameableFilter

   NameableFilter给Filter起个名字,如果没有设置默认就是FilterName;还记得之前的如authc吗?当我们组装拦截器链时会根据这个名字找到相应的拦截器实例;

2、OncePerRequestFilter

    OncePerRequestFilter用于防止多次执行Filter的;也就是说一次请求只会走一次拦截器链;另外提供enabled属性,表示是否开启该拦截器实例,默认enabled=true表示开启,如果不想让某个拦截器工作,可以设置为false即可。

3、ShiroFilter

    ShiroFilter是整个Shiro的入口点,用于拦截需要安全控制的请求进行处理,这个之前已经用过了。

4、AdviceFilter

    preHandler:类似于AOP中的前置增强;在拦截器链执行之前执行;如果返回true则继续拦截器链;否则中断后续的拦截器链的执行直接返回;进行预处理(如基于表单的身份验证、授权)

    postHandle:类似于AOP中的后置返回增强;在拦截器链执行完成后执行;进行后处理(如记录执行时间之类的);

    afterCompletion:类似于AOP中的后置最终增强;即不管有没有异常都会执行;可以进行清理资源(如接触Subject与线程的绑定之类的);

5、PathMatchingFilter

    PathMatchingFilter提供了基于Ant风格的请求路径匹配功能及拦截器参数解析的功能,如“roles[admin,user]”自动根据“,”分割解析到一个路径参数配置并绑定到相应的路径:

    pathsMatch:该方法用于path与请求路径进行匹配的方法;如果匹配返回true;

    onPreHandle:在preHandle中,当pathsMatch匹配一个路径后,会调用opPreHandler方法并将路径绑定参数配置传给mappedValue;然后可以在这个方法中进行一些验证(如角色授权),如果验证失败可以返回false中断流程;默认返回true;也就是说子类可以只实现onPreHandle即可,无须实现preHandle。如果没有path与请求路径匹配,默认是通过的(即preHandle返回true)。

6、AccessControlFilter

    AccessControlFilter提供了访问控制的基础功能;比如是否允许访问/当访问拒绝时如何处理等:

    isAccessAllowed:表示是否允许访问;mappedValue就是[urls]配置中拦截器参数部分,如果允许访问返回true,否则false;

    onAccessDenied:表示当访问拒绝时是否已经处理了;如果返回true表示需要继续处理;如果返回false表示该拦截器实例已经处理了,将直接返回即可。

    如果我们想进行访问访问的控制就可以继承AccessControlFilter;如果我们要添加一些通用拦截我们可以直接继承PathMatchingFilter。


二、默认使用的Filter

1、FormAuthenticationFilter

    基于表单的拦截器;如“/**=authc”,如果没有登录会跳到相应的登录页面登录,主要属性:

    usernameParam:对应表单提交的用户名参数名( username);  

    passwordParam:对应表单提交的密码参数名(password); 

    rememberMeParam:对应表单提交的密码参数名(rememberMe);  

    loginUrl:登录页面地址(/login.jsp);

    successUrl:登录成功后的默认重定向地址; 

    failureKeyAttribute:登录失败后错误信息存储key(shiroLoginFailure);


2、BasicHttpAuthenticationFilter

    Basic HTTP身份验证拦截器,主要属性: applicationName:弹出登录框显示的信息(application);


3、LogoutFilter

    退出拦截器,主要属性:redirectUrl:退出成功后重定向的地址(/);示例“/logout=logout”


4、UserFilter

    用户拦截器,用户已经身份验证/记住我登录的都可;示例“/**=user”


5、AnonymousFilter

匿名拦截器,即不需要登录即可访问;一般用于静态资源过滤;示例“/static/**=anon”



6、RolesAuthorizationFilter

    角色授权拦截器,验证用户是否拥有所有角色;主要属性: loginUrl:登录页面地址(/login.jsp);unauthorizedUrl:未授权后重定向的地址;示例“/admin/**=roles[admin]”


7、PermissionsAuthorizationFilter

    权限授权拦截器,验证用户是否拥有所有权限;属性和roles一样;示例“/user/**=perms["user:create"]”


8、PortFilter

    端口拦截器,主要属性:port(80):可以通过的端口;示例“/test= port[80]”,如果用户访问该页面是非80,将自动将请求端口改为80并重定向到该80端口,其他路径/参数等都一样   


9、HttpMethodPermissionFilter

    rest风格拦截器,自动根据请求方法构建权限字符串(GET=read, POST=create,PUT=update,DELETE=delete,HEAD=read,TRACE=read,OPTIONS=read, MKCOL=create)构建权限字符串;示例“/users=rest[user]”,会自动拼出“user:read,user:create,user:update,user:delete”权限字符串进行权限匹配(所有都得匹配,isPermittedAll)


10、SslFilter

    SSL拦截器,只有请求协议是https才能通过;否则自动跳转会https端口(443);其他和port拦截器一样;


11、NoSessionCreationFilter

    不创建会话拦截器,调用 subject.getSession(false)不会有什么问题,但是如果 subject.getSession(true)将抛出 DisabledSessionException异常;


   上面说的过滤器的属性是可以改的,比如如果你的表单的用户名密码等属性不是命名为username。那么可以修改一下的:

   修改登陆login.jsp

<TABLE border="0">
			<TBODY>
				<TR>
					<TD>用户名:<input type="text"  name="usernamemy" /></TD>
				</TR>
				<TR>
					<TD>密 码:<input type="password" name="passwordmy" /></TD>
				</TR>

				<TR>
					<TD align="center"><input type="submit" class="btnalink"
						value="登录" /></TD>
				</TR>
			</TBODY>
		</TABLE>

   修改表单拦截器

	<bean id="formAuthenticationFilter"
		class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
		<property name="usernameParam" value="usernamemy" />
		<property name="passwordParam" value="passwordmy" />
	</bean>

	<!-- Shiro 的Web过滤器 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" />
		<!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
		<property name="loginUrl" value="/login.do" />
		<property name="unauthorizedUrl" value="/refuse.do" />
		<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
		
		<property name="filters">
		    <map>  
              <entry key="authc" value-ref="formAuthenticationFilter"/>  
           </map> 
		</property>

   ...... 后面省略


三、项目实践,自定义过滤器

实现首次登陆需要修改密码;

1、修改数据库和实体对象,这里我们没有使用数据库,你可以自己完善这个项目,添加两个字段

	private boolean firstLogin;//true 是第一次登陆
	private String changedPassword;//已修改的密码

2、定义过滤器

public class ChangePasswordFilter extends AccessControlFilter {

	/**
	 * 登录地址
	 */
	static final String LOGIN_URL = "/login.do";
	/**
	 * 修改密码地址
	 */
	static final String NEW_PASSWORD_URL = "/changePassword.do";

	@Override
	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
			throws Exception {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {

		Subject subject = getSubject(request, response);
		if (subject.getPrincipal() == null) {// 表示没有登录,重定向到登录页面
			saveRequest(request);
			WebUtils.issueRedirect(request, response, LOGIN_URL);
		} else {
			User user = (User) subject.getPrincipal();
			if (user.getChangedPassword()== null ||user.getFirstLogin()) {
				if (StringUtils.hasText(NEW_PASSWORD_URL)) {// 如果首次登录未修改密码,则跳转到修改密码页面
					WebUtils.issueRedirect(request, response, NEW_PASSWORD_URL);
				} else {
					WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
				}
			}
		}
		return true;
	}

}

3、修改配置文件,使用这个过滤器

    <bean id="changedPassword" class="com.shiro.myfilter.ChangePasswordFilter" >
    </bean>

修改

	<!-- Shiro 的Web过滤器 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" />
		<!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
		<property name="loginUrl" value="/login.do" />
		<property name="unauthorizedUrl" value="/refuse.do" />
		<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->

		<property name="filters">
			<map>
				<entry key="authc" value-ref="verCodeFormAuthenticationFilter" />
				 <entry key="changedPassword" value-ref="changedPassword" />
			</map>
		</property>

定义那些路径使用这个过滤器

				/login.do = authc
				/changePassword.do=user
				/** = user,changedPassword