The spring app has a session bean, which contains certain data. That data is loaded from DB at init request to the controller, which is always a first request client calls at start up. That data is used for other requests by same user. Now, everything works fine on its own. However after trying to integrate the app into the system using zuul (which as far as I understand in this context simply redirects request from one url into another), it broke. Whenever a method is called after the init, the session bean's data is null.
Here is a snippet from service class:
#Autowired
TaskCache cache;
#Override
public void initUserSession() {
List<Task> data = loadTasks();
cache.setTasks(data);
LinearFilterStack<Task> fs = createFilterStack(data);
cache.setFilterStack(fs);
System.out.println(cache.hashCode()); //hashcode stays same
System.out.println(cache.getFilterStack() == null) //false
}
#Override
public List<Task> getTasks(Sort sort) {
System.out.println(cache.hashCode()); //hashcode stays same
System.out.println(cache.getFilterStack() == null) //true
LinearFilterStack<Task> fs = cache.getFilterStack();
List<Task> tasks = fs.filter(cache.getTasks()); //Obviously NPE
sortTasks(tasks, sort);
return tasks;
}
#Component
#Scope(value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.TARGET_CLASS)
public class TaskCache { ... }
And again, this only happens through zuul. I.e. if I use localhost:30022/rest/... it works, if I use localhost:8080/app/tasks/rest/... (which zuul redirects to localhost:30022/rest/...) I get NPE, because the cache bean loses its data after init request.
That could be caused by default behavior of Zuul that prevents passsing of cookie related headers.
The following is that default configuration of Zuul and it doesn't allow pass below headers to your downstream API servers.
zuul.sensitiveHeaders= Authorization,Cookie,Set-Cookie
So please try to define below properties. It will allow all your cookie related header to be passed to your API servers.
zuul.sensitiveHeaders= Authorization
You can find more details in section "Cookies and Sensitive Headers" of this document
Related
What's the expected way to set org.apache.tomcat.websocket.BLOCKING_SEND_TIMEOUT to increase a tomcat websocket timeout? Tomcat documentation states the following:
This may be changed by setting the property
org.apache.tomcat.websocket.BLOCKING_SEND_TIMEOUT in the user
properties collection attached to the WebSocket session.
The WebSocketSession I see available in the TextWebSocketHandler's afterConnectionEstablished method doesn't apear to have user properties. So, I assume that's not what the documentation means. In looking at TomcatRequestUpgradeStrategy, it appears to me that it never looks at an endpoint user properties. It also doesn't appear to me that you can overwrite TomcatRequestUpgradeStrategy since AbstractHandshakeHandler has a hardcoded class name for TomcatRequestUpgradeStrategy.
Please help.
org.apache.tomcat.websocket.BLOCKING_SEND_TIMEOUT is an user property you need to set on WebSocket API's Session, not the Spring abstractions of this interface.
You can configure it in the afterConnectionEstablished method by casting the Spring WebSocketSession to NativeWebSocketSession and retrieving the underlying WebSocket API session:
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
if (session instanceof NativeWebSocketSession) {
final Session nativeSession = ((NativeWebSocketSession) session).getNativeSession(Session.class);
if (nativeSession != null) {
nativeSession.getUserProperties()
.put("org.apache.tomcat.websocket.BLOCKING_SEND_TIMEOUT", 60_000L);
}
}
}
I'm developing a web application using springboot for the backend serving rest API and Angular7 for the frontend.
my application takes so much time to load because it has to perform a lot of processing on the server, so I decided to improve the performance by storing the cached data in order to load the page first and eventually update the data when processing ends.
This works but I'm having a problem:
When I update data in Angular these are normally saved in my database but if I update the page I don't see the changes because the browser continues to access the old cached data rather than getting the new ones modified.
Is there a way to invalidate certain data in the browser cache when they are modified?
Springboot:
My rest controller endpoint are similar to that:
#GetMapping(value = "/users")
public Iterable<User> getAllUsers(HttpServletResponse response) {
response.setHeader("Cache-Control", "max-age=3600");
return this.userService.findAll());
}
My service:
#Cacheable("users")
public Iterable<User> findAll() {
return this.userRepository.findAll();
}
My angular service:
getUsers(): Observable<User[]> {
return this.http.get<User[]>(`<ip>' + '/users');
}
I can see you're caching on the server side, you can evict cache by calling method annotated with #CacheEvict with the key you want to evict:
#CacheEvict(value = "users", allEntries = true)
Or you can do this programmatically by using CacheManager:
#Autowired
CacheManager cacheManager;
public void evictSingleCacheValue(String cacheName, String cacheKey) {
cacheManager.getCache(cacheName).evict(cacheKey);
}
You can check if there is any changes in your server side Time-based or Content-based controls by lightweight requests.
Time-based => your can use Last=Modified in the header
Content-based => you can use Etag
please check these two links;
https://devcenter.heroku.com/articles/increasing-application-performance-with-http-cache-headers
https://www.logicbig.com/quick-info/web/last-modified-and-if-modified-since.html
I'm new to Atmosphere framework & websockets.
I'm developing a single page app, As a first step I want to implement a basic authentication using AngularJs(Client side) and Atmosphere websockets(server side).
For authentication, in the beginning, I have used normal servlets and saved the User bean in HttpSession. But later I understood, I can't access the session attributes in the class(websocket service)
#AtmosphereService(broadcaster = JerseyBroadcaster.class)
So, I have created another Login AtmosphereService, In which suspend method checks the user and if user exists, then stores UserBean in ConcurrentMap against uuid key using following method so that I can access in future.
public void createSession(AtmosphereResource resource) {
logger.debug("Create session ({})", resource.uuid());
ConcurrentMap<String, Object> session = new ConcurrentHashMap<String, Object>();
ConcurrentMap<String, Object> prevSession = sessionsByUuid.putIfAbsent(resource.uuid(), session);
if (prevSession != null) {
logger.warn("Session already exists ({})", resource.uuid());
}
}
So in the client side, As I have AngularJS and configured routes, when the user logged in, I store the user-key in a rootscope variable and when ever route changes it checks as follows.
$rootScope.$on("$routeChangeStart", function(event, next, current) {
aNext = next;
if (next.templateUrl != "./partials/login.html") {
if($rootScope.loggedUser !=null && $rootScope.loggedUser!=""){
$location.path(nextPage);
}else{
$rootScope.goLoginPage();
}
}
});
But now the problem is If user refresh the page it goes to login page, because the $rootScope.loggedUser will not contain any thing.
Before, when I used simple servlets for authentication I used a ajax call to the servlet to check the user exists in the session or not and if he exists I assigned the user-key in $rootScope.loggedUser and route used to change properly.
Now as I'm using AtmosphereService instead of normal servlets for authentication, how can I make a request to either a servlet(in which I should access AtmosphereResource) or should I write another AtmosphereService to get the stored UserBean?
I'm using Spring-MVC with Spring Security for my web application. It includes user registration pages and private user panel. I have it set up currently with the following URL patterns:
whatever/myapp/login user log in
whatever/myapp/register?step=1 start registration
whatever/myapp/account/** private area views (pages)
whatever/myapp/pending view shown while post-registration processes complete
whatever/myapp/blocked account blocked view
whatever/myapp/register/retry if registration failed, allow retry
Essentially, these URLs below should require user authentication, i.e. require log-in:
whatever/myapp/account/** (private area pages)
whatever/myapp/pending (this page has a timer set to redirect to /account/home)
whatever/myapp/register/retry
This is quite straightforward to achieve using Spring security. However, regardless of user authentication through Spring security, private area pages should be accessible or not, depending on user's current account status (stored in my DB).
More specifically: if a user tries to access anything in the private area (/account/**), he should be shown the appropriate view (redirected to appropriate page), according to the status. I have these statuses defined:
suspended - relates to pending view
enabled - allow full access
disabled - not relevant here
retry_allowed- relates to retry view
blocked - relates to account-blocked view
Currently, I have a MVC interceptor setup to /account/**, that checks user status, and redirects to appropriate pages, but somehow I get the sense that this is not really the ideal or appropriate solution here, since I'm facing strange behavior, like multiple controller invocation... and also I'm not quite certain when to return true / false within preHandle() method. Here's the code snippet from the interceptor:
#Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object arg2)
throws Exception {
IPanelUser pUser = (IPanelUser) SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
// check principal first and then load from DB
// "suspended" is initial status upon registration
if(pUser.getCustomer().getStatus() == CustomerStatus.Suspended.getCode()) {
// if suspended, load from DB and update status
Customer customer = this.customerService.getUserByUsername(pUser.getUsername());
if(customer != null)
pUser.getCustomer().setStatus(customer.getStatus());
// still suspended? redirect to pending
if(pUser.getCustomer().getStatus() == CustomerStatus.Suspended.getCode()) {
response.sendRedirect("../pending");
return false;
}
}
if(pUser.getCustomer().getStatus() == CustomerStatus.Blocked.getCode()) {
// redirect to blocked page
response.sendRedirect("../blocked");
SecurityContextHolder.clearContext();
return false;
}
if(pUser.getCustomer().getStatus() == CustomerStatus.AllowRetry.getCode()) {
// redirect to CC submission page
response.sendRedirect("../register/retry");
return false;
}
if(pUser.getCustomer().getStatus() == CustomerStatus.Enabled.getCode() ||
pUser.getCustomer().getStatus() == CustomerStatus.Disabled.getCode()) {
// do nothing
}
return true;
}
.
Is this a valid approach ? Any alternative suggestions ?
All options are valid, it depends on the level of abstraction you want.
In a Filter, you only have access to HttpServletRequest and HttpServletResponse objects, so you are very much coupled with the Servlet API. You also don't (directly) have access to all the great Spring functionality like returning a view to be rendered or a ResponseEntity.
In a HandlerInterceptor, it's again more of the same. You can do your redirection or request handling directly in the preHandle() where you don't have access to the ModelAndView or set a flag which you check in postHandle(). You would have access to the ModelAndView but not to some other Spring MVC functionality.
Spring Security is a good alternative, but I find it has a lot of configuration that I don't like too much.
One final alternative, that I like the most, is to use AOP (you can do this with Spring Security or Shiro as well). You create an annotation like #Private and you annotate your #Controller handler methods. You use AOP to advise these methods. The advice basically checks some session or request attribute for a flag (authorized or not). If you are allowed, you continue executing the handler method, if not, you throw an UnauthorizedException (or similar). You then also declare an #ExceptionHandler for that exception where you have pretty much complete control over how the response is generated: a ModelAndView (and related), a ResponseEntity, annotate the handler with #ResponseBody, write the response directly, etc. I feel like you have much more control, if you want it.
In a Play! controller, I can create an interceptor method that will process every request before it arrives to the appropriate action.
public class Admin extends Application {
#Before
static void checkAuthentification() {
if(session.get("user") == null) login();
// otherwise,
User loggedOnUser = User.find("byUsername", session.get("user"));
}
public static void index() {
// any way to access loggedOnUser ?
List<User> users = User.findAll();
render(users);
}
…
}
Is there a way to set a value in the interceptor and access it in the action? Sort of like request.setAttribute() in servlets?
You can use renderArgs parameter from Controller (see here) or you can store the value in the Cache (we can assume that as the value was added miliseconds ago, your value will be available while in the same request).
Interceptors and actions share the same request context (request, response, session, etc). As stated above, you may elect to use renderArgs, but keep in mind that these values will be available in your views, which may not be what you want. If you want to keep the state between your interceptor and actions, just use the request.args hash instead.