Spring boot annotation for custom AuthZ interceptor
Hello Guys, In this blog we will look on how to create Custom Authorization rather than using any build OAuth library. There is no authorization setup, this is just for authorizations or how you can you share data between your handler and interceptors.
To do this we will be levering following components in spring boot.
- Http interceptors – to intercept request before it reaches final handler.
- Custom Annotations – To allow use to declare and store meta data on handlers which can be read on interceptors
- WebMVCConfigurer to register the interceptor.
Let’s start with setting up interceptors into spring web request lifecycle.
package com.adesigner.web1;
import org.springframework.web.servlet.HandlerInterceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class AuthZInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("Inside interceptor");
return HandlerInterceptor.super.preHandle(request, response, handler);
}
}
Now lets register interceptor
package com.adesigner.web1;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Component
public class WebConfigurations implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthZInterceptor());
}
}
This setup allows us to setup global interceptor in Spring’s request handling lifecycle. every request will pass through this interceptor. there is also option available to apply matchers for example if you want to run this interceptor just on API which starts with “/api/”, you can do that as well.
Now let’s create annotation to store some metadata on handlers
package com.adesigner.web1;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME)
public @interface AuthZScope {
String[] scope();
}
This allows us to pass some metadata from handlers to interceptor, you can also store additional metadata as well like role or some flag if its public API etc.
Then by reading this detail’s you can take decision on interceptor level on how to handle this request.
To Apply this on Controller/Rest Controller, we can use with following
package com.adesigner.web1;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@GetMapping("/user")
@AuthZScope(scope = {"user:get"})
public String getUser() {
return "Hello";
}
}
This adds scope variable on getUser handler, which can be read in interceptor and made decision based on this.
Now let’s update our interceptor to read this metadata
package com.adesigner.web1;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class AuthZInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("Inside interceptor");
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler;
AuthZScope annotation = method.getMethodAnnotation(AuthZScope.class);
if (annotation != null) {
String[] scope = annotation.scope();
System.out.print(String.join(",", scope));
// Now use this scope to authorize
}
}
return HandlerInterceptor.super.preHandle(request, response, handler);
}
}
With this, you can get the metadata defined on your handler.
Thanks for reading…