47

学习 Shiro(二):REST API Filter

 4 years ago
source link: http://muziyuchen.com/shiro-2/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Shiro Web 的设计目标仍是解决单体 Web 服务的安全问题,如配置 authc.loginUrl 登录页面,用户被禁止访问则重定向到登录页面。

显然,authc 过滤器的默认行为显然并不适合提供 REST API 的 Web 服务。

Shiro 简单易扩展,可以对 Shiro Web 进行定制化,从而更好支持 REST API。

目标如下所示:

  • 未登录,返回 HTTP 401 Unauthorized
  • 未授权,返回 HTTP 403 Forbidden

Filter

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,关键步骤如下:

  1. 请求目标 Servlet,Servlet Container 检测到存在两个 Filter 并创建了 FilterChain;
  2. 执行 FilterChain 中第一个 Filter 的 doFilter() 方法;
  3. 第一个 Filter 完成处理,调用 FilterChain 的 doFilter() 方法,从而执行 Filter Chain 中第二个 Filter 的 doFilter() 方法;
  4. 第二个 Filter 完成处理,调用 FilterChain 的 doFilter() 方法,从而执行 Servlet 的 service() 方法;
  5. Servlet 完成,第二个 Filter 调用 FilterChain 的 doFilter() 方法返回;
  6. 第二个 Filter 完成,第一个 Filter 调用 FilterChain 的 doFilter() 方法返回;
  7. 第一个 Filter 完成。

mm2Eref.gif

在 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

REST API Filter

以 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

参考


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK