Micronaut all routes forbidden - java

I created an app in Micronaut using JWT tokens for security
mn create-app --features=security-jwt,data-jdbc,reactor,graalvm example.micronaut.micronautguide --build=gradle --lang=java
And now all my routes are forbidden. How to exclude certain routes (ie. login) from JWT token checking. I tried both without annotation and with annotation IS_ANONYMOUS
package logfetcher;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Produces;
import io.micronaut.http.annotation.Produces;
import io.micronaut.security.annotation.Secured;
import io.micronaut.security.rules.SecurityRule;
#Controller("/log")
public class LogFethcerContoller
{
#Get
#Secured(
SecurityRule.IS_ANONYMOUS )
#Produces(MediaType.TEXT_PLAIN)
public String index() {
return "Hello World";
}
#Get("log")
public String index1() {
return "Hello World";
}
}
I get 401 on both routes.
How can I have routes that do not need the JWT token.

I get 401 on both routes.
From micronaut-projects.github.io/micronaut-security/3.2.0/guide/#gettingStarted...
By default Micronaut returns HTTP Status Unauthorized (401) for any
endpoint invocation. Routes must be explicitly allowed through the
provided mechanisms.
Question:
How can I have routes that do not need the JWT token.
There are a number of ways.
From micronaut-projects.github.io/micronaut-security/3.2.0/guide/#securityRule...
The decision to allow access to a particular endpoint to anonymous or
authenticated users is determined by a collection of Security Rules.
#PermitAll is one way to do it. Others are documented at the links above.

Related

Read JWT token in Spring Boot RouterFunction

In Spring Boot Controller implementation we can get the JwtAuthenticationToken as a parameter in our method. Same token can be read, manipulated and validated for authorization like below
#PostMapping("/hello")
#PreAuthorize("hasAuthority('SCOPE_Internal') or hasAuthority('ROLE_User')")
public Mono<String> testHello(JwtAuthenticationToken token) {
log.info("token is " + token.getTokenAttributes().toString());
return Mono.just("OK");
}
We are using reactive Spring Boot and we have replaced our controllers with RouterFunction. We are wondering how above feature - Authorization and get the token in our router method calls.
public RouterFunction<ServerResponse> route() {
return RouterFunctions.route(GET("/hello"), helloHandler::testHello);
}
When we tried passing the JwtAuthenticationToken in the router method call, it threw
Could not autowire. No beans of 'JwtAuthenticationToken' type found.
public RouterFunction<ServerResponse> route(JwtAuthenticationToken jwtAuthenticationToken) {
return RouterFunctions.route(GET("/hello"), helloHandler::testHello);
}
We came up this solution if it makes any sense, or valid. We ran into same issue lately as we began a journey of converting our legacy and synchronous spring boot server app to an asynchronous one. The JwtAuthenticationToken which we use to get some added attribute to the token used by the system works out of the box when we used the RestController and pass it as an argument in the protected endpoint method. But with Router Function we could not get it to work. After 1 day of painstaking research on google, stackoverflow, spring's reactive application resource server docs, we could not get any head way. However, this post got us thinking even more. so we came up with this solution:
#Slf4j
#Component
#RequiredArgsConstructor
public class FitnessAccountWebHandler {
private final FitnessAccountService accountService;
public Mono<ServerResponse> getAccountByUserId(ServerRequest request) {
String userId = request.pathVariable(AccountResourceConfig.USER_ID);
// This will give you the JwtAuthenticationToken if a Principal is
// returned or empty if no authentication is found
Mono<JwtAuthenticationToken> authentication = request
.principal()
.cast(JwtAuthenticationToken.class);
return authentication.flatMap(auth -> {
log.info("Subject: {}", auth.getName());
return accountService.getSomething(userId)
.flatMap(ServerResponse.ok()::bodyValue)
.switchIfEmpty(ServerResponse.notFound().build());
});
}
}
Hope this helps someone and save some time.

CORS in spring boot is not working - getting empty response

I am using Spring Boot (Tomcat + Java) for my backend app and React for my front end.
I am trying to access "http://localhost:8080/vr-backend-0.0.1-SNAPSHOT/stockdata/NIFTY19DECFUT?from=2019-12-03&to=2019-12-03" from "http://localhost:3000".
I added #CrossOrigin(origins = "*") both at the controller level and at endpoint level. I am getting empty result.
I added #CrossOrigin both at the controller level and at endpoint level. I am getting empty result.
If I remove the #CrossOrigin annotation, I am getting the Cors error. That means the annotation is necessary. The error is "Access to fetch at 'http://localhost:8080/vr-backend-0.0.1-SNAPSHOT/stockdata/NIFTY19DECFUT?from=2019-12-03&to=2019-12-03' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled."
If I copy paste the link in the browser directly, it is working and returns JSON.
If I call "https://cdn.rawgit.com/rrag/react-stockcharts/master/docs/data/MSFT.tsv" it is also returning result.
To avoid typos, I did console.log and I am pasting these from console.log.
I am using fetch for 'get'ting.
[Edit 1]
I am looking at the tomcat logs and will update it shortly.
[Edit 2]
TL;DR
It's not a CORS problem.
Full description:
1. The response for "http://localhost:8080/vr-backend-0.0.1-SNAPSHOT/stockdata/NIFTY19DECFUT?from=2019-12-03&to=2019-12-03" was 200.
I logged in the spring boot app and looked at the tomcat logs. It reaches the controller method and I get the proper results from DB. When I send the response back, I am getting empty result in the browser. As mentioned before, if I hit the url directly, I am getting proper results.
It is not a preflight OPTIONS request.
So it can be concluded that it is not a CORS problem. #CrossOrigin annotation works
I looked at the logs after posting the question. So this question can be closed as it is incorrect. If I am not able to fix the empty response problem, I will post a separate question as this question title is misleading.
Thank you all for the response.
I am using it in this way to allow requests from all origins. It is working for me for all of my projects.
package com.example.demo.filters;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
#Component
public class CORSFilter extends OncePerRequestFilter {
#Override
public void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
final FilterChain filterChain) throws ServletException, IOException {
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,
"Origin, Content-Type, Accept, Authorization, Accept-Language, connection, Cache-Control, Access-Control-Request-Method, Access-Control-Request-Headers");
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD");
response.setHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS,
"Access-Control-Allow-Origin, Access-Control-Allow-Credentials");
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, request.getHeader(HttpHeaders.ORIGIN));
filterChain.doFilter(request, response);
}
}
I've only ever configured CORS at the application level,
but the documentation for the #CrossOrigin annotation says that it allows all origins by default, so you might want to try
#CrossOrigin()
instead of
#CrossOrigin(origins = "*")
The documentation also says that it supports "the HTTP methods specified in the #RequestMapping annotation", so you may need to add the OPTIONS method in addition to GET or POST, or whatever method you are currently using (CORS requires support for the OPTIONS method).
You can just make use of #CrossOrigin in your api endpoint and not mention origin.
Specifying origin may cause impacts on multiple layers of environments too.
Keep in mind Access-Control-Allow-Origin: * is only used if you don't have any authentication method (requests without credentials). See:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled
Don't use 'no-cors' in your fetch call, because you'll get opaque responses. Either specify 'cors' or don't specify anything.
const response = await fetch("http://localhost:8080", {
method: 'GET',
// mode: 'cors',
headers: {
'Accept': 'application/json'
}
});
Actually, when you get that 200 OK but opaque response it's not a problem from the server anymore. The response is 200, but the data is opaque because the frontend is probably setting 'no-cors' or similar. See:
https://developers.google.com/web/updates/2015/03/introduction-to-fetch
Regarding the backend, in Spring Boot (w/Kotlin) I'm using this to send
Access-Control-Allow-Origin: * header, based on spring-boot-starter-security library.
package demo.config
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
#Configuration
class GeneralApiSecurityConfig() : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http.cors()
}
}
Just http.cors()
Oh, and it may be a piece of trivial advice but if you are messing with configurations, do restart your server each time, otherwise, chances are you are refreshing the frontend client but the server is stuck with previous attempts.

Getting subject from id token in Spring boot

I have configured my Spring Boot application as a resource server. And it is working perfectly by blocking all the request that does not contain the authentication bearer token. However, once I am in service I would like to get the subject(sub in JWT token) out of the token an use it. How can I fetch the value of subject from Spring Authentication framework?
Since I am using Spring Boot's auto-configuration, I do not validate the token myself, it is been done automaticlly so I do not know the subject.
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
...
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
...
I added Principle to the argument.
#Controller
public class SecurityController {
#RequestMapping(value = "/username", method = RequestMethod.GET)
#ResponseBody
public String currentUserName(Principal principal) {
return principal.getName();
}
}
This link helped me

Using PATCH with Jersey Client API for unit testing

I am working on a REST API implementation using Jersey. For PATCH (partial updates), I have implemented my own custom implementation of PATCH since Jersey does not support it.
Now I am trying to figure out how to write functional tests around that implementation. I am using jersey test framework for other methods (PUT, POST, GET, DELETE) that has that support available in that framework.
Is there a way where in I can extend jersey test framework implementation to write my functional tests for PATCH?
If not, are there any other test frameworks available that I can use to test my Jersey PATCH implementation?
If anyone can provide any examples, that would be great.
Assuming your implementation consists of a custom annotation like this
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.ws.rs.HttpMethod;
#HttpMethod("PATCH")
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface PATCH {}
Trying to do something like this with the Client
String response = target.request().method("PATCH", Entity.text("Hello"), String.class);
by default is not supported, and will an exception like
java.net.ProtocolException: Invalid HTTP method: PATCH
This is not a problem with the Client API directly, but with the lower level Java APIs. Seems to be some security restriction.
With the Client API we can override this by setting a property
HttpUrlConnectionProvider.SET_METHOD_WORKAROUND to true
In the JerseyTest, one way to configure the Client is to override configureClient, and set the property with the ClientConfig. You could just as easily set the property on the Client itself, but staying in the spirit of the JerseyTest framework (where we don't need to explicitly access the Client, the example below will just just override the method
public class PatchTest extends JerseyTest {
#Path("patch")
public static class PatchResource {
#PATCH
#Produces(MediaType.TEXT_PLAIN)
public String getPatch(String request) {
return "Patched " + request;
}
}
#Override
protected void configureClient(final ClientConfig config) {
config.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
}
#Override
public Application configure() {
return new ResourceConfig(PatchResource.class);
}
#Test
public void doPatchTest() {
WebTarget target = target("patch");
String response = target.request().method("PATCH", Entity.text("Hello"), String.class);
Assert.assertEquals("Patched Hello", response);
System.out.println(response);
}
}
To send the HTTP PATCH via JAX RS Client API without any extra configuration:
client.target("$baseUrl$restUsersUrl/$userId")
.request("application/json")
.build("PATCH", Entity.entity(json2Update, MediaType.APPLICATION_JSON))
.invoke()
Annotation #PATCH is now available in JAX-RS 2.1. You can implement this HTTP method on the server side like:
#PATCH
public Response updateResource() { ... }
As for the client side, you can do something like:
Response r = ClientBuilder.newClient()
.target("http://localhost:8080/patch")
.request()
.build("PATCH", Entity.text("patch"))
.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true)
.invoke();
Where SET_METHOD_WORKAROUND is used to avoid the protocol exception, as indicated by #peeskillet:
java.net.ProtocolException: Invalid HTTP method: PATCH
With simple Strings this works for me. But does anyone know how to do this when the Patch method does not accept and return a simple String? See my example below.
The return type in the Response differs from the type of the passed argument. Both of which are not simple types.
Instead of a 200, I always get a 400 and/or the message that it cannot construct the ObjectPatch instance. And I understand that, since it is an interface with only an apply method. But somehow on runtime it manages to construct an AttentionPatchResource object from it anyway. Unfortunately not when using the JerseyTest framework.
#PATCH
#Path("/something")
#Produces(MediaType.APPLICATION_JSON)
#Consumes({ PatchMediaTypes.APPLICATION_MERGE_PATCH_JSON, PatchMediaTypes.APPLICATION_JSON_PATCH })
public Response updateAttention( //
#Parameter(schema = #Schema(implementation = AttentionPatchResource.class)) ObjectPatch patch) {
Attention attention = attentionService.find();
AttentionPatchResource patchResource = attentionAdapter.toPatchResource(attention);
AttentionPatchResource patchedResource = patch.apply(patchResource);
Attention patchedAttention = attentionAdapter.fromPatchResource(attention, patchedResource);
AttentionResource resource = attentionAdapter.toResource(patchedAttention);
return Response.status(Status.OK).entity(resource).build();
}

How do I get the Source IP of a SOAP requesting client machine?

how do you get source ip, username, password, etc... of the client machine that sends a soap request? is there any of these details that one can pull for logging purposes?
I am using Java to handle the incoming SOAP requests. The service simply adds 2 numbers and is working, but I just need to get some client details.
Thanks, Lavanya
If you're using JAX-WS, inject a WebServiceContext like so:
import javax.annotation.Resource
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.servlet.http.HttpServletRequest;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;
#WebService()
public class Test
{
#Resource WebServiceContext context;
#WebMethod(operationName = "getInfo")
public String getInfo()
{
HttpServletRequest request = (HttpServletRequest)context.getMessageContext().get(MessageContext.SERVLET_REQUEST);
return "IP: "+request.getRemoteAddr()+", Port: "+request.getRemotePort()+", Host: "+request.getRemoteHost();
}
}
Will return something like:
IP: 127.0.0.1, Port: 2636, Host: localhost
Look at the API for the rest of the methods. Basically, once you have your HttpServletRequest object, the rest is pretty easy.
I have figured the solution like below -
#Endpoint
public class DataEndpoints {
....
....
private HttpServletRequest httpServletRequest;
#Autowired
public void setRequest(HttpServletRequest request) {
this.httpServletRequest = request;
}
#PayloadRoot(namespace = employeeNS, localPart = "syncRelation")
#ResponsePayload
public SyncRelationResponse dataSync(#RequestPayload SyncOrderRelation request) {
String incoming = "IP Address -> " + this.httpServletRequest.getRemoteAddr();
}
}
By using the following method, I can directly access HttpServletRequest. And then I can access all data i need.
I hope it will help someone in this context.
I am not surte I fully understand your idea of getting username and password of client machine.
In general with Soap look at Soap Header, they are supposed to carry the authentication information (which could be username, password or some kind of security toke).
For the IP, your Soap is coming over Http and therefore when you receive your request you can try and look at your Http headers to see what information it gives you. Though I have never tried to get the IP of the client from it, but it might be there in the HTTP header
What soap stack are u using. If u deployed it as a war file using axis it is pretty easy to do it. u need to get hold of the httprequestobject and call the HTTPServletRequest.getRemoteAddr() method on it.

Categories