Background
Each request will run through an API Gateway, each request will have a JWT with custom claim "x-token". Now, this "x-token" is an id that I will use to get the corresponding data in the DB. The "x-token" will be used in about 2 (out of 3) of my microservices.
Question
Where should this API call should happen?
My current plans are:
1. [API GATEWAY FILTER APPROACH]
Add an API Filter in my Spring Cloud Gateway to modify every (GET/POST) request
-Create a Filter and inside the filter perform an api call to get xTokenObject associated with the "x-token", and then modify to add the xTokenObject in the #RequestBody so that on the Controller part of my microservice I could easily use it.
PROS
API Gateway will be the only one have access to it
I can easily use it on the controller function
Sample Code on the Controller of my Microservice where the 'xTokenObject' was added on the Gateway Filter:
#PostMapping()
public ResponseEntity<SomeObject> someMethod(#RequestBody #Valid XTokenObject xTokenObject){
return ResponseEntity.ok(this.someService.someFunction(xTokenObject));
}
CONS
What if there is already a #RequestBody? How should I add this xTokenObject to that #RequestBody? And how can I access it on the Controller of my microservice?
2. [MICROSERVICE CALL]
Modify the header to add "x-token" in the Gateway Filter and then use it to easily call the API on the #Service of my microservice
PROS
Implementation is easy since adding of custom header is easy on the Gateway Filter than modifying the request body
Sample Code on the Service of my Microservice:
#Service
public class SomeService {
public SomeObject someFunction(String xTokenId) {
// API CALL HERE USING THE xTokenId
return someObject;
}
}
CONS
All of my microservice will have a connection to it
Additional Question and Considerations:
Which one is faster? Or there is another better way? The data on the Token Data Microservice might contain some sensitive data so it will be encrypted. So it will be on the same DB of my auth-server but different table. I will also consider using Redis to cache these data. I included a photo on this question so you guys have an overview.
I need to create a http interceptor for my spring boot app to check if the request's authorization heathers are valid, so I searched and it seems that I can use HandlerInterceptor or ClientHttpRequestInterceptor to do it.
What would be the ideal interceptor in this case? I need all requests to check this and deny access if they don't pass but to me it looks like both of the interceptors do the same thing, so what is the difference between each one?
Important detail:
I'm using #RestController to create my routes and if the route has an specific annotation (something like #IgnoreAuthentication) the interceptor won't need to check the authentication.
I try to use Websocket with spring boot backend (as an API) and Vuejs frontend.
I take a simple use case to expose my question. Some users are logged on my website, and there is a messaging feature. User A send a message to User B. User B is actually logged, and I want to notify User B that a new message is arrived.
I see 3 ways to do it with websockets :
1 - When User A send message, an Axios post is call to the API for saving message, and, if the Axios response is success, I call something like
this.stompClient.send("/app/foo", JSON.stringify(bar), {})
2 - When User A send message, I only call something like
this.stompClient.send("/app/foo", JSON.stringify(bar), {})
and it's my controller's method (annotated with #MessageMapping("/xxxx") #SendTo("/topic/yyyy")) that call facade, service, dao to first, save message, then return message to subscribers
3 - I keep my actuals controllers, facade, services and DAO, and juste add when save is successfull something like :
#Autowired SimpMessagingTemplate webSocket;
...
#GetMapping("/send-message")
public ResponseEntity sendMessage(#AuthenticationPrincipal User user, ....) {
service.saveMessage(....);
webSocket.convertAndSend("/ws/message-from", message);
without a new controller contains #MessageMapping("/xxxx") #SendTo("/topic/yyyy"). User B is just subscibed to "/ws/message-from"
Could you help me.
In the 3 way there is a good method ?
Thanks you.
The one and two method has no much difference as you use axios from npm for sending request and the other one you can directly,while the third one you use controller,and facade dao at single place.it is about architecture and how you wanna send your requests for your framework,as a requirement.
They serve best at their level,till you come with specific requirement.
The suggestion would be to use axios.
It has advantages:
supports older browsers (Fetch needs a polyfill)
has a way to abort a request
has a way to set a response timeout
has built-in CSRF protection
supports upload progress
performs automatic JSON data transformation
works in Node.js
I am very much new to web services. I have exposed some REST services using Jersey 2 in integration with Spring. Now I need to secure those rest services using authentication with username/password. I am told not to use Spring Security.
I have no idea of how to do this. I did search on the net but various links show various implementation and I am unable to decide how to proceed with it.
A common way for authenticating with username and password is to use Basic Authentication. Basically the client needs to send a request header Authorization, with the the header value as Basic Base64Encoded(username:password). So is my username is peeskillet and my password is pass, I, as a client, should set the header as
Authorization: Basic cGVlc2tpbGxldDpwYXNz
In a servlet environment, the container should have support for Basic authentication. You would configure this support on the web.xml. You can see an example in 48.2 Securing Web Applications of the Java EE tutorial. You will also notice in an example
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
That is for SSL support. This is recommended for Basic Authentication.
If you don't want to deal with the hassle of working with security domains and login modules, realm, and such, that would be required to customize the servlet support, or if you're just not in a servlet environment, implementing Basic Auth in a ContainerRequestFilter is really not too difficult.
You can see a complete example of how this could be done at jersey/examples/https-clientserver-grizzly. You should focus on the SecurityFilter
The basic flow in the filter goes something like this
Get the Authorization header. If it doesn't exist, throw an AuthenticationException. In which case the AuthenticationExceptionMapper will send out the header "WWW-Authenticate", "Basic realm=\"" + e.getRealm() + "\", which is part of the Basic Auth protocol
Once we have the header, we parse it just to get the Base64 encoded username:password. Then we decode it, then split it, then separate the user name and password. If any of this process fails, again throw the WebApplicationException that maps to a 400 Bad Request.
Check the username and password. The example source code just checks if the username is user and the password is password, but you will want to use some service in the filter to verify this information. If either of these fail, throw an AuthenticationException
If all goes well, a User is created from the authenticate method, and is injected into an Authorizer (which is a SecurityContext). In JAX-RS, the SecurityContext is normally used for authorization`.
For the authorization, if you want to secure certain areas for certain resources, you can use the #RolesAllowed annotation for your classes or methods. Jersey has support for this annotation, by registering the RolesAllowedDynamicFeature.
What happens under the hood is that the SecurityContext will be obtained from the request. With the example I linked to, you can see the Authorizer, it has an overridden method isUserInRole. This method will be called to check against the value(s) in #RolesAllowed({"ADMIN"}). So when you create the SecurityContext, you should make sure to include on the overridden method, the roles of the user.
For testing, you can simply use a browser. If everything is set up correctly, when you try and access the resource, you should see (in Firefox) a dialog as seen in this post. If you use cURL, you could do
C:/>curl -v -u username:password http://localhost:8080/blah/resource
This will send out a Basic Authenticated request. Because of the -v switch, you should see all the headers involved. If you just want to test with the client API, you can see here how to set it up. In any of the three cases mentioned, the Base64 encoding will be done for you, so you don't have to worry about it.
As for the SSL, you should look into the documentation of your container for information about how to set it up.
So this is really a matter what you would like to achieve. My case was to get this thing running with mobile and a One-Page-App JavaScript.
Basically all you need to do is generate some kind of header that value that will be needed in every consecutive request you client will make.
So you do a endpoint in which you wait for a post with user/password:
#Path("/login")
public class AuthenticationResource {
#POST
#Consumes("application/json")
public Response authenticate(Credentials credential) {
boolean canBeLoggedIn = (...check in your DB or anywher you need to)
if (canBeLoggedIn) {
UUID uuid = UUID.randomUUID();
Token token = new Token();
token.setToken(uuid.toString());
//save your token with associated with user
(...)
return Response.ok(token).type(MediaType.APPLICATION_JSON_TYPE).build();
} else {
return Response.status(Response.Status.UNAUTHORIZED).build();
}
}
}
Now you need to secure resource with need for that token:
#Path("/payment")
#AuthorizedWithToken
public class Payments {
#GET
#Produces("application/json")
public Response sync() {
(...)
}
}
Notice the #AuthorizedWithToken annotation. This annotaation you can create on your own using special meta annotation #NameBinding
#NameBinding
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface AuthorizedWithToken {}
And now for the filter that implements checking of the header:
#AuthorizedWithToken
#Provider
public class XAuthTokenFilter implements ContainerRequestFilter {
private static String X_Auth_Token = "X-Auth-Token";
#Override
public void filter(ContainerRequestContext crc) throws IOException {
String headerValue = crc.getHeaderString(X_Auth_Token);
if (headerValue == null) {
crc.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Missing " + X_Auth_Token + " value").build());
return;
}
if(! TOKEN_FOUND_IN_DB) {
crc.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("Wrong " + X_Auth_Token + " value").build());
return;
}
}
}
You can create any number of your own annotations checking for various things in the http request and mix them. However you need to pay attention to Priorities but that actually easy thing to find. This method needs using https but that is obvious.
Security comes in two main flavours :
Container Based
application based
the standard way to secure spring applications is to use Spring Security (formerly Acegi).
It would be interesting to know why you're not being allowed to use that.
You could use container based security, but I'm guessing that your use of spring precludes that option too.
Since the choice of Spring is usually to obviate the need for the use of a full J2EE container (Edit : though as pointed out below by others, most ordinary servlet containers do allow you to implement various container based security methods)
This really only leaves you with one option which is to roll your own security.
Your use of Jersey suggests that this might be a REST application.
In which case you really ought to stick with standard HTTP Authentication methods that
comes in the following flavours in reverse order of strength :
BASIC
Digest
Form
Certificate
REST applications are usually supposed to be 'stateless', which essentially rules out form based authentication (because you'd require the use of Session)
leaving you with BASIC, Digest and Certificate.
Your next question is, who am I authenticating. If you can expect to know the username AND the password of the user based on what URL they requested (say if it's one set of credentials for all users) then Digest is the best bet since the password is never sent, only a hash.
If you cannot know the Password (because you ask a third party system to validate it etc.) then you are stuck with BASIC.
But you can enhance the security of BASIC by using SSL, or better yet, combining BASIC with client certificate authentication.
In fact BASIC authentication over HTTPS is the standard technique for securing most REST applications.
You can easily implement a Servlet Filter that looks for the Authentication Header and validates the credentials yourself.
There are many examples of such filters, it's a single self contained class file.
If no credentials are found the filter returns 401 passing a prompt for basic auth in the response headers.
If the credentials are invalid you return 403.
App security is almost an entire career in itself, but I hope this helps.
As the former posts say, you could go with different options, with a varying overhead for implementation. From a practical view, if you're going to start with this and are looking for a comfortable way for a simple implementation, I'd recommend container-based option using BASIC authentication.
If you use tomcat, you can setup a realm, which is relatively simple to implement. You could use JDBCRealm, which gets you a user and password from specified columns in your database, and configure it via server.xml and web.xml.
This will prompt you for credentials automatically, everytime you are trying to access your application. You don't have any application-side implementation to do for that.
What I can tell you now is that you already did most of the 'dirty' job integrating Jersey with Spring. I recommend to you to go an Application-based solution, is it does not tie you to a particular container. Spring Security can be intimidating at first, but then when you tame the beast, you see it was actually a friendly puppy.
The fact is that Spring Security is hugely customizable, just by implementing their interfaces. And there is a lot of documentation and support. Plus, you already have a Spring based application.
As all you seek is guidance, I can provide you with some tutorials. You can take advantage from this blog.
http://www.baeldung.com/rest-with-spring-series/
http://www.baeldung.com/2011/10/31/securing-a-restful-web-service-with-spring-security-3-1-part-3/
There is a webapp, where every request consumes various external resources. The webapp tracks those consumed resources with request scooped bean. Then HandlerInterceptor's afterCompletion method calls TaskExecutor to store this information in DB. All is fine and dandy, but there goes the requirement to add bandwith consumption as another resource. Counting outgoing response size is a typical task for servlet filter (along with response wrapper and custom stream implementation). So this is done and is also working.
The problem is that I'd like to aggregate two things together. Obviously, I can't pass "bytes sent" to Spring HandlerInterceptor, because filter's doFilter() hasn't completed yet and the amount of bytes sent isn't known when the interceptor runs. So filter must be the place to aggregate all the resource usage and start async task to store it in DB. The problem is: how can I pass data from HandlerInterceptor to Filter. I've tried simple request.setAttribute() but surprisingly it didn't worked.
As a side note: I'm aware of request scooped bean lifecycle and at the handler I'm creating a simple POJO populated with data from scooped bean.
The problem turned out to be quite trival. The app was doing a lot of forward/dispatches as a part of somehwat normal request handling. It turned out that the my filter was called (so far, so good) then another filter down the chain was doing forward/dispatch which then did actual request processing. Default filter setup catches only plain requests and you need additional configuration (web.xml) to also filter forwards/dispatches. I just did that and that solved the problem.