I am currently working on a quite modular application where we have many jars that will be packaged and glued together in a war file.
Some of these jar files have REST resources that want to be secured. Common way is the #RolesAllowed annotation etc.
From my current knowledge this implies an existing web.xml in the WAR. This way we would have to implement jar-specific information (e.g. context roots) inside the WAR, not in the place where it belongs.
Like the most things nowadays - is there a way to programmatically set up security contexts etc. without a web.xml?
You can restrict access to your REST resources by registering RolesAllowedDynamicFeature in your REST configuration class that extends from ResourceConfig
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
super(ApplicationConfig.class);
register(RolesAllowedDynamicFeature.class);
}
}
So you can use your application roles on your resources methods like this
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.SecurityContext;
#Path("secured")
#PermitAll
public class SecuredResource {
#GET
#Path("user")
#RolesAllowed("user")
public String user(#Context SecurityContext sc) {
boolean test = sc.isUserInRole("user");
return (test) ? "true": "false";
}
#GET
#Path("admin")
#RolesAllowed("admin")
public String admin(#Context SecurityContext sc) {
boolean test = sc.isUserInRole("admin");
return (test) ? "true": "false";
}
}
Jersey documentation has more details on securing REST resources using annotations here
https://jersey.github.io/documentation/latest/security.html#d0e12428
I've not worked with JAX-RS for a while, but the last time I checked, when using annotation-based security, web.xml is not optional.
See my answer for details.
https://stackoverflow.com/a/20023018/839733
Related
A web application I've been working on recently the past like 2 weeks maybe for whatever reason when I finally tested it - won't seem to even enter the method that I have to return a JSON list of objects. I have included the Jackson library and Spring Boot Web, Tomcat, Data-JPA, Hibernate, MySQL, and a library to allow me to access JSP files. The index.jsp comes up but I almost feel like Spring Boot is giving me that free of charge as it's not even entering that method. I have been having the issue for a few days but trying to resolve it on my own - I found another answer that suggested to put a breakpoint inside one of the Spring classes but when I "debugged" it through Eclipse, it didn't even stop at that class - something about pattern matching - One answer suggested adding a context to the application.properties file - didn't help. I've reduced it to as simple as I think I can get it. Can anyone tell me what I might be doing wrong? Before my code, the project is on Github at: https://github.com/sfulmer/Scheduler.git
Here's my controller:
package net.draconia.schedule.controllers;
import java.util.List;
import net.draconia.schedule.beans.Event;
import net.draconia.schedule.dao.EventDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class ScheduleController
{
private static final Logger logger = LoggerFactory.getLogger(ScheduleController.class);
#Autowired
private EventDAO mObjDAO;
protected EventDAO getDAO()
{
return(mObjDAO);
}
//#GetMapping("/events")
#RequestMapping(value = "events", method = RequestMethod.GET)
public #ResponseBody List<Event> getEvents()
{
logger.debug("I got here");
return(getDAO().getList());
}
#GetMapping("/")
public String index()
{
return("index");
}
}
Here is the DAO interface - I'll show the class if necessary but this is what the controller looks at:
package net.draconia.schedule.dao;
import java.util.List;
import javax.persistence.EntityNotFoundException;
import net.draconia.schedule.beans.Event;
public interface EventDAO
{
public Event getEventById(final long lId) throws EntityNotFoundException;
public List<Event> getList();
public void remove(final Event objEvent);
public void removeById(final long lId);
public Event save(final Event objEvent);
}
The Event class is so long but if I need to include it, I will. The application.properties file is here:
spring.datasource.url = jdbc:mysql://localhost:3306/schedule
spring.datasource.username = root
spring.datasource.password = R3g1n# M1lL$ 1$ My Qu3eN!
spring.mvc.view.prefix: /WEB-INF/jsp/
spring.mvc.view.suffix: .jsp
server.servlet.contextPath=/scheduler
and here is my Application class(with the SpringBootApplication annotation):
package net.draconia.schedule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#SpringBootApplication(scanBasePackages = {"net.draconia.schedule.controller"})
public class ScheduleApp implements WebMvcConfigurer
{
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder)
{
return(builder.sources(ScheduleApp.class));
}
public static void main(String[] args)
{
SpringApplication.run(ScheduleApp.class, args);
}
}
I'm relatively new to Spring Boot but haven't ever ran into this problem ever before as I work with it at work and it works fine but we use entirely REST services there and I am using JSP files as well as sorta end-points that respond with JSON but you can't respond from REST services with JSP views so unfortunately I can't copy work's project to get that working or I would sigh Any thoughts on how I can get this working or what I am omitting?
My guess is that you're mixing things from Spring and Spring boot, and that's getting problems on loading beans, as you're probably changing the annotations load order or loading other beans rather than spring boot defaults as expected. For example, you implements WebMvcConfigurer, but you aren't providing any WebMvc Configuration, like a ViewResolver bean
My advice is to follow this guide: https://spring.io/guides/gs/spring-boot/
and use only the annotations from spring boot if using spring boot, or spring if using spring (they're similar, but not exactly the same, configuration is different).
Anyways, you can check loaded beans in Spring application context (Inject it in Application class) with ctx.getBeanDefinitionNames() method and see if your controller is there (i guess not).
By looking into code, my first impression is that, you have some typo in here:
#SpringBootApplication(scanBasePackages = {"net.draconia.schedule.controller"})
Your controller class package name has net.draconia.schedule.controllers.
So can you please correct your scanBasePackages with proper package name.
If that is not the case, please update full stack trace along with GET request which you are submitting into application. Will take a look & update answer accordingly.
I followed a few tutorials for settings up a very simple Jersey Webservice which were more than unclear to me.
Sometimes the tutorial was talking about changing the web.xml, others were saying that it is only necessary to have certain annotations in your service class.
So I ended up with the following conclusion:
Using Jersey 2.x you won't have to do anything specific in your web.xml, just have the jersey-container-servlet.jar in your classpath and create your service class as follows:
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
#ApplicationPath("rest")
public class RestService extends Application {
#GET
#Path("/sayhello")
#Produces(MediaType.TEXT_PLAIN)
public Response sayHello() {
return Response.ok("Hello World").build();
}
}
This should allow me to access the API using http://localhost:8080/EETest/rest/sayhello
I double checked that the project has been deployed with no error and the Tomcat7 server is running. All the jersey jars and dependencies are in my lib folder and have been added to the project libraries. The modified index.html is showing fine when calling http://localhost:8080/EETest
But the web service is not responding (404 page is showing instead).
I know it must be something very basic I'm doing wrong here ... I'm running out of options.
EDIT: This is my web.xml for what it's worth
<?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" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>EETest</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
Not sure where you learned to do this
#ApplicationPath("rest")
public class RestService extends Application {
#GET
#Path("/sayhello")
#Produces(MediaType.TEXT_PLAIN)
public Response sayHello() {
return Response.ok("Hello World").build();
}
}
But it's wrong. In JAX-RS, we have resource classes. The resource class should be annotated with #Path, not #ApplicationPath. The Latter is for the application configuration class. So you should have something like
#ApplicationPath("/rest")
public class AppConfig extends Application {}
#Path("/")
public class RestService {
#GET
#Path("/sayhello")
#Produces(MediaType.TEXT_PLAIN)
public Response sayHello() {
return Response.ok("Hello World").build();
}
}
What the empty class with the #ApplicationPath does is trigger classpath scanning. So the classpath will be scanned for classes that are annotated with #Path and #Provider, and those classes will be registered.
In the example, I used #Path("/") so that you can still use the same URL /rest/sayHello. But generally, the resource class will be have a path mapped to a collection URL, like /rest/animals, so you would use #Path("animals") on the class, and you can add sub-resources with methods in that class also annotated with #Path. Any method not annotated with #Path, but has a method like #GET, will be mapped to the root resource path /rest/animals.
A couple other things. Remember I mentioned the classpath scanning being triggered with the empty Application class annotated with #ApplicationPath. Well this is kind of discouraged. You could register classes explicitly instead
#ApplicationPath("/rest")
public class AppConfig extends Application {
#Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<>();
classes.add(RestService.class);
return classes;
}
}
But when we're using Jersey, it's preferred to use the Jersey specific classes (unless you have a requirement to keep it portable between JAX-RS implementations). With Jersey, you would use it's ResourceConfig class (which is a subclass of Application). You could register individual classes with the register method, of you can trigger package scanning (which is not the same as classpath scanning) using the packages method
#ApplicationPath("/rest")
public class AppConfig extends ResourceConfig {
public AppConfig() {
packages("the.packages.to.scan");
register(RestService.class);
}
}
i have used the jersey implementation of a jaxrs but iam unable to the programme following is case where i am getting problem any idea help me
in following programme i used the jersy 2.x implementaion of jaxrs
i implemented the programme using jersey implemetation of jax-rs(restfull)
2 classes i have written instead of web.xml i used the class
MyResource.java
package com.rest.application;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import com.rest.webservice.SampleService;
#ApplicationPath("rest")
public class MyResource {
private Set s;
public MyResource() {
s=new HashSet();
s.add(new SampleService());
}
public Set getSingletons() {
return s;
}
}
SampleService.java
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
#Path("sample")
public class SampleService {
#GET
#Produces("text/html")
#Path("{username}")
public String sayHello(#PathParam("username")String s) {
return "<font color='blue' size=8>Hello:" +s+ "</font>";
}
#GET
#Produces("text/plain")
public String sayBye() {
return "Bye";
}
}
i added the all jars needed to this programm
still i am getting following error
java.util.concurrent.ExecutionException:
org.apache.catalina.LifecycleException:
Failed to start component [StandardEngine[Catalina].
StandardHost[localhost].StandardContext[/RestApp2]]
Caused by: org.apache.catalina.LifecycleException:
Failed to start component [StandardEngine[Catalina].
StandardHost[localhost].StandardContext[/RestApp2]]
like this same error is displaying everywhere
when i changed the server tomcat 7 to 6
it is working but not displaying the output
will anybody have any idea thanking you in advance
This says, #ApplicationPath("rest") may be applied only to the subclass of Application.
Can you share more on what are you trying to do and what is the complete stack trace. Are you using web.xml ?
As #MSD mentioned, your use of #ApplicationPath is incorrect. See the Jersey documentation on Application Deployment to see all the different deployment options, and how they work in different environments.
Basically, the easiest way put the #ApplicationPath on an empty Application class
#ApplicationPath("/rest")
public class MyApplication extends Application {}
This will scan the entire classpath for #Provider and #Path annotated classes, to register with the application. Though this may seem easier, the more common approach, when working with Jersey is to use it's ResourceConfig class, which is a subclass of Application. You can register packages, which will scan the packages and subpackages
#ApplicationPath("/rest")
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("com.my.packages");
}
}
One benefit is that sometimes there will be third party dependencies that are annotated, but you don't want registered. To register individual classes just use register(...class) in the ResourceConfig.
Now the reason for the error in Tomcat 7 and not 6, is most likely because Tomcat 6 (servlet 2.5) does not have the sevlet pluggability mechanism, which uses the ServletContainerInitializer. The Jersey implementation of this initializer loads the application, looking for the #ApplicationPath on the Application subclass. If you're not in a 3.0 environment, this functionality will not work.
Note the Jersey initializer is included in the jersey-container-servlet jar. You can read more about it here
I've been trying to create a simple Restful WebService, using NetBeans Ide.
My Java EE Version is: Java EE 7 Web.
I created a new Java Web Application, setting this ContexPath: /DukesAgeService.
Now, running my application, browser display my Index.html page at:
http://localhost:8080/DukesAgeService/
so, everything works fine.
Then, I tried to create a simple restful resource using the RESTful Web Service Wizard.
So, I created this class:
package firstcup.webservice;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PUT;
/**
* REST Web Service
*
* #author nolanof
*/
#Path("dukesAge")
public class DukesAgeResource {
#Context
private UriInfo context;
/**
* Creates a new instance of DukesAgeResource
*/
public DukesAgeResource() {
}
/**
* Retrieves representation of an instance of firstcup.webservice.DukesAgeResource
* #return an instance of java.lang.String
*/
#GET
#Produces("text/plain")
public String getText() {
return "hello world";
}
}
But running my application, at url:
http://localhost:8080/DukesAgeService/dukesAge
I get a 404-not found page.
I exptected that any incoming get request that has the url of "/dukesAge" was handled by DukesAgeResource class getText method. Whats' wrong?
Thanks
You're probably missing the JAX-RS application servlet. You can either define it in the web.xml or if you want to go xml-less, you can use an Application subclass. The easiest way IMO is just to use the Application subclass annotated with #ApplicationPath. A servlet will be created and the servlet path will be set to the value in the annotation. Something like
#ApplicationPath("/rest")
public class RestApplication extends Application {
// All request scoped resources and providers
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(DukesAgeResource.class);
return classes;
}
// all singleton resources and providers
#Override
public Set<Object> getSingletons() {
Set<Object> singletons = new HashSet<>();
return singletons;
}
}
Then the resource should be accessed via
http://localhost:8080/DukesAgeService/rest/dukesAge.
There are other ways, but this is the portable way. Glassfish uses Jersey, but creating a Java EE web application from scratch in Netbeans will only import compile time Java EE standard classes (no Jersey dependencies). So the above is really your best bet to start off with.
You can see other deployment options at the Jersey Documentation. For some of the options, you may need to add some Jersey compile-time dependencies. That's why I just mentioned the above. No other jars needed.
Another thing that would cause a 404, is if you specify the JAX-RS servlet path as /*. This will conflict with the default servlet that serves the static resources like your html pages. That's why I set it to /rest.
UPDATE
It is also stated in the JAX-RS spec that if there are empty sets returned in the getClasses() and getSingletons(), implicit classpath scanning should occur. (provider) Classes annotated withe #Provider will by default be added as singletons and resource classes annotated with #Path will be per-request objects (meaning a new object is created each request). So you could alternatively just have
#ApplicationPath("/rest")
public class RestApplication extends Application {
// Left empty
}
and it should work just the same.
You may have initialized some path in your web.xml, probably that is why you are getting a 404 error while you call the service. Do check your web.xml and in case it is set to anything rather then * then please append that to your service call to get it working.
Currently, I do something similar to
import javax.annotation.Nonnull;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
#ApplicationPath("oauth")
public class OAuthApplication extends Application {
final Set<Class<?>> classes = new HashSet<>();
#Nonnull
#Override
public Set<Class<?>> getClasses() {
classes.add(RegisterResource.class);
return Collections.unmodifiableSet(classes);
}
}
No if I add ten new Resources on the ApplicationPath, I need to do
classes.add(<ClassName>.class);
ten times, it is tedious and sometimes forgetful as well.
Does JAX-RS or RESTEasy provide the way so that I can mention the package name and classes are scanned under it?
I know Jersey has something as
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("org.foo.rest;org.bar.rest");
}
}
Reference
Any thoughts/ideas?
UPDATE
Seems we can do following in web.xml
<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
Is there a specific Java equivalent?
"Is there a specific Java equivalent?"
Simply leave the class empty, meaning do not override getClasses() or getSingletons(). Per the spec - 2.3.2:
[...]
In either of the latter two cases, if both Application.getClasses and Application.getSingletons return an empty list then all root resource classes and providers packaged in the web application MUST be included in the published JAX-RS application. If either getClasses or getSingletons return a non-empty list then only those classes or singletons returned MUST be included in the published JAX-RS application.
So you can simply do
#ApplicationPath("oauth")
public class OAuthApplication extends Application {}
and your classpath will get scanned for #Path and #Provider classes. Override either method (returning a non-empty set), and only those classes will be added.
It should also be noted that public Map<String, Object> getProperties() can be safely overridden. You can use this to add arbitrary properties (even to register classes/features), as seen here, or if you need to configure a provider, you can do so in a Feature or DynamicFeature as seen here
Is a combination of all Application class, ResourceClass and #ApplicationPath
in agreement with the docs and the class ResourceConfig implements by jersey the way to do is:
#ApplicationPath(value = "/resource")
public class ApplicationREST extends ResourceConfig {
public ApplicationREST() {
//add single resources
register(SimpleResource1.class);
register(SimpleResource2.class);
...
//enable multipar feature
register(MultiPartFeature.class);
...
//enable scan package and recursive
packages(true, "com.example.rest");
}
}
I hope this helps you
It is very simple:
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("/webapi")
public class MyApp extends Application {
}
This will scan all the classes annotated with #Provider and #Path.