ch12:自定义Filter实现IP限制

2017-07-31  本文已影响0人  伊娃瓦力

对于Web应用的安全来说,最常使用的方式就是Servlet Filter,Filter能够用来增加应用特定的安全层,在最终调用具体业务逻辑前,Filter可以移除潜在的恶意行为,并增强用户体验。Spring Security提供就是基于Filter提供了各项安全卡控。但是有些时候,我们还是需要开发自定义Filter以满足应用的特殊要求,例如IP限制等。
在ch04我们已经使用Spring Security基于特定的URL设定可访问的IP地址。但是这是一种静态配置,而生产过程中往往需要动态可访问IP,这时就可以实现自定义Filter,并将其注入到Spring Security Filter Chain中,作为Filter Chain的一环。
这里我们假设系统管理员限制为某些IP地址,只有同时具备管理员身份和IP地址列表时才允许使用管理员权限。

自定义Filter

任何实现了Filter接口的类都可以注入到Spring Security Filter Chain中。这里我们使用Spring Security提供的OncePerRequestFilter作为父类。该类可以保证每个请求只调用一次改Filter。子类需要实现doFilterInternal方法。

public class IpFilter extends OncePerRequestFilter {
    private String targetRole;//目标角色
    private List<String> authorizedIpAddresses;//IP地址列表

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && targetRole != null) {
            boolean shouldCheck = false;
            for (GrantedAuthority authority : authentication.getAuthorities()) {
                if (authority.getAuthority().equals(targetRole)) {
                    shouldCheck = true;
                    break;
                }
            }
            if (shouldCheck && !authorizedIpAddresses.isEmpty()) {
                boolean authorized = false;
                for (String ipAddress : authorizedIpAddresses) {
                    if (request.getRemoteAddr().equals(ipAddress)) {
                        authorized = true;
                        break;
                    }
                }

                if (!authorized) {
                    throw new AccessDeniedException(
                            "Access has been denied for you IP address:" + request.getRemoteAddr());
                }
            }
        } else {
            System.out.println(
                    "The IPFilter should be placed after the user has been authenticated in the filter chain.");
        }
        filterChain.doFilter(request, response);
    }

    public String getTargetRole() {
        return targetRole;
    }

    public void setTargetRole(String targetRole) {
        this.targetRole = targetRole;
    }

    public List<String> getAuthorizedIpAddresses() {
        return authorizedIpAddresses;
    }

    public void setAuthorizedIpAddresses(List<String> authorizedIpAddresses) {
        this.authorizedIpAddresses = authorizedIpAddresses;
    }
}

这里我们简单的检查了用户身份、以及访问者的IP是否为Localhost(实际生产中IP地址检查要复杂的多)。不符合要求的访问将抛出AccessDeniedException。

配置IpFilter

创建好Filter后,需要将其注入到Filter Chain中,这时就需要考虑Filter依赖关系,已决定Filter在Filter Chain中的位置。IpFilter需要获得用户认证信息,所以应该在UsernamePasswordAuthenticationFilter之后。通常自定义Filter都在Chain最后几位上,经过Spring Security层层通用过滤后,再进行个性化和具体的过滤。此外,Spring Security将FilterSecurityInterceptor作为最后一个Filter,主要是验证访问者是否有权限,包括方法级验证等。IpFilter较FilterSecurityInterceptor更具体,所以IpFilter应在后者之前。具体配置如下:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
     http
                ......
                .and().addFilterBefore(ipFilter(), FilterSecurityInterceptor.class)
                ......
    }

    private Filter ipFilter() {
        List<String> ipAddresses = new ArrayList<>();
        ipAddresses.add("0:0:0:0:0:0:0:1");//localhost
        IpFilter ipFilter = new IpFilter();
        ipFilter.setTargetRole("ROLE_ADMIN");
        ipFilter.setAuthorizedIpAddresses(ipAddresses);
        return ipFilter;
    }

这里配置了要求具备ROLE_ADMIN的用户IP地址为Localhost。
通过本机和其他机器使用Admin用户登录,就可以测试不在IP地址列表的机器都被限制访问了。

代码示例:https://github.com/wexgundam/spring.security/tree/master/ch12

上一篇下一篇

猜你喜欢

热点阅读