Servlert规范中已经有自动跳转到保护页面(Http - Https)的方法了:
web.xml
Basic 认证 + SSL
回头想想前面的工作真可笑。
--------------------------
在Web工程中,如果使用HTTP Basic认证,或FORM认证,为了安全性,最好使用HTTPS的。
因此我们需要禁止HTTP访问,只能HTTPS访问。
如果大家感兴趣,可以研究下Spring Security(偶也不懂,貌似它能做到HTTPS与HTTP Session共享)。
先了解一下URL和URI在 java.net.* 包中的用法和区别。详尽描述请参考Javadoc描述:
URLTest.java
以下是偶的Filter:
ForceSSLFilter.java
web.xml中的配置
不足:Basic认证优先被Servlet容器处理,所以,会造成以下情况;
1. 用户在使用http访问必须要https访问的路径时,被提示输入用户名,密码
2. 然后,显示错误信息(“403 此URL必须使用HTTPS访问” - 如果 web.xml 中usingRedirect为false
或者 跳转到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>
- <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());
- }
- }
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);
- }
- }
- ...
- <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>
- ...
1. 用户在使用http访问必须要https访问的路径时,被提示输入用户名,密码
2. 然后,显示错误信息(“403 此URL必须使用HTTPS访问” - 如果 web.xml 中usingRedirect为false
或者 跳转到https的访问路径上,但又被要求重新输入一次密码