引入依赖
<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;
}