Endpoint Publish for REST Web Services - web-services

I've published JAX-WS web services with Endpoint.publish during development. Is there any such utility class exists (in JAX-RS) for publishing REST web services in jersey? I referred couple of articles, and majority of them are based on publishing the web services in some containers like Jetty, Grizzly etc.

Jersey-Grizzly has a very simple solution. From https://github.com/jesperfj/jax-rs-heroku:
package embedded.rest.server;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.sun.grizzly.http.SelectorThread;
import com.sun.jersey.api.container.grizzly.GrizzlyWebContainerFactory;
#Path("/hello")
public class Main {
public static void main(String[] args) {
final String baseUri = "http://localhost:7080/";
final Map<String, String> initParams = new HashMap<String, String>();
// Register the package that contains your javax.ws.rs-annotated beans here
initParams.put("com.sun.jersey.config.property.packages","embedded.rest.server");
System.out.println("Starting grizzly...");
try {
SelectorThread threadSelector =
GrizzlyWebContainerFactory.create(baseUri, initParams);
System.out.println(String.format("Jersey started with WADL "
+ "available at %sapplication.wadl.", baseUri));
}
catch(Exception e) {
e.printStackTrace();
}
}
#GET
#Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Well, this was easy!";
}
}
If you're using Maven, you'll need the following three dependencies:
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-grizzly</artifactId>
<version>1.15</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-bundle</artifactId>
<version>1.15</version>
</dependency>
<dependency>
<groupId>com.sun.grizzly</groupId>
<artifactId>grizzly-servlet-webserver</artifactId>
<version>1.9.18-i</version>
</dependency>
To test it, just open http://localhost:7080/hello in a browser.

I think you can use Provider interface to publishing a RESTful Web Service with JAX-WS.
The example class:
#WebServiceProvider
#BindingType(value=HTTPBinding.HTTP_BINDING)
public class AddNumbersImpl implements Provider {
#Resource
protected WebServiceContext wsContext;
public Source invoke(Source source) {
try {
MessageContext mc = wsContext.getMessageContext();
// check for a PATH_INFO request
String path = (String)mc.get(MessageContext.PATH_INFO);
if (path != null && path.contains("/num1") &&
path.contains("/num2")) {
return createResultSource(path);
}
String query = (String)mc.get(MessageContext.QUERY_STRING);
System.out.println("Query String = "+query);
ServletRequest req = (ServletRequest)mc.get(MessageContext.SERVLET_REQUEST);
int num1 = Integer.parseInt(req.getParameter("num1"));
int num2 = Integer.parseInt(req.getParameter("num2"));
return createResultSource(num1+num2);
} catch(Exception e) {
e.printStackTrace();
throw new HTTPException(500);
}
}
private Source createResultSource(String str) {
StringTokenizer st = new StringTokenizer(str, "=&/");
String token = st.nextToken();
int number1 = Integer.parseInt(st.nextToken());
st.nextToken();
int number2 = Integer.parseInt(st.nextToken());
int sum = number1+number2;
return createResultSource(sum);
}
private Source createResultSource(int sum) {
String body =
"<ns:addNumbersResponse xmlns:ns="http://java.duke.org"><ns:return>"
+sum
+"</ns:return></ns:addNumbersResponse>";
Source source = new StreamSource(
new ByteArrayInputStream(body.getBytes()));
return source;
}
}
To deploy our endpoint on a servlet container running with the JAX-WS
RI we need to create a WAR file.
The adjusted web.xml:
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee">
<listener>
<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener
</listener>
<servlet>
<servlet-name>restful-addnumbers</servlet-name>
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>restful-addnumbers</servlet-name>
<url-pattern>/addnumbers/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>60</session-timeout>
</session-config>
</web-app>
and need to add sun-jaxws.xml deployment descriptor to the WAR file.
<endpoints
xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"
version="2.0">
<endpoint
name="restful-addnumbers"
implementation="restful.server.AddNumbersImpl"
wsdl="WEB-INF/wsdl/AddNumbers.wsdl"
url-pattern="/addnumbers/*" />
</endpoints>
Or could be create simple HttpServer
import java.io.IOException;
import com.sun.jersey.api.container.httpserver.HttpServerFactory;
import com.sun.net.httpserver.HttpServer;
public class YourREST {
static final String BASE_URI = "http://localhost:9999/yourrest/";
public static void main(String[] args) {
try {
HttpServer server = HttpServerFactory.create(BASE_URI);
server.start();
System.out.println("Press Enter to stop the server. ");
System.in.read();
server.stop(0);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

Related

Arquillian API REST Example

I cannot found any example for jax-rs testing with arquillian. I use a wildfly 10 managed container.
I am trying to do it by my own, this is my sample code:
#RunWith(Arquillian.class)
public class DeploymentTest {
#Deployment(testable = false)
public static Archive<?> deploy() {
return ShrinkWrap.create(WebArchive.class, "cos-arq-test.war")
.addClasses(MANUEJB.class, HelloWorld.class, HelloWorldRESTImpl.class)
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
}
#ArquillianResource
private URL base;
private static WebTarget target;
#Before
public void setUpClass() throws MalformedURLException {
Client client = ClientBuilder.newClient();
target = client.target(URI.create(new URL(base, "rest/helloWorldREST").toExternalForm()));
}
#Test
#RunAsClient
public void testResponse(#ArquillianResource URL base) throws InterruptedException, ExecutionException {
System.out.println("====================================================");
System.out.println("This test should run inside the Wildfly 10 container");
System.out.println("====================================================");
try {
System.out.println("URL TARGET: " + target.getUri().toURL().toString());
} catch (MalformedURLException e) {
e.printStackTrace();
}
assertEquals("Hello", target.request().get().readEntity(String.class));
/*
Future<Response> r1 = target.request().async().get();
Response response = r1.get();
if (null != response) {
assertEquals(HttpStatus.OK, response.getStatus());
assertNotNull(response.getEntity());
assertEquals("Hello " + "manuel" + "!", response.readEntity(String.class));
}
*/
}
}
And this is my service code:
#Path("/helloWorldREST")
public class HelloWorldRESTImpl implements HelloWorld {
#GET
public Response sayHi() {
return Response.ok("Hello").build();
}
#Override
#GET
#Path("/sayHi/{name}")
#Produces(MediaType.APPLICATION_JSON)
public Response sayHi(#PathParam("name") String name) {
MANUEJB ejb = null;
javax.naming.Context initialContext = null;
try {
initialContext = new InitialContext();
} catch (NamingException e) {
e.printStackTrace();
}
try {
ejb = (MANUEJB) initialContext.lookup("java:app/cos-arq-test/MANUEJB");
} catch (NamingException e) {
e.printStackTrace();
}
String result = ejb.method(name);
return Response.ok(result).build();
}
}
But I get an error, and it does not find the service.
I use arquillian libraries with a managed wildfly 10 container:
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-bom</artifactId>
<version>1.1.11.Final</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
</dependency>
<dependency>
<groupId>org.wildfly.arquillian</groupId>
<artifactId>wildfly-arquillian-container-managed</artifactId>
<version>2.0.0.Final</version>
</dependency>
And this is my arquillian config for the container:
<arquillian xmlns="http://jboss.org/schema/arquillian"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
<container qualifier="wildfly10" default="true">
<configuration>
<property name="jbossHome">/home/manumg/test-containers/wildfly-10.1.0.Final</property>
</configuration>
</container>
For a starter example, you can see https://github.com/arquillian/arquillian-extension-rest/blob/master/rest-client/README.md.
The code described in the readme is in the same repository at https://github.com/arquillian/arquillian-extension-rest/tree/master/rest-client/test-app.
For a starter maven project, I suggest you using the maven archetype wildfly-jakartaee-webapp-archetype, related to Wildfly 21 (jakarta ee 8 container).
To run the test you have to:
Activate the profile arq-managed
Download Wildfly 21 (from https://www.wildfly.org/downloads/)
Define the JBOSS_HOME variable (pointing to the local Wildfly directory)

How to use Tuckey urlrewrite in spring boot to access service using ?wsdl

My clients wanted to access wsdl using ?wsdl notation, but the springboot service i have created is working with only '.wsdl' format. I need a working example/sample how to configure Tuckey urlrewrite in the springboot application.
I have tried using below code, but the application complaints as it cant find urlrewrite.xml (which i have placed in src/main/resources folder.
Q1: How can i make my service to be accessible using url below
http://localhost:8080/ws/organisation?wsdl
I have tried using below code, but tuckey cannot find the urlrewrite.xml which is under src/java/resources.
#Bean
public FilterRegistrationBean tuckeyRegistrationBean() {
final FilterRegistrationBean registrationBean = new ilterRegistrationBean();
registrationBean.setFilter(new UrlRewriteFilter());
registrationBean.addInitParameter("confPath", "urlrewrite.xml");
return registrationBean;
}
Finally I could figure out a solution. This is now reading urlrewrite.xml from the src/main/resources folder.
No need to declare above mentioned bean definition in the question post (public FilterRegistrationBean tuckeyRegistrationBean()), as the below code declared as #Component will automatically register with context and url-rewriting is performed.
#Component
public class WsdlUrlRewriteFilter extends UrlRewriteFilter {
private static final String CONFIG_LOCATION = "classpath:/urlrewrite.xml";
#Value(CONFIG_LOCATION)
private Resource resource;
#Override
protected void loadUrlRewriter(FilterConfig filterConfig) throws ServletException {
try {
Conf conf = new Conf(filterConfig.getServletContext(), resource.getInputStream(), resource.getFilename(), "");
checkConf(conf);
} catch (IOException ex) {
throw new ServletException("Unable to load URL-rewrite configuration file from " + CONFIG_LOCATION, ex);
}
}
}
Best option is to write your own filter as follows.You can make use of HttpServletRequestWrapper to handle the ?wsdl extension and let the server handle the request.
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
#Component
public class WSDLQuestionMarkReplaceFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
//put init logs
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
if ("wsdl".equalsIgnoreCase(httpRequest.getQueryString())) {
HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(httpRequest) {
#Override
public String getQueryString() {
return null;
}
#Override
public String getRequestURI() {
return super.getRequestURI() + ".wsdl";
}
};
chain.doFilter(requestWrapper, response);
} else {
chain.doFilter(request, response);
}
}
#Override
public void destroy() {
//put destroy logs
}
}
It worked for me. Hope it works for others too. SpringBoot+JAR
Please use the following dependency
<dependency>
<groupId>org.tuckey</groupId>
<artifactId>urlrewritefilter</artifactId>
<version>4.0.4</version>
</dependency>
Created urlrewrite.xml in resource folder
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite
PUBLIC "-//tuckey.org//DTD UrlRewrite 3.0//EN"
"http://www.tuckey.org/res/dtds/urlrewrite3.0.dtd">
<urlrewrite>
<rule>
<name>Domain Name Check</name>
<condition name="host" operator="notequal">www.userdomain.com</condition>
<from>^(.*)$</from>
<to type="redirect">http://www.userdomain.com$1</to>
</rule>
Added in main ApplicationRunner.java
#Bean
public FilterRegistrationBean tuckeyRegistrationBean() {
final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new CustomURLRewriter());
return registrationBean;
}
And created a CustomURLRewriter
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.tuckey.web.filters.urlrewrite.Conf;
import org.tuckey.web.filters.urlrewrite.UrlRewriteFilter;
import org.tuckey.web.filters.urlrewrite.UrlRewriter;
import javax.servlet.*;
import java.io.InputStream;
public class CustomURLRewriter extends UrlRewriteFilter {
private UrlRewriter urlRewriter;
#Autowired
Environment env;
#Override
public void loadUrlRewriter(FilterConfig filterConfig) throws ServletException {
try {
ClassPathResource classPathResource = new ClassPathResource("urlrewrite.xml");
InputStream inputStream = classPathResource.getInputStream();
Conf conf1 = new Conf(filterConfig.getServletContext(), inputStream, "urlrewrite.xml", "");
urlRewriter = new UrlRewriter(conf1);
} catch (Exception e) {
throw new ServletException(e);
}
}
#Override
public UrlRewriter getUrlRewriter(ServletRequest request, ServletResponse response, FilterChain chain) {
return urlRewriter;
}
#Override
public void destroyUrlRewriter() {
if(urlRewriter != null)
urlRewriter.destroy();
}
}

Can't get my REST web service to work (using Glassfish in Netbeans)

I'm writing a fairly simple restful web service project in Netbeans (used the Maven Web Application template). I am trying to run it on a Glassfish 4.1 server. I have used Tomcat in the past, but that's not really an option here. Basically, my problem is that I run the project, the server starts, but I just get a 404 error when I try to access the service in the browser.
Here is my source code:
package jp.go.aist.limrs;
import com.hp.hpl.jena.rdf.model.Model;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.websocket.server.PathParam;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.apache.commons.io.IOUtils;
#Path("/target")
public class ParserService
{
public static final String SERVER_LOC = "http://localhost:8080/LiMRS/";
public static final String MAPPINGS_LOC = "export.txt";
private String targetUrl;
private String microData;
private Model uDataModel;
private Model mappingsModel;
public ParserService() {}
public ParserService( String url )
{
this.targetUrl = url;
try {
parseMicro(url);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
#GET
#Path("/{url:.+}")
#Produces(MediaType.TEXT_PLAIN)
public String getMicro(#PathParam("url") String target)
{
this.targetUrl = target;
String domain = "_";
try {
URI uri = new URI(this.targetUrl);
domain = uri.getHost();
System.out.println("Domain is " + domain + "\n\n\n");
} catch (URISyntaxException ex) {
Logger.getLogger(jp.go.aist.LiMRS.LiMRService.class.getName()).log(Level.SEVERE, null, ex);
}
this.microData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<rdf:RDF xml:base=\"http://dbpedia.org/ontology/\" " +
"xmlns:_=\"" + domain + "\">\n\n";
try
{
parseMicro(URLEncoder.encode(this.targetUrl, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
return "";
}
return this.microData;
}
private void parseMicro(String target) throws MalformedURLException
{
try {
URL url = new URL("http://getschema.org/microdataextractor?url=" + target + "&out=rdf");
HttpURLConnection conn;
try {
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/json");
InputStream ins = conn.getInputStream();
StringWriter writer = new StringWriter();
IOUtils.copy(ins, writer, null);
this.microData += writer.toString() + ".";
} catch (IOException ex) {
Logger.getLogger(jp.go.aist.LiMRS.LiMRService.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (MalformedURLException ex) {
Logger.getLogger(jp.go.aist.LiMRS.LiMRService.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
The URL I'm using to test the service is: http://localhost:8080/LiMRS/target/http://www.rottentomatoes.com/m/jurassic_park/
(I know the URL is unencoded. There are forward slashes in the 'resource' part of the URL, after "/target/", but that is taken care of by the regex in the code and is not source of the problem.)
It's possible the problem is with the server itself, I don't know if there is any special configuration that needs to be done to Glassfish or if you can just run the project outright. I don't have a web.xml file. Unless I'm mistaken, I don't think I need one. What am I missing here?
You're going to need a web.xml or a Application/ResourceConfig subclass to configure the application. If you don't want to use a web.xml, the easiest thing you can do is have an empty Application class annotated with #ApplicationPath. This will cause the registration of all #Path classes you have
#ApplicationPath("/")
public class JaxRsApplication extends Application {}
You can see more options here

JBoss 7.1.1 Webservice programatic JAAS authentication

I have a simple POJO as a webservice that is mapped to, lets say /public/authenticate :
WebService
#SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
public class AuthWS{
#WebMethod
public boolean doAuthenticate(String securityToken) {
....
}
}
This webservice doesn't requre authentication and is not a protected resource.
I do have other private webservices mapped to path : /private/ws/*;
For the moment I use a security-domain that has a Database login module setup. It works fine, but user first needs to authenticate trought a web form based that makes a post request to /j_security_check. Only after this step user can use other private webservices.
I want to perform a programatically authentication after client calls this doAuthenticate method. So that client to be able to invoke other /private/ws/* webservice methods.
I'll type what I want to achieve:
#WebService
#SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
public class AuthWS{
#WebMethod
public boolean doAuthenticate(String securityToken) {
SomeSecurityManager manager= SomeSecurityManager.getDefaultManager()
Map<String,Object> map = new HashMap<String, Object>();
map.put("MY_CUSTOM_SECURITY_TOKEN",securityToken);
manager.doLogin(map);
// after webservice method returns, client should now be able to invoke other private webservice
// this means that the manager should associate with this session an authenticated user.
// in order that authorization to work.
}
}
And my CustomLoginModule :
class CustomLogModule implements LoginModule {
...
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
// store what's needed
}
public boolean login(){
// get securityToken send from the SomeSecurityManager and validate it.
// get user information from that token and store into Subject object.
}
}
And in my CustomLoginModule that implements JAAS LoginModule to check that securityToken with a custom logic, verify if it right signed with a public key for example. That securityToken contains information about principal.
If you need more details, feel free to ask.
Thanks.
EDITED
1.) Created custom-login-module.jar together with module.xml
<module xmlns="urn:jboss:module:1.1" name="custom.login.module">
<resources>
<resource-root path="custom-login-module.jar"/>
</resources>
<dependencies>
<module name="org.picketbox"/>
<module name="javax.api"/>
<module name="org.slf4j"/>
</dependencies>
</module>
2.) Added custom-login-module.jar and module.xml into jboss-as-7.1.1.Final\modules\custom\login\module
3.) custom-login-module.jar contains :
public class CustomCallbackHandler implements CallbackHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomCallbackHandler.class);
private String token;
public CustomCallbackHandler(String token) {
this.token= token;
}
public String getToken() {
return token;
}
#Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof TokenCallback) {
((TokenCallback) callback).setToken(token);
}
}
}
}
public class TokenCallback implements Callback {
private static final Logger LOGGER = LoggerFactory.getLogger(TokenCallback.class);
private String token;
public TokenCallback() {
}
public String getToken() {
return token;
}
public void setToken(String token) {
LOGGER.info("Setting token = " + token);
this.token = token;
}
}
public class CustomLoginModule extends AbstractServerLoginModule {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomLoginModule.class);
#Override
public boolean login() throws LoginException {
LOGGER.info("Doing login()");
boolean login = super.login();
super.loginOk = true;
return login;
}
#Override
protected Principal getIdentity() {
return new UserPrincipal("some user");
}
#Override
protected Group[] getRoleSets() throws LoginException {
return new Group[]{new MyGroup()}; // that and has name 'dummy'
}
}
These are only dummy implementations.
My web application is deployed from within a .war archive. And it Contains following :
jboss-web.xml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE jboss-web
PUBLIC -//JBoss//DTD Web Application 2.3V2//EN
http://www.jboss.org/j2ee/dtd/jboss-web_3_2.dtd>
<jboss-web>
<security-domain>custom-auth</security-domain>
</jboss-web>
web.xml
<?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_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>WebApp</display-name>
<session-config>
<session-timeout>120</session-timeout>
</session-config>
<security-constraint>
<web-resource-collection>
<web-resource-name>All resources</web-resource-name>
<description>Protects all private resources</description>
<url-pattern>/private/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>dummy</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<security-role>
<role-name>dummy</role-name>
</security-role>
<servlet>
<servlet-name>Private</servlet-name>
<servlet-class>com.company.private.PrivateWs</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Private</servlet-name>
<url-pattern>/private/PrivateWs</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>AuthWS</servlet-name>
<servlet-class>com.company.auth.AuthWS</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AuthWS</servlet-name>
<url-pattern>/AuthWS</url-pattern>
</servlet-mapping>
</web-app>
#WebService
#SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
public class AuthWS{
private static final Logger LOGGER = LoggerFactory.getLogger(AuthWS.class);
#WebMethod
public boolean doAuthenticate(String token) {
tryProgrammaticLogin(token);
return true;
}
private void tryProgrammaticLogin(String token) {
LoginContext loginContext = null;
try {
loginContext = new LoginContext("custom-auth", new CustomCallbackHandler(token));
loginContext.login();
} catch (LoginException e) {
LOGGER.info("Some problem occured when trying to custom login.", e);
}
}
}
The call to doAuthenticate from my ws client works but the problem is that after try ProgrammaticLogin an exception occurs. And the PrivateWS is not accesible by client.
17:33:40,901 INFO [com.mycompany.AuthWS] (http--0.0.0.0-8080-1) Some problem occured when trying to custom login.: javax.security.auth.login.LoginException: Login Failure: all modules ignored
at javax.security.auth.login.LoginContext.invoke(LoginContext.java:921) [rt.jar:1.6.0_26]
at javax.security.auth.login.LoginContext.access$000(LoginContext.java:186) [rt.jar:1.6.0_26]
at javax.security.auth.login.LoginContext$4.run(LoginContext.java:683) [rt.jar:1.6.0_26]
at java.security.AccessController.doPrivileged(Native Method) [rt.jar:1.6.0_26]
at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:680) [rt.jar:1.6.0_26]
at javax.security.auth.login.LoginContext.login(LoginContext.java:579) [rt.jar:1.6.0_26]
standalone.xml from jboss configuration directory contains:
<security-domain name="custom-auth">
<authentication>
<login-module code="com.mycompany.CustomLoginModule" flag="required" module="custom.login.module"/>
</authentication>
</security-domain>
Please tell me if the way of doing authentication with creating a new LoginContext object is the right way of doing. I can't understand why this problem occurs.

Web service soap header authentication

I have a web service, i want to authenticate the user from the soap header. That is, i want to check a token id (random number) in soap header and validate it against a value in my database and if the number matches i allow the request to go through otherwise i dont want to allow execution of my web method.
Is there any clean way of doing it using SOAP headers?
Thanks,
Mrinal Jaiswal
Have you looked into WS-Security? Assuming you're not already using it for something else, you could carry your token in the Username element, etc.
<?xml version="1.0"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-1">
<wsse:Username>yourusername</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">yourpassword</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<yourbodygoeshere>
</soapenv:Body>
</soapenv:Envelope>
I created a web service using JDK API, and do a simple authentication by soap header.
This simple project provide two services:
login
get message from server
Client posts username and password in the soap body to server, if login successfully, the server will return a token in the soap header.
Clients calls getMessage service by including this token in the soap header, server check the token, if it is a logged in user, then return a success message, otherwise return a failed message.
The following is the code:
package com.aug.ws;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.jws.WebParam.Mode;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
import javax.xml.ws.Holder;
//Service Endpoint Interface
#WebService
#SOAPBinding(style = Style.RPC)
public interface HelloWorld {
#WebMethod
void login(String userName, String password, #WebParam(header = true, mode = Mode.OUT, name = "token") Holder<String> token);
String getMessage(String message);
}
package com.aug.ws;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebParam.Mode;
import javax.jws.WebService;
import javax.xml.namespace.QName;
import javax.xml.ws.Holder;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;
import com.sun.xml.internal.ws.api.message.Header;
import com.sun.xml.internal.ws.api.message.HeaderList;
import com.sun.xml.internal.ws.developer.JAXWSProperties;
#WebService(endpointInterface = "com.aug.ws.HelloWorld")
public class HelloWorldImpl implements HelloWorld {
private Map<String, String> authorizedUsers = new HashMap<String, String>();
#Resource
WebServiceContext wsctx;
#Override
#WebMethod
public void login(String userName, String password, #WebParam(header = true, mode = Mode.OUT, name = "token") Holder<String> token) {
if (("user1".equals(userName) && "pwd1".equals(password)) || ("user2".equals(userName) && "pwd2".equals(password))) {
String tokenValue = "authorizeduser1234" + userName;
token.value = tokenValue;
authorizedUsers.put(tokenValue, userName);
System.out.println("---------------- token: " + tokenValue);
}
}
#Override
#WebMethod
public String getMessage(String message) {
if (isLoggedInUser()) {
return "JAX-WS message: " + message;
}
return "Invalid access!";
}
/**
* Check token from SOAP Header
* #return
*/
private boolean isLoggedInUser() {
System.out.println("wsctx: " + wsctx);
MessageContext mctx = wsctx.getMessageContext();
HeaderList headerList = (HeaderList) mctx.get(JAXWSProperties.INBOUND_HEADER_LIST_PROPERTY);
String nameSpace = "http://ws.aug.com/";
QName token = new QName(nameSpace, "token");
try {
Header tokenHeader = headerList.get(token, true);
if (tokenHeader != null) {
String user = authorizedUsers.get(tokenHeader.getStringContent());
if (user != null) {
System.out.println(user + " has logged in.");
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
package com.aug.endpoint;
import javax.xml.ws.Endpoint;
import com.aug.ws.HelloWorldImpl;
public class HelloWorldPublisher {
/**
* #param args
*/
public static void main(String[] args) {
Endpoint.publish("http://localhost:9000/ws/hello", new HelloWorldImpl());
System.out.println("\nWeb service published # http://localhost:9000/ws/hello");
System.out.println("You may call the web service now");
}
}
package com.aug.client;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import com.aug.ws.HelloWorld;
import com.sun.xml.internal.ws.api.message.HeaderList;
import com.sun.xml.internal.ws.api.message.Headers;
import com.sun.xml.internal.ws.developer.JAXWSProperties;
import com.sun.xml.internal.ws.developer.WSBindingProvider;
public class HelloWorldClient {
private static final String WS_URL = "http://localhost:9000/ws/hello?wsdl";
private static final String NAME_SPACE = "http://ws.aug.com/";
public static String login() throws Exception {
URL url = new URL(WS_URL);
QName qname = new QName(NAME_SPACE, "HelloWorldImplService");
Service service = Service.create(url, qname);
HelloWorld hello = service.getPort(HelloWorld.class);
hello.login("user1", "pwd1", null);
WSBindingProvider bp = (WSBindingProvider) hello;
HeaderList headerList = (HeaderList) bp.getResponseContext().get(JAXWSProperties.INBOUND_HEADER_LIST_PROPERTY);
bp.close();
return headerList.get(new QName(NAME_SPACE, "token"), true).getStringContent();
}
public static void getMessage() throws Exception {
String token = login();
System.out.println("token: " + token);
URL url = new URL(WS_URL);
QName qname = new QName(NAME_SPACE, "HelloWorldImplService");
Service service = Service.create(url, qname);
HelloWorld hello = service.getPort(HelloWorld.class);
WSBindingProvider bp = (WSBindingProvider) hello;
bp.setOutboundHeaders(
Headers.create(new QName(NAME_SPACE, "token"), token)
);
System.out.println(hello.getMessage("hello world"));
bp.close();
}
public static void main(String[] args) throws Exception {
getMessage();
}
}