====== Securing JAX-RS endpoints with JWT ====== Libraries: * [[https://connect2id.com/products/nimbus-jose-jwt|Nimbus JOSE+JWT]] (''[[https://mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt/5.4|com.nimbusds:nimbus-jose-jwt]]''): * :YES: Implements JWT, JWS, JWE, JWK, JWA. Is used by [[https://github.com/spring-projects/spring-security/issues/4434|Spring Security]]. * :NO: JAR size is bigger than others (300KB). Uses "yet another" JSON serializer (''net.minidev:json-smart''). * [[https://docs.jboss.org/resteasy/docs/3.1.4.Final/userguide/html/ch39.html|RestEasy JOSE-JWT]] (''[[https://mvnrepository.com/artifact/org.jboss.resteasy/jose-jwt/4.0.0.Beta1|org.jboss.resteasy:jose-jwt]]''): * :YES: Re-uses RestEasy libraries / infrastructure. Compact size of 62KB. * :NO: Depends on legacy Jackson v1.x provider (''org.jboss.resteasy:resteasy-jackson-provider'' but not ''org.jboss.resteasy:resteasy-jackson2-provider''). Dependency ''org.bouncycastle:bcprov-jdk15on'' is not optional. * [[https://bitbucket.org/b_c/jose4j/wiki/Home|jose.4.j]] (''[[https://mvnrepository.com/artifact/org.bitbucket.b_c/jose4j/0.6.3|org.bitbucket.b_c:jose4j]]''): * :YES: Implements JWT, JWS, JWE, JWK. * :NO: Includes (perhaps patched) sources of ''com.googlecode.json-simple:json-simple'' however provides adapters for other Java-to-JSON mapping frameworks. Size is 252KB. * [[github>jwtk/jjwt|Java JWT]] (''[[https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt/0.9.0|io.jsonwebtoken:jjwt]]''): * :YES: Is maintained by team of contributors. Uses ''com.fasterxml.jackson.core:jackson-databind''. Reasonable size of 110KB. * :NO: Implements JWT and JWS only. * [[github>auth0/java-jwt|Java JWT]] (''[[https://mvnrepository.com/artifact/com.auth0/java-jwt/3.3.0|com.auth0:java-jwt]]''): * :YES: Uses ''com.fasterxml.jackson.core:jackson-databind''. Small size of 49KB. * :NO: Implements JWT and JWS only. * [[github>inversoft/prime-jwt|Prime JWT]] (''[[https://mvnrepository.com/artifact/com.inversoft/prime-jwt/1.2.1|com.inversoft:prime-jwt]]''): * :YES: Uses ''com.fasterxml.jackson.core:jackson-databind''. Uses Java8 date types. Smallest size of 36KB. * :NO: Implements JWT and JWS only. Low test coverage, few issues are identified ([[github>inversoft/prime-jwt/issues/1|#1]], [[github>inversoft/prime-jwt/issues/2|#2]]). * [[https://jwt.io/|JSON Web Tokens home site]] * [[https://stackoverflow.com/q/23808460/267197|JWT (JSON Web Token) library for Java]] * [[https://abhirockzz.wordpress.com/2016/03/18/json-web-token-in-action-with-jax-rs/|JSON Web Token in action with JAX-RS]] * [[https://antoniogoncalves.org/2016/10/03/securing-jax-rs-endpoints-with-jwt/|Securing JAX-RS Endpoints with JWT]] * [[https://www.iana.org/assignments/jose/jose.xhtml|Registered header names for JOSE]], [[https://www.iana.org/assignments/jwt/jwt.xhtml|Registered header names for JWT claims]] * [[habrahabr>337600|Аутентификация при помощи JWT и Spring Security]] -- uses [[github>jwtk/jjwt|Java JWT]] and implementation actually is equivalent to any persisted randomly generated authentication token. * [[habrahabr>278411|Аутентификация с использованием Spring Security и JWT-токенов]] -- uses [[github>jwtk/jjwt|Java JWT]] From [[http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/|Stop using JWT for sessions, chapter "What is JWT good for, then?"]]: The case when the server that issues JWT also persists it to e.g. database and then later validates it, is equivalent to simply writing a random authentication token (sort of session ID) with a timestamp to the database((See [[http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work/|Stop using JWT for sessions, part 2: Why your solution doesn't work]])). Signing such authentication token is pointless (has no benefit). JWT is only good in true microservice environment, when authentication services issues short-living JWT token which can be used across several microservices, any of which can validate JWT token without contacting authentication server. ====== Creating a SOAP Web Service using Java6 ====== ===== Introduction ===== In this article we shortly describe how to setup Web Service using JAX-WS technology and [[software:maven]]. We will create a client project, a service project and a shared common project (which contains JAX-WS generated classes). The projects' directory structure will be: * ''my-common/'' * ''src/'' * ''wsdl/'' * ''jaxws/'' * ''pom.xml'' * ''my-service'' * ''src/'' * ''webapp/'' * ''pom.xml'' * ''my-client'' * ''src/'' * ''webapp/'' * ''pom.xml'' ===== Starting from XSD/WSDL: using [[https://jax-ws.dev.java.net/nonav/2.1.7/docs/wsimport.html|wsimport]] ===== Actually, when I've started the project, I have realised that using ''wsgen'' -> WSDL -> ''wsimport'' scheme is a bit complicated and excessive: you will be generated the same set of JAXB beans both for server and client, which you might want to share between Web Service and Java Web Client. So what we can do: * Create a Java class (the implementation of my future Web Service) with all methods, input/output arguments classes and exceptions defined. The implementation should be marked with ''@WebService'' annotation. * Run [[https://jax-ws.dev.java.net/nonav/2.1.7/docs/wsgen.html|wsgen]] for it: [INFO] Scanning for projects... [INFO] ------------------------------------------------------------------------ [INFO] Building My DB Service [INFO] task-segment: [clean, install] [INFO] ------------------------------------------------------------------------ [INFO] [resources:resources] ... [INFO] [compiler:compile] [INFO] Compiling 4 source files to my-db-service\target\classes [INFO] [jaxws:wsgen {execution: default}] Note: ap round: 1 [ProcessedMethods Class: org.mycompany.service.db.service.MyDBServiceImpl] [should process method: addConcept hasWebMethods: true ] [endpointReferencesInterface: false] [declaring class has WebSevice: true] [returning: true] [WrapperGen - method: addConcept(java.lang.String,java.lang.String,org.mycompany.service.db.service.ConceptType)] [method.getDeclaringType(): org.mycompany.service.db.service.MyDBServiceImpl] [requestWrapper: org.mycompany.service.db.service.jaxws.AddConcept] [ProcessedMethods Class: java.lang.Object] org\mycompany\service\db\service\jaxws\AddConcept.java org\mycompany\service\db\service\jaxws\AddConceptResponse.java org\mycompany\service\db\service\jaxws\MyDBServiceExceptionBean.java Note: ap round: 2 [INFO] [war:war] ... * Modify the resulting WSDL/XSD as necessary. Due to limitation, that [[http://www.jcp.org/en/jsr/detail?id=181|JSR-181]] service endpoint scanner, implemented by Sun, does not implement merging of annotation properties of implementation and interface, we have to duplicate some of information in ''@WebService'' annotation of implementation class (in particular, add ''targetNamespace'' and mention ''endpointInterface''). package org.mycompany.service.db.service; import java.util.Arrays; import java.util.Date; import javax.jws.WebService; import org.mycompany.service.Concept; import org.mycompany.service.ConceptType; import org.mycompany.service.db.jaxws.MyDBService; import org.mycompany.service.db.jaxws.MyDBServiceException; @WebService(name = "MyDBService", endpointInterface = "org.mycompany.service.db.jaxws.MyDBService", targetNamespace = "http://service.mycompany.org/") public class MyDBServiceImpl implements MyDBService { /** * This method simply creates a new {@link Concept} instance based on passed arguments. */ @Override public Concept addConcept(String id, String creator, ConceptType type) throws MyDBServiceException { return new Concept(id, new Date(), new Concept.Types(Arrays.asList(type)), creator); } } Now we are ready to do the reverse thing: generate beans and Web Service interface, complete the implementation and write a sample client for the service. In order to automate ''wsimport'' process we will use [[https://jax-ws-commons.dev.java.net/jaxws-maven-plugin/|JAX-WS Maven Plugin]]. org.codehaus.mojo jaxws-maven-plugin generate-sources wsimport ${project.build.sourceDirectory} true wsdl MyDBService.wsdl After execution we see: > mvn install [INFO] Scanning for projects... [INFO] ------------------------------------------------------------------------ [INFO] Building My DB Common [INFO] task-segment: [install] [INFO] ------------------------------------------------------------------------ [INFO] [jaxws:wsimport {execution: default}] [INFO] Processing: wsdl\MyDBService.wsdl ... parsing WSDL... generating code... org\mycompany\service\AddConcept.java org\mycompany\service\AddConceptResponse.java org\mycompany\service\Concept.java org\mycompany\service\ConceptType.java org\mycompany\service\MyDBImplService.java org\mycompany\service\MyDBService.java org\mycompany\service\MyDBServiceException.java org\mycompany\service\MyDBServiceException_Exception.java org\mycompany\service\ObjectFactory.java org\mycompany\service\package-info.java ... ===== Using JAXB customizations ===== OK, what we get as the result: * Both JAXB and JAX-WS entities are generated in the same package, which is, by default, constructed from a target namespace of our service (''%%targetNamespace="http://service.mycompany.org/"%%''). This is sometimes not handy, as we want to separate them. Also this causes a minor name collision for the class ''MyDBServiceException'', which is from one side JAXB bean for serialization of this exception (and is not a real ''Exception'') and JAX-WS entity ugly called ''MyDBServiceException_Exception'' which is used in ''throws'' statements of service interface methods (and is a real ''Exception''). We can separate packages for JAXB and JAX-WS in order to resolve this collision implicitly. For this we will add the following to ''jaxws/jaxws-binding.xml'': * The XSD type ''xsd:dateTime'' is represented with class ''javax.xml.datatype.XMLGregorianCalendar'', which you might wish to change to ''java.util.Date''((Also I do not recommend to do so, as ''java.util.Date'' does not carry any timezone information, that means, on the server side dates will always be presented as timestamps for GMT+0 timezone)). To make a trick we need to create a helper class with static methods that actually makes a conversion((See [[http://fusesource.com/docs/framework/2.2/jaxws/JAXWSCustomTypeMappingJavaType.html|here]] and [[http://docs.sun.com/app/docs/doc/819-3669/bnbbv|here]] for more information about JAXB ''javaType'' customization)): package org.mycompany.service.db.jaxb; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import javax.xml.bind.DatatypeConverter; public class XSDateTimeCustomBinder { public static Date parseDateTime(String dateTime) { return DatatypeConverter.parseDate(dateTime).getTime(); } public static String printDateTime(Date date) { final Calendar cal = new GregorianCalendar(); cal.setTime(date); return DatatypeConverter.printDateTime(cal); } } and use this class in JAXB ''javaType'' customization in ''jaxws/jaxb-binding.xml'': Unfortunately customization of package and class name for XML adapters is not yet supported (see [[jaxbtracker>611|issue#611]] and [[jaxbtracker>506|issue#506]]) but the workaround is described [[https://jaxb.dev.java.net/guide/Customizing_Java_packages.html|here]]. Also at least in JAXB v2.1.13 there is a problem if your adapter would like to throw an exception (should be thrown also by ''XmlAdapter'' implementation), see [[jaxbtracker>780|issue#780]]. The complete file looks like this((More information about JAX-WS/JAXB customization you can find [[https://jax-ws.dev.java.net/jax-ws-20-fcs/docs/customizations.html|here]] and [[http://www.huihoo.org/openweb/java_web_services_tutorial/JAXBusing4.html|here]])): WSDL/XSD location is given as a relative path to JAX-WS/JAXB customization file, e.g. for given file ''jaxws/jaxb-binding.xml'' and XSD location ''wsdl/MyDBService.xsd'' the relative location will be ''../wsdl/MyDBService.xsd''. Also we need to mention it in ''pom.xml'': org.codehaus.mojo jaxws-maven-plugin 1.12 generate-sources wsimport ... jaxws In prior to v1.12 of ''jaxws-maven-plugin'' there was a problem with ''javaType'' customization: if fails to compile XML adapter as it does not include ''XSDateTimeCustomBinder'' class into compilation set. Further customizations involve JAXB2 plugins. These plugins hook into generation process and benefit from possibility to manipulate JAXB model to generate more programmer-friendly code((See [[https://jaxb2-commons.dev.java.net/|here]], [[http://confluence.highsource.org/display/J2B/JAXB2+Basics+Plugins|here]], [[http://confluence.highsource.org/display/J2B/User+Guide|here]] and [[http://ozgwei.blogspot.com/2007/08/generating-developer-friendly-codes.html|here]] for more information about JAXB2 plugins)). Unfortunately, due to [[jaxwsctracker>45|issue#45]] it is not possible to inject JAXB plugins into JAX-WS using [[https://jax-ws-commons.dev.java.net/jaxws-maven-plugin/|jaxws-maven-plugin]]. Also due to some conflicts with build-in Java6 JAXB API, JAXB plugins do not work (see [[http://forums.java.net/jive/thread.jspa?messageID=362952#268266|here]]). As a workaround I have reallocated most popular plugins to ''com.sun.tools.xjc.addon'' package. The complete JAX-WS maven plugin + JAXB plugins with fake version 1.12.3 can be downloaded {{jaxws-maven-plugin-1.12.3.jar|from here}}((The sources of the plugin are available {{jaxws-maven-plugin-1.12.3-sources.jar|here}})) and installed via ''[[http://maven.apache.org/plugins/maven-install-plugin/usage.html|mvn install:install-file]] -Dfile={{jaxws-maven-plugin-1.12.3.jar}} -DpomFile={{jaxws-maven-plugin-1.12.3.pom}} -DgroupId=org.codehaus.mojo -DartifactId=jaxws-maven-plugin -Dversion=1.12.3 -Dpackaging=jar''. The version {{jaxws-maven-plugin-1.12.4.jar|1.12.4}} has ''Xew'' plugin excluded as it was forked into [[github>dmak/jaxb-xew-plugin/|separate project]]. Refer [[github>dmak/jaxb-xew-plugin/blob/master/samples/jaxws-maven-plugin-visitor/pom.xml|that config]] as example. Now you can include it into your ''pom.xml'': org.codehaus.mojo jaxws-maven-plugin 1.12.3 ... -Xxew -instantiate early -delete -Xcommons-lang -Xcommons-lang:ToStringStyle=SHORT_PREFIX_STYLE -Xdefault-value -Xfluent-api -Xvalue-constructor ''-Xcommons-lang'' should be mentioned twice: once to activate the plugin and another time to pass the plugin the actual arguments. After re-running code generation we get better possibilities to code: /* * This class is not compilable after -Xxew is applied. */ /* public static Concept createDummyConcept_Normal() { final Concept concept = new Concept(); final Concept.Types types = new Concept.Types(); concept.setCreator("robot"); concept.setCreationDateTime(new Date()); concept.setTypes(types); types.getType().add(ConceptType.CREATURE); types.getType().add(ConceptType.HUMAN); types.getType().add(ConceptType.WOMAN); return concept; } */ public static Concept createDummyConcept_ListAccess() { final Concept concept = new Concept(); concept.setCreator("computer"); concept.setCreationDateTime(new Date()); // -Xxew demo: concept.getTypes().add(ConceptType.CREATURE); concept.getTypes().add(ConceptType.HUMAN); concept.getTypes().add(ConceptType.WOMAN); return concept; } public static Concept createDummyConcept_Construtor() { // -Xvalue-constructor demo: return new Concept("99", new Date(), "neighbour", Arrays.asList(ConceptType.CREATURE, ConceptType.HUMAN, ConceptType.WOMAN)); } public static Concept createDummyConcept_FluentAPI() { // -Xfluent-api demo: return new Concept().withId("01").withCreationDateTime(new Date()).withCreator("author").withTypes( ConceptType.CREATURE, ConceptType.CHILD); } Further improvement comes from the fact that staring from v2.1.4 of JAX-WS RI it is not necessary to run ''wsgen'' to generate wrapper classes for input/output arguments of webservice methods((See [[http://weblogs.java.net/blog/2008/03/03/alternative-jax-ws-ris-wsgen|here]] the note about how WSDL can be completely dynamically autogenerated)). So all we need is to generate JAXB beans for our datatypes XSD. We will also suppress generation of file headers with timestamp information, as it makes life with source version control easier. That can be done with the help of the following ''pom.xml'': ... commons-lang commons-lang 2.4 org.jvnet.jaxb2.maven2 maven-jaxb2-plugin 0.7.2 generate ${project.build.sourceDirectory} xsd jaxb false false true -no-header -Xxew -instantiate early -delete -Xcommons-lang -Xcommons-lang:ToStringStyle=SHORT_PREFIX_STYLE -Xdefault-value -Xfluent-api -Xvalue-constructor dk.conspicio.jaxb.plugins jaxb-xew-plugin 1.3 org.jvnet.jaxb2_commons.tools.xjc.plugin jaxb-commons-lang-plugin 1.0 org.jvnet.jaxb2_commons.tools.xjc.plugin jaxb-default-value-plugin 1.0 org.jvnet.jaxb2_commons.tools.xjc.plugin jaxb-fluent-api-plugin 2.1.8 org.jvnet.jaxb2_commons.tools.xjc.plugin jaxb-value-constructor-plugin 1.0 commons-lang commons-lang 2.4 maven2-repository.dev.java.net http://download.java.net/maven/2/ Notes: * Due to [[jaxbtracker>697|issue#697]] ''commons-lang'' should be mentioned in '''' as well as in '''' section. * All JAXB plugins are not available in Maven public replository, so you have to install them manually using fake ''groupId'' and ''artifactId''. * [[http://www.conspicio.dk/blog/bjarne/jaxb-xmlelementwrapper-plugin|jaxb-xew-plugin]]((This plugin is not supported anymore, consider migrating to ''[[github>dmak/jaxb-xew-plugin/|jaxb-xew-plugin]]'')) should be triggered first, as it changes the class model. Otherwise you'll get compilation problems in methods generated before it removes the unnecessary classes. ===== Deployment to tomcat server ===== First, let's create deployment descriptors. For client, ''web.xml'' is empty (using default JSP servlet) and for server we need a special servlet, that will handle requests for services and route them to corresponding service endpoint. So the files to be placed to ''webapp/WEB-INF'' folder are: com.sun.xml.ws.transport.http.servlet.WSServletContextListener WSServlet com.sun.xml.ws.transport.http.servlet.WSServlet 1 WSServlet /my-service 60 And now we have to include ''jaxws-rt'' into our package, that actually holds the servlet and also tune ''maven-war-plugin'' to get ''WEB-INF'' resources from ''webapp'' folder: org.mycompany.db-service my-db-service war 0.1-SNAPSHOT ... ${project.groupId} my-db-common 0.1-SNAPSHOT com.sun.xml.ws jaxws-rt 2.1.4 runtime src maven-war-plugin webapp ... Refer [[software:maven#how_to_deploy_to_remote_tomcat|How to deploy to remote Tomcat]] concerning how to deploy our Web Service and client to remote Tomcat / JBoss. ===== Implementing a JAX-WS service using [[https://jax-ws-commons.dev.java.net/spring/|Spring JAX-WS]] ===== This approach is relatively simple to implement: * You have to mark your bean as ''@WebService'' as described above. * You add ''jaxws-spring'' dependency instead of ''jaxws-ri''((Due to [[jaxwsctracker>47|issue#47]] the dependant artefacts of ''jaxws-spring'' may not be correctly resolved)): org.jvnet.jax-ws-commons.spring jaxws-spring 1.8 runtime * You pass the location of your Spring Web context via servlet context attribute ''contextConfigLocation'' and use ''WSSpringServlet'' to handle requests. * Servlet URL and binding URL should be the same org.springframework.web.context.ContextLoaderListener contextConfigLocation /WEB-INF/application.context.xml jaxws-servlet com.sun.xml.ws.transport.http.servlet.WSSpringServlet 1 jaxws-servlet /test-ws ... If you wish to use ''WSSpringServlet'' in more complicated configurations (e.g. when the service is wrapped into AOP proxy to support transactions) first head [[http://code-hut.blogspot.com/2008/03/exposing-spring-beans-as-web-services.html|this note]]. ===== Links ===== * The complete projects can be downloaded from {{my-service.zip|here}} and {{my-service-jaxb.zip|here}} * [[http://blog.vinodsingh.com/2008/09/building-jax-ws-web-service.html|Building JAX-WS web service]] * [[http://java.sun.com/mailers/techtips/enterprise/2008/TechTips_Jan08.html|Using JAX-WS with Maven]] * [[http://jbossws.jboss.org/mediawiki/index.php?title=Quick_Start|JBossWS -- Quick Start]] * [[http://ws.apache.org/axis2/1_5_1/jaxws-guide.html|Apache Axis2 JAX-WS Guide]] * [[http://www.fransvanbuul.net/?p=98|JAX-WS / wsimport problems]] by Cavia Porcellus enumerates all current problems with the technology and offers the alternaive solution: perform bean generation with ''maven-antrun-plugin''. * [[https://www.mkyong.com/webservices/jax-ws/jax-ws-hello-world-example/|JAX-WS Hello World Example with Java6]] * [[http://community.jboss.org/wiki/JBWSFAQ|JBoss WS FAQ]] * [[http://community.jboss.org/wiki/JBossWSStackComparison|How does JBossWS compare to other SOAP stacks]] -- the comparison matrix of Axis, Celtix, Glue, JBoss, XFire and GlassFish. * [[http://community.jboss.org/wiki/JBossWS-JAX-WSTools|JAX-WS Tools for JBoss]] -- creating and consuming JBoss WebServices. * [[http://singztechmusings.wordpress.com/category/web-services/apache-cxf/|Set of technical articles on Apache CXF]] (How to add custom HTTP headers to a web service request? How to add custom SOAP message headers to a web service request? Apache CXF-based Java Web Services: How to log/print the payload XML of incoming SOAP request using ''LoggingInInterceptor''?) {{tag>SOAP WebService Spring JBoss Maven CXF JWT JOSE}}