Jetty:配置安全
用${jetty.home}和${jetty.base}配置安全
Jetty 9.1中:
1)${jetty.home}是jetty公布(二进制)的文件夹路径;
2)${jetty.base}是用户定制化的文件夹路径。
这样分化:
1)同意你管理多个Jetty安装;
2)当你升级Jetty后,更easy保留你当前的配置。
很多其它的信息在后面讲《启动Jetty》时会详述。
并且,Jetty 9.1參数化了全部的标准XML配置。比如SSL,參数如今仅是在start.ini中的属性,不须要编辑XML文件。
Jetty 9.1也使用模块。Jetty不再直接的为一个特征列出全部的库、属性和XML文件,改为使用软件模块,而且start.jar机制同意你创建新模块。你定义一个模块在一个modules/*.mod文件里,包含库、依赖、XML、和模板INI文件。你仅仅须要用--module=name命令行选项就可以载入模块。模块使用依赖来控制库和XML文件的顺序,很多其它的信息在《启动Jetty》讲述。
在Jetty 9.1中配置SSL
以下是一个使用${jetty.home}和${jetty.base}配置SSL的样例,当中也包含了模块怎么工作的细节。
这个样例假定你的Jetty公布放在/home/user/jetty-distribution-9.1.0.RC0。
1)创建一个base目录
[/home/user]$ mkdir my-base [/home/user]$ cd my-base
2)为SSL、HTTP和webapp部署添加模块
[my-base]$ java -jar /home/user/jetty-distribution-9.1.0.RC0/start.jar --add-to-start=ssl,http,deploy ssl initialised in ${jetty.base}/start.ini (appended) ssl enabled in ${jetty.base}/start.ini DOWNLOAD: http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/plain/jetty-server/src/main/config/etc/keystore to etc/keystore server initialised in ${jetty.base}/start.ini (appended) server enabled in ${jetty.base}/start.ini http initialised in ${jetty.base}/start.ini (appended) http enabled in ${jetty.base}/start.ini server enabled in ${jetty.base}/start.ini deploy initialised in ${jetty.base}/start.ini (appended) deploy enabled in ${jetty.base}/start.ini MKDIR: ${jetty.base}/webapps server enabled in ${jetty.base}/start.ini
3)查看你的目录
[my-base]$ ls -la total 20 drwxrwxr-x 4 user group 4096 Oct 8 06:55 ./ drwxr-xr-x 103 user group 4096 Oct 8 06:53 ../ drwxrwxr-x 2 user group 4096 Oct 8 06:55 etc/ -rw-rw-r-- 1 user group 815 Oct 8 06:55 start.ini drwxrwxr-x 2 user group 4096 Oct 8 06:55 webapps/
4)拷贝你的WAR文件到webapps
[my-base]$ ls -la [my-base]$ cp ~/code/project/target/gadget.war webapps/
5)拷贝你的keystore
[my-base]$ cp ~/code/project/keystore etc/keystore
6)编辑start.ini配置你的SSL设置
[my-base]$ cat start.ini
7)初始化模块ssl
--module=ssl
8)为安全重定向定义port
jetty.secure.port=8443
9)建立一个示范keystore和truststore
jetty.keystore=etc/keystore jetty.truststore=etc/keystore
10)设置示范password
jetty.keystore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4 jetty.keymanager.password=OBF:1u2u1wml1z7s1z7a1wnl1u2g jetty.truststore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
11)初始化模块server
--module=server threads.min=10 threads.max=200 threads.timeout=60000 #jetty.host=myhost.com jetty.dump.start=false jetty.dump.stop=false
12)初始化模块http
--module=http jetty.port=8080 http.timeout=30000
13)初始化模块部署
--module=deploy
查看你如今的配置:
[my-base]$ java -jar /home/user/jetty-distribution-9.1.0.RC0/start.jar --list-config Java Environment: ----------------- java.home=/home/user/java/jdk-7u21-x64/jre java.vm.vendor=Oracle Corporation java.vm.version=23.21-b01 java.vm.name=Java HotSpot(TM) 64-Bit Server VM java.vm.info=mixed mode java.runtime.name=Java(TM) SE Runtime Environment java.runtime.version=1.7.0_21-b11 java.io.tmpdir=/tmp Jetty Environment: ----------------- jetty.home=/home/user/jetty-distribution-9.1.0.RC0 jetty.base=/home/user/my-base jetty.version=9.1.0.RC0 JVM Arguments: -------------- (no jvm args specified) System Properties: ------------------ jetty.base = /home/user/my-base jetty.home = /home/user/jetty-distribution-9.1.0.RC0 Properties: ----------- http.timeout = 30000 jetty.dump.start = false jetty.dump.stop = false jetty.keymanager.password = OBF:1u2u1wml1z7s1z7a1wnl1u2g jetty.keystore = etc/keystore jetty.keystore.password = OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4 jetty.port = 8080 jetty.secure.port = 8443 jetty.truststore = etc/keystore jetty.truststore.password = OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4 threads.max = 200 threads.min = 10 threads.timeout = 60000 Jetty Server Classpath: ----------------------- Version Information on 11 entries in the classpath. Note: order presented here is how they would appear on the classpath. changes to the --module=name command line options will be reflected here. 0: 3.1.0 | ${jetty.home}/lib/servlet-api-3.1.jar 1: 3.1.RC0 | ${jetty.home}/lib/jetty-schemas-3.1.jar 2: 9.1.0.RC0 | ${jetty.home}/lib/jetty-http-9.1.0.RC0.jar 3: 9.1.0.RC0 | ${jetty.home}/lib/jetty-continuation-9.1.0.RC0.jar 4: 9.1.0.RC0 | ${jetty.home}/lib/jetty-server-9.1.0.RC0.jar 5: 9.1.0.RC0 | ${jetty.home}/lib/jetty-xml-9.1.0.RC0.jar 6: 9.1.0.RC0 | ${jetty.home}/lib/jetty-util-9.1.0.RC0.jar 7: 9.1.0.RC0 | ${jetty.home}/lib/jetty-io-9.1.0.RC0.jar 8: 9.1.0.RC0 | ${jetty.home}/lib/jetty-servlet-9.1.0.RC0.jar 9: 9.1.0.RC0 | ${jetty.home}/lib/jetty-webapp-9.1.0.RC0.jar 10: 9.1.0.RC0 | ${jetty.home}/lib/jetty-deploy-9.1.0.RC0.jar Jetty Active XMLs: ------------------ ${jetty.home}/etc/jetty.xml ${jetty.home}/etc/jetty-http.xml ${jetty.home}/etc/jetty-ssl.xml ${jetty.home}/etc/jetty-deploy.xml
如今启动Jetty:
[my-base]$ java -jar /home/user/jetty-distribution-9.1.0.RC0/start.jar 2013-10-08 07:06:55.837:INFO:oejs.Server:main: jetty-9.1.0.RC0 2013-10-08 07:06:55.853:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:/home/joakim/my-base/webapps/] at interval 1 2013-10-08 07:06:55.872:INFO:oejs.ServerConnector:main: Started ServerConnector@72974691{HTTP/1.1}{0.0.0.0:8080}
回想配置
以下又一次回想上面的配置。
${jetty.base}和${jetty.home}
首先注意${jetty.base}和${jetty.home}的划分。
1)${jetty.home}是你的公布位于的路径,未改变的,为编辑的;
2)${jetty.base}是你的定制位于的路径。
模块
注意你配置的--module=<name>,你打包模块(库、配置XML和属性)进入一个单个的单元,包含依赖。
你能查看模块的列表:
[my-base]$ java -jar /home/user/jetty-distribution-9.1.0.RC0/start.jar --list-modules Jetty All Available Modules: ---------------------------- Module: annotations LIB: lib/jetty-annotations-${jetty.version}.jar LIB: lib/annotations/*.jar XML: etc/jetty-annotations.xml depends: [plus] Module: client LIB: lib/jetty-client-${jetty.version}.jar depends: [] Module: debug XML: etc/jetty-debug.xml depends: [server] Module: deploy LIB: lib/jetty-deploy-${jetty.version}.jar XML: etc/jetty-deploy.xml depends: [webapp] enabled: ${jetty.base}/start.ini Module: ext LIB: lib/ext/*.jar depends: [] Module: http XML: etc/jetty-http.xml depends: [server] enabled: ${jetty.base}/start.ini Module: https XML: etc/jetty-https.xml depends: [ssl] Module: ipaccess XML: etc/jetty-ipaccess.xml depends: [server] Module: jaas LIB: lib/jetty-jaas-${jetty.version}.jar XML: etc/jetty-jaas.xml depends: [server] Module: jaspi LIB: lib/jetty-jaspi-${jetty.version}.jar LIB: lib/jaspi/*.jar depends: [security] Module: jmx LIB: lib/jetty-jmx-${jetty.version}.jar XML: etc/jetty-jmx.xml depends: [] Module: jndi LIB: lib/jetty-jndi-${jetty.version}.jar LIB: lib/jndi/*.jar depends: [server] Module: jsp LIB: lib/jsp/*.jar depends: [servlet] Module: jvm depends: [] Module: logging XML: etc/jetty-logging.xml depends: [] Module: lowresources XML: etc/jetty-lowresources.xml depends: [server] Module: monitor LIB: lib/jetty-monitor-${jetty.version}.jar XML: etc/jetty-monitor.xml depends: [client, server] Module: npn depends: [] Module: plus LIB: lib/jetty-plus-${jetty.version}.jar XML: etc/jetty-plus.xml depends: [server, security, jndi] Module: proxy LIB: lib/jetty-proxy-${jetty.version}.jar XML: etc/jetty-proxy.xml depends: [client, server] Module: requestlog XML: etc/jetty-requestlog.xml depends: [server] Module: resources LIB: resources depends: [] Module: rewrite LIB: lib/jetty-rewrite-${jetty.version}.jar XML: etc/jetty-rewrite.xml depends: [server] Module: security LIB: lib/jetty-security-${jetty.version}.jar depends: [server] Module: server LIB: lib/servlet-api-3.1.jar LIB: lib/jetty-schemas-3.1.jar LIB: lib/jetty-http-${jetty.version}.jar LIB: lib/jetty-continuation-${jetty.version}.jar LIB: lib/jetty-server-${jetty.version}.jar LIB: lib/jetty-xml-${jetty.version}.jar LIB: lib/jetty-util-${jetty.version}.jar LIB: lib/jetty-io-${jetty.version}.jar XML: etc/jetty.xml depends: [] enabled: ${jetty.base}/start.ini Module: servlet LIB: lib/jetty-servlet-${jetty.version}.jar depends: [server] Module: servlets LIB: lib/jetty-servlets-${jetty.version}.jar depends: [servlet] Module: setuid LIB: lib/setuid/jetty-setuid-java-1.0.1.jar XML: etc/jetty-setuid.xml depends: [server] Module: spdy LIB: lib/spdy/*.jar XML: etc/jetty-ssl.xml XML: etc/jetty-spdy.xml depends: [ssl, npn] Module: ssl XML: etc/jetty-ssl.xml depends: [server] enabled: ${jetty.base}/start.ini Module: stats XML: etc/jetty-stats.xml depends: [server] Module: webapp LIB: lib/jetty-webapp-${jetty.version}.jar depends: [servlet] Module: websocket LIB: lib/websocket/*.jar depends: [annotations] Module: xinetd XML: etc/jetty-xinetd.xml depends: [server] Jetty Active Module Tree: ------------------------- + Module: server [enabled] + Module: http [enabled] + Module: servlet [transitive] + Module: ssl [enabled] + Module: webapp [transitive] + Module: deploy [enabled]
上面包括了模块名、使用的库、使用的XML配置、以及他们依赖的其他模块(甚至包括可选的),而且标注了模块是否激活。
你能够通过编辑${jetty.base}/start.ini来管理激活模块列表。
假设你想启动一个新模块:
[my-base] $ java -jar ../jetty-distribution-9.1.0.RC0/start.jar --add-to-start=https
这添加--module=行和相关的属性(上面提到的參数化值)到你的start.ini。
參数
接下来是參数化全部的标准配置XML。在这个样例中,全部的SSL參数都是start.ini中的属性,非常少或者没有编辑XML的须要。
在${jetty.base}中覆盖${jetty.home}
最后,你能在${jetty.base}中覆盖你在${jetty.home}中看到的不论什么东西,即使XML配置和库。
在《启动Jetty》中将有更具体的论述。
在Jetty 9.1中配置SSL总结
1)下载并解压Jetty 9.1到/home/user/jetty-distribution-9.1.0.RC1;
2)不使用了编辑该公布,到你的base目录;
[my-base]$ java -jar /home/user/jetty-distribution-9.1.0.RC1/start.jar
------Jetty 9.1公布提供了XML配置文件,在这里是jetty-http.xml和jetty-ssl.xml。你能在${jetty.home}/etc/中找到他们。
------我们在那些XML中參数化全部的配置项。你如今能用简单的属性来设置这些值,或者在命令行中,或者在${jetty.base}/start.ini中。
------当你激活HTTP和HTTPS模块时,Jetty自己主动加入相应的库和XML到Jetty。除非你有高级别的自己定义设置(比如监听两个不同的port,用SSL在每个上面,每个都有自己的keystore和配置),你应该不须要改动XML文件。
3)用模块配置HTTPS:
------http -> server
------https -> ssl -> server
你能在${jetty.home}/modules/中找到模块的细节。为SSL包含modules/http.mod、modules/https.mod、modules/ssl.mod和modules/server.mod。
理论上,这些细节对你是不重要的。重要的是你想使用HTTPS,而且想配置它。你通过加入--module=https到你的start.ini来达到。默认情况下,模块系统是完整的,且包含全部的依赖模块。
不须要启动Jetty,在全部的模块被解析后,你能够查看配置,通过:
[my-base] $ java -jar ../jetty-distribution-9.1.0.RC0/start.jar --list-config
注意JAR包在磁盘中,并不意味着他们被使用,他们的使用通过配置项控制。
用--list-config来查看配置。注意仅公布版本号中的JAR的子集被使用,你激活的模块决定这个子集。
[my-base]$ java -jar ~/jetty-distribution-9.1.0.RC0/start.jar --list-config
认证
Jetty server内的web应用(或上下文)的安全涉及两个方面:
1)认证:web应用能通过一个机制配置确定用户标识,这通过标准声明、Jetty提供的机制和这节将讲到的配置方式共同配置。
2)授权:一旦用户的标识被确认(或否决),web应用能通过带有安全限制的标准描写叙述符配置用户可以訪问哪些资源。
配置一个认证机制
Jetty server支持集中标准的认证机制:BASIC;DIGEST;FORM;CLIENT-CERT;以及其它能被插入到使用可扩展的JASPI或者SPNEGO机制的机制。
内在地,配置一个认证机制是通过设置一个Authenticator接口的实例到上下文的SecurityHandler来实现的,但大部分实例都是是通过在web.xml中设置< login-config>元素或者使用注解来实现的。
以下是一个样例,来自jetty-test-webapp web.xml(http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/tests/test-webapps/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml?h=release-9),配置BASIC认证:
<login-config> <auth-method>BASIC</auth-method> <realm-name>Test Realm</realm-name> </login-config>
jetty-test-webapp web.xml也包含凝视掉的DIGEST和FORM配置的样例:
<login-config> <auth-method>FORM</auth-method> <realm-name>Test Realm</realm-name> <form-login-config> <form-login-page>/logon.html?param=test</form-login-page> <form-error-page>/logonError.html?param=test</form-error-page> </form-login-config> </login-config>
使用FORM认证,你也必须配置产生一个登录表格和处理错误的页面的URL。以下简单的HTML表格来自test webapp logon.html(http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/tests/test-webapps/test-jetty-webapp/src/main/webapp/logon.html?h=release-9):
<HTML> <H1>FORM Authentication demo</H1> <form method="POST" action="j_security_check"> <table border="0" cellspacing="2" cellpadding="1"> <tr> <td>Username:</td> <td><input size="12" value="" name="j_username" maxlength="25" type="text"></td> </tr> <tr> <td>Password:</td> <td><input size="12" value="" name="j_password" maxlength="25" type="password"></td> </tr> <tr> <td colspan="2" align="center"> <input name="submit" type="submit" value="Login"> </td> </tr> </table> </form> </HTML>
认证机制为上下文/web应用定义服务端怎么从client获取认证证书,可是它不定义服务端怎么检查这些证书是否有效。为了检查证书,server和/或上下文也须要配置LoginService实例,能够通过域名匹配。
安全域
安全域用于防止你的web应用被未认证的进入。保护基于认证(表示谁正在请求进入webapp)和进入控制(限制什么能被訪问和怎么訪问)。
webapp在web.xml中静态地配置它的安全要求。认证通过<login-config>元素控制。訪问控制通过<security-constraint>和<security-role-ref>元素指定。当一个请求请求一个受保护的资源时,web容器检查用户是否被认证,而且用户所在的角色是否有该资源的訪问权限。
Servlet指导手冊没有指定在WEB-INF/web.xml中的静态安全信息怎么被匹配到容器的执行时环境。在Jetty中,LoginService履行这个职责。
LoginService有一个唯一name,而且同意訪问用户列表。每一个用户都有认证信息(比如:password)和与之关联的角色列表。
你能够依据须要配置一个或者多个不同的LoginService。单一的域将表明你希望你的全部web应用共享安全信息。不同的域同意你在不同的webapp将划分安全信息。
当一个请求请求认证和授权时,Jetty将用web.xml中的<login-config>元素内的<realm-name>子元素履行对LoginService的一次精确匹配。
安全域范围
一个LoginService有一个唯一名称,由一个用户列表组成。每一个用户有认证信息(比如:password)和与之关联的角色列表。你能依据你的须要配置一个或多个不同的域:
1)配置一个单个的LoginService,你的全部web应用共享安全信息。
2)配置不同的LoginService,为webapp划分不同的安全信息。
全局范围
假设你定义LoginService在Jetty配置文件里,比如${jetty.home}/etc/jetty.xml,那么它在一个Server实例上对全部的web应用都是有效的。以下是一个样例,定义一个in-memory类型的LoginService,叫做HashLoginService:
<Configure id="Server" class="org.eclipse.jetty.server.Server"> <Call name="addBean"> <Arg> <New class="org.eclipse.jetty.security.HashLoginService"> <Set name="name">Test Realm</Set> <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set> <Set name="refreshInterval">0</Set> </New> </Arg> </Call> </Configure>
假设你在一个Server上定义超过一个LoginService,你将须要为每个上下文指定你想使用哪一个。你能够告诉上下文LoginService的名称,或者传递它给LoginService实例。最以下是一个样例:
<Configure class="org.eclipse.jetty.webapp.WebAppContext"> <Get name="securityHandler"> <!-- Either: --> <Set name="loginService"> <New class="org.eclipse.jetty.security.HashLoginService"> <Set name="name">Test Realm</Set> </New> </Set> <!-- or if you defined a LoginService called "Test Realm" in jetty.xml : --> <Set name="realmName">Test Realm</Set> </Get>
每webapp范围
你也能够为单独的web应用定义LoginService。以下是怎么为上下文定义相同的HashLoginService:
<Configure class="org.eclipse.jetty.webapp.WebAppContext"> <Set name="contextPath">/test</Set> <Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/test</Set> <Get name="securityHandler"> <Set name="loginService"> <New class="org.eclipse.jetty.security.HashLoginService"> <Set name="name">Test Realm</Set> <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set> </New> </Set> </Get> </Configure>
Jetty提供了一组不同的LoginService类型,以下将做介绍。
配置一个LoginService
一个LoginService实例有一个认证机制,被用于检查被认证机制收集的username和证书的有效性。Jetty提供了以下的LoginService的实现:
------HashLoginService
用户域是一个hash表,通过编程填充或者通过Java属性文件填充。
------JDBCLoginService
使用一个JDBC连接到一个SQL数据库进行认证。
------DataSourceLoginService
用JNDI定义认证的DataSource。
------JAASLoginService
用JAAS提供商进行验证。
------SpnegoLoginService
SPNEGO认证。
一个LoginService的实例能通过以下的方式匹配到上下文/webapp:
1)一个LoginService实例能够被直接设置到SecurityHandler实例,通过代码或者IoC XML。
2)用LoginService实例(作为依赖bean设置到Server实例中)的名称匹配定义在web.xml中的域名。
3)假设仅一个单个的LoginService实例被设置到Server,则它被用于上下文的登录service。
HashLoginService
HashLoginService是一个简单有效的登录service,用于从一个Java属性文件载入username、证书和角色集,属性文件格式例如以下;
username: password[,rolename ...]
这里:
------username
用户的唯一标识
------password
用户password(可能被扰乱或者被MD5加密)
------rolename
用户的角色
比如:
admin: CRYPT:ad1ks..kc.1Ug,server-administrator,content-administrator,admin other: OBF:1xmk1w261u9r1w1c1xmq guest: guest,read-only
你使用一个名称和属性文件地址配置HashLoginService:
<Item> <New class="org.eclipse.jetty.security.HashLoginService"> <Set name="name">Test Realm</Set> <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set> </New> </Item>
你也能配置它定期检測属性文件改变,假设发生变化则又一次载入它。reloadInterval的单位是秒:
<New class="org.eclipse.jetty.security.HashLoginService"> <Set name="name">Test Realm</Set> <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set> <Set name="reloadInterval">5</Set> <Call name="start"></Call> </New>
JDBCLoginService
在这个实现中,认证和角色信息被存储在数据库中,通过JDBC訪问。一个属性文件定义JDBC连接和数据库表信息。以下是属性文件的样例:
jdbcdriver = org.gjt.mm.mysql.Driver url = jdbc:mysql://localhost/jetty username = jetty password = jetty usertable = users usertablekey = id usertableuserfield = username usertablepasswordfield = pwd roletable = roles roletablekey = id roletablerolefield = role userroletable = user_roles userroletableuserkey = user_id userroletablerolekey = role_id cachetime = 300
数据库表的格式是(伪sql):
users ( id integer PRIMARY KEY, username varchar(100) NOT NULL UNIQUE KEY, pwd varchar(50) NOT NULL ); user_roles ( user_id integer NOT NULL, role_id integer NOT NULL, UNIQUE KEY (user_id, role_id), INDEX(user_id) ); roles ( id integer PRIMARY KEY, role varchar(100) NOT NULL UNIQUE KEY );
这里:
------users为每一个用户包括一个条目,包括:
------id:用户的唯一标识
------user:username
------pwd:用户password(可能被扰乱或者被MD5加密)
------user-roles是一个表格,每行表示一个角色授权给一个用户:
------user_id:用户唯一标识
------role_id:用户角色
------roles是一个表格,包括系统中的全部角色
------id:角色的唯一标识
------role:角色的可读的名称
假设你想使用扰乱、MD5扰乱或者加密password,users表的pwd列必须足够大。
你定义一个JDBCLoginService,须要指定域名和描写叙述数据库的属性文件路径:
<New class="org.eclipse.jetty.security.JDBCLoginService"> <Set name="name">Test JDBC Realm</Set> <Set name="config">etc/jdbcRealm.properties</Set> </New>
授权
依照servlet指导文档,授权基于角色。就像我们看到过的,一个LoginService关联的一个用户相应一组角色。当一个用于请求一个受保护的资源时,LoginService将被用于认证请求的用户,然后确认用户相应的角色集中是否存在对该资源有权限的角色。
在Servlet 3.1之前,基于角色的授权能定义:
1)授权于一组命名的角色
2)訪问全然禁止,不管不论什么角色
3)訪问授权给一个相应web.xml中定义的不论什么角色的用户。这通过在<security-constraint>中为<auth-constraint>的<role-name>指定为"*"表明
Servlet 3.1添加了还有一个授权:
1)訪问授权给被认证的不论什么用户,不管不论什么角色。这通过在<security-constraint>中为<auth-constraint>的<role-name>指定值"**"来表明
限制表单内容
提交到服务端的表单内容被Jetty组织为一个參数map交给web应用处理。这个机制是easy被攻击的,client可以通过提交大数据量的表单内容或者大量的表单keys来消耗服务端的内容和CPU,这就是拒绝服务攻击(DOS)。因此Jetty限制可以提交到Jetty的数据和keys的数量。
Jetty同意的默认的最大值是200000 bytes和1000 keys.你能改变这个默认值为特定Server实例上的一个特定的webapp或者全部的webapp。
配置表单显示为一个webapp
为了为单个webapp配置表单限制,上下文处理器(或者webappContext)实例必须用以下的方式配置:
ContextHandler.setMaxFormContentSize(int maxSizeInBytes); ContextHandler.setMaxFormKeys(int formKeys);
这些方法能够在代码中直接调用,但更通常的是通过上下文XML或者WEB-INF/jetty-web.xml配置:
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
...
<Set name="maxFormContentSize">200000</Set>
<Set name="maxFormKeys">200</Set>
</Configure>
配置表单限制为Server
假设一个上下文没有指定表单限制,那么将使用server的配置。以下是在jetty.xml中配置:
<configure class="org.eclipse.jetty.server.Server"> ... <Call name="setAttribute"> <Arg>org.eclipse.jetty.server.Request.maxFormContentSize</Arg> <Arg>100000</Arg> </Call> <Call name="setAttribute"> <Arg>org.eclipse.jetty.server.Request.maxFormKeys</Arg> <Arg>2000</Arg> </Call> </configure>
别名文件和符号链接(Aliased Files and Symbolic links)
web应用将常常提供静态内容。然而因为文件系统常常为相同的文件提供多个别名,导致安全限制和其它servlet URI空间映射通过别名被绕过。
比較典型的样例是在Windows文件系统下的大写和小写敏感和8.3文件名称实现。假设一个webapp中的文件叫做/mysecretfile.txt,被安全限制保护,相应URI为/mysecretfile.txt,那么一个对/MySecretFile.TXT的请求将不匹配URI限制,由于URI是大写和小写敏感的,可是Windows文件系统将报告存在这个文件而且为这个请求提供服务,从而绕过安全限制。比大写和小写敏感更少见的是Windows文件系统也支持8.3文件名称,主要是为了兼容性。因此一个对URI为/MYSECR~1.TXT的请求将不匹配安全限制,但文件系统会找到相应的文件并提供服务。
有一些别名的样例,不只在windows上:
1)NTFS Alternate流命名为这样c:\test\file.txt::$DATA:name。
2)OpenVMS支持文件版本号化,/mysecret.txt;N表示相应/mysecret.txt的版本号N,本质上是别名。
3)clearcase软件配置管理系统提供一个文件系统,在一个文件名称中的@@表示相应一个特定版本号的别名。
4)unix文件系统支持/./foo.txt作为/foo.txt的别名。
5)一些JVM实现不对的假定null字符是一个字符串的终结符,以至于一个文件名称/foobar.txt%00是/foobar.txt的别名。
6)Unix符号链接和硬链接是别名的一种形式,同意相同的文件或目录有多个名称。
另外,不只URI安全限制能被绕过。比如匹配模式*.jsp到JSP Servlet的URI映射能够被绕过,通过请求一个别名象/foobar.jsp%00,导致JSP的源码被文件系统返回,而不是运行这个JSP。
好的安全实践
别名导致的问题,一部分是因为标准web应用安全模式是同意全部的请求除非请求被安全限制明白否定。一个安全的最佳实践是否定全部的请求,仅仅同意那些特定标注为同意的。以这样的方式设计web应用安全限制是可能的,但在全部的场景下这样做是困难的,因此它不是默认的。因此探測和否定对别名静态内容的请求对Jetty来说是重要的。
别名探測
要Jetty知道被文件系统实现的全部别名是不可能的,因此它不会尝试去做已知的别名检查。取而代之Jetty通过用一个文件的标准路径来探測别名。假设一个被Jetty处理的文件资源有一个标准名,不同于请求这个资源的名称,那么Jetty确定这是一个资源的别名请求,它将被返回通过ServletContext.getResource(String)方法(或类似的),而不是作为静态资源服务。
假设Jetty正执行在一个windows操作系统上,那么一个叫/MySecret.TXT的文件将有一个精确匹配的标准名。于是当对/mysecret.txt或者/MYSECR~1.TXT的请求到达时,因为和标准名不匹配,这些请求被觉得是对别名的请求,他们将不被作为静态资源服务,终于可以一个404响应返回。
服务别名和符号链接
不是全部的别名都是坏的,或者被觉得尝试破坏安全限制的。当组合复杂的web应用时,符号链接是非常实用的,然而默认Jetty将不服务他们。因此Jetty上下文支持一个可扩展的AliasCheck机制,为了同意别名资源被作为有条件的服务。在这个方面,“好”别名能被探測而且服务。Jetty提供了几种AliasCheck接口的通用的实现,作为ContextHandler的内嵌类:
------ApproveAliases
接收全部别名(使用需慎重!)
------AllowSymLinkAliasChecker
使用java-7 Files.readSymbolicLink(path) and Path.toRealPath(...) APIs检查别名,通过的作为有效的符号链接。
一个应用能够自由的实现它自己的别名检查。别名检查能通过上下文部署文件或者WEB-INF/jetty-web.xml安装:
<!-- Allow symbolic links --> <Call name="addAliasCheck"> <Arg><New class="org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker"/></Arg> </Call>
安全password扰乱
有非常多地方你可能想使用和存储password,比如为SSL链接器和域中的用户password。
password能被存储在明文、被扰乱的、被检验和的和被加密的,为了添加安全性。为了password安全,方法的选择依赖于你正在什么地方使用password。在很多场景下,比如keystorepassword和摘要式身份验证,系统必须又一次得到原始password,这须要用到扰乱方法。扰乱算法的缺陷是它仅保护password免于任意查看。
当存储的password同用户输入的进行比較时,代码能对用户输入应用和保护存储password相同的算法,然后比較结果,使password认证更加安全。
类org.eclipse.jetty.http.security.Password能被用于产生全部password的变化。
不带參数运行能够看到使用指导:
$ export JETTY_VERSION=9.0.0-SNAPSHOT $ java -cp lib/jetty-util-$JETTY_VERSION.jar org.eclipse.jetty.util.security.Password Usage - java org.eclipse.jetty.util.security.Password [<user>] <password> If the password is ?, the user will be prompted for the password
比如,为了产生一个安全的password版本号,假定用户"me",password"blah",则:
$ export JETTY_VERSION=9.0.0.RC0 $ java -cp lib/jetty-util-$JETTY_VERSION.jar org.eclipse.jetty.util.security.Password me blah blah OBF:20771x1b206z MD5:639bae9ac6b3e1a84cebb7b403297b79 CRYPT:me/ks90E221EY
你如今能剪切,然后粘贴你选择的安全版本号进入你的配置文件或者java代码。
比如,以下的最后一行展示了你怎么将上面产生的加密后的password剪切并粘贴到你的属性文件里,供LoginService使用:
admin: CRYPT:ad1ks..kc.1Ug,server-administrator,content-administrator,admin other: OBF:1xmk1w261u9r1w1c1xmq guest: guest,read-only me:CRYPT:me/ks90E221EY
注意:不要忘记拷贝OBF:、MD5:或者CRYPT:前缀,否则Jetty将无法正确使用。
你也能用在Jetty xml文件里使用扰乱的password。以下是一个样例,为JDBC DataSource设置扰乱的password:
<New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource"> <Arg></Arg> <Arg>jdbc/DSTest</Arg> <Arg> <New class="com.jolbox.bonecp.BoneCPDataSource"> <Set name="driverClass">com.mysql.jdbc.Driver</Set> <Set name="jdbcUrl">jdbc:mysql://localhost:3306/foo</Set> <Set name="username">dbuser</Set> <Set name="password"> <Call class="org.eclipse.jetty.util.security.Password" name="deobfuscate"> <Arg>OBF:1ri71v1r1v2n1ri71shq1ri71shs1ri71v1r1v2n1ri7</Arg> </Call> </Set> <Set name="minConnectionsPerPartition">5</Set> <Set name="maxConnectionsPerPartition">50</Set> <Set name="acquireIncrement">5</Set> <Set name="idleConnectionTestPeriod">30</Set> </New> </Arg> </New>
JAAS支持
兴许补充
Spnego支持
兴许补充