How to implement Session Tracking in spring MVC? - spring

I am very new to spring mvc, I have to develop a web application based on session tracking and my application is annotation based. In my web app I have route each page based on the username and role existence in session. Initially I have been using HttpSession as parameter to controller method, but it is very difficult to check each and every request. I know there are many application level security ways in spring, but I really couldn't understand how to use them. Please suggest me some solutions, For all help thanks in advance.
After updating with interceptors:
Controller class
// Method to showLogin page to user
#RequestMapping(value = "user")
public ModelAndView showLoginToUser(#ModelAttribute("VMFE") VmFeUser VMFE,HttpSession session) {
System.out.println("#C====>showLoginToUser()===> ");
ModelAndView view = new ModelAndView();
//session.setAttribute("user_name", "no_user");
try {
view.setViewName("login");
} catch (Exception e) {
e.printStackTrace();
}
return view;
}
Interceptor
public class HelloWorldInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle (HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
RequestMapping rm = ((HandlerMethod) handler).getMethodAnnotation(
RequestMapping.class);
boolean alreadyLoggedIn = request.getSession()
.getAttribute("user_name") != null;
boolean loginPageRequested = rm != null && rm.value().length > 0
&& "login".equals(rm.value()[0]);
if (alreadyLoggedIn && loginPageRequested) {
//response.sendRedirect(request.getContextPath() + "/app/main-age");
return false;
} else if (!alreadyLoggedIn && !loginPageRequested) {
System.out.println("REDIRECTING===");
response.sendRedirect(request.getContextPath() + "/user");
return false;
}
return true;
}
}

Using spring security you can implement session tracking and apply filters to validate requests. Spring security is very easy to implement. Kindly follow spring security tutorial click here.
You can also check my git repo for implementation click here. It's a angular spring boot application and i have used spring security and JWT for authentication and authorization.
Hope it helps you thanks.

Related

Start a session from a given id in spring

How can I create a session in a spring mvc application from a given ID instead of a generated one?
I want to fixate the session.
The fixation will be started by a trusted ui service. This trusted service forwards all requests. Thus the fixation can't begin in browser. It is not intended to do it without this ui service.
Providing a HttpSessionIdResolver bean does not work, since it only changes the location in HTTP response. Eg Session ID in HTTP header after authorization.
Are there any solutions without creating a shadow session management?
Session is required for keycloak integration. Guess it's not possible to use keycloak in stateless mode.
Thanks in advance
It is possible to fixate a session with spring-session.
#Configuration
#EnableSpringHttpSession
public class SessionConfig {
#Bean
public HttpSessionIdResolver httpSessionIdResolver() {
return new HttpSessionIdResolver() {
public List<String> resolveSessionIds(HttpServletRequest request) {
final var sessionId = request.getHeader("X-SessionId");
request.setAttribute(SessionConfig.class.getName() + "SessionIdAttr", sessionId);
return List.of(sessionId);
}
public void setSessionId(HttpServletRequest request, HttpServletResponse response, String sessionId) {}
public void expireSession(HttpServletRequest request, HttpServletResponse response) {}
};
}
#Bean
public SessionRepository<MapSession> sessionRepository() {
return new MapSessionRepository(new HashMap<>()) {
#Override
public MapSession createSession() {
var sessionId =
(String)RequestContextHolder
.currentRequestAttributes()
.getAttribute(SessionConfig.class.getName()+"SessionIdAttr", 0);
final var session = super.createSession();
if (sessionId != null) {
session.setId(sessionId);
}
return session;
}
};
}
In #resolveSessionIds() you can read the ID and store for later use.
createSession() is called when no session has been found and a new one is required. Here you can create the session with previously remembered ID in MapSession#setId(String).
Even if is possible, IMO it is not a good idea. There might be other architectural solutions/problems.

How to use WebSession in Spring WebFlux to persist data?

I am trying to develop web application using Spring WebFlux5.0.1 and Spring boot v2.0 M6 version. Requirement is to store objects in session and use it in subsequent pages/controllers.
Controller
#Controller
public class TestController {
#RequestMapping("/")
public Mono<String> testSession(Model model,ServerWebExchange swe){
Mono<WebSession> session = swe.getSession();
System.out.println("In testSession "+session);
model.addAttribute("account", new Account());
return Mono.just("account");
}
}
I was able to get Websession object from ServerWebExchange but i dont see methods to set/get attributes
Need help to understand how to use WebSession object in reactive world
Is it what you want to do ?
swe.getSession().map(
session -> {
session.getAttribute("foo"); // GET
session.getAttributes().put("foo", "bar") // SET
}
);
The accepted solution is incomplete in my opinion since it doesn't show the whole controller method, here it is how it would be done:
#PostMapping("/login")
fun doLogin(#ModelAttribute credentials: Credentials, swe: ServerWebExchange): Mono<String> {
return swe.session.flatMap {
it.attributes["userId"] = credentials.userId
"redirect:/globalPosition".toMono()
}
}

Redirect using Spring boot interceptor

I have created a web app using spring boot and freemarker and implemented interceptor(HandlerInterceptorAdapter).
Inside the interceptor, when user is not logged then it will redirect to login page. This works fine. But the problem is that the controller is being executed first before redirecting to the login page.
My Interceptor Code:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
User userSession = (User) request.getSession().getAttribute("user");
if (userSession == null) {
response.sendRedirect("/login");
}
}
Controller class(after response.sendRedirect, this controller is still being excuted). Why? I'm stack in with this problem.
#RequestMapping("/home")
public String home(Model model, HttpServletRequest httpServletRequest) {
String returnPage = "home-admin";
User user = (User) httpServletRequest.getSession().getAttribute("user");
if(user != null){
String accessType = accessTypeRepository.getAccessType(user.getAccessId());
if(StrUtil.isEqual(accessType, AccessTypeConst.MANAGER.getType())){
returnPage = "home-manager";
}
}
return returnPage;
}
You should return false from your interceptor if you are done with execution.
Returns:
true if the execution chain should proceed with the next interceptor or the handler itself. Else, DispatcherServlet assumes that this interceptor has already dealt with the response itself.
http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/HandlerInterceptor.html
Change
if (userSession == null) {
response.sendRedirect("/login");
}
to
if (userSession == null) {
response.sendRedirect("/login");
return false;
}
In interceptor preHandle() function.
return false to let Spring framework assume that request has been handled by the spring interceptor itself and no further processing is needed.
return true to let Spring know to process the request through another spring interceptor or to send it to handler method (Your Controller Function) if there are no further spring interceptors.
So, In this case return false at end in interceptor preHandle function.
When i use return false, i take "Error: Exceeded maxRedirects. Probably stuck in a redirect loop http://localhost:8080/api/login"
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(true){
response.sendRedirect("/api/login");
return false;
}
return true;
}
for anyone who’s searching for the answer to the same question from #calisci
That’s probably cuz u r NOT excluding the redirect url from request
Try add this before redirect
if(!request.getRequestURL().toString().endswith("/put redirect url here")
Glad if help.

Invalidating session with CDI+JSF not working

I'm trying to implement a logout in my application, so I made this:
public String logout(){
try{
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext ex = facesContext .getExternalContext();
ex.invalidateSession();
return "success";
}catch(Exception e){
return "error";
}
}
But when I check if the user is logged, it says yes:
public class AuthenticateListener implements PhaseListener {
#Override
public void afterPhase(PhaseEvent event) {
AuthorizedUser authorized = (AuthorizedUser) Util.getHandler("authorized");
if (authorized.getUser() == null) {
System.out.println("Not Logged");
} else {
System.out.println("Logged");
}
}
#Override
public void beforePhase(PhaseEvent event) {
// TODO Auto-generated method stub
}
#Override
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
}
Am I missing something? Shouldn't I get a new instance of AuthorizedUser (sessionScoped) after invalidating my session?
EDIT: Adding the getHandler, if someone needs it ;)
public static Object getHandler(String handlerName) {
FacesContext facesContext = FacesContext.getCurrentInstance();
ELContext elContext = facesContext.getELContext();
ELResolver resolver = facesContext.getApplication().getELResolver();
Object uh = resolver.getValue(elContext, null, handlerName);
return uh;
}
The session is still available in the current request-response. It's not available anymore in the next request. You need to send a redirect after the invalidate so that the browser will be instructed to send a new request on the given URL.
return "success?faces-redirect=true";
Or if you're still using old fashioned navigation cases (the return values namely suggests that; it's strange to have a view with filename "success"), then add <redirect/> to the navigation case instead.
If that still doesn't work, then the bug is in how you're storing the user in session. For example, it's instead actually been stored in the application scope which may happen when you mix CDI #Named with JSF #SessionScoped, or when you assigned the logged-in user as a static variable instead of an instance variable.
See also:
How to invalidate session in JSF 2.0?
Performing user authentication in Java EE / JSF using j_security_check
Use this piece of code inside logout method:
HttpSession oldsession = (HttpSession) FacesContext
.getCurrentInstance().getExternalContext().getSession(false);
oldsession.invalidate();
This will work. Let me know please if it was helpful for you.

how to implement Spring Security SpEL isFullyAuthenticated() programmatically in a controller?

I have a controller where I would like to check if a user in Fully Authenticated similar to what Spring Security isFullyAuthenticated() expression provides. How do I do that?
Solution I am using based on Tomasz Nurkiewicz answer below and just stealing the implementation from org.springframework.security.access.expression.SecurityExpressionRoot
public class SpringSecurityUtils {
private static final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
public static boolean isFullyAuthenticated()
{
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return !trustResolver.isAnonymous(authentication) && !trustResolver.isRememberMe(authentication);
}
}
Looking at the source code of SecurityExpressionRoot and AuthenticationTrustResolverImpl looks like you can use the following condition:
public boolean isFullyAuthenticated(Authentication auth) {
return !(auth instanceof AnonymousAuthenticationToken ||
auth instanceof RememberMeAuthenticationToken);
}
Where you obtain authentication e.g. using:
SecurityContextHolder.getContext().getAuthentication()
You can call the isUserInRole() method of SecurityContextHolderAwareRequestWrapper or the HttpServletRequest using the string IS_AUTHENTICATED_FULLY:
request.isUserInRole("IS_AUTHENTICATED_FULLY");

Resources