Can you change a method's signature in Spring using aspects?
Like effectively transform the following:
#GetMapping("/thing")
#User // custom annotation that should authenticate the user
public ResponseEntity getThing() {
... // user is successfully authenticated, get the "thing" from the database
}
into:
#GetMapping("/thing")
public ResponseEntity getThing(#CookieValue("Session-Token") String sessionToken) {
User user = authenticator.authenticateSessionTokenOrThrow(sessionToken);
... // user is successfully authenticated, get the "thing" from the database
}
With the user variable also becoming available for use in the method body.
If not, how can I achieve the same result without repeating the code (parameter and authenticator call) everywhere?
Aspects aren't meant for that.
Yes, they can effectively modify .class files bytecode, with compile time or run time weaving, but they do not override methods' signatures.
Also, the default Spring AOP Aspects are implemented in pure Java, and thus cannot touch the bytecode layer. For that you'd need AspectJ.
Tools for customizing bytecode at run/compile time are ASM, ByteBuddy, CGLIB or Javassist.
However, you can probably accomplish this via an Annotation Processor, which lets you modify the actual sources, instead of the already compiled bytecode.
If not, how can I achieve the same result without repeating the code
(parameter and authenticator call) everywhere?
Possible solutions are
HandlerInterceptor, which simply throws an Exception if the user isn't authenticated
Standard Spring AOP advice, which simply throws an Exception if the user isn't authenticated
Spring Security
1 is pretty easy.
2 is more time-consuming
3 imho, seems the best match for authentication, but it's the most complex, probably
The HandlerInterceptor can choose which methods it applies to?
No, unfortunately. I had a requirement a couple of months ago to "cover" only certain methods with an Interceptor, and I implemented a custom solution, which simply look for an annotation specified on the method itself.
This is an extract of my custom HandlerInterceptor, which looks for the CheckInit annotation, first on the type, and then on the method, for a more specific customization.
#Override
public boolean preHandle(
final HttpServletRequest request,
final HttpServletResponse response,
final Object handler
) throws Exception {
if (handler instanceof HandlerMethod) {
if (shouldCheckInit((HandlerMethod) handler)) {
checkInit();
}
}
return true;
}
private static boolean shouldCheckInit(final HandlerMethod handlerMethod) {
final var typeAnnotation = handlerMethod.getBeanType().getAnnotation(CheckInit.class);
final var shouldCheckInit = typeAnnotation != null && typeAnnotation.value();
final var methodAnnotation = handlerMethod.getMethodAnnotation(CheckInit.class);
return (methodAnnotation == null || methodAnnotation.value()) && shouldCheckInit;
}
private void checkInit() throws Exception {
if (!manager.isActive()) {
throw new NotInitializedException();
}
}
The "Standard Spring AOP advice" seems interesting, do you have a link
for that?
Spring AOP documentation - look for the Java-based configuration (I hate XML)
AspectJ really touches the bytecode and can modify signatures as well?
You could make AspectJ modify signatures. Just fork the project and modify its Java Agent or compiler.
AFAIK Annotation Processors cannot modify classes, they can only
create new ones.
The thing is, they don't modify .class files, instead they modify source files, which means they simply edit them. E.g. Lombok uses annotation processing to modify source files.
But yes, the modified sources are written to a new file.
Related
Our application calls many external API's which take a session token of the current user as input. So what we currently do is in a controller, get the session token for the user and pass it into a service which in turn might call another service or some API client. To give an idea, we end up with something like this (example is .NET but something similar is I think possible in Java)
public IActionResult DoSomething(string something)
{
this.someService.DoSomethingForUser(this.HttpContext.SessionToken, something);
return View();
}
And then we have
public class SomeService
{
private readonly IApiClient apiClient;
public SomeService(IApiClient apiClient)
{
this.apiClient = apiClient;
}
public void DoSomethingForUser(string sessionToken, something)
{
this.apiClient.DoSomethingForUser(sessionToken, something);
}
}
It can also happen that in SomeService another service is injected which in turn calls the IApiClient instead of SomeService calling IApiClient directly, basically adding another "layer".
We had a discussion with the team if it isn't better to instead of passing the session token, inject it using DI so you get something like this:
public IActionResult DoSomething(string something)
{
this.someService.DoSomethingForUser(something);
return View();
}
And then we have
public class SomeService
{
private readonly IUserService userService;
private readonly IApiClient apiClient;
public SomeService(IUserService userService, IApiClient apiClient)
{
this.userService = userService;
this.apiClient = apiClient;
}
public void DoSomethingForUser(string something)
{
this.apiClient.DoSomethingForUser(userService.SessionToken, something);
}
}
The IUserService would have an IHttpContextAccessor injected:
public class UserService : IUserService
{
private readonly IHttpContextAccessor httpContextAccessor;
public UserService(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public string SessionToken => httpContextAccessor.HttpContext.SessionToken;
}
The benefits of this pattern are I think pretty clear. Especially with many services, it keeps the code "cleaner" and you end up with less boilerplate code to pass a token around.
Still, I don't like it. To me the downsides of this pattern are more important than its benefit:
I like that passing the token in the methods is concise. It is clear that the service needs some sort of authentication token for it to function. I'm not sure if you can call it a side effect but the fact that a session token is magically injected three layers deep is impossible to tell just by reading the code
Unit testing is a bit more tedious if you have to Mock the IUserService
You run into problems when calling this in another thread, e.g. calling SomeService from another thread. Although these problems can be mitigated by injecting another concrete type of IUserService which gets the token from some place else, it feels like a chore.
To me it strongly feels like an anti pattern but apart from the arguments above it is mostly a feeling. There was a lot of discussion and not everybody was convinced that it was a bad idea. Therefor, my question is, is it an anti pattern or is it perfectly valid? What are some strong arguments for and against it, hopefully so there can be not much debate that this pattern is indeed, either perfectly valid or something to avoid.
I would say the main point is to enable your desired separation of concerns. I think it is a good question if expressed in those terms. As Kit says, different people may prefer different solutions.
REQUEST SCOPED OBJECTS
These occur quite naturally in APIs. Consider the following example, where a UI calls an Orders API, then the Orders API forwards the JWT to an upstream Billing API. A unique Request ID is also sent, in case the flow experiences a temporary problem. If the flow is retried, the Request ID can be used by APIs to prevent data duplication. Yet business logic should not need to know about either the Request ID or the JWT.
BUSINESS LOGIC CLASS DESIGN
I would start by designing my logic classes with my desired inputs, then work out the DI later. In my example the OrderService class might use claims to get the user identity and also for authorization. But I would not want it to know about HTTP level concerns:
public class OrderService
{
private readonly IBillingApiClient billingClient;
public OrderService(IBillingApiClient billingClient, ClaimsPrincipal user)
{
this.billingClient = billingClient;
}
public async void CreateOrder(OrderInput data)
{
this.Authorize();
var order = this.CreateOrder(data);
await this.billingClient.CreateInvoice(order);
}
}
DI SETUP
To enable my preferred business logic, I would write a little DI plumbing, so that I could inject request scoped dependencies in my preferred way. First, when the app starts, I would create a small middleware class. This will run early in the HTTP request pipeline:
private void ConfigureApiMiddleware(IApplicationBuilder api)
{
api.UseMiddleware<ClientContextMiddleware>();
}
In the middleware class I would then create a ClientContext object from runtime data. The OrderService class will run later, after next() is called:
public class ClientContextMiddleware
{
public async Task Invoke(HttpContext context)
{
var jwt = readJwt(context.Request);
var requestId = readRequestId(context.Request);
var holder = context.RequestServices.GetService<ClientContextHolder>();
holder.ClientContext = new ClientContext(jwt, requestIO);
await this.next(context);
}
}
In my DI composition at application startup I would express that the API client should be created when it is first referenced. In the HTTP request pipeline, the OrderService request scoped object will be constructed after the middleware has run. The below lambda will then be invoked:
private void RegisterDependencies(IServiceCollection services)
{
this.services.AddScoped<IApiClient>(
ctx =>
{
var holder = ctx.GetService<ClientContextHolder>();
return new ApiClient(holder.context);
});
this.services.AddScoped<ClientContextHolder>();
}
The holder object is just due to a technology limitation. The MS stack does not allow you to create new request scoped injectable objects at runtime, so you have to update an existing one. In a previous .NET tech stack, the concept of child container per request was made available to developers, so the holder object was not needed.
ASYNC AWAIT
Request scoped objects are stored against the HTTP request object, which is the correct behaviour when using async await. The current thread ID may switch, eg from 4 to 6 after the call to the Billing API.
If the OrderService class has a transient scope, it could get recreated when the flow resumes on thread 6. If this is the case, then resolution will continue to work.
SUMMARY
Designing inputs first, then writing some support code if needed is a good approach I think, and it is also useful to know the DI techniques. Personally I think natural request scoped objects that need to be created at runtime should be usable in DI. Some people may prefer a different approach though.
See in dotnet the area that I am an expert is not an anti standard on the contrary it is the model that many adopt but it is not a model that I would follow for the following reasons
it is not clear where is the token for those who read and use it being an anti clean code
you load important information in a place that is frequently accessed by the framework in the case of .netCore
your classes will reference a large property carrying a lot of unnecessary information when you could have created a more clean model that costs less memory and allocation time, I'm saying this because the HttpAcessor carries all the information relevant to your request
As I would take care of readability (clean code) and improve my performance
I would make a middleware or filter in my flow mvc where I would do the authentication part and create a class like:
public class TokenAuthenciationValues
{
public string TokenClient { get; set; }
public string TokenValue { get; set; }
}
Of course my method is an example but in my middleware I would implement it by loading its token values after calling the necessary apis (of course this model needs an interface and it needs to be configured as .AddScoped() in the case of .net)
That way I would use it in my methods only instantiating my ITokenAuthenciationValues in the constructor and I would have clear and clean information loaded in memory during the entire request
If it is necessary in the middle of the request to change the token any class can access it and change its value
I would have less memory allocated unused in my classes since the IHttpAcessor contract the ITokenAuthenciationValues only has relevant information
Hope this helps
I am trying to implement custom annotation and aspect which will insert path variable into request body before validation.
For now it looks like this...
#Aspect
#Component
public class AddParameterToFormAspect {
#Before("#annotation(addParameterToForm)")
public void addParameterToForm(JoinPoint joinPoint, AddParameterToForm addParameterToForm) {
String form = addParameterToForm.form();
String pathVariable = addParameterToForm.pathVariable();
CodeSignature methodSignature = (CodeSignature) joinPoint.getSignature();
List<String> methodParamNames = Arrays.asList(methodSignature.getParameterNames());
int formIndex = 0;
int pathVariableIndex = 0;
for(String s : methodSignature.getParameterNames()) {
if(s.equals(form)) {
formIndex = methodParamNames.indexOf(s);
}
if(s.equals(pathVariable)) {
pathVariableIndex = methodParamNames.indexOf(s);
}
}
Object[] methodArgs = joinPoint.getArgs();
Object formObject = methodArgs[formIndex];
Field pathVariableObject;
try {
pathVariableObject = formObject.getClass().getDeclaredField(pathVariable);
pathVariableObject.setAccessible(true);
pathVariableObject.set(formObject, methodArgs[pathVariableIndex]);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
Controller example of working annotation...
#PostMapping("/test/{username}")
#AddParameterToForm(pathVariable = "username", form = "user")
public String test(#PathVariable String username, #RequestBody User user) {
return user.getUsername();
}
Controller example of validation not working...
#PostMapping("/{domainCode}")
#AddParameterToForm(pathVariable = "domainCode", form = "userAddForm")
public ResponseEntity<UserDto> saveUserForDomain(#PathVariable(name="domainCode") String domainCode, #RequestBody #Valid final UserAddForm userAddForm, BindingResult results) {...}
Adding path variable to form works but it seems #Valid no longer works, problem is probably in join point expression... How can I make it to do advice before validation and then validate?
Changing method parameters in a #Before advice is not meant to work. You should use an #Around advice in order to change parameters before calling thisJoinPoint.proceed(). This is because when calling thisJoinPoint.getArgs() you get copies of primitive type parameters, you cannot manipulate the originals in a before-advice. You are lucky that you want to manipulate object types in this case, so that is the reason it works. Using an around-advice would enable you to pass completely new arguments to a method or just manipulate the original objects, you are free to choose.
Furthermore, you should - whenever possible - use args() in order to bind your method arguments of interest to advice parameters in order to be able to interact with them in a non-cryptic and type-safe manner. Creating a local variable and assigning some value to it will not influence the method parameter of the same type at all. Why should it?
Feel free to ask follow-up questions if this explanation is not comprehensive enough for you. Then I could add some sample code for you, too.
Update after question edit:
After having inspected you code a bit more closely and in addition to my remarks earlier today in my comments under your question, disregarding the content of your aspect code, your actual problem is that the validation check cause by #Valid annotations is performed before the method is executed. I.e. what is validated is not the state after the aspect has done its job (populate member fields in your target objects) but the state before the aspect runs. It is actually the same problem discussed in this question, see also M. Deinum's and my suggestions how to solve it:
Maybe you want to try full AspectJ via LTW (load-time weaving) and see if a call() pointcut instead of the implicit execution() pointcut used by Spring AOP solves the problem. You would weave into the calling code (method calls) instead of the callee (method execution) itself. Chances are, that this happens before validation is performed.
A more Spring-like way to solve it is to use a Spring interceptor (M. Deinum mentions HandlerInterceptor) instead of an aspect. There is also a link to an example by someone else.
Having said that, I still recommend to refactor your code so as not to use reflection and matching strings on method parameter names or class member names. I think you could also get rid of your custom annotation by matching your pointcut on methods parameters with #RequestBody and #PathVariable annotations.
i.e. In he following query method in a spring repository neither a nor b are required from an HTTP request. Is it possible to enforce the presence of these parameters at the repository level?
I would like to be explicit with the API I expose to the client. Right now no params, a, b, a&b are all accepted by the exposed endpoint. However I only want to expose a&b.
List<Thing> findByBAndC(#Param(value="a") Long a,#Param(value="b") Long b);
Don't know of any Spring Data way to do it, but spontanously I can think of some ways...
You could use a custom #Query where only if both are present ( "is not null" ) something would be returned, if that's enough
You could also (ab)use security with #PreAuthorize to check if both parameters are not null, but that sounds smelly.
Probably the most easy (and least smelly) way I can think of is to write your own Aspect that wraps around the method and throws an exception of both parameters are not present... For example, create your own custom annotation, put it before your method and then write an aspect, something like (not tested):
#Around("#annotation(com.example.AllParametersRequired.class)")
public Object throwExceptionOnMissingParameters(ProceedingJoinPoint pjp) throws Throwable {
int nullCount = Arrays.stream(pjp.getArgs()).filter( o -> o == null).count();
if (nullCount > 0) throw new RuntimeException("Null is not allowed.);
return pjp.proceed();
}
You will probably have to experiment there a little bit, to see which pointcut is the best for your case, but I don't see why you shouldn't be able to wrap an aspect around Spring Data's repository methods. Anyway, here's a link to the Spring AOP documentation, which will probably be helpful if you want to go that way: Link
Can Spring's caching framework be made aware of the authentication status of the request context, or is it easier to roll your own caching solution?
Regardless of the fact I find the use case super weird, you can condition caching for pretty much anything that works with SpEL. And since you can call any method you want with SpEL, you're good to go.
I realized that it is harder than it should but the following works. First create a static method that does your check (you can use the SecurityContextHolder for that)
public class SecurityChecker {
public static boolean isSecured() {
// Whatever
return SecurityContextHolder.getContext().getAuthentication() != null;
}
}
Then in your annotated method, specify the following (assuming myCache should be affected):
#Cacheable(cacheNames = "myCache", condition = "T(com.example.SecurityChecker).isSecured()")
public Foo doIt(String key) { ... }
There's two problems currently:
You can't create a meta-annotation to avoid repeating the condition attribute over and over again (see SPR-13475)
The SpEL setup does not allow you to easily call a method on a bean (which would be nicer than calling a static method). I've just created SPR-13812 for that
I have a servlet that invokes generic actions passing in a form and object (depending on what the action needs)
CommitmentServlet.java
CommitmentListDAO clDAO = new CommitmentListDAO();
CommitmentItemForm form = new CommitmentItemForm(clDAO);
CommitmentItem obj = new CommitmentItem();
actionMap.put(null, new ListAction(form);
actionMap.put("list", new ListAction(form);
actionMap.put("view", new ViewAction(form, obj)
actionMap.put("delete", new DeleteAction(form, obj);
actionMap.put("edit", new EditAction(form, obj);
ControllerAction action = (ControllerAction) actionMap.get(request.getParameter("method"));
action.service(request, response);
EditAction.java
public class EditAction implements ControllerAction {
private Form form;
private Object obj;
public EditAction(Form form, Object obj) {
this.form = form;
this.obj = obj;
}
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
obj = form.edit(request);
request.setAttribute("obj", obj);
request.setAttribute("form", form);
if (form.isSucces()) {
RequestDispatcher view = request.getRequestDispatcher(success page);
view.forward(request, response);
}
else {
RequestDispatcher view = request.getRequestDispatcher(failure page);
view.forward(request, response);
}
}
}
The actual business logic is located in the form object passed in to the generic action.
The generic actions allow me to quickly get CRUD Controller functionality for any new objects. I just have to code the business logic form such as here
CommitmentItemForm.java
public Object edit(HttpServletRequest request) {
CommitmentItem commitmentItem = null;
STKUser authenticatedUser = (STKUser) request.getSession().getAttribute("STKUserSession");
String ownedByBadge = null;
List deptSupervisorList = null;
try {
deptSupervisorList = STKUserDAO.getList(authenticatedUser.getDepartment()); //<--- Static call is it OK??
commitmentItem = CommitmentListDAO.retreive(request.getParameter("commitment_id"), authenticatedUser);
ownedByBadge = commitmentItem.getOwned_by();
}
catch (DAOException e) {
setError(FORM_RESULTS, e.getMessage());
}
catch (ValidatorException e) {
// ValidatorExceptions are thrown when the DAO can not find a record
setError(FORM_RESULTS, e.getMessage());
LOGGER.log(Level.INFO, e.getMessage(), authenticatedUser);
}
if (ownedByBadge != null) {
if (ownedByBadge.equals(authenticatedUser.getBadge()) || ownedByBadge.equals(authenticatedUser.getAtaBadge())) {
}
else {
setError(FORM_RESULTS, "You are not authorized to edit this data.");
LOGGER.log(Level.INFO, "Error - You are not authorized to edit this data '" + commitmentItem.getCommitment_id() + "'", authenticatedUser);
}
}
request.setAttribute("deptSupervisorList", deptSupervisorList); // <--- Is this acceptable to do???
return commitmentItem;
}
1) is my approach of setting a request attribute and returning an object in method un orthodox?
2) I'm making a static call to get the deptSupervisorList. Is this asking for trouble??
3) Does my servlet, generic action, business form seem like an acceptable approach to develop a java web application without using a framework?
EDIT:
What is the difference?
Static
deptSupervisorList = STKUserDAO.getList(authenticatedUser.getDepartment());
vs
non-static
STKUserDAO userDAO = new STKUserDAO();
deptSupervisorList = userDAO.getList(authenticatedUser.getDepartment());
public static List getList(String dept) throws DAOException {
...
}
First some caveats:
This is subjective
I agree with SidCool that the answer is to take a look at some of the existing web application frameworks out there. If anything, just to find out how they do it.
I'm a massive fan of dependency injection
To answer your questions:
It's not great to pass data around in request attributes because: it's not type safe; it's a bit of an invisible bag of things -- always better if you can see output objects in the type signature; at some point you'll find yourself wanting to store two things in the request attributes under the same name
Dependency injection is the way of the future. Making a static call is bad because: you've now tightly coupled the two objects making reuse harder as well as making it harder to test
I'd definitely have a look at some other frameworks here. Most of them tend to have a single dispatch servlet, I think you'll end up writing a lot of very similar looking servlets. A lot of frameworks will also use reflection to try and get the transformation between request and POJO done as early and as easily as possible.
Other:
All of your actions are off parameters i.e. ?method=[list,view,delete,edit]. Often it is preferable to use routes (e.g. index.html is usually used for 'list').
To answer your feedback / questions from the comments:
Running on older version of Java
Wow, that sucks. There are frameworks that run on Java 1.4, though. Spring MVC would be my recommendation but there are more here. That said, the reason that I suggested looking at other frameworks wasn't just to use them but more to be inspired by them. Writing your own Web Application Framework is practically a rite of passage and can be pretty fun. Writing it in such a constrained environment just adds to the challenge.
What I'd suggest:
Try out a recent Java framework or even a non Java one (e.g. Ruby on Rails), just to see what's possible
When writing your own only framework, just use 1 servlet and dispatch down to your various 'controllers'. The reason for this is that Servlets are not great at letting you put your whole application together (what Spring MVC does, is loads up the 'application' using a ContextListener and then servlets and filters look up the 'application' from the ServletContext)
The tight coupling of static
Tight coupling is when two objects can't be used without each other, ever. Why is this bad, you ask? Because you can never reuse the code for something else (say, if you decided to load some data from a file, introduced a caching layer, used it in a different project etc.). Most importantly, some would say, is that it is difficult to test it. This is because you can't just replace the object that you statically call with another one. Interfaces are usually used to decouple objects but realistically, you can do it just by setting the object in via dependency injection (which is a complicated way of saying: put it in the constructor or as a setter).
OO and being a civil engineer
It's all good. Some of the best programmers I know didn't start out that way. For me, using the Dependency Injection pattern is an awesome way to write 'good' code by default. Note: if you look at Dependency Injection, you don't need a framework for it. You just need to construct all of your objects in one place and all of your objects should get all of their dependencies either in the constructor or in a setter. Not static methods or singletons allowed.
What's the difference
An alternative 'what's the difference' that better illustrates what I mean would look like this:
// code in your application builder
// assuming an interface called UserDAO
UserDAO userDAO = new STKUserDAO();
CommitmentItemForm form = new CommitmentItemForm(userDao);
public class CommitmentItemForm {
private final UserDAO userDao;
public CommitmentItemForm(UserDAO userDao) { this.userDao = userDao; }
public Object edit(HttpServletRequest request) {
...
deptSupervisorList = userDao.getList(authenticatedUser.getDepartment());
...
}
}
vs
public class CommitmentItemForm {
public CommitmentItemForm()
public Object edit(HttpServletRequest request) {
...
deptSupervisorList = STKUserDAO.getList(authenticatedUser.getDepartment());
...
}
}
The static method definitely looks like it's less work, so why is it so bad? Essentially, it's because in the static version, you can never look up the deptSupervisorList from anything but an STKUserDAO. In the non static version, you can supply any implementation of the UserDAO interface. This means that you could use the same CommitmentItemForm code regardless of whether:
You were doing it in a test and you were creating a Mock version of UserDAO that returned an exception every time so that you could test that
You found out that you needed to retrieve your departments list from a JSON HTTP REST web service, or from a file
It's also immediately obvious from the signature of CommitmentItemForm that it needs a UserDAO to function (because it's required in the constructor).
This is one of those little things that if you do it with all of your code, you will find that your code is not only more flexible, it's more testable, more reusable and the parts that you suddenly find you need to change in the future are better isolated.