[英]basic authentication fails with glassfish
首先,我为这篇长篇文章道歉。 这是我之前的问题( 7u21更新后弹出身份验证所需窗口 )的延续,但是我缩小了搜索范围。 简而言之,自Java 7u21以来,我的BASIC身份验证似乎已被破坏。
通过JNLP文件启动的小程序根本不起作用,并提供了身份验证弹出窗口。
设置
首先,我建立了一个带有usertable和grouptable的MySQL数据库 。
接下来我在Glassfish中设置了一个jdbcRealm 。 请注意,数据库用户和数据库密码字段为空,因为我使用的是JNDI(请参见下文):
Glassfish境界设置:
JDNI配置 (如domain.xml文件中所示):
<jdbc-connection-pool connection-validation-method="auto-commit" datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" wrap-jdbc-objects="false" res-type="javax.sql.DataSource" name="mysql_mit_rohhPool">
<property name="URL" value="jdbc:mysql://localhost:3306/mit?zeroDateTimeBehavior=convertToNull"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="Password" value="****"></property>
<property name="portNumber" value="3306"></property>
<property name="databaseName" value="mit"></property>
<property name="User" value="****"></property>
<property name="serverName" value="localhost"></property>
</jdbc-connection-pool>
<jdbc-resource pool-name="mysql_mit_rohhPool" jndi-name="jdbc/DB_MIT"></jdbc-resource>
完成此操作后,我将默认域更改为新创建的jdbcRealm,并检查Default principal to role mapping :
测试
毕竟,为了测试,我在Netbeans中创建了一个简单的WebService,它从数据库中获取一些国家并配置web.xml进行BASIC身份验证:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet>
<servlet-name>ServletAdaptor</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<description>Multiple packages, separated by semicolon(;), can be specified in param-value</description>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>service</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ServletAdaptor</servlet-name>
<url-pattern>/webresources/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<security-constraint>
<display-name>Basic Protection</display-name>
<web-resource-collection>
<web-resource-name>REST</web-resource-name>
<description/>
<url-pattern>/webresources/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<description/>
<role-name>dummy</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>jdbcRealm</realm-name>
</login-config>
<security-role>
<description>Dummy</description>
<role-name>dummy</role-name>
</security-role>
为了测试Web服务,我在NetBeans中右键单击它并单击Test RESTful Web Service 。 一个新的Internet Explorer窗口打开并显示一个登录屏幕,我输入虚拟用户的凭据,一切正常。
接下来,我创建一个简单的JavaFX FXML项目来获取这些国家/地区。 我有一个班级(谁使用泽西岛)看起来像跟随。 这是由Netbeans 7.3生成的代码:
private WebResource webResource;
private Client client;
private static final String BASE_URI = "http://localhost:8080/myWS/webresources";
public CountriesClient() {
com.sun.jersey.api.client.config.ClientConfig config = new com.sun.jersey.api.client.config.DefaultClientConfig();
client = Client.create(config);
webResource = client.resource(BASE_URI).path("entities.countries");
}
public void close() {
client.destroy();
}
public void setUsernamePassword(String username, String password) {
client.addFilter(new com.sun.jersey.api.client.filter.HTTPBasicAuthFilter(username, password));
}
public <T> T findAll_XML(Class<T> responseType) throws UniformInterfaceException {
WebResource resource = webResource;
return resource.accept(javax.ws.rs.core.MediaType.APPLICATION_XML).get(responseType);
}
在我的FXML Controller文件中,我将此方法链接到一个按钮:
@FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
CountriesClient c = new CountriesClient();
c.setUsernamePassword("dummy", "****");
String r = c.findAll_XML(String.class);
System.out.println(r);
c.close();
}
这是关于我的项目的设置。 现在,当我在Netbeans中测试它或者我通过* .jar文件启动它时,一切都按预期工作,它给了我以下输出:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><countriess><countries><country>Belgium</country><id>1</id></countries><countries><country>Ireland</country><id>2</id></countries><countries><country>United Kingdom</country><id>3</id></countries><countries><country>Poland</country><id>4</id></countries></countriess>
但是,一旦我通过* .jnlp文件启动applet,我就会收到这个恼人的弹出窗口抱怨凭据:
java控制台记录了这个:
network: Cache entry found [url: http://localhost:8080/myWS/webresources/entities.countries, version: null] prevalidated=false/0
cache: Adding MemoryCache entry: http://localhost:8080/myWS/webresources/entities.countries
cache: Resource http://localhost:8080/myWS/webresources/entities.countries has expired.
cache: Resource http://localhost:8080/myWS/webresources/entities.countries has cache control: no-cache.
network: Connecting http://localhost:8080/myWS/webresources/entities.countries with proxy=DIRECT
network: Connecting socket://localhost:8080 with proxy=DIRECT
network: Firewall authentication: site=localhost/127.0.0.1:8080, protocol=http, prompt=jdbcRealm, scheme=basic
network: ResponseCode for http://localhost:8080/myWS/webresources/entities.countries : 401
network: Encoding for http://localhost:8080/myWS/webresources/entities.countries : null
network: Connecting http://localhost:8080/myWS/webresources/entities.countries with proxy=DIRECT
basic: JNLP2ClassLoader.findClass: com.sun.jersey.core.header.InBoundHeaders: try again ..
basic: JNLP2ClassLoader.findClass: com.sun.jersey.core.util.StringKeyStringValueIgnoreCaseMultivaluedMap: try again ..
network: Downloading resource: http://localhost:8080/myWS/webresources/entities.countries
Content-Length: 322
Content-Encoding: null
network: Wrote URL http://localhost:8080/myWS/webresources/entities.countries to File C:\Users\stbrunee\AppData\LocalLow\Sun\Java\Deployment\cache\6.0\6\4b456206-236d2196-temp
cache: MemoryCache replacing http://localhost:8080/myWS/webresources/entities.countries (refcnt=0). Was: URL: http://localhost:8080/myWS/webresources/entities.countries | C:\Users\stbrunee\AppData\LocalLow\Sun\Java\Deployment\cache\6.0\6\4b456206-15cb0b99.idx Now: URL: http://localhost:8080/myWS/webresources/entities.countries | C:\Users\stbrunee\AppData\LocalLow\Sun\Java\Deployment\cache\6.0\6\4b456206-236d2196.idx
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><countriess><countries><country>Belgium</country><id>1</id></countries><countries><country>Ireland</country><id>2</id></countries><countries><country>United Kingdom</country><id>3</id></countries><countries><country>Poland</country><id>4</id></countries></countriess>
在服务器端(glassfish日志)
FINE: [Web-Security] Policy Context ID was: myWS/myWS
FINE: [Web-Security] hasUserDataPermission perm: ("javax.security.jacc.WebUserDataPermission" "/webresources/entities.countries" "GET")
FINE: [Web-Security] hasUserDataPermission isGranted: true
FINE: [Web-Security] Policy Context ID was: myWS/myWS
FINE: [Web-Security] Codesource with Web URL: file:/myWS/myWS
FINE: [Web-Security] Checking Web Permission with Principals : null
FINE: [Web-Security] Web Permission = ("javax.security.jacc.WebResourcePermission" "/webresources/entities.countries" "GET")
FINEST: JACC Policy Provider: PolicyWrapper.implies, context (myWS/myWS)- result was(false) permission (("javax.security.jacc.WebResourcePermission" "/webresources/entities.countries" "GET"))
FINE: [Web-Security] hasResource isGranted: false
FINE: [Web-Security] hasResource perm: ("javax.security.jacc.WebResourcePermission" "/webresources/entities.countries" "GET")
FINE: [Web-Security] Policy Context ID was: myWS/myWS
FINE: [Web-Security] hasUserDataPermission perm: ("javax.security.jacc.WebUserDataPermission" "/webresources/entities.countries" "HEAD")
FINE: [Web-Security] hasUserDataPermission isGranted: true
FINE: [Web-Security] Policy Context ID was: myWS/myWS
FINE: [Web-Security] Codesource with Web URL: file:/myWS/myWS
FINE: [Web-Security] Checking Web Permission with Principals : null
FINE: [Web-Security] Web Permission = ("javax.security.jacc.WebResourcePermission" "/webresources/entities.countries" "HEAD")
FINEST: JACC Policy Provider: PolicyWrapper.implies, context (myWS/myWS)- result was(false) permission (("javax.security.jacc.WebResourcePermission" "/webresources/entities.countries" "HEAD"))
FINE: [Web-Security] hasResource isGranted: false
FINE: [Web-Security] hasResource perm: ("javax.security.jacc.WebResourcePermission" "/webresources/entities.countries" "HEAD")
//NOW I PRESS CANCEL AT THE POPUP WINDOW CLIENT SIDE
FINE: [Web-Security] Setting Policy Context ID: old = null ctxID = myWS/myWS
FINE: [Web-Security] hasUserDataPermission perm: ("javax.security.jacc.WebUserDataPermission" "/webresources/entities.countries" "GET")
FINE: [Web-Security] hasUserDataPermission isGranted: true
FINE: [Web-Security] Policy Context ID was: myWS/myWS
FINE: [Web-Security] Codesource with Web URL: file:/myWS/myWS
FINE: [Web-Security] Checking Web Permission with Principals : null
FINE: [Web-Security] Web Permission = ("javax.security.jacc.WebResourcePermission" "/webresources/entities.countries" "GET")
FINEST: JACC Policy Provider: PolicyWrapper.implies, context (myWS/myWS)- result was(false) permission (("javax.security.jacc.WebResourcePermission" "/webresources/entities.countries" "GET"))
FINE: [Web-Security] hasResource isGranted: false
FINE: [Web-Security] hasResource perm: ("javax.security.jacc.WebResourcePermission" "/webresources/entities.countries" "GET")
FINEST: Processing login with credentials of type: class com.sun.enterprise.security.auth.login.common.PasswordCredential
FINE: Logging in user [dummy] into realm: jdbcRealm using JAAS module: jdbcRealm
FINE: Login module initialized: class com.sun.enterprise.security.auth.login.JDBCLoginModule
FINEST: JDBC login succeeded for: dummy groups:[dummy]
FINE: JAAS login complete.
FINE: JAAS authentication committed.
FINE: Password login succeeded for : dummy
FINE: Set security context as user: dummy
FINE: [Web-Security] Policy Context ID was: myWS/myWS
FINE: [Web-Security] Codesource with Web URL: file:/myWS/myWS
FINE: [Web-Security] Checking Web Permission with Principals : dummy, dummy
FINE: [Web-Security] Web Permission = ("javax.security.jacc.WebResourcePermission" "/webresources/entities.countries" "GET")
FINE: [Web-Security] hasResource isGranted: true
FINE: [Web-Security] hasResource perm: ("javax.security.jacc.WebResourcePermission" "/webresources/entities.countries" "GET")
我真的不明白一些事情:
您遇到的症状的原因是Oracle 在Web Start中默认使用JDK7启用了HTTP响应的缓存 :
默认情况下启用缓存:默认情况下,现在启用在Web启动模式下运行的应用程序代码的网络内容缓存。 这允许应用程序改进性能并与applet执行模式保持一致。 为确保使用最新的内容副本,应用程序可以使用
URLConnection.setUseCaches(false)
或请求标头Cache-Control
值no-cache/no-store
。
所以我做的是,在创建Jersey客户端后设置此标头:
Client client = Client.create();
client.addFilter( new HTTPBasicAuthFilter( userId, password ) );
client.addFilter( new ClientFilter() {
@Override
public ClientResponse handle( ClientRequest cr )
throws ClientHandlerException {
List<Object> cacheControlRequestValues = new ArrayList<Object>();
cacheControlRequestValues.add( "no-cache" );
cacheControlRequestValues.add( "no-store" );
cr.getHeaders().put( HttpHeaders.CACHE_CONTROL, cacheControlRequestValues );
return getNext().handle( cr );
}
}
现在,如果上述规范是正确的,并且Web Start的实现将遵循HTTP / 1.1引用 ,其中说明
no-store指令的目的是防止无意释放或保留敏感信息(例如,在备份磁带上)。 no-store指令适用于整个消息,可以在响应中或在请求中发送。 如果在请求中发送,则缓存不得存储此请求的任何部分或对其的任何响应。
我们没问题 - 网络嗅探器证明客户端正确设置了Cache-Control标头。 此外,泽西岛的ContainerResponseFilter向我展示了请求中的标头设置正确。 另一方面, 响应没有设置Cache-Control标头。 根据规范无所谓,但实际上Web Start一直在缓存响应!
所以我编写了一个ContainerResponseFilter,它将请求中的Cache-Control头复制到响应中:
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseFilter;
import javax.ws.rs.core.HttpHeaders;
import java.util.ArrayList;
import java.util.List;
public class CacheControlCopyFilter
implements ContainerResponseFilter
@Override
public ContainerResponse filter( ContainerRequest containerRequest, ContainerResponse containerResponse ) {
if ( containerRequest.getRequestHeader( HttpHeaders.CACHE_CONTROL ) != null ) {
List<Object> responseCacheControlValues = new ArrayList<Object>( containerRequest.getRequestHeader( HttpHeaders.CACHE_CONTROL ).size() );
for ( String value : containerRequest.getRequestHeader( HttpHeaders.CACHE_CONTROL ) ) {
responseCacheControlValues.add( value );
}
containerResponse.getHttpHeaders().put( HttpHeaders.CACHE_CONTROL, responseCacheControlValues );
}
return containerResponse;
}
}
并在web.xml
激活它
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>my.package.CacheControlCopyFilter</param-value>
</init-param>
然后你必须删除你的Java客户端缓存:
"javaws -viewer" -> General
-> Settings...
-> Delete Files...
-> Select all three check boxes
-> OK
和voilà,没有更烦人的身份验证弹出窗口:)
我已经将上面的代码转换为使用Jersey 2.0 API。
在客户端,ClientFilter替换为ClientRequestFilter,并且过滤器已注册。 此外,过滤器接口不像1.0 API中那样链接。
Client client = ClientBuilder.newClient(clientConfig);
client.register(new HttpBasicAuthFilter(username, password));
client.register(new ClientRequestFilter() {
@Override
public void filter(ClientRequestContext crc) throws IOException {
List<Object> cacheControlRequestValues = new ArrayList<>();
cacheControlRequestValues.add("no-cache");
cacheControlRequestValues.add("no-store");
crc.getHeaders().put(HttpHeaders.CACHE_CONTROL, cacheControlRequestValues);
}
});
在服务器端,接口名称ContainerResponseFilter是相同的,但方法签名有点不同。 不需要web.xml,因为您可以在类的开头指定@Provider注释(对我来说这是使解决方案起作用的棘手部分)。
@Provider
public class CacheControlCopyFilter implements ContainerResponseFilter {
@Override
public void filter(
ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
if (requestContext.getHeaderString(HttpHeaders.CACHE_CONTROL) != null) {
List<Object> responseCacheControlValues = new ArrayList<>(
requestContext.getHeaders().get(HttpHeaders.CACHE_CONTROL).size());
for (String value : requestContext.getHeaders().get(HttpHeaders.CACHE_CONTROL)) {
responseCacheControlValues.add(value);
}
responseContext.getHeaders().put(HttpHeaders.CACHE_CONTROL, responseCacheControlValues);
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.