I have a JAX-RS application deployed in WildFly. The application's endpoints shall be protected by Keycloak with Access Type: bearer-only. This works perfectly fine for WildFly versions up to 24.
Starting from WildFly 25 the Keycloak adapter is deprecated and one should migrate to the new Elytron subsystem. According to this WildFly issue https://issues.redhat.com/browse/WFLY-15485 however the OIDC adapter is not ready yet to work with bearer-only. But it is mentioned that it should still be possible using the Keycloak Wildfly adapter.
Also the latest Keycloak documentation and this thread in Google Groups states this.
So I installed the adapter from this location and ran the installation script:
https://github.com/keycloak/keycloak/releases/download/16.1.1/keycloak-oidc-wildfly-adapter-16.1.1.zip
./bin/jboss-cli.sh --file=bin/adapter-elytron-install-offline.cli -Dserver.config=standalone-full.xml
When deploying the application I get thte following error message:
java.lang.IllegalStateException: The required mechanism 'KEYCLOAK' is not available in mechanisms [BASIC, CLIENT_CERT, DIGEST, FORM] from the HttpAuthenticationFactory
Setup
WildFly 26 (Jakarta EE 8)
Keycloak 16.1.1
web.xml
<web-app 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"
version="3.0">
<!-- Security configuration -->
<security-constraint>
<web-resource-collection>
<web-resource-name>admin-api</web-resource-name>
<url-pattern>/administration/*</url-pattern>
<url-pattern>/operations/*</url-pattern>
<url-pattern>/applications/*</url-pattern>
<url-pattern>/entities/*</url-pattern>
</web-resource-collection>
</security-constraint>
<login-config>
<auth-method>KEYCLOAK</auth-method>
<realm-name>this is ignored currently</realm-name>
</login-config>
<security-role>
<role-name>*</role-name>
</security-role>
</web-app>
I managed to implement Bearer Token Authorization with Keycloak and Elytron in WildFly 26 in order to control access to RESTful Web services in a Web Module (.war) of an Enterprise Application (.ear), but the solution is not without problems. This is what I did:
Define an elytron token realm
/subsystem=elytron/token-realm=xyz2ap112-token-realm/:add(\
principal-claim=preferred_username,\
oauth2-introspection={\
client-id=xyz2ap112-web-api,\
client-secret=${env.keycloak_client_secret},\
introspection-url=${env.keycloak_introspection_url}\
}\
)
Define an elytron role decoder
/subsystem=elytron/simple-role-decoder=xyz2ap112-realm-access-roles/:add(\
attribute=realm_access_roles\
)
Warning: the default "Token Claim Name" for Keycloak realms is "realm_access.roles". For this role decoder to work, I had to change it to "realm_access_roles" (no dot). I'll mention this again when I talk about the problems with this solution.
Define an elytron security domain
/subsystem=elytron/security-domain=xyz2ap112-token-security-domain/:add(\
realms=[{realm="xyz2ap112-token-realm",role-decoder="xyz2ap112-realm-access-roles"}],\
default-realm=xyz2ap112-token-realm,\
permission-mapper=default-permission-mapper\
)
Define an elytron HTTP authentication factory
/subsystem=elytron/http-authentication-factory=xyz2ap112-web-api-authentication-factory/:add(\
security-domain=xyz2ap112-token-security-domain,\
mechanism-configurations=[{\
mechanism-name=BEARER_TOKEN,\
mechanism-realm-configurations=[realm-name=xyz2ap112-token-realm]\
}],\
http-server-mechanism-factory=global\
)
Define two application security domains
ejb3 subsystem
/subsystem=ejb3/application-security-domain=xyz2ap112-web-api-security-domain/:add(\
security-domain=xyz2ap112-token-security-domain\
)
Warning: the war that contains the web services does not contain the EJBs it needs; those are in a separate EJB Module (.jar). I guess that is why I had to define this application security domain in the ejb3 subsystem.
undertow subsystem
/subsystem=undertow/application-security-domain=xyz2ap112-web-api-security-domain/:add(\
http-authentication-factory=xyz2ap112-web-api-authentication-factory,\
override-deployment-config=true\
)
Configure application’s jboss-web.xml and web.xml
<jboss-web>
<context-root>/xyz2ap112-web-api</context-root>
<resource-ref>
<res-ref-name>jdbc/xyz2ap112</res-ref-name> <!-- Logical name only. -->
<jndi-name>java:/jdbc/xyz2ap112</jndi-name> <!-- Real JNDI name. -->
</resource-ref>
<security-domain>xyz2ap112-web-api-security-domain</security-domain>
</jboss-web>
The security-domain is the application security domain defined in the undertow subsystem.
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>xyz2ap112-web-api-security-domain</realm-name>
</login-config>
The real-name in login-config is the application security domain defined in the undertow subsystem.
PROBLEMS
As I said before, this solution is not without problems. Given that my Enterprise Application (.ear) also has another Web Module (.war), which contains the GUI components of the application and no Web Services, this solution works as long as the auth-method of that second Web Module is FORM or BASIC. And, as you might have already guessed, I want to use OIDC.
Using OIDC to control access to the application is very straightforward, as properly explained by Farah Juma in her article Securing WildFly Apps with OpenID Connect. But that works as long as the "Token Claim Name" of the Keycloak realm is "realm_access.roles" (its default value). With that name the simple-role-decoder doesn’t work. So, I guess a custom role-decoder is required. Given that my application is able to define and administer roles and role assignments on its own, instead of writing a custom role-decoder, I used a constant-role-mapper to get a single role that allows the web services to execute and check permissions using the roles defined within the application. Once again, that works as long as the auth-method of that second Web Module is FORM or BASIC; with OIDC, the web services are not executed; client gets an HTTP 500 (see below). There is no additional information in the logs of any of the running WildFly (Keycloak and application).
This is the oidc.json file of the GUI Web Module:
{
"client-id": "xyz2ap112-web",
"confidential-port": 8543,
"principal-attribute": "preferred_username",
"provider-url": "http://localhost:8180/auth/realms/jrcam",
"public-client": true,
"ssl-required": "external"
}
And this is the client exception:
Exception in thread "main" javax.ws.rs.InternalServerErrorException: HTTP 500 Internal Server Error
at org.glassfish.jersey.client.JerseyInvocation.convertToException(JerseyInvocation.java:1098)
at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:883)
at org.glassfish.jersey.client.JerseyInvocation.lambda$invoke$1(JerseyInvocation.java:767)
at org.glassfish.jersey.internal.Errors.process(Errors.java:316)
at org.glassfish.jersey.internal.Errors.process(Errors.java:298)
at org.glassfish.jersey.internal.Errors.process(Errors.java:229)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:414)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:765)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:428)
at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:324)
at org.xyz.jax.rs.client.base.AbstractFacadeServiceClient.find(AbstractFacadeServiceClient.java:28)
at xyz2.BarrioFacadeClient.find(BarrioFacadeClient.java:40)
at xyz2.BarrioFacadeClient.main(BarrioFacadeClient.java:24)
If the auth-method of the Web Services Web module is OIDC, the response the client gets is html corresponding to the Keycloak login page.
<html xmlns="http://www.w3.org/1999/xhtml" class="login-pf">
...
<h1 id="kc-page-title">
Sign in to your account
</h1>
...
</html>
This is the oidc.json file of the Web Services Web Module:
{
"client-id": "xyz2ap112-web-api",
"confidential-port": 8543,
"principal-attribute": "preferred_username",
"provider-url": "http://localhost:8180/auth/realms/jrcam",
"ssl-required": "external",
"bearer-only": true,
"verify-token-audience": true,
"realm-public-key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk3PD30r3SQBqnO15g/Jc5z3NFnt9HLA6QlQt2QLtxvGhLcerTD2rVWCst/4NSQev9dBscFnwxXyAoZAqTm7w0oPzlhw1Xbqt1dpKdNjMtbJxmpqzCRLTjmNatPmoAGx+9TWOPKw1qfEwZOy9xOqnCbBeT5eGCAXci+wvt8mpNX9lpAguFxgpFtyVc0at35Lw3BdZ13+6Ljxu6Z+mam1tQ9mwey0ubfhV3NK0eN8jruKWrCyGw6DRbmvKFTwQa5akDbMWt3H/HaSLMXBOrBKq9He6azVL3dkbdd40drgHtI8G+ANC1NhOPzjPtuifo9U2wHD6o8S03o35mm4xjJNcqQIDAQAB",
"credentials": {
"secret": "8c98045a-4640-46e7-9f68-74a289e43b7e"
}
}
I hope this partial solution will help someone and also that someone can tell me how to implement a complete solution.
I finally got it working without the Keycloak adapter, i.e. using the new built-in Elytron subsystem.
oidc.json (located in the WEB-INF directory)
{
"realm": "myrealm",
"client-id": "my-client-app",
"auth-server-url": "${keycloak.url}/auth",
"provider-url": "${keycloak.url}/auth/realms/myrealm",
"bearer-only": true,
"enable-cors": true,
"ssl-required": "none"
}
web.xml
<web-app 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"
version="3.0">
<!-- other configuration -->
<login-config>
<auth-method>OIDC</auth-method>
</login-config>
</web-app>
Related
we are currently migrating from legacy security subsystem to Elytron and have a Struts2 based web application deployed in JBoss EAP 7.3.6 which should support multiple "flavors" of authentication.
The standard way of logging in should be that a user manually provides credentials in a login form (j_security_check) and clicks the corresponding button. This works well with Elytron in our setup.
The second possibility is, that the GET request to protected content of the web application can contain a custom cookie that contains a JWT token. This cookie is intercepted by a io.undertow.server.HttpHandler which deals with the incoming request in its io.undertow.server.HttpHandler#handleRequest method. This handler is registered by io.undertow.servlet.api.DeploymentInfo#addSecurityWrapper with a DeploymentInfo which is provided by an implementation of io.undertow.servlet.ServletExtension. The ServletExtension is registered as a service provider in META-INF/services/io.undertow.servlet.ServletExtension.
The request handling in our implementation of io.undertow.server.HttpHandler#handleRequest extracts the JWT token from the cookie, pre-validates it and determines the contained username. This username and the token as a password are used as inputs for a call to javax.servlet.http.HttpServletRequest#login.
With the legacy security subsystem, the behavior of the server was, that this call to login triggered the authentication against the configured legacy security domain AND created a session in Undertow so that the HTTP 200 response for the previous GET request contained a Set-Cookie header with a fresh JSESSIONID cookie.
With Elytron, javax.servlet.http.HttpServletRequest#login doesn't do anything, neither an authentication against an Elytron security domain and security realm nor the creation of a session is triggered. The browser simply shows the login form which should get skipped by the described interception process.
I debugged the implementation of javax.servlet.http.HttpServletRequest#login that comes with JBoss. We start in io.undertow.servlet.spec.HttpServletRequestImpl#login which calls login = sc.login(username, password). This SecurityContext, when using Elytron, is org.wildfly.elytron.web.undertow.server.SecurityContextImpl. org.wildfly.elytron.web.undertow.server.SecurityContextImpl#login first checks if (httpAuthenticator == null). The httpAuthenticator is only set in org.wildfly.elytron.web.undertow.server.SecurityContextImpl#authenticate which gets called by a call to javax.servlet.http.HttpServletRequest#authenticate.
This explains, why a plain call to io.undertow.servlet.spec.HttpServletRequestImpl#login was doing nothing. I tried to call javax.servlet.http.HttpServletRequest#authenticate first, to instantiate that httpAuthenticator internally, and then javax.servlet.http.HttpServletRequest#login. This at least finally triggered the authentication and authorization against the configured Elytron security domain and security realm. Authentication/authorization were successful but Undertow still didn't issue a new JSESSIONID cookie and the browser again showed the login form instead of proceeding to the protected resources.
I'm currently out of ideas, how to proceed with this issue und how to achieve the same behavior as with the legacy security subsystem. Why does the Elytron implementation of io.undertow.security.api.SecurityContext behave so differently compared to the one for legacy security (io.undertow.security.impl.SecurityContextImpl)? How am I supposed to log in programatically in a FORM based web application using Elytron with javax.servlet.http.HttpServletRequest#login and/or javax.servlet.http.HttpServletRequest#authenticate?
The relevant JBoss configuration for all this looks like this:
Undertow:
<application-security-domains>
<application-security-domain name="my_app_security_domain" http-authentication-factory="MyHttpAuthFactory"/>
</application-security-domains>
Elytron:
<security-domains>
<security-domain name="MySecurityDomain" default-realm="MyCachingRealm" permission-mapper="default-permission-mapper">
<realm name="MyCachingRealm" role-decoder="FromRolesAttributeDecoder"/>
</security-domain>
</security-domains>
<security-realms>
<custom-realm name="MyCustomRealm" module="module name redacted" class-name="class name redacted"/>
<caching-realm name="MyCachingRealm" realm="MyCustomRealm" maximum-age="300000"/>
<identity-realm name="local" identity="$local"/>
</security-realms>
<mappers>
<simple-permission-mapper name="default-permission-mapper" mapping-mode="first">
<permission-mapping>
<principal name="anonymous"/>
<permission-set name="default-permissions"/>
</permission-mapping>
<permission-mapping match-all="true">
<permission-set name="login-permission"/>
<permission-set name="default-permissions"/>
</permission-mapping>
</simple-permission-mapper>
<constant-realm-mapper name="local" realm-name="local"/>
<constant-realm-mapper name="MyRealmMapper" realm-name="MyCachingRealm"/>
<simple-role-decoder name="FromRolesAttributeDecoder" attribute="Roles"/>
</mappers>
<http>
<http-authentication-factory name="MyHttpAuthFactory" security-domain="MySecurityDomain" http-server-mechanism-factory="global">
<mechanism-configuration>
<mechanism mechanism-name="FORM" realm-mapper="MyRealmMapper">
<mechanism-realm realm-name="MyRealm"/>
</mechanism>
</mechanism-configuration>
</http-authentication-factory>
<provider-http-server-mechanism-factory name="global"/>
</http>
This was a bug in JBoss EAP which has been fixed in EAP 7.3.8 and 7.4.1. See https://issues.redhat.com/browse/JBEAP-21737 and https://issues.redhat.com/browse/JBEAP-21738 for details.
I have JBoss EAP 7
So I have done:
unzip keycloak-eap7-adapter-dist-3.2.1.Final.zip
and
./bin/jboss-cli.sh --file=adapter-install-offline.cli
and added the <security-constraint> element to web.xml:
<security-constraint>
<web-resource-collection>
<web-resource-name>Admins</web-resource-name>
<url-pattern>/admin/*</url-pattern>
...
<security-role>
<role-name>admin</role-name>
...
and in my standalone.xml, I have:
<subsystem xmlns="urn:jboss:domain:keycloak:1.1">
<secure-deployment name="snack.war">
<realm>Netzportal</realm>
<resource>netzportal</resource>
<public-client>true</public-client>
<auth-server-url>http://localhost:8180/auth</auth-server-url>
<ssl-required>EXTERNAL</ssl-required>
</secure-deployment>
</subsystem>
And in the keycloak admin console I have registered the web application as client. The client opens at http://localhost:10080/czo/login.xhtml. So I have entered http://localhost:10080/czo/* as Valid Redirect URIs.
But when the application is running and I open http://localhost:10080/czo/login.xhtml, I do not get redirected to keycloak (which is also running)
You seems to be missing following entry ( since it does not appear in your listed web.xml configuration) which actually tell which kind of authentication to use for login
<login-config>
<auth-method>KEYCLOAK</auth-method>
<realm-name>Netzportal</realm-name>
</login-config>
For more details on configuration check the docs.
I had the same problem, after a lot of suffering, I found that the web.xml file in the wrong folder
After logging in using HttpServletRequest.login(String, String), using the code below, on following requests I still get a Basic Authentication prompt. Why is the login function not working in my configuration?
My endpoint:
#POST
#Path("login")
#Consumes(MediaType.APPLICATION_JSON)
public void login(#Valid LoginRequest loginRequest) {
try {
User user = userController.findUserByUsername(loginRequest.getUsername()).orElseThrow(NotFoundException::new);
httpServletRequest.login(loginRequest.getUsername(), loginRequest.getPassword());
log.info(securityContext); // not null now!
}
catch (ServletException e) {
throw new NotAuthorizedException(e.getMessage(), e, AuthenticationHeaderFilter.CHALLENGE);
}
}
And my jboss-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web xmlns="http://www.jboss.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.jboss.com/xml/ns/javaee
http://www.jboss.org/j2ee/schema/jboss-web_5_1.xsd">
<security-domain>MyRealm</security-domain>
</jboss-web>
And my web.xml:
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>MyRealm</realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>user</role-name>
</security-role>
<security-constraint>
<display-name>Authenticated content</display-name>
<web-resource-collection>
<web-resource-name>Authentication required</web-resource-name>
<url-pattern>/api/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<display-name>Anonymous content</display-name>
<web-resource-collection>
<web-resource-name>Exclude from Security</web-resource-name>
<url-pattern>/api/me/login</url-pattern>
</web-resource-collection>
</security-constraint>
Actually, the contract for HttpServletRequest#login does not mandate that the authenticated identity be remembered for the duration of the HTTP session (if one already exists), and certainly not that an HTTP session should be created upon successful authentication (for if one does not exist).
Technically speaking, the HttpServletRequest#login call goes straight through to the identity store (the method's Javadoc uses the term login mechanism for that). An identity store is a kind of database that typically only performs the credential validation and does not have knowledge about its environment (i.e. doesn't know about HTTP sessions, or remote EJB context IDs, or JCA inflow security IDs of whatever).
The authentication mechanism IS aware of its environment, and this one is invoked by calling HttpServletRequest#authenticate. But, this would normally be expected to start an interaction dialog with the user when not being authenticated yet, not remember the authenticated identity in the session if the user happens to be authenticated (the fact this happens to work on JBoss seems more like a coincidence than something that is supposed to happen).
That all said, section 13.10 of the Servlet spec does allow containers to create an HTTP session:
Containers may create HTTP Session objects to track login state. If a
developer creates a session while a user is not authenticated, and the
container then authenticates the user, the session visible to
developer code after login must be the same session object that was
created prior to login occurring so that there is no loss of session
information.
(emphasis mine)
But... it's not overly clear if this text is in regard to calling the login() method or the authenticate() one.
In short, this is one of the many small gaps in the Java EE security spec; it's just not defined how to programmatically do a login with a given username/password and explicitly say if you want or do not want that to be for the current request only or for the remainder of the HTTP session.
We hope to fix issues like this in the Java EE Security API (JSR 375) for Java EE 8.
The answer is that after invoking httpServletRequest#login(String, String) you should still invoke httpSevletRequest#authenticate(HttpServletResponse). My final, working code, is:
httpServletRequest.login(loginRequest.getUsername(), loginRequest.getPassword());
httpServletRequest.authenticate(httpServletResponse);
As you want programmatic authentication, there is no need of <login-config> in web.xml
I have been trying to secure an application, which is deployed to glassfish 3 using annotation instead of the deployment descriptor. However, I haven't been able to get it working correctly. If I try to access the service, I end up with a server error 500, which displays this message:
type Exception report
message
descriptionThe server encountered an internal error () that prevented it from fulfilling this request.
exception
javax.servlet.ServletException: javax.ejb.AccessLocalException: Client not authorized for this invocation
root cause
javax.ejb.AccessLocalException: Client not authorized for this invocation
The EJB looks like this:
#Path("/myresource")
#Stateless
#RolesAllowed("user-role")
public class MyResource {
#GET
#Path("/{uuid}")
public Response getData(#PathParam("uuid") final String uuid) {
....
}
}
sun-web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD GlassFish Application Server 3.0 Servlet 3.0//EN"
"http://www.sun.com/software/appserver/dtds/sun-web-app_3_0-0.dtd">
<sun-web-app>
<security-role-mapping>
<role-name>user-role</role-name>
<group-name>user-group</group-name>
</security-role-mapping>
</sun-web-app>
This is the web.xml:
<web-app id="myservice" version="2.5" 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_2_5.xsd">
<display-name>org.test.myservice</display-name>
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>org.test.myservice.rest</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>file</realm-name>
</login-config>
<security-role>
<role-name>user-role</role-name>
</security-role>
</web-app>
The file realm in glassfish is set up using the user and role specified in the sun-web.xml and has been working well, when setting up the application security via deployment descriptor.
If I understand this document correctly I do not have to link security role references if their names are the same. http://docs.oracle.com/javaee/5/tutorial/doc/bnbyl.html#bnbyt
Any ideas what I am missing?
Edit
Related to the problem of not being able to specify the required information with annotations, there is a another problem, which caused me to think about this issue. Maybe that will make the initial question a little clearer:
Taken above example, the resource /myresource/* is only available for users with role 'user-role'. However, if there is a second resource at path /myresource/*/thumbnail (translating to /myresource/[uuid]/thumbnail) which should be available without authentication, this is not possible by specifying security-constraints with url-mapping, since it does not seem to be possible to use the wildcard between constants. However, this would be doable by specifying the roles, that are allowed to access a method by annotions. As described above, I haven't been able to do so. How could a mapping like that be done?
You need to use the security-constraint element in web.xml descriptor in order to block specific resources and paths, and to specify the authorization constraints.
This doesn't mean that you can't add more fine-grained controls using Programmatic Security, as explained in Oracle's Java EE 6 Tutorial:
Programmatic security is embedded in an application and is used to make security decisions. Programmatic security is useful when declarative security alone is not sufficient to express the security model of an application.
As per your edited question.
I would use the security-constraint element for blocking the access to all non-registered users. This will force everybody to authenticate, so that your application knows the roles they have.
Then you can fine-grain control the access to the various resources using programmatic security.
With basic authentication I guess there are no other ways. If you want to avoid authentication for basic users, you need to go with form authentication and handle the authentication programmatically behind the scenes, authenticating them even if they aren't aware of, by using HttpServletRequest#login().
In both ways you should be able to setup rights in the way you have described. If you want to handle the unauthorized exception more smoothly, you'd better remove the #RolesAllowed annotation and instead use something like:
#GET
#Path("/{uuid}")
public Response getData(#PathParam("uuid") final String uuid, #Context SecurityContext sc) {
if (sc.isUserInRole("MyRole")) {
return result;
} else {
return notAllowedResult;
}
}
The Roles-Allowed is an EJB construct and not congruent with access to the resource, which is handled by the security constraint.
Unfortunately, the two security concepts do not mesh as well as they should, and instead of getting a 401 if you're not authorized (a web concept), you get the security exception that you are receiving (and EJB concept). In fact, I don't know what error you will receive if you annotate an EJB web service with a RolesAllowed and try to access the web service with an invalid role. I assume you'll get a SOAP fault, in that case.
The EJB security is a system that keeps unauthorized people out, but it's a last ditch effort. It assumes that any decisions to route folks to the method calls is already done up front. For example, there's no high level way to test if a method is allowed or not, rather you can only call it and catch the exception.
So the harsh truth is beyond coarse gatekeepers, you want to leverage Programmatic Security.
I want to pass a security context to my rest service.
On server side I try to get this with:
public Response postObject(#Context SecurityContext security, JAXBElement<Object> object) {
System.out.println("Security Context: " + security.getUserPrincipal());
.....
But actually the Syso is null.
On Client side im just doing:
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
client.addFilter(new HTTPBasicAuthFilter("user", "password"));
So, do I have to change in addition something in my web.xml to get it working?
I hoped its working without setting up static users in the tomcat user xml. So I can compare the user/password from security context with my "persistent" user/password hashmap located server sided. But when it is not working without tomcat user xml, how can it be done to add dynamically user to that user xml? When I ve static users I cant register a new user. I dont want to use this attempt: http://objecthunter.congrace.de/tinybo/blog/articles/89 cuz I want just to work with a semi persistence like a HashMap of user/password.
Besides another question: Why does everybody refer to Apache HttpClient when it is about security in Jersey, when it is working like I wrote as well?
My attempt refers to this post:
Jersey Client API - authentication
You need to set up your application on the server so that it requires Basic authentication. I.e. include something like the following in the web.xml in your application war file - otherwise Tomcat does not perform the authentication and does not populate the security context.
<security-constraint>
<display-name>Authentication Constraint</display-name>
<web-resource-collection>
<web-resource-name>all</web-resource-name>
<description/>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<description>authentication required</description>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>realm_name</realm-name>
</login-config>