Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed last month.
Improve this question
i have an api
public interface UserService{
#GetMapping("/get-user-data")
public Response getUserData();
}
i have my impl as below
public class UserServiceImpl implements UserService{
private boolean isAuthorized = false;
public Response getUserData(User user){
//check if user is authorized
isAuthorized = isUserAuthorized()?true:false;
//perform other things based on above boolean
return someResponse;
}
}
i want this boolean "isAuthorized" must be set to false for every call to this api. whatever i have above isn't working, how to achieve this?
Instead of keeping track of the state of the boolean variable, usually we perform some action based on the positive condition:
public Response getUserData(User user){
if (isUserAuthorized()) {
// actions that should happen only when authorized
} else {
// actions that should happen only when NOT authorized
}
}
But if you really want to keep track of that state, you can always set the variable to false at the end of the method call:
//check if user is authorized
isAuthorized = isUserAuthorized()?true:false;
//perform other things based on above boolean
isAuthorized = false;
return someResponse;
Note: that may cause issues related to concurrency, but if that doesn't concern you, no harm.
if you want to call a business logic method for all api methods, you need to use a web HandlerInterceptor, by example:
#Component
public class AppInterceptor implements HandlerInterceptor {
private static Logger logger = LoggerFactory.getLogger(AppInterceptor.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
var authorized = UserIsAuthorizedDummy(); // <- you business logic here
request.setAttribute("is_authorized", authorized);
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) {}
private boolean UserIsAuthorizedDummy() {
return true;
}
}
And configure this interceptor with a WebMvcConfigurer implementation, by example:
#Configuration
public class AppConfig implements WebMvcConfigurer {
#Autowired
AppInterceptor appInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(appInterceptor);
}
}
Then you can use it in you controller methods, and you will see and access the is_authorized atrribute from request object. example:
#GetMapping("/user")
public LocalResponse getUser(HttpServletRequest request) {
var response = new LocalResponse();
// check AppInterceptor.preHandle() to see when "is_authorized" attribute is set
response.setAuthorized((boolean) request.getAttribute("is_authorized"));
if (response.isAuthorized()) {
response.setUserId("1111");
}
return response;
}
#GetMapping("/profile")
public LocalResponse getProfile(HttpServletRequest request) {
var response = new LocalResponse();
// check AppInterceptor.preHandle() to see when "is_authorized" attribute is set
response.setAuthorized((boolean) request.getAttribute("is_authorized"));
if (response.isAuthorized()) {
response.setUserId("1111-22222");
}
return response;
}
You can see a working example in the following repository: https://github.com/eriknyk/springboot-web-interceptor-demo/
Related
i'm trying to use cache (caffeine) with Spring boot and im having a problem. I need to check the header "header-name" in every call but application is caching it so after first request with the right header, dont matter what header i send and the application will not check it and is just returning data from the cache, is there anyway that i can force spring to check header and then get data from cache?
#GetMapping("/request-a")
#Cacheable(cacheNames = "cachename", key = "#root.methodName")
public ResponseEntity<?> makeRequest(#RequestHeader("header-name") String headerName) {
this.authConfig.headerCheck(headerName);
/*
code
*/
}
I already used header "Cache-Control:no-cache" and didnt resolve my problem.
Thanks in advance.
Edit1: method "headerCheck" just check if its equal to another String or not null.
Found a solution:
Create a classe that implements HandlerInterceptor and use preHandle method.
#Component
public class CheckHeaderInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// validate what you want, on error return false
// if everything its ok, return true
}
}
Then register the handler with:
public class WebMvcConfig implements WebMvcConfigurer {
#Autowired
private CheckHeaderInterceptor interceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor).addPathPatterns("url that you wannna use handler");
}
}
I am developing spring boot project. I have a HandlerInterceptorAdapter, in where I have some logic to intercept and check request data in preHandle(...).
public class MyCustomInterceptor implements HandlerInterceptorAdapter{
// I only override the preHandle(...)
#Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response){
//my custom logic here.
// if a condition is not met, I return false, my understanding is that it would stop this request from proceeding forward
if(conditionNotMet) {
log.info("Condition NOT met, returns false!")
response.setStatus(400);
return false;
}
log.info("Condition met, returns true!")
// otherwise, returns true
return true;
}
}
In above interceptor's preHandle(), I have some custom logic, then I check if certain condition is not met, I return false to stop proceeding forward the request, otherwise I return true there. My understanding is that if I return true eventually, the request would hit the corresponding rest controller.
The interceptor is registered and functional well:
#Configuration
public class MyConfigurationAdapter implements WebMvcConfigurer {
#Autowired
private MyCustomInterceptor myCustomInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myCustomInterceptor);
}
}
I have a rest controller:
#RestController
#RequestMapping(value = "/api/myservice/")
public class MyController {
#PostMapping(value = "add_note")
public MyResponse addNote(#Valid #RequestBody AddNotePayload payload) {
// I see logs in interceptor, it returns true but I don't see this log
log.info(" Start adding note...");
...
}
The URL maps to the addNote(...) function of above controller is POST http//localhost:8080/api/myservice/add_note. When I fire to this endpoint on Postman, I see the logs in preHandle() of my interceptor that the condition there is met and preHandler() returns true. Because it returns true there, so I expected that the request would be proceeded forward to controller level, but I don't see the log in addNote() of MyController. Why? What could be the reason?
I think my problems is architecture-based, but not so sure so I'll try to explain the reality of it.
The software I'm working on is monolithic oriented but fully exposed with SOAP. The architecture is as followed :
The fondamental concept is what we called "Transaction", which is defined by its request and response (used for SOAP). It structures configurable business logic as well (editing documents, workflows, etc)
We have 1 bean configured in application-context.xml for each Transaction (PersonViewTransaction, PersonSelectTransaction, PersonEditTransaction for example)
All of them inherit
from the same abstract class AbstractTransaction which implements controls on the request provided
that AbstractTransaction implements an interface ITransaction
We build a TransactionFactory which, on any call with beanId and request, calls static methods calling every ITransaction's methods for the Transaction identified by beanId.
When a call is made through Webservices, a Servlet will call this factory, and execute the same logic, as a call from the web project (maven) would do calling directly the factory.
In other words :
Definition of ITransaction
/**
* The main interface defining what a transaction is.
*/
public interface ITransaction <Q extends GenericRequest, P extends GenericResponse> {
P businessLogic(Q req);
void executeControls(String transactionId, Q params, PerimetreAppel environnement)
default void controleLicence() throws NassurCoreException
{
//TODO : contrôles de la licence à effectuer.
}
/**
* Log before transaction
*/
default void logEntree() throws NassurCoreException
{
//TODO to be implemented
}
/**
* Log after transaction
*/
default void logSortie() throws NassurCoreException
{
//TODO to be implemented
}
/**
* Checks that the user can call transaction
*/
default void controleDroits() throws NassurCoreException
{
//TODO to be implemented
}
//...
//Some other method not necessarily implemented in each transaction
}
/**
* The abstract defining the default behaviour of a transaction
*/
public abstract class AbstractTransaction<Q extends GenericRequest, P extends GenericResponse> implements ITransaction<Q, P>{
#Autowired
private ControlService controlService;
//.... some other autowired fields
#Autowired
protected UserDetailsService userDetailsService;
protected String idTransaction;
//Every transaction must implement its own logic
P businessLogic(Q params);
#Override
public void initialisation(String trId)
{
this.idTransaction = trId;
}
#Override
#Transactional
public void executeControls(String transactionId, Q params, PerimetreAppel environnement)
{
//Some controls found from database, and executed
// A control consists of a combination between java checks and some Jruby executed from the Service
List<SomeEntity> list = someEntityRepository.findByTransactionId(transactionId);
for(SomeEntity e : list){
controlService.executeUnitControl(e, params);
}
}
}
Transactions
//Example Transaction
public class PersonViewTransaction extends AbstractTransaction<PersonViewRequest, PersonViewResponse>{
#Override
#Transactional
public PersonViewResponse businessLogic(PersonViewRequest req){
//...
}
}
//Example Transaction
public class PersonEditTransaction extends AbstractTransaction<PersonEditRequest, PersonEditResponse> {
#Override
#Transactional
public PersonEditResponse businessLogic(PersonEditRequest req){
//...
}
}
//Example Transaction
public class PersonSearchTransaction extends AbstractTransaction<PersonSearchRequest, PersonSearchResponse> {
#Override
#Transactional
public PersonSearchResponse businessLogic(PersonSearchRequest req){
//...
}
}
Those Beans are declared in application-context.xml.
Factory
public class TransactionFactory
{
public static GenericResponse executeTransaction(String id, GenericRequest req, HttpServletRequest request)
throws SomeExceptions
{
ServletContext servletCtx = request.getSession().getServletContext();
ApplicationContext actualCtx = WebApplicationContextUtils.getWebApplicationContext(servletCtx);
ITransaction tr = (ITransaction<GenericRequest, GenericResponse>) actualCtx.getBean(id.name());
if (tr == null)
{
throw new Exception("internal Error", null);
}
return executeStack(id, tr, req, request);
}
private static GenericResponse executeStack(String id, ITransaction tr, GenericRequest req, HttpServletRequest request)
throws SomeExceptions
{
try
{
tr.initialisation(id);
tr.controleLicence();
tr.logEntree();
tr.controleDroits();
//...Several operations calling ITransaction
tr.executeControls(id, req, provenance);
//Calling transaction logic
GenericResponse resp = tr.businessLogic(req);
//...Many "after-business" other calls
return resp;
}
catch (SomeExceptions e)
{
logger.error(e);
throw e;
}
}
}
By some mystery, when two calls are made simultaneously on the same transactionId, the executeControls throws some weird returns, as if both executions where mixed in the same bean (to me it appears thread-safe but not that sure...) :
List<SomeEntity> list = someEntityRepository.findByTransactionId(transactionId);
for(SomeEntity e : list){
controlService.executeUnitControl(e, params);
}
executeUnitControl checks if the control must apply, and throws an error if needed. To be more into detail, this is based on a JRuby script that takes params as parameter to build configurable and non-Java based business logic.
Thanks a lots for your help, and if there are french people, I'd love to chat with them any time to explain more details if needed !
I have a Spring MVC (v4.1.3) web application with javascript UI. I have implemented a custom DispatcherServlet and configured the same in web.xml
There is a unique screen code which is sent in the HTTP Header of each request made by the UI to server.
In the doService method of my custom dispatcher servlet, I capture the HTTP Header and put the value in a ThreadLocal dto variable. I access this ThreadLocal variable in the service layer for performing some audit logic which is common for all requests.
Code from CustomDispatcherServlet:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
String uiCode = request.getHeader("uiCode");
if ((uiCode != null && !uiCode.trim().isEmpty())) {
UiCodeDto uiCodeDto = new UiCodeDto(uiCode);
final ThreadLocal<UiCodeDto> threadLocalUser = new ThreadLocal<UiCodeDto>();
threadLocalUser.set(uiCodeDto);
}
...
super.doService(request, response);
}
Code from service layer:
UiCodeDto temp = ThreadLocalUtil.getUiCodeDto(Thread.currentThread());
Code of ThreadLocalUtil to retrieve the value from ThreadLocal:
public final class ThreadLocalUtil {
public static UiCodeDto getUiCodeDto(Thread currThread) {
UiCodeDto UiCodeDto = null;
try {
Field threadLocals = Thread.class.getDeclaredField("threadLocals");
threadLocals.setAccessible(true);
Object currentThread = threadLocals.get(currThread);
Field threadLocalsMap = currentThread.getClass().getDeclaredField("table");
threadLocalsMap.setAccessible(true);
threadLocalsMap.setAccessible(true);
Object[] objectKeys = (Object[]) threadLocalsMap.get(currentThread);
for (Object objectKey : objectKeys) {
if (objectKey != null) {
Field objectMap = objectKey.getClass().getDeclaredField("value");
objectMap.setAccessible(true);
Object object = objectMap.get(objectKey);
if (object instanceof UiCodeDto) {
UiCodeDto = (UiCodeDto) object;
break;
}
}
}
} catch (Exception e) {
...
}
return UiCodeDto;
}
}
The problem is as follows -
1. I am getting random values of screen code - which means the value of some http request N is coming in http request N+1.
2. There are null DTOs in ThreadLocal variable with same name - hence, sometimes when I access the ThreadLocal in service layer, I get a null
I need help in understanding the behavior of ThreadLocal in DispatcherServlet - why would it get values of another request in doService method?
Thanks in advance.
Your code is error prone and hard to understand also why would you need a custom DispatcherServlet. A filter seems more suited for this task.
public class UiCodeFilter extends OncePerRequestFilter {
protected void doFilterInternally(HttpServletRequest req, HttpServletResponse res, FilterChain chain) {
try {
String uiCode = req.getHeader("uiCode");
if ((uiCode != null && !uiCode.trim().isEmpty())) {
UiCodeDto uiCodeDto = new UiCodeDto(uiCode);
UiCodeHolder.set(uiCodeDta);
}
chain.doFilter(req, res);
} finally {
UiCodeHolder.clear(); // Always clear!
}
}
}
The UiCodeHolder has a static ThreadLocal to keep the value.
public abstract class UiCodeHolder {
static ThreadLocal<UiCodeDto> current = new ThreadLocal<>()
public void set(UiCodeDto uiCode) {
current.set(uiCode);
}
public UiCodeDta get() {
return current.get();
}
public void clear() {
current.remove(); // for older versions use current.set(null);
}
}
In your service you can now simply do UiContextHolder.get() to obtain the correct value. The UiCodeFilter takes care of setting the value and at the end of the request clears the value again to prevent leaking.
This approach doesn't require ugly reflection hooks, is quite easy to understand is is used by Spring, Hibernate and frameworks alike.
A more Spring way of doing this is to use a request-scoped bean to extract and hold the header:
#Component
#Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UiCodeDto {
private String uiCode;
#Inject
public void setCode(HttpServletRequest req) {
uiCode = req.getHeader("uiCode");
}
public String getUiCode() {
return uiCode;
}
}
And you can use it like a normal bean:
#Service
public class RandomService {
#Inject
UiCodeDto uiCodeDto;
public void handle() {
System.out.println(uiCodeDto.getUiCode());
}
}
I want to implement high level resource filtering on URLs with a servlet filter and lower level action filtering on methods with an interceptor but my interceptor does not get fired on the EJB method called from the servlet filter.
Interceptor annotation Interface:
#Inherited
#InterceptorBinding
#Retention (RUNTIME)
#Target({TYPE, METHOD})
public #interface Permit {
#Nonbinding
String[] actions() default "N/A";
}
The Interceptor:
#Permit
#Interceptor
public class PermitInterceptor {
#AroundInvoke
public Object verifyPermission(InvocationContext context) throws Exception {
Method method = context.getMethod();
if(method.isAnnotationPresent(Permit.class)) {
Permit permitAnnotation = method.getAnnotation(Permit.class);
List<String> permittedActions = Arrays.asList(permitAnnotation.actions());
List<String> userActions = SecurityContextHandler.getActiveUser().getActions();
if(!Collections.disjoint(permittedActions, userActions)){
return context.proceed();
}
}
throw new PermissionException("You do NOT have the required permissions to perform this action");
}
}
Servlet Filter:
#WebFilter(urlPatterns = {"/*"})
public class AccessFilter implements Filter {
#EJB
private RulesBean rules;
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
try{
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String url = request.getRequestURI();
if(rules.isAllowed(url)){
chain.doFilter(request, response);
}else{
//handle as necessary
}
}catch(Exception ex){
//handle as necessary
}
}
}
And finally here's what the EJB RulesBean that I want to use to manage routing/interception for all my servlets looks like;
Rules:
#Stateless
#LocalBean
public class RulesBean {
private static final String CUSTOMERS = "/customers"
public boolean isAllowed(String url) throws PermissionException {
switch(url){
case CUSTOMERS: return canViewAllCustomers();
default: return true;
}
}
/*This should trigger PermitInterceptor before entering method and
should throw my custom PermissionException if permission fails*/
#Permit(actions={"ViewCustomers"})
private boolean canViewAllCustomers(){
return true;
}
...
//Other tests carried out here ...
}
Unfortunately PermitInterceptor doesn't get called before entering canViewAllCustomers() method.
Amazingly however, PermitInterceptor gets triggered when canViewAllCustomers() is made public and called directly as rules.canViewAllCustomers() instead of through the helper method rules.isAllowed(String url). But this isn't helpful in my case, as it gives me no single entry point for my URL checks which essentially means I have to do all the checks in the Servlet Filter.
QUESTION: Please can anybody shed more light on the reason why things are occurring in this manner?... and suggestions about the best way to implement this scenario is highly welcome. Thanks.
NOTE: (To give more perspective)
You may be wondering why I want to do this OR more specifically why the RuleBean even exists at all... The reason is simply because a good number of my Servlets aren't doing much except route response to a view that triggers a server-side DataTables ajax call which populates the tables, hence I really need to ensure that the request for the view doesn't even get through to the if...else condition that fetches the view unless the permission checks in the interceptor is satisfied.
See sample servlet below;
#WebServlet ("/customers/*")
public class CustomerServlet extends VelocityViewServlet {
private static final String CUSTOMERS = "/customers"
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String uri = request.getRequestURI();
if(uri.equals(CUSTOMERS)){
String view = Path.getTemplate("/customers/index.vm");
request.setAttribute("headerTitle", "Customers");
request.getRequestDispatcher(view).forward(request, response);
}else if(...){
...
}
}
}
You invoke canViewAllCustomers() within isAllowed() directly, which gives the Application Server no chance to intercept the call.
Interception works with proxy classes. When you inject the EJB into your servlet, like you did with:
#EJB
private RulesBean rules;
what actually gets injected is not an EJB instance, but a proxy class, that the application server created at runtime (you can see this with the debugger). Invocations on that proxy class will be intercepted for transactions, custom interceptors, etc. and then delegated to the actual class instance.
So what you need to do is either put canViewAllCustomers() into a new EJB, that you can let the application server inject into your RulesBean class,
or you can retrieve a reference of your RulesBean class from inside isAllowed() like so:
#Stateless
#LocalBean
public class RulesBean {
private static final String CUSTOMERS = "/customers"
#Resource
SessionContext ctx;
public boolean isAllowed(String url) throws PermissionException {
switch(url){
case CUSTOMERS: return self().canViewAllCustomers();
default: return true;
}
}
private RulesBean self() {
return ctx.getBusinessObject(RulesBean.class);
}
/*This should trigger PermitInterceptor before entering method and
should throw my custom PermissionException if permission fails*/
#Permit(actions={"ViewCustomers"})
public boolean canViewAllCustomers(){
return true;
}
}