#Required not working in spring? - java

Am I using this in a wrong way, or #Required has no effect?
Here I annotated bean Non setter with #Required, but app starts normally and prints "null".
I would expect app to report an error and not start properly.
#SpringBootApplication
public class ReqNotWorking {
public static void main(String[] args) {
SpringApplication.run(ReqNotWorking.class, args);
}
class Non{
private String req;
public String getReq() {
return req;
}
#Required
public void setReq(String req) {
this.req = req;
}
}
#Bean
public Non non(){
Non non = new Non();
//non.setReq("error");
return non;
}
#Bean
public CommandLineRunner commandLineRunner(Non non){
return args -> {
System.out.println(non.getReq());
};
}
}

You're instantiating the class Non via the new keyword so Spring has no way to apply its post-processors (RequiredAnnotationBeanPostProcessor, specifically) and verify the value of the field.
As a side-note, the #Required annotation probably doesn't make sense in a Java-configured Spring application, where all beans are implicitly required, unless otherwise noted.
You didn't describe where the "req" string is supposed to come from, but if it's available at startup time, you could leverage the #Value mechanism:
#Component
static class Non {
#Value("${req.value}")
private String req;
public String getReq() {
return req;
}
}

Related

Spring Rest Set Custom Object in Context

I am using Spring Rest for creating Rest APIs. For authentication, I have created a filter that extends from OncePerRequestFilter, this filter check if a valid token is present in the header. I have to set some custom object information in Spring context. So that I can retrieve it in my Controller classes. Something like:
AuthenticationFilter extends OncePerRequestFilter {
// validation goes here
requestContext.setSecurityContext(new SecurityContext() {
public Principal getUserPrincipal() {
return new UserInfo("", token, userID, userType);
}
}
});
Don't create custom filters for authentications. User security context instead it's pretty powerful.
I don't know about any option how put custom object into spring context. But I can show you how to create your custom context in spring framework and then use it as any other context. Of course you will be able to put custom objects into it.
PSEUDO CODE:
1.)
Create custom object which you want to have in context:
public class UserInfo implements Serializable {
private String id;
private String email;
//Getters and setters
}
2.)
Create custom context:
#Service
public class UserInfoContext {
private static final ThreadLocal<UserInfo> userInfoThreadLocal = new InheritableThreadLocal<>();
public void setUserInfo(UserInfo userInfo) {
UserInfoContext.userInfoThreadLocal.set(userInfo);
}
public UserInfo getUserInfo() {
return UserInfoContext.userInfoThreadLocal.get();
}
public void clearContext() {
UserInfoContext.userInfoThreadLocal.remove();
}
}
3.)
Create custom interceptor for initializing custom object inside custom context
#Service
public class UserInfoInterceptor implements HandlerInterceptor {
private UserInfoContext userInfoContext;
#Autowired
public UserInfoInterceptor(UserInfoContext userInfoContext) {
this.userInfoContext = userInfoContext;
}
#Override
public boolean preHandle(HttpServletRequest requestServlet, HttpServletResponse responseServlet, Object handler) throws Exception {
// Call userInfoContext.setUserInfo() with custom data.
return true;
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception {
userInfoContext.clearContext();
}
}
Thanks to ThreadLocal you are able to save custom information about current user.

Creating and injecting a per request scoped variable

I would like to have a variable that follows along the full lifecycle of a request in java EE.
For example it could be for a logging function, so that I can filter all log entries by request.
The key part that I want to get at is that it must be relatively easy to implement in an already existing application so if possible some sort of dependency injection that gets the variable related to the specific request.
I've tried injectiong a #RequestScoped variable, but it doesn't work since it is only scoped to the container. I would need to be able to inject the same object to different containers. Is this at all possible?
EDIT: I want something along the lines of this:
#RequestScoped
public class RequestVariables {
public String id;
}
#Stateless
public class Logger {
#Inject
private RequestVariables requestVariables;
public void log(String message) {
System.out.println(requestVariables.id + ":" + message);
}
}
#Stateless
public class Service {
#Inject
private Logger logger;
#Inject
private RequestVariables requestVariables;
public void save(String data) {
logger.log("Save");
session.save(data + requestVariables.id); //Maybe add request parameter to save aswell
}
}
public class API {
#Inject
private Service service;
#Inject
private Logger logger;
#Inject
private RequestVariables requestVariables;
#Path("/1")
#GET
public Response get(#QueryParam("data") String data) {
requestVariables.id = UUID.randomUUID().toString()
service.save(data);
logger.log("Get");
return Response.status(204).build();
}
}
Currently this is what I have experimented with:
#RequestScoped
public class RequestScope {
private int test = 0;
public RequestScope(int test) {
this.test = test;
}
public RequestScope(){}
public int getTest() {
return test;
}
public void setTest(int test) {
this.test = test;
}
}
#Provider
public class RequestScopeFilter implements ContainerRequestFilter {
#Inject
private javax.inject.Provider<RequestScope> requestScopeProvider;
#Context
private HttpServletRequest request;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestScopeProvider.get().setTest(42);
request.setAttribute("test", "superTest");
}
}
#Stateless
#TransactionManagement(TransactionManagementType.BEAN)
#TransactionAttribute(value=TransactionAttributeType.NOT_SUPPORTED)
public class Service {
#Context
private HttpServletRequest httpServletRequest;
#Inject
private Provider<RequestScope> requestScopeProvider;
public void test() {
RequestScope scope = requestScopeProvider.get();
String test = (String)httpServletRequest.getAttribute("test");
}
}
So when I get the scope from my service then it is a new object with test set to 0, and then it throws an NPE since httpServletRequest is null
option #1
Implement an Interceptor and set the request id as HttpServletRequest attribute:
#AroundInvoke
public Object setRequestId(InvocationContext ic) throws Exception {
HttpServletRequest request = [..] // getHttpServletRequest(ic);
request.setAttribute("request-id", UUID.randomUUID().toString());
return ic.proceed();
}
Then use HttpServletRequest everywhere you need it
#Context
private HttpServletRequest httpRequest;
option #2
If want just to filter your logs by an unique id, you can configure your Logger to print the thread name: [%t]
Example: Log4j PatternLayout
option #3
Use a custom java bean to encapsulate the request data (query param, request id etc.) and pass this bean across your application services.
public class API {
#Inject
private Service service;
#Path("/1")
#GET
public Response get(MyCustomRequestBean data) {
service.doSomejob(data);
return Response.status(204).build();
}
}
Set the request id and query param in ParamConverter:
Jax-RS ParamConverter - ParamConverterProvider method return type mismatch
You can inject a provider in your service:
#Inject
Provider<RequestVariables> vars
And then call get () to get the instance. If you try to get () in a thread outside a request scope context you'll get an exception. I would however try to structure in a way that would not allow this to happen
A solution that I found is to use ThreadLocal variables. It seems rather dirty, but it works since each request is executed on it's own thread(as far as I am aware). So this is what I got:
public class RequestScope {
private static final ThreadLocal<String> id = ThreadLocal.withInitial(() -> UUID.randomUUID().toString());
public static String get() {
return id.get();
}
}
With that I can also easily exchange the ThreadLocal to return something more specific if so desired.
And I can get the variables from pretty much anywhere, assuming that the request is not starting a different thread

SPRING #RestController: Passing Cookie to a Service from Every Method

I have a #RestController in which every method needs to pass a (WebSSO) cookie down to a service. The service in turn uses the cookie for authentication. I am autowiring the service bean in controller. The service has a setter setCredentials(String webSSOCookie). One easy way is to call this setter in every method. I would like to do it better way; for instance using HandlerInterceptor. However the HandlerInterceptor does not have access to the controller (and hence its members) - am I right?
In jersey I could use filter. How do I achieve it in SPRING?
#RestController
#RequestMapping("/documents")
public class ECMRestController {
#Autowired
public ECMService ecmService;
#RequestMapping(value="/{documentId}", method=RequestMethod.DELETE)
public void deleteDocument(#RequestParam("documentId") String documentId) throws IllegalArgumentException, HttpClientErrorException {
// I could get and pass the cookie to ecmService in every method.
// ecmService.setCredentials(webSSOCookieObtainedfromRequest);
// However I don't want to do it that way.
ecmService.deleteDocument(documentId);
}
// Other REST Methods that need to pass the cookie in the same way.
}
You can request the SecurityContextHolder to query the current Authentication that you had customized in a filter.
MyCustomAuth auth = (MyCustomAuth) SecurityContextHolder.getContext().getAuthentication();
auth.getCookie();
or you can just use ThreadLocal in a context that can be retrieve from anywhere:
public class CookieContext {
private static final ThreadLocal<Cookie> COOKIE = new ThreadLocal<>();
private static final CookieContext INSTANCE = new CookieContext();
public void setCookie(Cookie value) {
COOKIE.set(value);
}
public Cookie getCookie() {
return COOKIE.get();
}
public static CookieContext getContext() {
return INSTANCE;
}
}
public class CookieInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
CookieContext context = CookieContext.getContext();
context.setCookie(request.getCookies()[0]);
}
}

Error: Ambiguous handler methods mapped for HTTP path in spring data rest

this is the code:
#Import(Appconfig.class)
#RestController
//#RequestMapping("/api/destination/find")
public class RestApi01_Controller {
#Autowired
public CountryRepo Country_Repository;
#Autowired
public CityRepo City_Repository;
#Autowired
public AirportRepo Airport_Repository;
#Autowired
public ResortRepo Resort_Repository;
#RequestMapping(value="/api/destination/find/{city}", method=RequestMethod.GET)
public List<Master_City> getCity(#PathVariable String city) {
return City_Repository.findByCityLikeIgnoreCase(city);
}
#RequestMapping(value="/api/destination/find/{airportname}", method=RequestMethod.GET)
public List<Master_Airport> getAirportname(#PathVariable String airportname) {
return Airport_Repository.findByAirportnameLikeIgnoreCase(airportname);
}
#RequestMapping(value="/api/destination/find/{resortname}", method=RequestMethod.GET)
public List<Master_Resort> getResortname(#PathVariable String resortname) {
return ResortRepository.findByResortnameLikeIgnoreCase(resortname);
}
}
Your REST Endpoints are pointing basically to the same URI pattern.
Taking the first two as an example,
/api/destination/find/{city}
and
/api/destination/find/{airportname}
Since {city} and {airportname} are placeholders, and both the URI are HTTP GET methods, Spring is not able to make out which API to be used when a call to say /api/destination/find/london.
Instead of this, use request parameters. Something like this
/api/destination/find?airport={airportname}
and
/api/destination/find?city={city}

How to mock a SecurityContext

Endpoints with Jersey.
I want to secure an endpoint with a ContainerRequestFilter
#Provider
#Secured
public class AuthorizationRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
final SecurityContext securityContext =
requestContext.getSecurityContext();
//TODO: on logger here...
System.out.printf("Filtering %s request... AuthorizationRequestFilter\n", requestContext.getMethod());
requestContext.getHeaders().add("X-Secured-By", "Jersey >_<");
System.out.printf("SecurityContext: %s (%s).\n", securityContext, securityContext.getAuthenticationScheme());
if (securityContext == null || !securityContext.isUserInRole("privileged")) {
requestContext.abortWith(new UnauthorizedResponse().getResponse());
}
}
}
The annotation #Secured:
#NameBinding
#Retention(RetentionPolicy.RUNTIME)
public #interface Secured {}
So I can do this:
#Path("foobar")
public class FooResource {
//...
#Context
SecurityContext securityContext;
//...
#GET
#Secured
#Path(value = "foo")
#Produces(MediaType.APPLICATION_JSON)
public Response getFoo(#Context SecurityContext sc, #Context UriInfo ui, #Context HttpHeaders hh) {
// ...
}
//...
And I'm doing it right (I think), because with my test I don't even pass through the getFoo endpoint but is the ContainerRequestFilter that kicks me out. In fact I receive this (the "X-Secured-By" header is hand-made):
Headers: {X-Secured-By=[Jersey >_< kicked you out!], Content-Length=[97], Date=[Wed, 03 Dec 2014 17:46:50 GMT], Content-Type=[application/json], X-Powered-By=[Jersey ^_^]}
Response: InboundJaxrsResponse{ClientResponse{method=GET, uri=http://localhost:9998/urler/test, status=401, reason=Unauthorized}}
Now it would be nice to mock the SecurityContext.
This is what I'm doing... and if I'm here, it's obviously silly and/or wrong.
public class UrlerResourceTest extends JerseyTest {
//....
#Override
public TestContainerFactory getTestContainerFactory() {
GrizzlyTestContainerFactory grizzlyTestContainerFactory = new GrizzlyTestContainerFactory();
System.out.printf("The GrizzlyTestContainerFactory: %s ", grizzlyTestContainerFactory);
// just for debugging...
return grizzlyTestContainerFactory;
}
#Test
public void testSecuredEndpoint() throws JSONException {
SecurityContext securityContext = Mockito.mock(SecurityContext.class);
Mockito.when(securityContext.isUserInRole(anyString())).thenReturn(true);
Mockito.when(securityContext.getAuthenticationScheme()).thenReturn("Just Mocking...");
ReflectionTestUtils.setField(resource, "securityContext", securityContext, SecurityContext.class);
final Response response = target("foobar")
.path("foo")
.request(MediaType.APPLICATION_JSON)
.get();
System.out.println(getFormattedStringResponseInfo(response));
JSONObject entity = new JSONObject(response.readEntity(String.class));
assertTrue(entity.get("secured").equals(true));
assertTrue(response.getHeaders().containsKey("X-Secured-By"));
assertEquals(Status.OK.getStatusCode(), response.getStatus());
}
How can I mock the SecurityContext in my tests?
Thank you so much in advance.
Disclaimer: I'm not really a Mockito user, but from what I understand, mocking is used for situations where you have injected class dependencies (fields), and you mock those dependencies. In which case you still need to set the field with the mocked object. For example
public class TestClass {
TestService testService;
public void doTest() {
System.out.println(testService.getString());
}
public void setTestService(TestService testService) {
this.testService = testService;
}
}
public class TestService {
public String getString() {
return "Hello world";
}
}
#Test
public void toTest() {
TestService testService = Mockito.mock(TestService.class);
Mockito.when(testService.getString()).thenReturn("Hello Squirrel");
TestClass testClass = new TestClass();
testClass.setTestService(testService);
testClass.doTest();
}
You can see we are setting the the TestService in the TestClass with the mocked object. It's not greatest example, as we could simple instantiate TestService, but it shows, from my understanding, how the mocking should work.
That being said, I don't see how it is possible to do this with the AuthorizationRequestFilter, as it's handled by the test container, and we are not instantiating it for a unit test. Even if we were, it would seem intrusive (and redundant) to add a SecurityContext field.
So without a full integration test, where we are starting the server, and using the server's authentication capabilities, it will be difficult to handle the SecurityContext per this use case, as the SecurityContext is created by the container, taking information from the underlying servlet containers authentication mechanism.
One way you can achieve this though (which IMO doesn't seem very elegant - but works), without a full integration test, is to create a a filter which performs before your AuthorizationRequestFilter, and set the SecurityContext from there. Testing aside, this is actually pretty common in cases where we need to implement outr own custom authentication mechanism.
An example of how you might do this for your unit test, might be something like:
public class UrlerResourceTest extends JerseyTest {
...
#Override
public Application configure() {
return new ResourceConfig(FooResource.class)
.register(AuthorizationRequestFilter.class)
.register(AuthenticationFilter.class);
}
#Provider
#Priority(Priorities.AUTHENTICATION)
public static class AuthenticationFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setSecurityContext(new SecurityContext() {
#Override
public Principal getUserPrincipal() {
return new Principal() {
#Override
public String getName() {
return "Stackoverflow";
}
};
}
#Override
public boolean isUserInRole(String string) {
return "privileged".equals(string);
}
#Override
public boolean isSecure() { return true; }
#Override
public String getAuthenticationScheme() { return "BASIC"; }
});
}
}
...
}
This filter will perform before the AuthorizationRequestFilter because of the #Priority annotation. We've set it to Priorities.AUTHENTICATION which will before before any other filter without such annotation. (See Priorities API and Priorities with Jersey. Also the SecurityContext will be passed along between filters and also be injected into your resource class.
As I said, I don't think this is very elegant to have to create another filter, but it works for this purpose. Also I am not too familiar with the Jersey Test Framework, as I'm still beginning with it, but there are many configuration options for deployment within a servlet context. I don't know if we can configure the needed authentication mechanism for this case, but it might be something worth looking into.
Edit: In the beginning I explained about setting the field for the test object, but we can also pass the mocked object to a method. For example we could mock the ContainterRequestContext in the filter method, and call filter ourselves, passing the mocked ContainerRequestContext. But this is only useful when we are actually unit testing the filter class and instantiating it ourselves, which is not the case here.

Categories