zoukankan      html  css  js  c++  java
  • (HTTPS)-强制 SSL (HTTPS)Filter

    汗,无知真可怕,Servlert规范中已经有自动跳转到保护页面(Http - Https)的方法了:
    web.xml
     
        <security-constraint>
            <display-name>Test Auth</display-name>
            <web-resource-collection>
              <web-resource-name>Protected Area</web-resource-name>
              <url-pattern>/*</url-pattern> <!-- 整站SSL -->
              <http-method>DELETE</http-method>
              <http-method>GET</http-method>
              <http-method>POST</http-method>
              <http-method>PUT</http-method>
            </web-resource-collection>
            <user-data-constraint>
              <description>SSL required</description>
              <transport-guarantee>CONFIDENTIAL</transport-guarantee>
            </user-data-constraint>
          </security-constraint>
     
    Basic 认证 + SSL
     
        <security-constraint>
            <display-name>Test Auth</display-name>
            <web-resource-collection>
              <web-resource-name>Protected Area</web-resource-name>
              <url-pattern>/auth/*</url-pattern>
              <http-method>DELETE</http-method>
              <http-method>GET</http-method>
              <http-method>POST</http-method>
              <http-method>PUT</http-method>
            </web-resource-collection>
            <auth-constraint>
              <role-name>ADMIN</role-name>
              <role-name>USER</role-name>
            </auth-constraint>
            <user-data-constraint>
              <transport-guarantee>CONFIDENTIAL</transport-guarantee>
            </user-data-constraint>
          </security-constraint>
     
    回头想想前面的工作真可笑。
    --------------------------
    在Web工程中,如果使用HTTP Basic认证,或FORM认证,为了安全性,最好使用HTTPS的。
    因此我们需要禁止HTTP访问,只能HTTPS访问。
    如果大家感兴趣,可以研究下Spring Security(偶也不懂,貌似它能做到HTTPS与HTTP Session共享)。
    先了解一下URL和URI在java.net.* 包中的用法和区别。详尽描述请参考Javadoc描述:
    URLTest.java
     
        package me.test;
        import java.net.URI;
        import java.net.URL;
        public class URLTest {
            public static void main(String[] args) throws Exception {
                URL u1 = new URL(
                        "ftp://zhang3:123456@192.168.1.1:556/zhang.txt?k1=v1&k2=v2#aa");
                System.out.println(u1.toString());
                System.out.println(u1.toExternalForm());
                System.out.println(u1.toURI());
                URL u2 = new URL(
                        "https",
                        u1.getHost(),
                        8443,
                        u1.getFile());
                // 以下三行的数据均为:
                // https://192.168.1.1:8443/zhang.txt?k1=v1&k2=v2
                System.out.println(u2.toString());
                System.out.println(u2.toExternalForm());
                System.out.println(u2.toURI());
                System.out.println("----------------");
                URI i1 = new URI(
                        "ftp://zhang3:123456@192.168.1.1:556/zhang.txt?k1=v1&k2=v2#aa");
                URI i2 = new URI(
                        "https",
                        i1.getUserInfo(),
                        i1.getHost(),
                        8443,
                        i1.getPath(),
                        i1.getQuery(),
                        i1.getFragment());
                // 以下两行均输出
                // https://zhang3:123456@192.168.1.1:8443/zhang.txt?k1=v1&k2=v2#aa
                System.out.println(i2.toURL());
                System.out.println(i2.toString());
            }
        }
     
    以下是偶的Filter:
    ForceSSLFilter.java
     
        /*
         * @(#)ForceSSLFilter.java
         *
         * Copyright (c) 2011, Digital Yingtan Construction Committee.
         */
        package me.test;
        import java.io.IOException;
        import java.net.URI;
        import java.net.URISyntaxException;
        import java.util.ArrayList;
        import java.util.List;
        import java.util.regex.Pattern;
        import javax.servlet.Filter;
        import javax.servlet.FilterChain;
        import javax.servlet.FilterConfig;
        import javax.servlet.ServletException;
        import javax.servlet.ServletRequest;
        import javax.servlet.ServletResponse;
        import javax.servlet.http.HttpServletRequest;
        import javax.servlet.http.HttpServletResponse;
        import org.apache.commons.lang.StringUtils;
        /**
         * 对某些路径强制使用SSL的Filter。
         * 如果请求的URL不是 https,则会使其重定向到相应的https路径上。
         *
         * 注意:此Filter目前只能用于无状态的WebService等路径。因为https与http在Tomcat等
         * Servlet容器中会使用不同的SESSION。
         *
         * @author 张亮亮 2011/05/26 新建
         */
        public class ForceSSLFilter implements Filter {
            /** 需要强制使用SSL的路径。 */
            protected List<String> sslPaths = null;
            /** 不需要强制使用SSL的路径。 */
            protected List<String> noSslPaths = null;
            /** SSL的端口。 */
            protected int sslPort = 443;
            /** 是否使用重定向。 */
            protected boolean usingRedirect = true;
            /**
             * 获得初始值。
             *
             * @param fc 配置信息
             */
            public void init(FilterConfig fc) throws ServletException {
                // 参数:需要强制使用SSL的路径
                String paths = fc.getInitParameter("sslPaths");
                sslPaths = new ArrayList<String>();
                if (StringUtils.isNotBlank(paths)) {
                    for (String regexStr : paths.split(",")) {
                        if (StringUtils.isNotBlank(regexStr)) {
                            sslPaths.add(regexStr.trim());
                        }
                    }
                }
                // 参数:不需要强制使用SSL的路径
                paths = fc.getInitParameter("noSslPaths");
                noSslPaths = new ArrayList<String>();
                if (StringUtils.isNotBlank(paths)) {
                    for (String regexStr : paths.split(",")) {
                        if (StringUtils.isNotBlank(regexStr)) {
                            noSslPaths.add(regexStr.trim());
                        }
                    }
                }
                // 参数:SSL的端口
                String port = fc.getInitParameter("sslPort");
                if (StringUtils.isNotBlank(port)) {
                    sslPort = Integer.valueOf(port);
                }
                // 参数:是否使用重定向
                String redirect = fc.getInitParameter("usingRedirect");
                if (StringUtils.isNotBlank(redirect)) {
                    usingRedirect = Boolean.valueOf(redirect);
                }
            }
            /**
             *
             */
            public void destroy() {
            }
            /**
             *单点登录主要处理方法。
             *
             * @param req 请求
             * @param resp 响应
             * @param filterChain 响应链
             */
            public void doFilter(ServletRequest req, ServletResponse resp,
                    FilterChain filterChain) throws IOException, ServletException {
                HttpServletRequest request = (HttpServletRequest) req;
                HttpServletResponse response = (HttpServletResponse) resp;
                String servletPath = request.getServletPath();
                // 不需要SSL?
                boolean needFilter = true;
                for (String regexStr : noSslPaths) {
                    if (Pattern.matches(regexStr, servletPath)) {
                        needFilter = false;
                        break;
                    }
                }
                if (needFilter && !request.isSecure()) {
                    // 是否需要强制SSL?
                    boolean needRedirect = false;
                    for (String regexStr : sslPaths) {
                        if (Pattern.matches(regexStr, servletPath)) {
                            needRedirect = true;
                            break;
                        }
                    }
                    // 进行跳转
                    if (needRedirect) {
                        if (usingRedirect) {
                            try {
                                URI reqUri = new URI(request.getRequestURL().toString());
                                URI newUri = new URI("https", reqUri.getUserInfo(),
                                        reqUri.getHost(), sslPort, reqUri.getPath(),
                                        reqUri.getQuery(), reqUri.getFragment());
     
                                response.sendRedirect(newUri.toString());
                                response.flushBuffer();
                                return;
                            } catch (URISyntaxException e) {
                                throw new RuntimeException("请求的URL格式不正确。", e);
                            }
                        } else {
                            response.sendError(403, "此URL必须使用HTTPS访问。");
                            return;
                        }
                    }
                }
                filterChain.doFilter(request, response);
            }
        }
     
    web.xml中的配置
     
        ...
        <filter>
            <filter-name>forceSSLFilter</filter-name>
            <filter-class>
              me.test.ForceSSLFilter
            </filter-class>
            <init-param>
               <param-name>sslPaths</param-name>
               <param-value>/auth/.*</param-value>
            </init-param>
            <init-param>
               <param-name>noSslPaths</param-name>
               <param-value>/auth/1/.*</param-value>
            </init-param>
            <init-param>
               <param-name>sslPort</param-name>
               <param-value>8443</param-value>
            </init-param>
            <init-param>
               <param-name>usingRedirect</param-name>
               <param-value>false</param-value>
            </init-param>
          </filter>
        ...
     
    不足:Basic认证优先被Servlet容器处理,所以,会造成以下情况;
    1. 用户在使用http访问必须要https访问的路径时,被提示输入用户名,密码
    2. 然后,显示错误信息(“403 此URL必须使用HTTPS访问” - 如果 web.xml 中usingRedirect为false
    或者 跳转到https的访问路径上,但又被要求重新输入一次密码 )
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    Web前端学习第五天——————HTML篇.019页面布局练习
    1111
    开发者如何利用数据分析提高收入
    开发者进行广告合作的几大误区
    高仿人人Android梦想版终极源码发送(转)
    移动开发者如何获取免费流量
    Inno setup常用代码【收藏】
    Inno setup常用代码补充【收藏】
    QT for Window程序部署
    Inno Setup自定义卸载文件名称【收藏】
  • 原文地址:https://www.cnblogs.com/crazylqy/p/4074015.html
Copyright © 2011-2022 走看看