Bootstrap

shiro概述(三)拦截器

引入依赖

<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.2.2</version>
        </dependency>

一、shiro内置拦截器:shiro使用了与servlet一样的Filter进行接口扩展,常用shiro内置拦截器:

(1)NameableFilter:NameableFilter给Filter起个名字,如果没有设置默认就是FilterName;当我们组装拦截器链时会根据这个名字找到相应的拦截器实例;

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

(3)ShiroFilter:是整个Shiro的入口点,用于拦截需要安全控制的请求进行处理。

(4)AdviceFilter:提供了AOP风格的支持,类似于SpringMVC中的Interceptor。

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

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

(7)默认拦截器:org.apache.shiro.web.filter.mgt.DefaultFilter 中的枚举拦截器

public enum DefaultFilter {
    anon(AnonymousFilter.class),
    authc(FormAuthenticationFilter.class),
    authcBasic(BasicHttpAuthenticationFilter.class),
    authcBearer(BearerHttpAuthenticationFilter.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),
    invalidRequest(InvalidRequestFilter.class);
}

默认拦截器名

拦截器类

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

身份验证相关的

authc

org.apache.shiro.web.filter.authc

.FormAuthenticationFilter

基于表单的拦截器;如 “`/**=authc`”,如果没有登录会跳到相应的登录页面登录;主要属性:usernameParam:表单提交的用户名参数名( username);  passwordParam:表单提交的密码参数名(password); rememberMeParam:表单提交的密码参数名(rememberMe);  loginUrl:登录页面地址(/login.jsp);successUrl:登录成功后的默认重定向地址; failureKeyAttribute:登录失败后错误信息存储 key(shiroLoginFailure);
authcBasic

org.apache.shiro.web.filter.authc

.BasicHttpAuthenticationFilter

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

logout

org.apache.shiro.web.filter.authc

.LogoutFilter

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

user

org.apache.shiro.web.filter.authc

.UserFilter

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

anon

org.apache.shiro.web.filter.authc

.AnonymousFilter

匿名拦截器,即不需要登录即可访问;

1.一般用于静态资源过滤;示例 “/static/**=anon”

2.后台绕过登陆权限鉴权调试接口等

授权相关的

roles

org.apache.shiro.web.filter.authz

.RolesAuthorizationFilter

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

perms

org.apache.shiro.web.filter.authz

.PermissionsAuthorizationFilter

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

port

org.apache.shiro.web.filter.authz

.PortFilter

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

rest

org.apache.shiro.web.filter.authz

.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);

ssl

org.apache.shiro.web.filter.authz

.SslFilter

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

noSessionCreation

org.apache.shiro.web.filter.session

.NoSessionCreationFilter

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

二、自定义拦截器:其本质就是一个 Filter;所以 Filter 能做的它就能做。常见的有

1、extends扩展OncePerRequestFilter:

2、extends扩展AdviceFilter:

3、extends扩展PathMatchingFilter:  如果要添加一些通用数据可以继承PathMatchingFilter;

4、extends扩展AccessControlFilter :如果想进行访问控制就可以继承AccessControlFilter;

5、基于表单登陆拦截器FormAuthenticationFilter:

(1)作用:主要有两个作用:

    ①拦截登录表单提交的路径(在拦截器工厂中配置),创建登录认证所需要的Token令牌,并进入登录认证流程。

   ②拦截要求登录才可以访问的路径时(在拦截规则中配置),如果已经登录则直接进入到要访问路径,如果未登录则访问被拒绝并跳转到登录页。

(2)方法介绍:

   ①createToken( ):此方法用于创建登录认证令牌,令牌内存储了登录认证时所需的数据。当我们需要扩展shiro原有令牌的时候会重写此方法,将扩展的令牌字段存入令牌。最典型的例子就是在令牌中增加了验证码字段。

/**
 * 创建Token
 */
@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
    String username = getUsername(request);//获取用户名 表单name:username
    String password = getPassword(request);//获取密码 表单name:password
    boolean rememberMe = isRememberMe(request);//获取是否记住我 表单name:rememberMe
    String captchaId = WebUtils.getCleanParam(request, "captchaId");//获取验证码id
    String captcha = WebUtils.getCleanParam(request, "captcha");//获取用户输入的验证码字符
    return new CaptchaAuthenticationToken(username, password,captchaId, captcha, rememberMe);//存入自己定义的包含验证码的Token
}

  ② onLoginSuccess( ):登录认证成功后的行为。此方法只有在进行登录认证成功后访问一次。之后再访问页面时不会调用此方法。

  ③onAccessDenied( ):访问被拒绝后的行为。在拦截规则中指定需要登陆后才能访问的路径,如果没有登录则认为访问被拒绝。此方法处理访问被拒绝后的逻辑,最常见的就是对于ajax访问拒绝的特殊处理。此方法处理完成后,默认行为会跳转到登录页(可以在拦截器工厂中配置 或 setLoginUrl方法设置登陆页)。

这里要注意如果是登录操作,拦截器会先拦截并判定为拒绝访问进入到此方法,然后再去进行创建Token和登录认证等操作。

  ④setLoginUrl( ):设置登陆页路径。默认/login.jsp,可以在拦截器工厂中配置 或 使用此方法重置。访问被拒绝时我们可以使用此方法返回到其他登陆页面,而不是之前的登录页。

@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
    setLoginUrl("/otherlLogin");
    return super.onAccessDenied(servletRequest, servletResponse);
}

 ⑤getUsername( ):获得登陆用户名。表单name值必须是username。

 ⑥getPassword( ): 获得登陆密码。表单name值必须是password。

 ⑦isRememberMe( ):获得是否记住我。表单name值必须是rememberMe。

三、拦截器链:

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

四、拦截器工厂类(ShiroFilterFactoryBean):

1、作用:这个类用于spring框架中配置过滤器,通过此工厂类可以很方便的配置拦截器的各种基本属性。

2、方法:

(1)setSecurityManager( ):必输。注入一个SecurityManager类,SecurityManager负责管理整个shiro核心验证功能。

(2)setLoginUrl( ):配置登录页路径。这是一个非常方便的用法,我们不需要在拦截器中来定义他。这里需要注意登录页即使不配置拦截规则也一定会被authc拦截。

(3)setSuccessUrl( ):配置登录成功页路径。这是一个非常方便的用法,不需要在拦截器中来定义他。

(4)setUnauthorizedUrl( ):配置没有权限跳转的页面。这是一个非常方便的用法,不需要在拦截器中来定义他。

(5)setFilterChainDefinitionMap( ):设置拦截规则。拦截规则是通过一个Map进行导入的。

(6)setFilters( ):用于注入自己实现的拦截器类。自定义拦截器一般是继承了shiro原有拦截器并重写了部分方法。

3、拦截规则写法::拦截规则使用Map<String, String>将路径与拦截器的映射关系进行匹配。如

Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
filterChainDefinitionMap.put("<拦截路径>", "<拦截器名称>");

 (1)<拦截器路径>:拦截器路径是一个从根路径开始的url,并支持通配符。登录页即使不配置也一定会被authc拦截。

 (2)<拦截器名称>:是shiro内置拦截器的名称,也可以自己实现拦截器为其定义名称。

anon - 无需登录认证和授权就可访问的路径使用anon拦截器

authc - 需要登录认证的路径使用authc拦截器

user - 用户拦截器,用户已经登录认证 或 已经记住我 的都可以通过。

(user拦截器会自动调用authc 拦截器,所以如果要使用记住我功能只配置user即可)

perms[role_name] - 需要权限验证的路径使用perms拦截器。中括号内为权限名称列表。

多个拦截器可以混合使用用逗号分隔,如

Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
filterChainDefinitionMap.put("/", "anon"); //无需登录认证和授权就可访问的路径使用anon拦截器
filterChainDefinitionMap.put("/home/**", "user");//需要登录认证的路径使用authc或user拦截器
filterChainDefinitionMap.put("/user/**", "user,perms[user-jurisdiction]");//需要权限授权的路径使用perms拦截器
filterChainDefinitionMap.put("/admin/**", "user,perms[admin-jurisdiction]");//authc和perms拦截器可同时使用
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);//设置拦截规则

4、demo:

/**
 * 获得拦截器工厂类
 */
@Bean (name = "authenticationFilter")
public AuthenticationFilter authenticationFilter() {
    return new AuthenticationFilter();
}
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager,AuthenticationFilter authenticationFilter) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(securityManager);//设置SecurityManager,必输
    shiroFilterFactoryBean.setLoginUrl("/login");//配置登录路径(登录页的路径和表单提交的路径必须是同一个,页面的GET方式,表单的POST方式)
    shiroFilterFactoryBean.setSuccessUrl("/home");//配置登录成功页路径
    shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");//配置没有权限跳转的页面

    Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
    filterChainDefinitionMap.put("/", "anon"); //无需登录认证和授权就可访问的路径使用anon拦截器
    filterChainDefinitionMap.put("/home/**", "user");//需要登录认证的路径使用authc或user拦截器
    filterChainDefinitionMap.put("/user/**", "user,perms[user-jurisdiction]");//需要权限授权的路径使用perms拦截器
    filterChainDefinitionMap.put("/admin/**", "user,perms[admin-jurisdiction]");//authc和perms拦截器可同时使用
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);//设置拦截规则

    Map<String, Filter> map = new HashMap<String, Filter>();
    map.put("authc", authenticationFilter);//自定义拦截器覆盖了FormAuthenticationFilter登录拦截器所用的拦截器名authc
    shiroFilterFactoryBean.setFilters(map);//添加自定义拦截器

    return shiroFilterFactoryBean;
}


 

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;