Jetty:配置安全

用${jetty.home}和${jetty.base}配置安全

Jetty 9.1中:
 1)${jetty.home}是jetty发布(二进制)的目录路径;
 2)${jetty.base}是用户定制化的目录路径。
这样分化:
 1)允许你管理多个Jetty安装;
 2)当你升级Jetty后,更容易保留你当前的配置。
更多的信息在后面讲《启动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)为安全重定向定义端口

jetty.secure.port=8443

9)建立一个示范keystore和truststore

jetty.keystore=etc/keystore
jetty.truststore=etc/keystore

10)设置示范密码

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。除非你有高级别的自定义设置(例如监听两个不同的端口,用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应用定义服务端怎么从客户端获取认证证书,但是它不定义服务端怎么检查这些证书是否有效。为了检查证书,server和/或上下文也需要配置LoginService实例,可以通过域名匹配。

安全域

安全域用于防止你的web应用被未认证的进入。保护基于认证(表示谁正在请求进入webapp)和进入控制(限制什么能被访问和怎么访问)。
webapp在web.xml中静态地配置它的安全要求。认证通过<login-config>元素控制。访问控制通过<security-constraint>和<security-role-ref>元素指定。当一个请求请求一个受保护的资源时,web容器检查用户是否被认证,并且用户所在的角色是否有该资源的访问权限。
Servlet指导手册没有指定在WEB-INF/web.xml中的静态安全信息怎么被匹配到容器的运行时环境。在Jetty中,LoginService履行这个职责。
LoginService有一个唯一name,并且允许访问用户列表。每个用户都有认证信息(例如:密码)和与之关联的角色列表。
你可以根据需要配置一个或者多个不同的LoginService。单一的域将表明你希望你的所有web应用共享安全信息。不同的域允许你在不同的webapp将划分安全信息。
当一个请求请求认证和授权时,Jetty将用web.xml中的<login-config>元素内的<realm-name>子元素履行对LoginService的一次精确匹配。

安全域范围

一个LoginService有一个唯一名称,由一个用户列表组成。每个用户有认证信息(例如:密码)和与之关联的角色列表。你能根据你的需要配置一个或多个不同的域:
 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实例有一个认证机制,被用于检查被认证机制收集的用户名和证书的有效性。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: password[,rolename ...]

这里:
 ------username
 用户的唯一标识
 ------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:用户名
  ------pwd:用户密码(可能被扰乱或者被MD5加密)
 ------user-roles是一个表格,每行表示一个角色授权给一个用户:
  ------user_id:用户唯一标识
  ------role_id:用户角色
 ------roles是一个表格,包含系统中的所有角色
  ------id:角色的唯一标识
  ------role:角色的可读的名称
如果你想使用扰乱、MD5扰乱或者加密密码,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应用处理。这个机制是容易被攻击的,客户端可以通过提交大数据量的表单内容或者大量的表单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>

安全密码扰乱

有很多地方你可能想使用和存储密码,例如为SSL链接器和域中的用户密码。
密码能被存储在明文、被扰乱的、被检验和的和被加密的,为了增加安全性。为了密码安全,方法的选择依赖于你正在什么地方使用密码。在许多场景下,例如keystore密码和摘要式身份验证,系统必须重新得到原始密码,这需要用到扰乱方法。扰乱算法的缺陷是它仅保护密码免于随意查看。
当存储的密码同用户输入的进行比较时,代码能对用户输入应用和保护存储密码同样的算法,然后比较结果,使密码认证更加安全。
类org.eclipse.jetty.http.security.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

例如,为了产生一个安全的密码版本,假定用户"me",密码"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代码。
例如,下面的最后一行展示了你怎么将上面产生的加密后的密码剪切并粘贴到你的属性文件中,供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文件中使用扰乱的密码。下面是一个例子,为JDBC DataSource设置扰乱的密码:

<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支持

后续补充

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值