Shiro Web 的设计目标仍是解决单体 Web 服务的安全问题,如配置 authc.loginUrl 登录页面,用户被禁止访问则重定向到登录页面。
显然,authc 过滤器的默认行为显然并不适合提供 REST API 的 Web 服务。
Shiro 简单易扩展,可以对 Shiro Web 进行定制化,从而更好支持 REST API。
目标如下所示:
Shiro Web 使用了 Filter 对 HTTP 请求和响应进行过滤。 javax.servlet.Filter 接口定义如下:
public interface Filter {
public void init(FilterConfig filterConfig) throws ServletException;
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
public void destroy();
}
在 Shiro Web 中 Filter 的实现是 org.apache.shiro.web.servlet.ShiroFilter 类。在 Shiro Spring 中 Filter 的实现是 org.apache.shiro.spring.web.SpringShiroFilter 类。
如果定义了两个 Filter,关键步骤如下:
doFilter() 方法; doFilter() 方法,从而执行 Filter Chain 中第二个 Filter 的 doFilter() 方法; doFilter() 方法,从而执行 Servlet 的 service() 方法; doFilter() 方法返回; doFilter() 方法返回;
在 Shiro Web 中,路径规则:
/user/** = authc /** = anon
会转化为一个 FilterChain,而 authc 和 anon 就是 Shiro Web 默认提供的 Filter 实现。
默认 Filter 表格:
| 名称 | 实现类 | |
|---|---|---|
| anon | org.apache.shiro.web.filter.authc.AnonymousFilter | |
| authc | org.apache.shiro.web.filter.authc.FormAuthenticationFilter | |
| authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter | |
| logout | org.apache.shiro.web.filter.authc.LogoutFilter | |
| noSessionCreation | org.apache.shiro.web.filter.session.NoSessionCreationFilter | |
| perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter | |
| port | org.apache.shiro.web.filter.authz.PortFilter | |
| rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter | |
| roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter | |
| ssl | org.apache.shiro.web.filter.authz.SslFilter | |
| user | org.apache.shiro.web.filter.authc.UserFilter |
以 Spring Boot 集成 org.apache.shiro:shiro-spring-boot-web-starter 为例,演示如何自定义 Filter。
public class RestApiFilter extends FormAuthenticationFilter { // ①
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
Subject subject = getSubject(request, response);
if (subject.isAuthenticated()) { // ②
HttpServletResponse httpResponse = WebUtils.toHttp(response);
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
} else { // ③
HttpServletResponse httpResponse = WebUtils.toHttp(response);
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
}
return false;
}
}
① 修改 authc 在访问禁止的行为,选择继承 org.apache.shiro.web.filter.authc.FormAuthenticationFilter 并覆盖其 onAccessDenied() 方法;
② 如果没有认证,返回 HTTP 401 Unauthorized;
③ 如果没有鉴权,返回 HTTP 403 Forbidden。
添加自定义 Filter:
@Configuration
public class ShiroWebFilterConfiguration extends AbstractShiroWebFilterConfiguration {
@Bean
@Override
protected ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean bean = super.shiroFilterFactoryBean();
bean.getFilters().put("api", new RestApiFilter());
return bean;
}
}
使用自定义 Filter:
/api/** = api