How to know that a session is expired? - spring

I set values to the session object in the method of a controller after success of login :
#RequestMapping(value = "/", method = RequestMethod.POST)
public ModelAndView processLogin(Model model, HttpServletRequest request, HttpSession session, #RequestParam String login, #RequestParam String pwd) {
if ( utilisateurDao.verifierLoginUser(login) ) {
if ( utilisateurDao.verifierUser(login, pwd) ) {
HashMap<String, String> criteres = new HashMap<String, String>();
criteres.put("user_login", login);
criteres.put("user_passwd", pwd);
List<Utilisateur> users = utilisateurDao.lireParCritere(criteres);
session.setAttribute("user_code", ((Utilisateur)users.get(0)).getUser_code());
session.setAttribute("menu", menuDao.afficherMenuParUtilisateur((Integer)session.getAttribute("user_code"), env, request, session));
criteres.clear();
users.clear();
criteres.put("user_code", String.valueOf(session.getAttribute("user_code")));
users = utilisateurDao.lireParCritere(criteres);
session.setAttribute("user_names", ((Utilisateur)users.get(0)).getNoms());
session.setAttribute("logout_menu", env.getProperty("menu.logout"));
return new ModelAndView("redirect:/accueil");
} else {
ModelAndView modelViewLogin = new ModelAndView("redirect:/");
modelViewLogin.addObject("e", "p").addObject("l", login);
return modelViewLogin;
}
} else {
ModelAndView modelViewLogin = new ModelAndView("redirect:/");
modelViewLogin.addObject("e", "l");
return modelViewLogin;
}
}
Then I opened the app inactive for some minutes. After that I went to the "accueil" path. Then the menu was not shown anymore ! The menu was got from session. So how to know that the session is expired and where is the convenient place to test it ?

By default in spring security session is stored in SessionRegistry.
By using SecurityContext you can get this info in your controller code.
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
If you want to be notified when session has expired or person logged out you can always register listener on SessionDestroyedEvent- documentation.
example:
#Component
public class LogoutListener implements ApplicationListener<SessionDestroyedEvent> {
#Override
public void onApplicationEvent(SessionDestroyedEvent event) {
//do your stuff here
}
}
Its also worth to refer to spring docs for that subject.

You can make a Interceptor,
#Component
public class RequestInterceptor extends HandlerInterceptorAdapter
In this interceptor you can control the HttpServletRequest
and check if obj exists into them and then you can throw to a new SessionExpiredException and catch with #ExceptionMapper (https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc)
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
if (request.getSession().getAttribute("user")==null) {
throw new SessionExpiredException();
}
return true;
}

I check like below. I think it might be help.
public boolean isUserLoggedIn(HttpServletRequest request) throws IOException {
SecurityContext securityContext = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
if(securityContext != null) {
Authentication authentication = securityContext.getAuthentication();
if(null != authentication && authentication.isAuthenticated() != true)
return false;
else
return true;
} else {
return false;
}
}

Related

SecurityContextHolder authentication object not available to subsequent requests from the client

Inside getUserObject() method we are not able to get Authentication object. It's available for 1st request only. But its setting to null for subsequent requests from client. So please help me to configure it properly so that its available for all the requests calls.
I am not sure how to configure inside configure method in AuthConfig.java so that authentication object would be available for all the requests chain
AuthConfig.java:
#Configuration
#EnableWebSecurity
public class AuthConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/callback", "/", "/auth0/authorize", "/resources/**", "/public/**", "/static/**",
"/login.do", "/logout.do", "/thankYou.do", "/customerEngagement.do",
"/oldCustomerEngagement.do", "/registerNew.do", "/forgotPassword.do", "/checkMongoService.do",
"/reset.do", "/rlaLogin.do", "/fnfrefer.do", "/thankYouLeadAggregator.do", "/referral")
.permitAll()
.anyRequest().authenticated().and().
logout()
.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler());
}
------------------------------------------------------------------------------
AuthController.java:
#RequestMapping(value = "/callback", method = RequestMethod.GET)
public void callback(HttpServletRequest request, HttpServletResponse response)
throws IOException, IdentityVerificationException {
try {
Tokens tokens = authenticationController.handle(request, response);
DecodedJWT jwt = JWT.decode(tokens.getIdToken());
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
Authentication auth = new UsernamePasswordAuthenticationToken(jwt.getSubject(), jwt.getToken(), grantedAuths);
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(auth);
response.sendRedirect(config.getContextPath(request) + "/loancenter/home.do");
} catch (Exception e) {
LOG.info("callback page error");
response.sendRedirect(config.getContextPath(request) + "/loancenter");
}
}
--------------------------------------------------------------------------------
HomeController.java:
#Controller
public class DefaultController implements InitializingBean {
#RequestMapping(value = "home.do")
public ModelAndView showCustomerPage(HttpServletRequest req, HttpServletResponse res, Model model) {
ModelAndView mav = new ModelAndView();
try {
User user = getUserObject(req);
if(user==null) {
LOG.info("User not found in session");
mav.setViewName(JspLookup.LOGIN);
return mav;
}
} catch (Exception e) {
LOG.error("Exception in Home page ", e);
}
return mav;
}
protected User getUserObject(HttpServletRequest request) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
LOG.info("authentication::{}", authentication);
User user = null;
if (authentication == null) {
return user;
}
if (authentication.getPrincipal() instanceof User) {
user = (User) authentication.getPrincipal();
LOG.info("User already authenticated and logging :{}", user.getEmailId());
sendUserLoginEmailToLO(user);
} else {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
DecodedJWT jwt = JWT.decode(token.getCredentials().toString());
user = userProfileDao.findByUserEmail(jwt.getClaims().get("email").asString());
if (user != null) {
LOG.info("First time authentication:{}", user.getEmailId());
boolean auth0EmailVerified = jwt.getClaims().get("email_verified").asBoolean();
LOG.info("First time authentication email verified flag from auth0:{}", auth0EmailVerified);
LOG.info("First time authentication email verified flag from nlc:{}", user.getEmailVerified());
if (BooleanUtils.isFalse(user.getEmailVerified()) && auth0EmailVerified) {
LOG.info("Email is verified in Auth0, updating email_verified flag to true in DB for userId: {}",
user.getId());
userProfileDao.verifyEmail(user.getId());
LOG.info("First time authentication updated email verified flag in nlc db:{}", user.getEmailId());
}
if (user.getNewEmailVerified() != null && BooleanUtils.isFalse(user.getNewEmailVerified())) {
LOG.info("The user is verifying his email: set his verified to true");
userProfileDao.verifyNewEmail(user.getId());
}
Authentication auth = new UsernamePasswordAuthenticationToken(user, jwt.getToken(),
token.getAuthorities());
messageServiceHelper.checkIfUserFirstLogin(user);
LOG.info("Authentication provided for user : {}", user.getEmailId());
LOG.debug("Auth object constructed : {}", auth);
SecurityContextHolder.getContext().setAuthentication(auth);
HttpSession session = request.getSession(true);
session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
sendUserLoginEmailToLO(user);
}
}
return user;
}
}

Spring Security - Adding a custom filter for Authorization

we are implementing some api.
The authentification is done in the Front, and it send us a Bearer in the authorization Header.
We have created a method with validates the token (it calls some rest services xxx ) and another method with returns a list of roles that the user has .
Is it possible to create a filter that will do this verification ??
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
#Override
protected void doFilter(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
System.out.println("IN FILTER");
String header = req.getHeader("Authorization");
if (header == null || !header.startsWith("Bearer")) {
chain.doFilter(req, res);
return;
}
String token = request.getHeader(HEADER_STRING);
if (validateMyToken(token)) {
List<String> mygroups = getMyGroups(token);
} else {
throw new InvalidTokenException("Token is not Ok :(");
}
#HOW DO I PUT MY GROUPS, SOMEWHERE FOR VERIFICATION ???
}
}
And use it in my controller :
#RestController
#RequestMapping(value = "/applications/")
public class TestController {
#PreAuthorize("hasRole('ROLE_ADMIN')")
#GetMapping(value = "/test")
public String getContacts() {
System.out.println("IN CONTROLLER");
return "toto";
}
}
I know I have to do something in WebSecurity, but don't know how
You need to create a custom UserDetails implementation https://docs.spring.io/spring-security/site/docs/4.2.6.RELEASE/apidocs/org/springframework/security/core/userdetails/UserDetails.html . After that create a proper Authorities collection according to your List<String> mygroups = getMyGroups(token);. If you want to secure methods with roles just mark them with #Secured / #RolesAllowed or just like you wrote with #PreAuthorize("hasRole('ROLE_ADMIN')")

Spring Security 5 Stateless OAuth2 Login - how to implement cookies based AuthorizationRequestRepository

I'm trying to have Google/Facebook login using Spring Security 5 OAuth2 login feature. But the problem I'm facing is that I'm coding a stateless API, whereas Spring security 5 uses HttpSessionOAuth2AuthorizationRequestRepository to store authorization requests, which uses session. So, I thought not to use that and code a cookie based implementation, which looks as below:
public class HttpCookieOAuth2AuthorizationRequestRepository implements AuthorizationRequestRepository<OAuth2AuthorizationRequest> {
private static final String COOKIE_NAME = "some-name";
#Override
public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) {
Assert.notNull(request, "request cannot be null");
return fetchCookie(request)
.map(this::toOAuth2AuthorizationRequest)
.orElse(null);
}
#Override
public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request,
HttpServletResponse response) {
Assert.notNull(request, "request cannot be null");
Assert.notNull(response, "response cannot be null");
if (authorizationRequest == null) {
deleteCookie(request, response);
return;
}
Cookie cookie = new Cookie(COOKIE_NAME, fromAuthorizationRequest(authorizationRequest));
cookie.setPath("/");
cookie.setHttpOnly(true);
response.addCookie(cookie);
}
private String fromAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest) {
return Base64.getUrlEncoder().encodeToString(
SerializationUtils.serialize(authorizationRequest));
}
private void deleteCookie(HttpServletRequest request, HttpServletResponse response) {
fetchCookie(request).ifPresent(cookie -> {
cookie.setValue("");
cookie.setPath("/");
cookie.setMaxAge(0);
response.addCookie(cookie);
});
}
#Override
public OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request) {
// Question: How to remove the cookie, because we don't have access to response object here.
return loadAuthorizationRequest(request);
}
private Optional<Cookie> fetchCookie(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0)
for (int i = 0; i < cookies.length; i++)
if (cookies[i].getName().equals(COOKIE_NAME))
return Optional.of(cookies[i]);
return Optional.empty();
}
private OAuth2AuthorizationRequest toOAuth2AuthorizationRequest(Cookie cookie) {
return SerializationUtils.deserialize(
Base64.getUrlDecoder().decode(cookie.getValue()));
}
}
The above basically stores the data in a cookie instead of session. I've a couple of questions:
How exactly to code the removeAuthorizationRequest method above? I wanted the cookie removed there, but we don't have access to the response object.
Does the above (cookie based) approach look okay? E.g. any security issues?
Update: Have created an issue at https://github.com/spring-projects/spring-security/issues/5313 . Until that's addressed, here is a workaround I came up with: https://www.naturalprogrammer.com/blog/1681261/spring-security-5-oauth2-login-signup-stateless-restful-web-services

Spring MVC redirect failed

I wanted to do a login page with Spring MVC + Mybatis, the controllers are as below:
#Controller
public class LoginController {
private final UserService userService;
#Autowired
public LoginController(#Qualifier("userService") UserService userService) {
this.userService = userService;
}
#RequestMapping(value = "/login")
public ModelAndView Login(String username, String password,
ModelAndView mv, HttpSession session) {
User user = userService.login(username, password);
// **This is where problems occured**
if (user != null) {
session.setAttribute("user", user);
mv.setView(new RedirectView("/main"));
} else {
mv.addObject("message","Login failed.");
mv.setViewName("loginForm");
}
return mv; // with correct value, but responses 404 error
}
#RequestMapping(value = "/loginForm")
public String LoginForm() {
return "loginForm";
}
#RequestMapping(value = "/main")
public String Main(Model model) {
return "main";
}
}
I have two jsp files:
/WEB-INF/jsp/loginForm.jsp
/WEB-INF/jsp/main.jsp
Whether I submit the login form correctly or not, there is a 404 error:
HTTP Status 404 - /WEB-INF/jsp/login.jsp
Running in debug mode, the mv was returned with "/main" correctly, so it confused me.
Code below works, but i cant add error message to view anymore.
#RequestMapping(value = "/login")
public String Login(String username, String password, HttpSession session) {
User user = userService.login(username, password);
if (user != null) {
session.setAttribute("user", user);
return "redirect:/main";
} else {
session.setAttribute("message","Login failed");
return "redirect:/loginForm";
}
}
i have face same issue but it's help for me
ModelAndView model = new ModelAndView();
if (user != null) {
session.setAttribute("user", user);
model = new ModelAndView("/main");
} else {
mv.addObject("message","Login failed.");
model = new ModelAndView("/loginForm");
}
return model;

Dynamic post logout redirection url based on user?

i am wondering how i could implement a post logout redirection using a custom logout handler. I have implemented a CustomLogoutSuccessHandler but i have no way off access http session data that has previous been set by the user who has logged in. The data is alway empty...
class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
private static final ThreadLocal<Authentication> AUTH_HOLDER = new ThreadLocal<Authentication>()
void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
AUTH_HOLDER.set authentication
// reading session variable...
request.session?.variable // but this is always empty
try {
super.handle(request, response, authentication)
}
finally {
AUTH_HOLDER.remove()
}
}
#Override
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {
Authentication auth = AUTH_HOLDER.get()
String url = super.determineTargetUrl(request, response)
// do something with the url based on session data..
url
}
}
I do not know if there is any easy way to do this but came up with the below solution.
All you have to do is set the setTargetUrlParameter in your LogoutSuccessHandler. For that I made use of the implementation of HttpServletRequestWrapper written by Lincoln Baxter, III here for adding a parameter to the current request. Here is the relevant code.
public class PrettyFacesWrappedRequest extends HttpServletRequestWrapper
{
private final Map<String, String[]> modifiableParameters;
private Map<String, String[]> allParameters = null;
/**
* Create a new request wrapper that will merge additional parameters into
* the request object without prematurely reading parameters from the
* original request.
*
* #param request
* #param additionalParams
*/
public PrettyFacesWrappedRequest(final HttpServletRequest request,
final Map<String, String[]> additionalParams)
{
super(request);
modifiableParameters = new TreeMap<String, String[]>();
modifiableParameters.putAll(additionalParams);
}
#Override
public String getParameter(final String name)
{
String[] strings = getParameterMap().get(name);
if (strings != null)
{
return strings[0];
}
return super.getParameter(name);
}
#Override
public Map<String, String[]> getParameterMap()
{
if (allParameters == null)
{
allParameters = new TreeMap<String, String[]>();
allParameters.putAll(super.getParameterMap());
allParameters.putAll(modifiableParameters);
}
//Return an unmodifiable collection because we need to uphold the interface contract.
return Collections.unmodifiableMap(allParameters);
}
#Override
public Enumeration<String> getParameterNames()
{
return Collections.enumeration(getParameterMap().keySet());
}
#Override
public String[] getParameterValues(final String name)
{
return getParameterMap().get(name);
}
}
and then in the CustomLogoutSuccessHandler, I add this targetUrl as the parameter like this:
#Component
public class MyCustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
HttpServletRequest wrappedRequest = request;
if (authentication != null) {
//do something with the Principal and add the corresponding url
Map<String, String[]> extraParams = new TreeMap<String, String[]>();
extraParams.put("targetUrl", new String[] {"/target.xhtml"});
wrappedRequest = new PrettyFacesWrappedRequest(request, extraParams);
setTargetUrlParameter("targetUrl");
}
setDefaultTargetUrl("/general/main.xhtml");
super.onLogoutSuccess(wrappedRequest, response, authentication);
}
}
and the relevant change to the applicationContext:
<http>
<logout logout-url="/j_spring_security_logout"
success-handler-ref="myCustomLogoutSuccessHandler"
invalidate-session="true"/>
</http>
<beans:bean id="myCustomLogoutSuccessHandler" class="com.examples.MyCustomLogoutSuccessHandler"/>

Resources