I would like to protect a SOAP JAX-WS webservice with a java security authentication but, because a lot of users are already using it, i don't want to ask them to implement the authentication on their side.
The idea is to permit the access to everybody to the WS (no login required, as is right now) but if anyone pass newly authentication HTTP Basic login i will detect it (request.getUserPrincipal()) and i will enable some new functionalities.
I'm trying to use java security but i didn't find how to specify that the basic authentication is OPTIONAL for a specific security-constraint and not required. This is a problem because if i use it i cannot grant anonymous access anymore.
I'm using Java EE 5 on Glassish v2.1.1
Example:
#WebService
public class MyWs {
#Resource
protected WebServiceContext wsContext;
#WebMethod()
public void myWebMethod(){
HttpServletRequest request = (HttpServletRequest) wsContext.getMessageContext().get(MessageContext.SERVLET_REQUEST);
if (request.getPrincipal() != null && request.getPrincipal().getName().equals("myAuthenticatedUser")){
//do something new
}
//do the same as before
}
}
Related
I have a WCF Web Service sitting on a client's IIS server secured with NTLM authentication - I have no control over the authentication configuration on that server.
I need to integrate my WSO2 ESB server with this service, but I can't find a way to get the ESB to authenticate automatically. I have successfully pushed requests through the ESB to the service with web applications, but I was prompted to provide my Windows credentials during that process - I would like for this to not happen.
I have attempted to set up an NTLM proxy on my server, but couldn't figure this out either.
Any guidance would be much appreciated.
Strainy
Ok, i found your answer. As you know, WSO2 ESB uses Axis2 for web services. You must add NTLM configuration in Axis2 config file (ESB_HOME/repository/conf/axis2/axis2.xml).
This links, describes the configuration.
http://wso2.com/library/161/
http://axis.apache.org/axis2/java/core/docs/http-transport.html
There were a few components to getting this working correctly. It's hard to find it all written down in one place, so I'll attempt to provide an end-to-end overview here.
I first had to use a class mediator within my WSO2 ESB in-sequence to handle the sending and the NTLM authentication. The class mediator references a custom class which takes the message context from the mediation flow (called the Synapse message context) and extracts the SOAP envelope. I then loaded the Synapse SOAP envelope into an Axis2 message context object. I then used an Axis2 client along with the message context to submit my authenticated request to the server. The authentication for NTLM through Axis2 comes from the JCIFS_NTLMScheme class, which you can reference here.
Note: you'll have to play with the logging configuration in that class to make it work with WSO2. I just removed the " org.sac.crosspather.common.util* " libraries and altered any logging I saw to use the Apache Commons logging capability
Create a Custom Mediator Project in WSO2 Developer Studio
Create a new project in Developer studio. Right click the project node in the project explorer and select "New > Mediator Project".
This will generate a bit of boilerplate code for you - that is, a class which extends AbstractMediator and which implements an "mediate()" method which Synapse will call when it comes to executing the logic defined within your sequence.
public class NTLMAuthorisation extends AbstractMediator {
public boolean mediate(MessageContext context){
//Mediation Logic
return true;
}
}
Expose Some Variables/Properties to the User
The class mediator looks for variables which are publicly accessible and exposes them in the WSO2 configuration. This is helpful before you can create a re-usable mediator which adapts itself to properties or values defined in the WSO2 Carbon Web UI. Here we need to expose seven variables: soapAction, SoapEndpoint, domain, host, port, username, and password. Expose the variables by defining your instance variables, along with their accessors and mutators.
This is all really quite useful for using the WSO2 Secure Vault to store your NTLM password and fetching other configuration from a system registry with properties.
public class NTLMAuthorisation extends AbstractMediator {
private String soapAction;
private String soapEndpoint;
private String domain;
private String host;
private int port;
private String username;
private String password;
public boolean mediate(MessageContext context) {
//Mediation Logic
return true;
}
public void setSoapAction(String _soapAction){
soapAction = _soapAction;
}
public String getSoapAction(){
return soapAction;
}
public void setSoapEndpoint(String _soapEndpoint){
soapEndpoint = _soapEndpoint;
}
public String getSoapEndpoint(){
return soapEndpoint;
}
public void setDomain(String _domain){
domain = _domain;
}
public String getDomain(){
return domain;
}
public void setHost(String _host){
host = _host;
}
public String getHost(){
return host;
}
public void setPort(int _port){
port = _port;
}
public int getPort(){
return port;
}
public void setUsername(String _username){
username = _username;
}
public String getUsername(){
return username;
}
public void setPassword(String _password){
password = _password;
}
public String getPassword(){
return password;
}
}
The Custom Mediation Logic
Make sure you created an JCIFS_NTLMScheme class from here and have added the org.samba.jcifs dependency to your Maven dependencies like so:
<dependency>
<groupId>org.samba.jcifs</groupId>
<artifactId>jcifs</artifactId>
<version>1.3.17</version>
</dependency>
Now you can use the following mediate method in your custom mediator class:
public boolean mediate(MessageContext context) {
//Build NTLM Authentication Scheme
AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, JCIFS_NTLMScheme.class);
HttpTransportProperties.Authenticator auth = new HttpTransportProperties.Authenticator();
auth.setUsername(username);
auth.setPassword(password);
auth.setDomain(domain);
auth.setHost(host);
auth.setPort(port);
ArrayList<String> authPrefs = new ArrayList<String>();
authPrefs.add(AuthPolicy.NTLM);
auth.setAuthSchemes(authPrefs);
//Force Authentication - failures will get caught in the catch block
try {
//Build ServiceClient and set Authorization Options
ServiceClient serviceClient = new ServiceClient();
Options options = new Options();
options.setProperty(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, auth);
options.setTransportInProtocol(Constants.TRANSPORT_HTTP);
options.setTo(new EndpointReference(soapEndpoint));
options.setAction(soapAction);
serviceClient.setOptions(options);
//Generate an OperationClient from the ServiceClient to execute the request
OperationClient opClient = serviceClient.createClient(ServiceClient.ANON_OUT_IN_OP);
//Have to translate MsgCtx from Synapse to Axis2
org.apache.axis2.context.MessageContext axisMsgCtx = new org.apache.axis2.context.MessageContext();
axisMsgCtx.setEnvelope(context.getEnvelope());
opClient.addMessageContext(axisMsgCtx);
//Send the request to the server
opClient.execute(true);
//Retrieve Result and replace mediation (synapse) context
SOAPEnvelope result = opClient.getMessageContext(WSDLConstants.MESSAGE_LABEL_IN_VALUE).getEnvelope();
context.setEnvelope(result);
} catch (AxisFault e) {
context.setProperty("ResponseCode", e.getFaultCodeElement().getText());
return false; //This stops the mediation flow, so I think it executes the fault sequence?
}
return true;
}
Package as an OSGi Bundle and Deploy to the Server
At this stage you should be able to your custom mediator project within the project explorer in WSO2 Developer Studio and from the context menu select Export Project as Deployable Archive. Follow the prompts to save the JAR file somewhere on your system. After generating the JAR file, locate it and transfer it to the [ESB_HOME]/repository/components/dropins directory. You may need to restart the server for it to detect the new external library.
Using the Custom Mediator
In your sequence, you should now be able to add a class mediator and reference your custom class using the package name and class name together, for example: org.strainy.ntlmauthorisation.
I have a lot of proxies in WSO2 ESB that I have to securize. I need them to be securized using Username Token when deploy, instead of browsing to the dashboard and enabling it one by one.
Any help?
I guess currently, you need to use management console and do it. From the UI, it is calling a backend web service. You can automate process by automating this backend web service. This web service is exposed by following component [1]. You can use soapui or some client program to automate this web service.
[1] http://svn.wso2.org/repos/wso2/carbon/platform/trunk/components/security/org.wso2.carbon.security.mgt/
I had similar requirement, here is how I solved it
Apply Role security to WSO2 ESB Proxy using Java API
Also you can find the test case here on how to use the methods
http://svn.wso2.org/repos/wso2/tags/carbon/3.2.3/products/bps/2.1.1/modules/integration/org.wso2.bps.management.test/src/test/java/org/wso2/bps/management/SecurityTest.java
Well here how the code snippet goes to secure any proxy service with default security scenarios of WSO2 ESB. In WSO2 ESB "scenario1" signifies Usernametoken based security. Now if you wish to secure your proxy with scenario1 follow the below code snippet:
public void applySecurityOnService(String serviceName, String policyId,
String[] userGroups, String[] trustedKeyStoreArray,
String privateStore)
throws SecurityAdminServiceSecurityConfigExceptionException,
RemoteException {
ApplySecurity applySecurity;
applySecurity = new ApplySecurity();
applySecurity.setServiceName(serviceName);
applySecurity.setPolicyId("scenario" + policyId); //scenario1 i.e. for Usernametoken security policyId should be 1
applySecurity.setTrustedStores(trustedKeyStoreArray);
applySecurity.setPrivateStore(privateStore);
applySecurity.setUserGroupNames(userGroups);
stub.applySecurity(applySecurity);
_logger.info("Security Applied Successfully");
}
Here is how you may call this method from your client class:
applySecurityOnService("MyProxy", "1", new String[]{"TestRole"}, new String[]{"wso2carbon.jks"}, "wso2carbon.jks");
I have a standalone web-service client. When invoking any of the web-methods an additional "cookie" string must be implicitly(not as a web-method parameter) passed to the WS. The WS on the other end must be able to obtain the string and use it. How can this be achieved?
I invoke the service in the following way:
Service srv = Service.create(new URL(WSDL), QNAME);
myClassPort = srv.getPort(MyClass.class);
What I need is to put some code before the first line, which would make the client send this "cookie" string every time I invoke some remote method via myClassPort. Thx.
By default JAX-WS web services and clients are stateless. When a client makes a request, the server responds and sets a cookie on the connection, if it participates in a session. But, the JAX-WS client ignores that cookie and the server treats subsequent requests as new interaction. When the session is enabled, JAX-WS client sends the same cookie with each subsequent request so that server can keep track of the client session.
So you should not be using either cookies or HTTP sessions with web services. Return a token ID as part of the response; then the client can send that along with the next request.
Anyway:
JAX-WS web service clients must be configured to maintain session information (such as cookies), using the javax.xml.ws.session.maintain property.
Other web service stacks may have similar mechanisms.
On the Server Side
JAX-WS uses some handy annotations defined by Common Annotations for the Java Platform (JSR 250), to inject the web service context and declaring lifecycle methods.
WebServiceContext holds the context information pertaining to a request being served.
You don't need to implement javax.xml.rpc.server.ServiceLifecycle. With JAX-WS Web Service all you need to do is mark a field or method with #Resource. The type element MUST be either java.lang.Object or javax.xml.ws.WebServiceContext.
#WebService
public class HelloWorld {
#Resource
private WebServiceContext wsContext;
public void sayHello(){
MessageContext mc = wsContext.getMessageContext();
HttpSession session = ((javax.servlet.http.HttpServletRequest)mc.get(MessageContext.SERVLET_REQUEST)).getSession();
}
}
There are some misleading answers to this question, so I will attempt to highlight current best practices. Most of these suggestions are part of the OWASP security guidelines, which I strongly recommend anyone working on web development to review.
1) ALWAYS use temporary (session scoped) cookies.
2) All cookies should be protected and encrypted.
3) Do not pass tokens in request payloads
4) For any requests which return data that may be sent back to the server, include a nonce (single use token) in your responses.
5) later requests should (must) include the nonce and the cookie
Again, my recommendation is to review the OWASP guidelines and proceed accordingly.
You may want to look into using a service provider for authentication - this is much smarter than brewing your own solution as there are literally a million details that all must be correct. Auth0.com is one of these.
Is it possible to use #RolesAllowed annotation on a JAX-WS webservice and if so how?
I have a webservice on glassfish 3.1.1 using Basic Authentication but restrictions expressed using #RolesAllowed are ignored. The role information should be available, as I can access it like this:
#Resource
WebServiceContext wsContext;
if (wsContext.isUserInRole("READ"))
log.info("Role: READ");
I get the expected role but still all methods are accessible, even if #RolesAllowed is set to different role. #DenyAll is not working as well.
If these annotations are not supported, is it possible to use deployment descriptors to manage access to webservice methods based on user roles?
Edit:
This part of the JAVA EE 6 tutorial describes the usage of #RolesAllowed annotation. It reads
For Java EE components, you define security roles using the #DeclareRoles and #RolesAllowed metadata annotations.
Web services are not listed as Java EE components in the first part of the tutorial, so it looks like the security annotations are not supported.
Edit2
Following Izan's post, I gave this another try. Here is what I did:
#Webservice
#DeclareRoles(value = {"READ", "UPDATE", "DELETE"})
public class ServiceImpl implements Service {
#Override
#WebMethod(operationName = "helloWorld")
#RolesAllowed({"NONE"})
public String helloWorld() throws Exception {
return "Hello World!";
}
}
Using this kind of setup, everybody can access the method, no matter what roles are set. Users get authenticated (can see that in audit.log) but no authorization takes place. As stated above, I can access the role from WebServiceContext (I actually do manual authorization using this info).
Adding #Stateless annotation, let's me use the security annotations. So #permitAll works as expected. But using roles still does not work, as user don't get authenticated now. They show up as ANONYMOUS in audit log and access is denied to them.
My web.xml looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>OneMore</display-name>
<security-constraint>
<display-name>WebServiceSecurity</display-name>
<web-resource-collection>
<web-resource-name>Authorized users only</web-resource-name>
<url-pattern>/service</url-pattern>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>READ</role-name>
<role-name>UPDATE</role-name>
<role-name>DELETE</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
<security-role>
<role-name>READ</role-name>
</security-role>
<security-role>
<role-name>UPDATE</role-name>
</security-role>
<security-role>
<role-name>DELETE</role-name>
</security-role>
</web-app>
Glassfish-web.xml just maps role names to group names, like this:
<security-role-mapping>
<role-name>READ</role-name>
<group-name>READ</group-name>
</security-role-mapping>
Edit 3
Thanks to Izan and countless tries later I finally got it working.
As said before, the main point was switching from a plain web service to an EJB web service by adding #Stateless annotation. This allows for using the security annotations.
This change required to change the deployment descriptors as well. While the original web service required a glassfish-web.xml for setting up the roles, a glassfish-ejb-jar.xml is required afterwards.
Maybe this is a pretty dumb question, but are your webservices EJBs? As noted in Security Annotations and Authorization in GlassFish and the Java EE 5 SDK
The annotations #PermitAll, #DenyAll and #RolesAllowed are defined for specifying permissions of EJB business method
I use those annotations with bottom-up WS from stateless EJBs and they work like a charm in JBoss.
EDIT 1 #TPete
I'll add some code to show you more or less what I'm doing.
#Stateless
#WebService()
#WebContext(contextRoot = WSContextRoot.CTX_ROOT,
authMethod = "BASIC")
#EndpointConfig(configName = "Standard WSSecurity Endpoint")
#SecurityDomain(value = "myDeclaredDomain")
#RolesAllowed({ "AUTHORISED" })
#SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
public class MyWS implements MyInterface {
#Override
public void doSomething(){
//impl
}
}
And as for the interface
#Remote
#WebService
public interface MyInterface {
#WebMethod(operationName="doSomething")
public void doSomething();
}
WebContext, EndpointConfig and SecurityDomain are JBoss annotation, but I suppose there is something similar for GlassFish, or an equivalent way of doing it. The security domain is included in a deployment descriptor for jboss, and defined in the login-config.xml from the configuration files of JBoss.
EDIT 2 #TPete
I suppose you need to add some EJB deployment descriptors from Glassfish, a sun-ejb-jar.xml file package inside your EAR. Again, from the same article as posted in the answer, there is a Using Deployment Descriptors chapter that states
For EJB web service endpoints with #RolesAllowed, you need to specify the type of authentication to use by specifying the and elements in sun-ejb-jar.xml. For username-password authentication, set the element to BASIC, as shown in the following example. This step is required only for EJB web service endpoints, and is not required for EJBs.
Since you are defining an EJB web service endpoint, I think you should put this descriptor in you EAR. Have a quick look at that article, it describes quite well the process you are following :-)
The original question is old but I'm still leaving a comment just in case someone like me stumbles across it. Starting with EJB 3.1, EJBs may be packaged in a WAR module but when it comes to securing them, EJB deployment descriptors need to be used. What is not clear in the spec is that EJBs may not be declared as Servlets in web.xml or else the app will fail to start.
Here's an excellent article about packaging EJBs in WAR modules and the differences with packaging in EJB JAR modules:
http://pic.dhe.ibm.com/infocenter/wasinfo/v8r0/index.jsp?topic=%2Fcom.ibm.websphere.nd.multiplatform.doc%2Finfo%2Fae%2Fae%2Fcejb_ejbinwar.html
i have a local client j2se application and backend is derby(javadb) database and dao is jpa eclipselink .
how do i send these database pojo to a remote database which linked with spring ( jsp) application on tomcat server
simply this is a rich client with swing which connects to tomcat deployed web application. The client should receive data and send data through HTTP requests to the server-side of the service,
what would be the best solution ??
01) direct database connection/transaction through socket using Eclipselink
02) web service ??
03) just send post request to spring web application and convert it to POJO and persist to database
how do i achieve this??
DISCLAIMER I am not suggesting you port your app from Spring to EJB. Despite how people like to compare them as exclusively one or the other, you can use them both. Its your app, you can be as pragmatic as you want to be :)
You don't necessarily have to use Web Services if you wanted. You could drop the OpenEJB war file into Tomcat as well and create an Remote EJB to send data back and forth.
Once you drop in OpenEJB you can put a remote #Stateless bean in your app like so:
#Stateless
#Remote
public class MyBean implements MyBeanRemote {
//...
}
public interface MyBeanRemote {
// any methods you want remotely invoked
}
Then look it up and execute it over HTTP from your Swing app like so:
Properties p = new Properties();
p.put("java.naming.factory.initial", "org.apache.openejb.client.RemoteInitialContextFactory");
p.put("java.naming.provider.url", "http://tomcatserver:8080/openejb/ejb");
// user and pass optional
p.put("java.naming.security.principal", "myuser");
p.put("java.naming.security.credentials", "mypass");
InitialContext ctx = new InitialContext(p);
MyBean myBean = (MyBean) ctx.lookup("MyBeanRemote");
Client-side all you need are the openejb-client.jar and javaee-api.jar from the OpenEJB war file and your own classes.
Since it's already a Spring app don't bother trying to use #PersistenceContext to get a reference to the EntityManager so the EJB can use it. Just figure out how to expose the EntityManagerFactory that Spring creates (or you create) to the EJB via any means possible. The direct and ugly, but effective, approach would be a static on the MyBean class and a bit of startup logic that sets it. You'd just be using the EJB for remoting so no need for fancier integration.
If you did really need web services for non-java communication or something, you can add #WebService to the top of your bean and then it will have WSDL and all that generated for it:
#Stateless
#Remote
#WebService(portName = "MyBeanPort",
serviceName = "MyBeanService",
targetNamespace = "http://superbiz.org/wsdl"
endpointInterface = "org.superbiz.MyBeanRemote")
public class MyBean implements MyBeanRemote {
//...
}
public interface MyBeanRemote {
// any methods you want remotely invoked
}
Then you can also use the same bean as a web service like:
Service service = Service.create(
new URL("http://tomcatserver:8080/MyBeanImpl?wsdl"),
new QName("http://superbiz.org/wsdl", "MyBeanService"));
assertNotNull(service);
MyBeanRemote myBean = service.getPort(MyBeanRemote.class);
Both approaches are over http, but the web service approach will be a bit slower as it isn't a binary protocol.