上节讲了web应用的过滤器,这节我们自己定义一个过滤器。
(1 ) 我们来定义一个Filter用于限制某些url只能某些特定IP地址进行访问,代码如下:
public class IpIntAuthorizationFilter extends AuthorizationFilter {
private static Logger logger = Logger.getLogger(IpIntAuthorizationFilter.class);
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
logger.info("来访者的IP是:"+request.getRemoteAddr());
//限制只有内网能访问
if(!request.getRemoteAddr().startsWith("10.128.249")){
throw new IOException("No permission");
}
return true;
}
}
AuthorizationFilter是用于做身份认证用的filter,继承自AccessControlFilter,当访问不允许时会重定向到unauthorizedUrl上去
(2) 在容器中声明filter
<bean id="ipfilter" class="com.rada.framework.filter.IpIntAuthorizationFilter"></bean>
(3) 将ipfilter注册到过滤器链中去
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login" />
<property name="successUrl" value="/" />
<property name="unauthorizedUrl" value="/403" />
<property name="filters">
<map>
<entry key="ipfilter" value-ref="ipfilter"/>
</map>
</property>
<property name="filterChainDefinitions">
<value>
/* = ipfilter,authcBasic
</value>
</property>
</bean>
大功告成,原则上,任何操作request的请求都可以声明新的过滤器来实现。在此基础上我们可以实现很多功能。
如果我们想进行访问访问的控制就可以继承AccessControlFilter;如果我们要添加一些通用数据我们可以直接继承PathMatchingFilter。
比如以下的代码就大概演示了org.apache.shiro.web.filter.authc.FormAuthenticationFilter工作流程:
public class FormLoginFilter extends PathMatchingFilter {
private String loginUrl = "/login.jsp";
private String successUrl = "/";
@Override
protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
if(SecurityUtils.getSubject().isAuthenticated()) {
return true;//已经登录过
}
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if(isLoginRequest(req)) {
if("post".equalsIgnoreCase(req.getMethod())) {//form表单提交
boolean loginSuccess = login(req); //登录
if(loginSuccess) {
return redirectToSuccessUrl(req, resp);
}
}
return true;//继续过滤器链
} else {//保存当前地址并重定向到登录界面
saveRequestAndRedirectToLogin(req, resp);
return false;
}
}
private boolean redirectToSuccessUrl(HttpServletRequest req, HttpServletResponse resp) throws IOException {
WebUtils.redirectToSavedRequest(req, resp, successUrl);
return false;
}
private void saveRequestAndRedirectToLogin(HttpServletRequest req, HttpServletResponse resp) throws IOException {
WebUtils.saveRequest(req);
WebUtils.issueRedirect(req, resp, loginUrl);
}
private boolean login(HttpServletRequest req) {
String username = req.getParameter("username");
String password = req.getParameter("password");
try {
SecurityUtils.getSubject().login(new UsernamePasswordToken(username, password));
} catch (Exception e) {
req.setAttribute("shiroLoginFailure", e.getClass());
return false;
}
return true;
}
private boolean isLoginRequest(HttpServletRequest req) {
return pathsMatch(loginUrl, WebUtils.getPathWithinApplication(req));
}
}
onPreHandle主要流程:
1、首先判断是否已经登录过了,如果已经登录过了继续拦截器链即可;
2、如果没有登录,看看是否是登录请求,如果是get方法的登录页面请求,则继续拦截器链(到请求页面),否则如果是get方法的其他页面请求则保存当前请求并重定向到登录页面;
3、如果是post方法的登录页面表单提交请求,则收集用户名/密码登录即可,如果失败了保存错误消息到“shiroLoginFailure”并返回到登录页面;
4、如果登录成功了,且之前有保存的请求,则重定向到之前的这个请求,否则到默认的成功页面。
此示例来自开涛老师的博客
第八章 拦截器机制