本系列博文目录:https://my.oschina.net/u/3452433/blog/907396
ShiroFilter拦截器是整个Shiro的入口,用于拦截需要安全控制的请求并进行处理。
shiro封装了很多不同用途的拦截器,这里只介绍几个比较常用的拦截器,更详细介绍可以参考博文
《第八章 拦截器机制——跟我学Shiro》:http://jinnianshilongnian.iteye.com/blog/2025656
登录拦截器(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。
拦截器工厂类(ShiroFilterFactoryBean)
这个类用于spring框架中配置过滤器。
通过此工厂类可以很方便的配置拦截器的各种基本属性。
方法:
setSecurityManager:
必输。注入一个SecurityManager类,SecurityManager负责管理整个shiro核心验证功能。
setLoginUrl:
配置登录页路径。这是一个非常方便的用法,我们不需要在拦截器中来定义他。
这里需要注意登录页即使不配置拦截规则也一定会被authc拦截。
在这有一个事情比较坑,setLoginUrl所配置的路径即是页面的访问路径,同时又是登陆表单提交的路径。页面访问使用的是GET方式提交,表单使用的是POST方式提交。
/**
* LoginController中的内容
*/
//根路径
@RequestMapping(value = "/", method = RequestMethod.GET)
public String loginForm() {
return "redirect:login";
}
//登录页面
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginInput(@ModelAttribute("message") String message) {
//判断是否已经登录 或 是否已经记住我
if (SecurityUtils.getSubject().isAuthenticated() || SecurityUtils.getSubject().isRemembered()) {
return "redirect:/home";
} else {
return "login";
}
}
//登录表单提交
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(HttpServletRequest request, RedirectAttributes redirectAttributes) {
//登录认证逻辑
}
在这里说明一下如果不一致导致的现象
如果只配了页面的访问路径则,登录时将不会触发shiro登录拦截器。
如果只配了登陆表单提交的路径则,拦截器判断为未登录时将无法回到登录页面。
setSuccessUrl:
配置登录成功页路径。这是一个非常方便的用法,我们不需要在拦截器中来定义他。
setUnauthorizedUrl:
配置没有权限跳转的页面。这是一个非常方便的用法,我们不需要在拦截器中来定义他。
setFilterChainDefinitionMap:
设置拦截规则。拦截规则是通过一个Map进行导入的。
拦截规则写法详见“拦截规则”部分。
setFilters:
用于注入自己实现的拦截器类。自定义拦截器一般是继承了shiro原有拦截器并重写了部分方法。
一个spring boot配置文件中拦截器工厂类的例子:
/**
* 获得拦截器工厂类
*/
@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;
}
拦截规则写法
拦截规则使用Map<String, String>将路径与拦截器的映射关系进行匹配。
形式如:
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
filterChainDefinitionMap.put("<拦截路径>", "<拦截器名称>");
<拦截器路径>
拦截器路径是一个从根路径开始的url,并支持通配符。
登录页即使不配置也一定会被authc拦截。
<拦截器名称>
是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);//设置拦截规则